Merge branch 'release_2.4.0' into stable
2
.bowerrc
@ -1,3 +1,3 @@
|
||||
{
|
||||
"directory": "awx/ui/static/lib"
|
||||
"directory": "awx/ui/client/lib"
|
||||
}
|
||||
|
||||
14
.gitignore
vendored
@ -12,18 +12,19 @@ awx/public/media
|
||||
awx/public/static
|
||||
awx/ui/tests/test-results.xml
|
||||
awx/ui/static/js/awx.min.js
|
||||
awx/ui/static/js/local_config.js
|
||||
awx/ui/static/js/local_settings.json
|
||||
awx/ui/client/src/local_settings.json
|
||||
awx/ui/static/css/awx.min.css
|
||||
awx/main/fixtures
|
||||
awx/*.log
|
||||
tower/tower_warnings.log
|
||||
celerybeat-schedule
|
||||
awx/ui/static/docs
|
||||
awx/ui/dist
|
||||
awx/ui/static
|
||||
awx/ui/build_test
|
||||
|
||||
# Python & setuptools
|
||||
__pycache__
|
||||
build
|
||||
/build
|
||||
/deb-build
|
||||
/rpm-build
|
||||
/tar-build
|
||||
@ -37,12 +38,13 @@ build
|
||||
/Brocfile.js
|
||||
/bower.json
|
||||
/package.json
|
||||
/testem.yml
|
||||
node_modules/**
|
||||
/tmp
|
||||
npm-debug.log
|
||||
|
||||
# UI build debugging
|
||||
/DEBUG-*
|
||||
/DEBUG
|
||||
|
||||
# Testing
|
||||
.coverage
|
||||
@ -50,6 +52,8 @@ npm-debug.log
|
||||
coverage.xml
|
||||
htmlcov
|
||||
pep8.txt
|
||||
scratch
|
||||
testem.log
|
||||
|
||||
# Mac OS X
|
||||
*.DS_Store
|
||||
|
||||
@ -3,7 +3,7 @@ recursive-include awx/static *.ico
|
||||
recursive-include awx/templates *.html
|
||||
recursive-include awx/api/templates *.md *.html
|
||||
recursive-include awx/ui/templates *.html
|
||||
recursive-include awx/ui/dist *
|
||||
recursive-include awx/ui/static *
|
||||
recursive-include awx/playbooks *.yml
|
||||
recursive-include awx/lib/site-packages *
|
||||
recursive-include requirements *.txt
|
||||
@ -12,8 +12,10 @@ recursive-include docs/licenses *
|
||||
recursive-exclude awx devonly.py*
|
||||
recursive-exclude awx/api/tests *
|
||||
recursive-exclude awx/main/tests *
|
||||
recursive-exclude awx/ui/client *
|
||||
recursive-exclude awx/settings local_settings.py*
|
||||
include tools/scripts/request_tower_configuration.sh
|
||||
include tools/scripts/request_tower_configuration.ps1
|
||||
include tools/scripts/ansible-tower-service
|
||||
include tools/munin_monitors/*
|
||||
include tools/sosreport/*
|
||||
|
||||
158
Makefile
@ -2,12 +2,18 @@ PYTHON = python
|
||||
SITELIB=$(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
|
||||
OFFICIAL ?= no
|
||||
PACKER ?= packer
|
||||
PACKER_BUILD_OPTS ?= -var 'official=$(OFFICIAL)' -var 'aw_repo_url=$(AW_REPO_URL)'
|
||||
GRUNT ?= $(shell [ -t 0 ] && echo "grunt" || echo "grunt --no-color")
|
||||
BROCCOLI ?= ./node_modules/.bin/broccoli
|
||||
TESTEM ?= ./node_modules/.bin/testem
|
||||
TESTEM_DEBUG_BROWSER ?= Chrome
|
||||
BROCCOLI_BIN ?= ./node_modules/.bin/broccoli
|
||||
MOCHA_BIN ?= ./node_modules/.bin/mocha
|
||||
NODE ?= node
|
||||
NPM_BIN ?= npm
|
||||
DEPS_SCRIPT ?= packaging/bundle/deps.py
|
||||
AW_REPO_URL ?= "http://releases.ansible.com/ansible-tower"
|
||||
GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
CLIENT_TEST_DIR ?= build_test
|
||||
|
||||
# Determine appropriate shasum command
|
||||
UNAME_S := $(shell uname -s)
|
||||
@ -28,8 +34,10 @@ GIT_REMOTE_URL = $(shell git config --get remote.origin.url)
|
||||
BUILD = 0.git$(DATE)
|
||||
ifeq ($(OFFICIAL),yes)
|
||||
RELEASE ?= 1
|
||||
AW_REPO_URL ?= http://releases.ansible.com/ansible-tower
|
||||
else
|
||||
RELEASE ?= $(BUILD)
|
||||
AW_REPO_URL ?= http://jenkins.testing.ansible.com/ansible-tower_nightlies_RTYUIOPOIUYTYU/$(GIT_BRANCH)
|
||||
endif
|
||||
|
||||
# Allow AMI license customization
|
||||
@ -52,11 +60,9 @@ endif
|
||||
ifeq ($(OFFICIAL),yes)
|
||||
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)
|
||||
SDIST_TAR_NAME=$(NAME)-$(VERSION)
|
||||
PACKER_BUILD_OPTS=-var-file=vars-release.json
|
||||
else
|
||||
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)-$(RELEASE)
|
||||
SDIST_TAR_NAME=$(NAME)-$(VERSION)-$(RELEASE)
|
||||
PACKER_BUILD_OPTS=-var-file=vars-nightly.json
|
||||
endif
|
||||
SDIST_TAR_FILE=$(SDIST_TAR_NAME).tar.gz
|
||||
SETUP_TAR_FILE=$(SETUP_TAR_NAME).tar.gz
|
||||
@ -98,14 +104,15 @@ MOCK_BIN ?= mock
|
||||
MOCK_CFG ?=
|
||||
RPM_SPECDIR= packaging/rpm
|
||||
RPM_SPEC = $(RPM_SPECDIR)/$(NAME).spec
|
||||
# Provide a fallback value for RPM_DIST
|
||||
RPM_DIST ?= $(shell rpm --eval '%{?dist}' 2>/dev/null)
|
||||
# Provide a fallback value for RPM_DIST
|
||||
ifeq ($(RPM_DIST),)
|
||||
RPM_DIST = .el6
|
||||
RPM_DIST = .el6
|
||||
endif
|
||||
RPM_ARCH ?= $(shell rpm --eval '%{_arch}' 2>/dev/null)
|
||||
# Provide a fallback value for RPM_ARCH
|
||||
ifeq ($(RPM_ARCH),)
|
||||
RPM_ARCH = $(shell uname -m)
|
||||
RPM_ARCH = $(shell uname -m)
|
||||
endif
|
||||
RPM_NVR = $(NAME)-$(VERSION)-$(RELEASE)$(RPM_DIST)
|
||||
|
||||
@ -147,7 +154,7 @@ endif
|
||||
.PHONY: clean rebase push requirements requirements_dev requirements_jenkins \
|
||||
real-requirements real-requirements_dev real-requirements_jenkins \
|
||||
develop refresh adduser syncdb migrate dbchange dbshell runserver celeryd \
|
||||
receiver test test_coverage coverage_html ui_analysis_report test_ui test_jenkins dev_build \
|
||||
receiver test test_coverage coverage_html ui_analysis_report test_jenkins dev_build \
|
||||
release_build release_clean sdist rpmtar mock-rpm mock-srpm rpm-sign \
|
||||
devjs minjs testjs testjs_ci node-tests browser-tests jshint ngdocs sync_ui \
|
||||
deb deb-src debian debsign pbuilder reprepro setup_tarball \
|
||||
@ -173,17 +180,20 @@ clean-grunt:
|
||||
|
||||
# Remove UI build files
|
||||
clean-ui:
|
||||
rm -rf awx/ui/static/dist
|
||||
rm -rf DEBUG
|
||||
rm -rf awx/ui/build_test
|
||||
rm -rf awx/ui/static/
|
||||
rm -rf awx/ui/dist
|
||||
rm -rf awx/ui/static/docs
|
||||
|
||||
# Remove packer artifacts
|
||||
clean-packer:
|
||||
rm -rf packer_cache
|
||||
rm -rf packaging/packer/packer_cache
|
||||
rm -rf packaging/packer/output-virtualbox-iso/
|
||||
rm -rf packaging/packer/output-vmware-iso
|
||||
rm -f packaging/packer/ansible-tower-*.box
|
||||
rm -rf packaging/packer/ansible-tower*-ova
|
||||
rm -rf packaging/packer/ansible-tower*-vmx
|
||||
rm -f Vagrantfile
|
||||
|
||||
clean-bundle:
|
||||
@ -192,7 +202,10 @@ clean-bundle:
|
||||
# Remove temporary build files, compiled Python files.
|
||||
clean: clean-rpm clean-deb clean-grunt clean-ui clean-tar clean-packer clean-bundle
|
||||
rm -rf awx/lib/site-packages
|
||||
rm -rf awx/lib/.deps_built
|
||||
rm -rf dist/*
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
rm -rf build $(NAME)-$(VERSION) *.egg-info
|
||||
find . -type f -regex ".*\.py[co]$$" -delete
|
||||
|
||||
@ -295,6 +308,11 @@ server: server_noattach
|
||||
servercc: server_noattach
|
||||
tmux -2 -CC attach-session -t tower
|
||||
|
||||
# Alternate approach to tmux to run all development tasks specified in
|
||||
# Procfile. https://youtu.be/OPMgaibszjk
|
||||
honcho:
|
||||
honcho start
|
||||
|
||||
# Run the built-in development webserver (by default on http://localhost:8013).
|
||||
runserver:
|
||||
$(PYTHON) manage.py runserver
|
||||
@ -345,17 +363,6 @@ test_coverage:
|
||||
coverage_html:
|
||||
coverage html
|
||||
|
||||
ui_analysis_report: reports/ui_code node_modules Gruntfile.js
|
||||
$(GRUNT) plato:report
|
||||
|
||||
reports/ui_code: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
||||
rm -rf reports/ui_code
|
||||
$(BROCCOLI) build reports/ui_code -- --no-concat --no-tests --no-styles --no-sourcemaps
|
||||
|
||||
# Run UI unit tests
|
||||
test_ui: node_modules minjs_ci Gruntfile.js
|
||||
$(GRUNT) karma:ci
|
||||
|
||||
# Run API unit tests across multiple Python/Django versions with Tox.
|
||||
test_tox:
|
||||
tox -v
|
||||
@ -364,44 +371,90 @@ test_tox:
|
||||
test_jenkins:
|
||||
$(PYTHON) manage.py jenkins -v2 --enable-coverage --project-apps-tests
|
||||
|
||||
Gruntfile.js: packaging/grunt/Gruntfile.js
|
||||
# UI TASKS
|
||||
# --------------------------------------
|
||||
|
||||
Gruntfile.js: packaging/node/Gruntfile.js
|
||||
cp $< $@
|
||||
|
||||
Brocfile.js: packaging/grunt/Brocfile.js
|
||||
Brocfile.js: packaging/node/Brocfile.js
|
||||
cp $< $@
|
||||
|
||||
bower.json: packaging/grunt/bower.json
|
||||
bower.json: packaging/node/bower.json
|
||||
cp $< $@
|
||||
|
||||
package.json: packaging/grunt/package.template
|
||||
package.json: packaging/node/package.template
|
||||
sed -e 's#%NAME%#$(NAME)#;s#%VERSION%#$(VERSION)#;s#%GIT_REMOTE_URL%#$(GIT_REMOTE_URL)#;' $< > $@
|
||||
|
||||
sync_ui: node_modules Brocfile.js
|
||||
$(NODE) tools/ui/timepiece.js awx/ui/dist -- --debug
|
||||
testem.yml: packaging/node/testem.yml
|
||||
cp $< $@
|
||||
|
||||
# Update local npm install
|
||||
node_modules: package.json
|
||||
$(NPM_BIN) install
|
||||
touch $@
|
||||
|
||||
devjs: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
||||
$(BROCCOLI) build awx/ui/dist -- --debug
|
||||
awx/ui/%: node_modules clean-ui Brocfile.js bower.json
|
||||
$(BROCCOLI_BIN) build $@ -- $(UI_FLAGS)
|
||||
|
||||
# Build minified JS/CSS.
|
||||
minjs: awx/ui/dist/tower.min.css.gz
|
||||
awx/ui/dist/tower.min.css.gz: node_modules Brocfile.js
|
||||
$(BROCCOLI) build awx/ui/dist -- --silent --no-debug --no-tests --compress --no-docs --no-sourcemaps
|
||||
# Concatenated, non-minified build; contains debug code and sourcemaps; does not include any tests
|
||||
devjs: awx/ui/static
|
||||
|
||||
minjs_ci: node_modules clean-ui Brocfile.js
|
||||
$(BROCCOLI) build awx/ui/dist -- --no-debug --compress --no-docs
|
||||
# Concatenated, minified, compressed (production) build with no sourcemaps or tests
|
||||
minjs: UI_FLAGS=--silent --compress --no-docs --no-debug --no-sourcemaps $(EXTRA_UI_FLAGS)
|
||||
minjs: awx/ui/static
|
||||
|
||||
# Performs build to awx/ui/build_test and runs node tests via mocha
|
||||
testjs: UI_FLAGS=--node-tests --no-concat --no-styles $(EXTRA_UI_FLAGS)
|
||||
testjs: awx/ui/build_test node-tests
|
||||
|
||||
# Performs nonminified, noncompressed build to awx/ui/static and runs browsers tests with testem ci
|
||||
testjs_ci: UI_FLAGS=--no-styles --no-compress --browser-tests --no-node-tests --no-sourcemaps $(EXTRA_UI_FLAGS)
|
||||
testjs_ci: awx/ui/static testem.yml browser-tests-ci
|
||||
|
||||
# Performs nonminified, noncompressed build to awx/ui/static and runs browsers tests with testem ci in Chrome
|
||||
testjs_debug: UI_FLAGS=--no-styles --no-compress --browser-tests --no-node-tests --no-sourcemaps $(EXTRA_UI_FLAGS)
|
||||
testjs_debug: awx/ui/static testem.yml browser-tests-debug
|
||||
|
||||
# Runs node tests via mocha without building
|
||||
node-tests:
|
||||
NODE_PATH=awx/ui/build_test $(MOCHA_BIN) --full-trace $(shell find awx/ui/build_test -name '*-test.js') $(MOCHA_FLAGS)
|
||||
|
||||
# Runs browser tests on PhantomJS. Outputs the results in a consumable manner for Jenkins.
|
||||
browser-tests-ci:
|
||||
PATH=./node_modules/.bin:$(PATH) $(TESTEM) ci --file testem.yml -p 7359 -R xunit
|
||||
|
||||
# Runs browser tests using settings from `testem.yml` you can pass in the browser you'd
|
||||
# like to run the tests on (Defaults to Chrome, other options Safari, Firefox, and PhantomJS).
|
||||
# If you want to run the tests in Node (which is the quickest, but also more difficult to debug),
|
||||
# make sure to run the testjs/node-tests targets
|
||||
browser-tests-debug:
|
||||
PATH=./node_modules/.bin:$(PATH) $(TESTEM) --file testem.yml -l $(TESTEM_DEBUG_BROWSER)
|
||||
|
||||
# Check .js files for errors and lint
|
||||
jshint: node_modules Gruntfile.js
|
||||
$(GRUNT) $@
|
||||
|
||||
# Generate UI code documentation
|
||||
ngdocs: devjs Gruntfile.js
|
||||
$(GRUNT) $@
|
||||
|
||||
# Launch watcher for build process
|
||||
sync_ui: node_modules Brocfile.js testem.yml
|
||||
$(NODE) tools/ui/timepiece.js awx/ui/static $(WATCHER_FLAGS) -- $(UI_FLAGS)
|
||||
|
||||
# Build code complexity report for UI code
|
||||
ui_analysis_report: reports/ui_code node_modules Gruntfile.js
|
||||
$(GRUNT) plato:report
|
||||
|
||||
# Non-concatenated, non-minified build with no tests, no debug code, no sourcemaps for plato reports
|
||||
reports/ui_code: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
||||
rm -rf reports/ui_code
|
||||
$(BROCCOLI_BIN) build reports/ui_code -- --no-concat --no-debug --no-styles --no-sourcemaps
|
||||
|
||||
# END UI TASKS
|
||||
# --------------------------------------
|
||||
|
||||
# Build a pip-installable package into dist/ with a timestamped version number.
|
||||
dev_build:
|
||||
$(PYTHON) setup.py dev_build
|
||||
@ -438,7 +491,7 @@ release_clean:
|
||||
-(rm *.tar)
|
||||
-(rm -rf ($RELEASE))
|
||||
|
||||
dist/$(SDIST_TAR_FILE): awx/ui/dist/tower.min.css.gz
|
||||
dist/$(SDIST_TAR_FILE): minjs
|
||||
BUILD="$(BUILD)" $(PYTHON) setup.py sdist
|
||||
|
||||
sdist: dist/$(SDIST_TAR_FILE)
|
||||
@ -598,7 +651,7 @@ reprepro: deb-build/$(DEB_NVRA).deb reprepro/conf
|
||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) clearvanished
|
||||
for COMPONENT in non-free $(VERSION); do \
|
||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) -C $$COMPONENT remove $(DEB_DIST) $(NAME) ; \
|
||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) -C $$COMPONENT --keepunreferencedfiles --ignore=brokenold includedeb $(DEB_DIST) deb-build/$(DEB_NVRA).deb ; \
|
||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) -C $$COMPONENT --ignore=brokenold includedeb $(DEB_DIST) deb-build/$(DEB_NVRA).deb ; \
|
||||
done
|
||||
|
||||
|
||||
@ -609,6 +662,7 @@ reprepro: deb-build/$(DEB_NVRA).deb reprepro/conf
|
||||
amazon-ebs:
|
||||
cd packaging/packer && $(PACKER) build -only $@ $(PACKER_BUILD_OPTS) -var "aws_instance_count=$(AWS_INSTANCE_COUNT)" -var "product_version=$(VERSION)" packer-$(NAME).json
|
||||
|
||||
# virtualbox
|
||||
virtualbox-ovf: packaging/packer/ansible-tower-$(VERSION)-virtualbox.box
|
||||
|
||||
packaging/packer/ansible-tower-$(VERSION)-virtualbox.box: packaging/packer/output-virtualbox-iso/centos-7.ovf
|
||||
@ -617,12 +671,22 @@ packaging/packer/ansible-tower-$(VERSION)-virtualbox.box: packaging/packer/outpu
|
||||
packaging/packer/output-virtualbox-iso/centos-6.ovf:
|
||||
cd packaging/packer && $(PACKER) build packer-centos-6.json
|
||||
|
||||
virtualbox-centos-6: packaging/packer/output-virtualbox-iso/centos-6.ovf
|
||||
|
||||
packaging/packer/output-virtualbox-iso/centos-7.ovf:
|
||||
cd packaging/packer && $(PACKER) build packer-centos-7.json
|
||||
cd packaging/packer && $(PACKER) build -only virtualbox-iso packer-centos-7.json
|
||||
|
||||
virtualbox-centos-7: packaging/packer/output-virtualbox-iso/centos-7.ovf
|
||||
# virtualbox-iso: packaging/packer/output-virtualbox-iso/centos-6.ovf
|
||||
virtualbox-iso: packaging/packer/output-virtualbox-iso/centos-7.ovf
|
||||
|
||||
# vmware
|
||||
packaging/packer/output-vmware-iso/centos-7.vmx:
|
||||
cd packaging/packer && $(PACKER) build -only vmware-iso packer-centos-7.json
|
||||
|
||||
vmware-iso: packaging/packer/output-vmware-iso/centos-7.vmx
|
||||
|
||||
vmware-vmx: packaging/packer/ansible-tower-$(VERSION)-vmx/ansible-tower-$(VERSION).vmx
|
||||
|
||||
packaging/packer/ansible-tower-$(VERSION)-vmx/ansible-tower-$(VERSION).vmx: packaging/packer/output-vmware-iso/centos-7.vmx
|
||||
cd packaging/packer && $(PACKER) build -only vmware-vmx $(PACKER_BUILD_OPTS) -var "aws_instance_count=$(AWS_INSTANCE_COUNT)" -var "product_version=$(VERSION)" packer-$(NAME).json
|
||||
|
||||
# TODO - figure out how to build the front-end and python requirements with
|
||||
# 'build'
|
||||
@ -635,3 +699,15 @@ install:
|
||||
# Docker Compose Development environment
|
||||
docker-compose:
|
||||
docker-compose -f tools/docker-compose.yml up --no-recreate
|
||||
|
||||
docker-compose-test:
|
||||
cd tools && docker-compose run --rm --service-ports tower /bin/bash
|
||||
|
||||
mongo-debug-ui:
|
||||
docker run -it --rm --name mongo-express --link tools_mongo_1:mongo -e ME_CONFIG_OPTIONS_EDITORTHEME=ambiance -e ME_CONFIG_BASICAUTH_USERNAME=admin -e ME_CONFIG_BASICAUTH_PASSWORD=password -p 8081:8081 knickers/mongo-express
|
||||
|
||||
mongo-container:
|
||||
docker run -it --link tools_mongo_1:mongo --rm mongo sh -c 'exec mongo "$MONGO_PORT_27017_TCP_ADDR:$MONGO_PORT_27017_TCP_PORT/system_tracking_dev"'
|
||||
|
||||
psql-container:
|
||||
docker run -it --link tools_postgres_1:postgres --rm postgres:9.4.1 sh -c 'exec psql -h "$$POSTGRES_PORT_5432_TCP_ADDR" -p "$$POSTGRES_PORT_5432_TCP_PORT" -U postgres'
|
||||
|
||||
6
Procfile
Normal file
@ -0,0 +1,6 @@
|
||||
runserver: make runserver
|
||||
celeryd: make celeryd
|
||||
taskmanager: make taskmanager
|
||||
receiver: make receiver
|
||||
socketservice: make socketservice
|
||||
factcacher: make factcacher
|
||||
@ -4,7 +4,7 @@ Ansible Tower
|
||||
Tower provides a web-based user interface, REST API and task engine built on top of
|
||||
Ansible.
|
||||
|
||||
The current version under development is 2.1.0.
|
||||
The current version under development is 2.2.1.
|
||||
|
||||
Development releases always use the 'master' branch.
|
||||
|
||||
@ -31,10 +31,14 @@ Release History
|
||||
* 2.1.2, March 25, 2015
|
||||
* 2.1.3, April 15, 2015
|
||||
* 2.1.4, June 12, 2015
|
||||
* 2.1.5, June 15, 2015
|
||||
* 2.1.6, June 23, 2015
|
||||
* 2.2.0, July 14, 2015
|
||||
* 2.2.1, August 12, 2015
|
||||
* 2.2.2, August 19, 2015
|
||||
* 2.3.0, September 22, 2015
|
||||
* 2.3.1, October 2, 2015
|
||||
* 2.4.0, November 14, 2015
|
||||
|
||||
Any fixes should be applied on the appropriate release branch and be cherry-picked to
|
||||
master.
|
||||
@ -49,3 +53,5 @@ Refer to `setup/README.md` to get started deploying Tower.
|
||||
Refer to `docs/build_system.md` for more about Jenkins and installing nightly builds (as opposed to running from source).
|
||||
|
||||
Refer to `docs/release_process.md` for information on the steps involved in creating a release.
|
||||
|
||||
Refer to http://docs.ansible.com/ansible-tower/index.html for information on installing/upgrading, setup, troubleshooting, and much more.
|
||||
|
||||
@ -6,7 +6,7 @@ import sys
|
||||
import warnings
|
||||
import site
|
||||
|
||||
__version__ = '2.3.1'
|
||||
__version__ = '2.4.0'
|
||||
|
||||
__all__ = ['__version__']
|
||||
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import urllib
|
||||
|
||||
# Django
|
||||
from django.utils.timezone import now as tz_now
|
||||
from django.conf import settings
|
||||
|
||||
# Django REST Framework
|
||||
from rest_framework import authentication
|
||||
from rest_framework import exceptions
|
||||
@ -18,24 +25,38 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
||||
|
||||
model = AuthToken
|
||||
|
||||
def _get_x_auth_token_header(self, request):
|
||||
@staticmethod
|
||||
def _get_x_auth_token_header(request):
|
||||
auth = request.META.get('HTTP_X_AUTH_TOKEN', '')
|
||||
if isinstance(auth, type('')):
|
||||
# Work around django test client oddness
|
||||
auth = auth.encode(HTTP_HEADER_ENCODING)
|
||||
return auth
|
||||
|
||||
@staticmethod
|
||||
def _get_auth_token_cookie(request):
|
||||
token = request.COOKIES.get('token', '')
|
||||
if token:
|
||||
token = urllib.unquote(token).strip('"')
|
||||
return 'token %s' % token
|
||||
return ''
|
||||
|
||||
def authenticate(self, request):
|
||||
self.request = request
|
||||
|
||||
# Prefer the custom X-Auth-Token header over the Authorization header,
|
||||
# to handle cases where the browser submits saved Basic auth and
|
||||
# overrides the UI's normal use of the Authorization header.
|
||||
auth = self._get_x_auth_token_header(request).split()
|
||||
auth = TokenAuthentication._get_x_auth_token_header(request).split()
|
||||
if not auth or auth[0].lower() != 'token':
|
||||
auth = authentication.get_authorization_header(request).split()
|
||||
if not auth or auth[0].lower() != 'token':
|
||||
# Prefer basic auth over cookie token
|
||||
if auth and auth[0].lower() == 'basic':
|
||||
return None
|
||||
elif not auth or auth[0].lower() != 'token':
|
||||
auth = TokenAuthentication._get_auth_token_cookie(request).split()
|
||||
if not auth or auth[0].lower() != 'token':
|
||||
return None
|
||||
|
||||
if len(auth) == 1:
|
||||
msg = 'Invalid token header. No credentials provided.'
|
||||
@ -47,6 +68,7 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
||||
return self.authenticate_credentials(auth[1])
|
||||
|
||||
def authenticate_credentials(self, key):
|
||||
now = tz_now()
|
||||
# Retrieve the request hash and token.
|
||||
try:
|
||||
request_hash = self.model.get_request_hash(self.request)
|
||||
@ -55,25 +77,46 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
||||
request_hash=request_hash,
|
||||
)
|
||||
except self.model.DoesNotExist:
|
||||
raise exceptions.AuthenticationFailed('Invalid token')
|
||||
raise exceptions.AuthenticationFailed(AuthToken.reason_long('invalid_token'))
|
||||
|
||||
# Sanity check: Ensure that the token is still valid.
|
||||
# Tokens expire if they are not used for 30 minutes.
|
||||
if token.expired:
|
||||
raise exceptions.AuthenticationFailed('Token is expired')
|
||||
# Tell the user why their token was previously invalidated.
|
||||
if token.invalidated:
|
||||
raise exceptions.AuthenticationFailed(AuthToken.reason_long(token.reason))
|
||||
|
||||
# Sanity check: If the user is inactive, then return an error.
|
||||
# Explicitly handle expired tokens
|
||||
if token.is_expired(now=now):
|
||||
token.invalidate(reason='timeout_reached')
|
||||
raise exceptions.AuthenticationFailed(AuthToken.reason_long('timeout_reached'))
|
||||
|
||||
# Token invalidated due to session limit config being reduced
|
||||
# Session limit reached invalidation will also take place on authentication
|
||||
if settings.AUTH_TOKEN_PER_USER != -1:
|
||||
if not token.in_valid_tokens(now=now):
|
||||
token.invalidate(reason='limit_reached')
|
||||
raise exceptions.AuthenticationFailed(AuthToken.reason_long('limit_reached'))
|
||||
|
||||
# If the user is inactive, then return an error.
|
||||
if not token.user.is_active:
|
||||
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
||||
|
||||
# Refresh the token.
|
||||
# This updates the time that the token was last used, meaning that
|
||||
# now the token is valid for 30 minutes from "right now".
|
||||
token.refresh()
|
||||
# The token is extended from "right now" + configurable setting amount.
|
||||
token.refresh(now=now)
|
||||
|
||||
# Return the user object and the token.
|
||||
return (token.user, token)
|
||||
|
||||
|
||||
class TokenGetAuthentication(TokenAuthentication):
|
||||
|
||||
def authenticate(self, request):
|
||||
if request.method.lower() == 'get':
|
||||
token = request.GET.get('token', None)
|
||||
if token:
|
||||
request.META['HTTP_X_AUTH_TOKEN'] = 'Token %s' % token
|
||||
return super(TokenGetAuthentication, self).authenticate(request)
|
||||
|
||||
|
||||
class TaskAuthentication(authentication.BaseAuthentication):
|
||||
'''
|
||||
Custom authentication used for views accessed by the inventory and callback
|
||||
|
||||
@ -142,6 +142,8 @@ class APIView(views.APIView):
|
||||
'new_in_200': getattr(self, 'new_in_200', False),
|
||||
'new_in_210': getattr(self, 'new_in_210', False),
|
||||
'new_in_220': getattr(self, 'new_in_220', False),
|
||||
'new_in_230': getattr(self, 'new_in_230', False),
|
||||
'new_in_240': getattr(self, 'new_in_240', False),
|
||||
}
|
||||
|
||||
def get_description(self, html=False):
|
||||
@ -158,7 +160,7 @@ class APIView(views.APIView):
|
||||
'''
|
||||
ret = super(APIView, self).metadata(request)
|
||||
added_in_version = '1.2'
|
||||
for version in ('2.2.0', '2.1.0', '2.0.0', '1.4.8', '1.4.5', '1.4', '1.3'):
|
||||
for version in ('2.4.0', '2.3.0', '2.2.0', '2.1.0', '2.0.0', '1.4.8', '1.4.5', '1.4', '1.3'):
|
||||
if getattr(self, 'new_in_%s' % version.replace('.', ''), False):
|
||||
added_in_version = version
|
||||
break
|
||||
|
||||
@ -31,4 +31,17 @@ def feature_enabled(name):
|
||||
return False
|
||||
|
||||
# Return the correct feature flag.
|
||||
return get_license()['features'].get(name, False)
|
||||
return license['features'].get(name, False)
|
||||
|
||||
def feature_exists(name):
|
||||
"""Return True if the requested feature is enabled, False otherwise.
|
||||
If the feature does not exist, raise KeyError.
|
||||
"""
|
||||
license = get_license()
|
||||
|
||||
# Sanity check: If there is no license, the feature is considered
|
||||
# to be off.
|
||||
if 'features' not in license:
|
||||
return False
|
||||
|
||||
return name in license['features']
|
||||
|
||||
@ -601,6 +601,8 @@ class UserSerializer(BaseSerializer):
|
||||
ret = super(UserSerializer, self).to_native(obj)
|
||||
ret.pop('password', None)
|
||||
ret.fields.pop('password', None)
|
||||
if obj:
|
||||
ret['auth'] = obj.social_auth.values('provider', 'uid')
|
||||
return ret
|
||||
|
||||
def get_validation_exclusions(self):
|
||||
@ -628,6 +630,12 @@ class UserSerializer(BaseSerializer):
|
||||
new_password = None
|
||||
except AttributeError:
|
||||
pass
|
||||
if (getattr(settings, 'SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', None) or
|
||||
getattr(settings, 'SOCIAL_AUTH_GITHUB_KEY', None) or
|
||||
getattr(settings, 'SOCIAL_AUTH_GITHUB_ORG_KEY', None) or
|
||||
getattr(settings, 'SOCIAL_AUTH_GITHUB_TEAM_KEY', None) or
|
||||
getattr(settings, 'SOCIAL_AUTH_SAML_ENABLED_IDPS', None)) and obj.social_auth.all():
|
||||
new_password = None
|
||||
if new_password:
|
||||
obj.set_password(new_password)
|
||||
if not obj.password:
|
||||
@ -1349,6 +1357,7 @@ class CredentialSerializer(BaseSerializer):
|
||||
# FIXME: may want to make some of these filtered based on user accessing
|
||||
|
||||
password = serializers.CharField(required=False, default='')
|
||||
security_token = serializers.CharField(required=False, default='')
|
||||
ssh_key_data = serializers.CharField(required=False, default='')
|
||||
ssh_key_unlock = serializers.CharField(required=False, default='')
|
||||
become_password = serializers.CharField(required=False, default='')
|
||||
@ -1357,7 +1366,7 @@ class CredentialSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = Credential
|
||||
fields = ('*', 'user', 'team', 'kind', 'cloud', 'host', 'username',
|
||||
'password', 'project', 'ssh_key_data', 'ssh_key_unlock',
|
||||
'password', 'security_token', 'project', 'ssh_key_data', 'ssh_key_unlock',
|
||||
'become_method', 'become_username', 'become_password',
|
||||
'vault_password')
|
||||
|
||||
@ -2001,11 +2010,12 @@ class ScheduleSerializer(BaseSerializer):
|
||||
class ActivityStreamSerializer(BaseSerializer):
|
||||
|
||||
changes = serializers.SerializerMethodField('get_changes')
|
||||
object_association = serializers.SerializerMethodField('get_object_association')
|
||||
|
||||
class Meta:
|
||||
model = ActivityStream
|
||||
fields = ('*', '-name', '-description', '-created', '-modified',
|
||||
'timestamp', 'operation', 'changes', 'object1', 'object2')
|
||||
'timestamp', 'operation', 'changes', 'object1', 'object2', 'object_association')
|
||||
|
||||
def get_fields(self):
|
||||
ret = super(ActivityStreamSerializer, self).get_fields()
|
||||
@ -2030,6 +2040,13 @@ class ActivityStreamSerializer(BaseSerializer):
|
||||
logger.warn("Error deserializing activity stream json changes")
|
||||
return {}
|
||||
|
||||
def get_object_association(self, obj):
|
||||
try:
|
||||
return obj.object_relationship_type.split(".")[-1].split("_")[1]
|
||||
except:
|
||||
pass
|
||||
return ""
|
||||
|
||||
def get_related(self, obj):
|
||||
rel = {}
|
||||
if obj.actor is not None:
|
||||
|
||||
@ -3,4 +3,6 @@
|
||||
{% if new_in_145 %}> _Added in Ansible Tower 1.4.5_{% endif %}
|
||||
{% if new_in_148 %}> _Added in Ansible Tower 1.4.8_{% endif %}
|
||||
{% if new_in_200 %}> _New in Ansible Tower 2.0.0_{% endif %}
|
||||
{% if new_in_220 %}> _New in Ansible Tower 2.2.0_{% endif %}
|
||||
{% if new_in_220 %}> _New in Ansible Tower 2.2.0_{% endif %}
|
||||
{% if new_in_230 %}> _New in Ansible Tower 2.3.0_{% endif %}
|
||||
{% if new_in_240 %}> _New in Ansible Tower 2.4.0_{% endif %}
|
||||
@ -224,6 +224,7 @@ v1_urls = patterns('awx.api.views',
|
||||
url(r'^$', 'api_v1_root_view'),
|
||||
url(r'^ping/$', 'api_v1_ping_view'),
|
||||
url(r'^config/$', 'api_v1_config_view'),
|
||||
url(r'^auth/$', 'auth_view'),
|
||||
url(r'^authtoken/$', 'auth_token_view'),
|
||||
url(r'^me/$', 'user_me_list'),
|
||||
url(r'^dashboard/$', 'dashboard_view'),
|
||||
|
||||
@ -11,6 +11,7 @@ import time
|
||||
import socket
|
||||
import sys
|
||||
import errno
|
||||
from base64 import b64encode
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
@ -47,23 +48,27 @@ import qsstats
|
||||
# ANSIConv
|
||||
import ansiconv
|
||||
|
||||
# Python Social Auth
|
||||
from social.backends.utils import load_backends
|
||||
|
||||
# AWX
|
||||
from awx.main.task_engine import TaskSerializer, TASK_FILE, TEMPORARY_TASK_FILE
|
||||
from awx.main.tasks import mongodb_control
|
||||
from awx.main.access import get_user_queryset
|
||||
from awx.main.ha import is_ha_environment
|
||||
from awx.api.authentication import TaskAuthentication
|
||||
from awx.api.authentication import TaskAuthentication, TokenGetAuthentication
|
||||
from awx.api.utils.decorators import paginated
|
||||
from awx.api.filters import MongoFilterBackend
|
||||
from awx.api.generics import get_view_name
|
||||
from awx.api.generics import * # noqa
|
||||
from awx.api.license import feature_enabled, LicenseForbids
|
||||
from awx.api.license import feature_enabled, feature_exists, LicenseForbids
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.utils import * # noqa
|
||||
from awx.api.permissions import * # noqa
|
||||
from awx.api.renderers import * # noqa
|
||||
from awx.api.serializers import * # noqa
|
||||
from awx.fact.models import * # noqa
|
||||
from awx.main.utils import emit_websocket_notification
|
||||
|
||||
def api_exception_handler(exc):
|
||||
'''
|
||||
@ -186,12 +191,15 @@ class ApiV1ConfigView(APIView):
|
||||
license_reader = TaskSerializer()
|
||||
license_data = license_reader.from_file(show_key=request.user.is_superuser)
|
||||
|
||||
pendo_state = settings.PENDO_TRACKING_STATE if settings.PENDO_TRACKING_STATE in ('off', 'anonymous', 'detailed') else 'off'
|
||||
|
||||
data = dict(
|
||||
time_zone=settings.TIME_ZONE,
|
||||
license_info=license_data,
|
||||
version=get_awx_version(),
|
||||
ansible_version=get_ansible_version(),
|
||||
eula=render_to_string("eula.md"),
|
||||
analytics_status=pendo_state
|
||||
)
|
||||
|
||||
# If LDAP is enabled, user_ldap_fields will return a list of field
|
||||
@ -510,6 +518,45 @@ class ScheduleUnifiedJobsList(SubListAPIView):
|
||||
view_name = 'Schedule Jobs List'
|
||||
new_in_148 = True
|
||||
|
||||
class AuthView(APIView):
|
||||
|
||||
authentication_classes = []
|
||||
permission_classes = (AllowAny,)
|
||||
new_in_240 = True
|
||||
|
||||
def get(self, request):
|
||||
data = SortedDict()
|
||||
err_backend, err_message = request.session.get('social_auth_error', (None, None))
|
||||
auth_backends = load_backends(settings.AUTHENTICATION_BACKENDS).items()
|
||||
# Return auth backends in consistent order: Google, GitHub, SAML.
|
||||
auth_backends.sort(key=lambda x: 'g' if x[0] == 'google-oauth2' else x[0])
|
||||
for name, backend in auth_backends:
|
||||
if (not feature_exists('enterprise_auth') and
|
||||
not feature_enabled('ldap')) or \
|
||||
(not feature_enabled('enterprise_auth') and
|
||||
name in ['saml', 'radius']):
|
||||
continue
|
||||
login_url = reverse('social:begin', args=(name,))
|
||||
complete_url = request.build_absolute_uri(reverse('social:complete', args=(name,)))
|
||||
backend_data = {
|
||||
'login_url': login_url,
|
||||
'complete_url': complete_url,
|
||||
}
|
||||
if name == 'saml':
|
||||
backend_data['metadata_url'] = reverse('sso:saml_metadata')
|
||||
for idp in sorted(settings.SOCIAL_AUTH_SAML_ENABLED_IDPS.keys()):
|
||||
saml_backend_data = dict(backend_data.items())
|
||||
saml_backend_data['login_url'] = '%s?idp=%s' % (login_url, idp)
|
||||
full_backend_name = '%s:%s' % (name, idp)
|
||||
if err_backend == full_backend_name and err_message:
|
||||
saml_backend_data['error'] = err_message
|
||||
data[full_backend_name] = saml_backend_data
|
||||
else:
|
||||
if err_backend == name and err_message:
|
||||
backend_data['error'] = err_message
|
||||
data[name] = backend_data
|
||||
return Response(data)
|
||||
|
||||
class AuthTokenView(APIView):
|
||||
|
||||
authentication_classes = []
|
||||
@ -524,12 +571,30 @@ class AuthTokenView(APIView):
|
||||
try:
|
||||
token = AuthToken.objects.filter(user=serializer.object['user'],
|
||||
request_hash=request_hash,
|
||||
expires__gt=now())[0]
|
||||
expires__gt=now(),
|
||||
reason='')[0]
|
||||
token.refresh()
|
||||
except IndexError:
|
||||
token = AuthToken.objects.create(user=serializer.object['user'],
|
||||
request_hash=request_hash)
|
||||
return Response({'token': token.key, 'expires': token.expires})
|
||||
# Get user un-expired tokens that are not invalidated that are
|
||||
# over the configured limit.
|
||||
# Mark them as invalid and inform the user
|
||||
invalid_tokens = AuthToken.get_tokens_over_limit(serializer.object['user'])
|
||||
for t in invalid_tokens:
|
||||
# TODO: send socket notification
|
||||
emit_websocket_notification('/socket.io/control',
|
||||
'limit_reached',
|
||||
dict(reason=unicode(AuthToken.reason_long('limit_reached'))),
|
||||
token_key=t.key)
|
||||
t.invalidate(reason='limit_reached')
|
||||
|
||||
# Note: This header is normally added in the middleware whenever an
|
||||
# auth token is included in the request header.
|
||||
headers = {
|
||||
'Auth-Token-Timeout': int(settings.AUTH_TOKEN_EXPIRATION)
|
||||
}
|
||||
return Response({'token': token.key, 'expires': token.expires}, headers=headers)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class OrganizationList(ListCreateAPIView):
|
||||
@ -2092,12 +2157,6 @@ class SystemJobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
|
||||
relationship = 'schedules'
|
||||
parent_key = 'unified_job_template'
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
system_job = self.get_parent_object()
|
||||
if system_job.schedules.filter(active=True).count() > 0:
|
||||
return Response({"error": "Multiple schedules for Systems Jobs is not allowed"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return super(SystemJobTemplateSchedulesList, self).post(request, *args, **kwargs)
|
||||
|
||||
class SystemJobTemplateJobsList(SubListAPIView):
|
||||
|
||||
model = SystemJob
|
||||
@ -2207,6 +2266,7 @@ class JobRelaunch(RetrieveAPIView, GenericAPIView):
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
obj.launch_type = 'relaunch'
|
||||
new_job = obj.copy()
|
||||
result = new_job.signal_start(**request.DATA)
|
||||
if not result:
|
||||
@ -2788,6 +2848,7 @@ class UnifiedJobList(ListAPIView):
|
||||
|
||||
class UnifiedJobStdout(RetrieveAPIView):
|
||||
|
||||
authentication_classes = [TokenGetAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
|
||||
serializer_class = UnifiedJobStdoutSerializer
|
||||
renderer_classes = [BrowsableAPIRenderer, renderers.StaticHTMLRenderer,
|
||||
PlainTextRenderer, AnsiTextRenderer,
|
||||
@ -2804,8 +2865,10 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
return Response({'range': {'start': 0, 'end': 1, 'absolute_end': 1}, 'content': response_message})
|
||||
else:
|
||||
return Response(response_message)
|
||||
|
||||
|
||||
if request.accepted_renderer.format in ('html', 'api', 'json'):
|
||||
content_format = request.QUERY_PARAMS.get('content_format', 'html')
|
||||
content_encoding = request.QUERY_PARAMS.get('content_encoding', None)
|
||||
start_line = request.QUERY_PARAMS.get('start_line', 0)
|
||||
end_line = request.QUERY_PARAMS.get('end_line', None)
|
||||
dark_val = request.QUERY_PARAMS.get('dark', '')
|
||||
@ -2826,7 +2889,10 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
if request.accepted_renderer.format == 'api':
|
||||
return Response(mark_safe(data))
|
||||
if request.accepted_renderer.format == 'json':
|
||||
return Response({'range': {'start': start, 'end': end, 'absolute_end': absolute_end}, 'content': body})
|
||||
if content_encoding == 'base64' and content_format == 'ansi':
|
||||
return Response({'range': {'start': start, 'end': end, 'absolute_end': absolute_end}, 'content': b64encode(content)})
|
||||
elif content_format == 'html':
|
||||
return Response({'range': {'start': start, 'end': end, 'absolute_end': absolute_end}, 'content': body})
|
||||
return Response(data)
|
||||
elif request.accepted_renderer.format == 'ansi':
|
||||
return Response(unified_job.result_stdout_raw)
|
||||
|
||||
@ -1,31 +1,2 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
from django.conf import settings
|
||||
|
||||
from mongoengine import connect
|
||||
from mongoengine.connection import get_db, ConnectionError
|
||||
from .utils.dbtransform import register_key_transform
|
||||
|
||||
logger = logging.getLogger('awx.fact')
|
||||
|
||||
# Connect to Mongo
|
||||
try:
|
||||
# Sanity check: If we have intentionally invalid settings, then we
|
||||
# know we cannot connect.
|
||||
if settings.MONGO_HOST == NotImplemented:
|
||||
raise ConnectionError
|
||||
|
||||
# Attempt to connect to the MongoDB database.
|
||||
connect(settings.MONGO_DB,
|
||||
host=settings.MONGO_HOST,
|
||||
port=int(settings.MONGO_PORT),
|
||||
username=settings.MONGO_USERNAME,
|
||||
password=settings.MONGO_PASSWORD,
|
||||
tz_aware=settings.USE_TZ)
|
||||
register_key_transform(get_db())
|
||||
except ConnectionError:
|
||||
logger.info('Failed to establish connect to MongoDB')
|
||||
|
||||
@ -1,12 +1,46 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
from mongoengine import connect
|
||||
from mongoengine.base import BaseField
|
||||
from mongoengine import Document, DateTimeField, ReferenceField, StringField, IntField
|
||||
from awx.fact.utils.dbtransform import KeyTransform
|
||||
from mongoengine.connection import get_db, ConnectionError
|
||||
from awx.fact.utils.dbtransform import register_key_transform, KeyTransform
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('awx.fact.models.fact')
|
||||
|
||||
|
||||
key_transform = KeyTransform([('.', '\uff0E'), ('$', '\uff04')])
|
||||
|
||||
# NOTE: I think it might be better to use register_connection here: https://github.com/MongoEngine/mongoengine/blob/0.9/mongoengine/connection.py#L21
|
||||
# but I'm not doing that because I don't see how we can also register the key transform as needed or set the tz_aware preference
|
||||
@classmethod
|
||||
def _get_db_monkeypatched(cls):
|
||||
""" Override the default _get_db mechanism to start a connection to the database """
|
||||
# Connect to Mongo
|
||||
try:
|
||||
# Sanity check: If we have intentionally invalid settings, then we
|
||||
# know we cannot connect.
|
||||
if settings.MONGO_HOST == NotImplemented:
|
||||
raise ConnectionError
|
||||
|
||||
# Attempt to connect to the MongoDB database.
|
||||
connect(settings.MONGO_DB,
|
||||
host=settings.MONGO_HOST,
|
||||
port=int(settings.MONGO_PORT),
|
||||
username=settings.MONGO_USERNAME,
|
||||
password=settings.MONGO_PASSWORD,
|
||||
tz_aware=settings.USE_TZ)
|
||||
register_key_transform(get_db())
|
||||
except ConnectionError:
|
||||
logger.info('Failed to establish connect to MongoDB')
|
||||
return get_db(cls._meta.get("db_alias", "default"))
|
||||
|
||||
Document._get_db = _get_db_monkeypatched
|
||||
|
||||
class TransformField(BaseField):
|
||||
def to_python(self, value):
|
||||
return key_transform.transform_outgoing(value, None)
|
||||
|
||||
155011
awx/fact/tests/data/file_scan.json
Normal file
@ -3,10 +3,12 @@
|
||||
|
||||
# Python
|
||||
from __future__ import absolute_import
|
||||
from django.utils.timezone import now
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import os
|
||||
import json
|
||||
|
||||
# Django
|
||||
from django.utils.timezone import now
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
# AWX
|
||||
from awx.fact.models.fact import * # noqa
|
||||
@ -14,6 +16,13 @@ from awx.fact.tests.base import BaseFactTest, FactScanBuilder, TEST_FACT_PACKAGE
|
||||
|
||||
__all__ = ['FactHostTest', 'FactTest', 'FactGetHostVersionTest', 'FactGetHostTimelineTest']
|
||||
|
||||
# damn you python 2.6
|
||||
def timedelta_total_seconds(timedelta):
|
||||
return (
|
||||
timedelta.microseconds + 0.0 +
|
||||
(timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
|
||||
|
||||
|
||||
class FactHostTest(BaseFactTest):
|
||||
def test_create_host(self):
|
||||
host = FactHost(hostname='hosty', inventory_id=1)
|
||||
@ -57,6 +66,27 @@ class FactTest(BaseFactTest):
|
||||
self.assertEqual(v.fact.id, f_obj.id)
|
||||
self.assertEqual(v.fact.module, 'packages')
|
||||
|
||||
# Note: Take the failure of this with a grain of salt.
|
||||
# The test almost entirely depends on the specs of the system running on.
|
||||
def test_add_fact_performance_4mb_file(self):
|
||||
timestamp = now().replace(microsecond=0)
|
||||
host = FactHost(hostname="hosty", inventory_id=1).save()
|
||||
|
||||
from awx.fact import tests
|
||||
with open('%s/data/file_scan.json' % os.path.dirname(os.path.realpath(tests.__file__))) as f:
|
||||
data = json.load(f)
|
||||
|
||||
t1 = now()
|
||||
(f_obj, v_obj) = Fact.add_fact(host=host, timestamp=timestamp, module='packages', fact=data)
|
||||
t2 = now()
|
||||
diff = timedelta_total_seconds(t2 - t1)
|
||||
print("add_fact save time: %s (s)" % diff)
|
||||
# Note: 20 is realllly high. This should complete in < 2 seconds
|
||||
self.assertLessEqual(diff, 20)
|
||||
|
||||
Fact.objects.get(id=f_obj.id)
|
||||
FactVersion.objects.get(id=v_obj.id)
|
||||
|
||||
class FactGetHostVersionTest(BaseFactTest):
|
||||
def setUp(self):
|
||||
super(FactGetHostVersionTest, self).setUp()
|
||||
|
||||
@ -146,7 +146,7 @@ class BaseAccess(object):
|
||||
def can_unattach(self, obj, sub_obj, relationship):
|
||||
return self.can_change(obj, None)
|
||||
|
||||
def check_license(self, add_host=False, feature=None):
|
||||
def check_license(self, add_host=False, feature=None, check_expiration=True):
|
||||
reader = TaskSerializer()
|
||||
validation_info = reader.from_file()
|
||||
if ('test' in sys.argv or 'jenkins' in sys.argv) and not os.environ.get('SKIP_LICENSE_FIXUP_FOR_TEST', ''):
|
||||
@ -154,9 +154,9 @@ class BaseAccess(object):
|
||||
validation_info['time_remaining'] = 99999999
|
||||
validation_info['grace_period_remaining'] = 99999999
|
||||
|
||||
if validation_info.get('time_remaining', None) is None:
|
||||
if check_expiration and validation_info.get('time_remaining', None) is None:
|
||||
raise PermissionDenied("license is missing")
|
||||
if validation_info.get("grace_period_remaining") <= 0:
|
||||
if check_expiration and validation_info.get("grace_period_remaining") <= 0:
|
||||
raise PermissionDenied("license has expired")
|
||||
|
||||
free_instances = validation_info.get('free_instances', 0)
|
||||
@ -262,6 +262,7 @@ class OrganizationAccess(BaseAccess):
|
||||
self.user in obj.admins.all())
|
||||
|
||||
def can_delete(self, obj):
|
||||
self.check_license(feature='multiple_organizations', check_expiration=False)
|
||||
return self.can_change(obj, None)
|
||||
|
||||
class InventoryAccess(BaseAccess):
|
||||
@ -672,22 +673,22 @@ class ProjectAccess(BaseAccess):
|
||||
- I am on a team associated with the project.
|
||||
- I have been explicitly granted permission to run/check jobs using the
|
||||
project.
|
||||
- I created it (for now?).
|
||||
- I created the project but it isn't associated with an organization
|
||||
I can change/delete when:
|
||||
- I am a superuser.
|
||||
- I am an admin in an organization associated with the project.
|
||||
- I created it (for now?).
|
||||
- I created the project but it isn't associated with an organization
|
||||
'''
|
||||
|
||||
model = Project
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Project.objects.filter(active=True).distinct()
|
||||
qs = qs.select_related('created_by', 'modified_by', 'credential', 'current_update', 'last_update')
|
||||
qs = qs.select_related('modified_by', 'credential', 'current_update', 'last_update')
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
team_ids = set(Team.objects.filter(users__in=[self.user]).values_list('id', flat=True))
|
||||
qs = qs.filter(Q(created_by=self.user) |
|
||||
qs = qs.filter(Q(created_by=self.user, organizations__isnull=True) |
|
||||
Q(organizations__admins__in=[self.user], organizations__active=True) |
|
||||
Q(organizations__users__in=[self.user], organizations__active=True) |
|
||||
Q(teams__in=team_ids))
|
||||
@ -719,7 +720,7 @@ class ProjectAccess(BaseAccess):
|
||||
def can_change(self, obj, data):
|
||||
if self.user.is_superuser:
|
||||
return True
|
||||
if obj.created_by == self.user:
|
||||
if obj.created_by == self.user and not obj.organizations.filter(active=True).count():
|
||||
return True
|
||||
if obj.organizations.filter(active=True, admins__in=[self.user]).exists():
|
||||
return True
|
||||
@ -863,52 +864,55 @@ class JobTemplateAccess(BaseAccess):
|
||||
'credential', 'cloud_credential', 'next_schedule')
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
credential_ids = set(self.user.get_queryset(Credential).values_list('id', flat=True))
|
||||
inventory_ids = set(self.user.get_queryset(Inventory).values_list('id', flat=True))
|
||||
credential_ids = self.user.get_queryset(Credential)
|
||||
inventory_ids = self.user.get_queryset(Inventory)
|
||||
base_qs = qs.filter(
|
||||
Q(credential_id__in=credential_ids) | Q(credential__isnull=True),
|
||||
Q(cloud_credential_id__in=credential_ids) | Q(cloud_credential__isnull=True),
|
||||
)
|
||||
org_admin_ids = set(base_qs.filter(
|
||||
org_admin_ids = base_qs.filter(
|
||||
Q(project__organizations__admins__in=[self.user]) |
|
||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__admins__in=[self.user]))
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
||||
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||
|
||||
team_ids = set(Team.objects.filter(users__in=[self.user]).values_list('id', flat=True))
|
||||
team_ids = Team.objects.filter(users__in=[self.user])
|
||||
|
||||
# TODO: I think the below queries can be combined
|
||||
deploy_permissions_ids = set(Permission.objects.filter(
|
||||
deploy_permissions_ids = Permission.objects.filter(
|
||||
Q(user=self.user) | Q(team_id__in=team_ids),
|
||||
active=True,
|
||||
permission_type__in=allowed_deploy,
|
||||
).values_list('id', flat=True))
|
||||
check_permissions_ids = set(Permission.objects.filter(
|
||||
)
|
||||
check_permissions_ids = Permission.objects.filter(
|
||||
Q(user=self.user) | Q(team_id__in=team_ids),
|
||||
active=True,
|
||||
permission_type__in=allowed_check,
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
perm_deploy_ids = set(base_qs.filter(
|
||||
perm_deploy_ids = base_qs.filter(
|
||||
job_type=PERM_INVENTORY_DEPLOY,
|
||||
inventory__permissions__in=deploy_permissions_ids,
|
||||
project__permissions__in=deploy_permissions_ids,
|
||||
inventory__permissions__pk=F('project__permissions__pk'),
|
||||
inventory_id__in=inventory_ids,
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
perm_check_ids = set(base_qs.filter(
|
||||
perm_check_ids = base_qs.filter(
|
||||
job_type=PERM_INVENTORY_CHECK,
|
||||
inventory__permissions__in=check_permissions_ids,
|
||||
project__permissions__in=check_permissions_ids,
|
||||
inventory__permissions__pk=F('project__permissions__pk'),
|
||||
inventory_id__in=inventory_ids,
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
base_ids = org_admin_ids.union(perm_deploy_ids).union(perm_check_ids)
|
||||
return base_qs.filter(id__in=base_ids)
|
||||
return base_qs.filter(
|
||||
Q(id__in=org_admin_ids) |
|
||||
Q(id__in=perm_deploy_ids) |
|
||||
Q(id__in=perm_check_ids)
|
||||
)
|
||||
|
||||
def can_read(self, obj):
|
||||
# you can only see the job templates that you have permission to launch.
|
||||
@ -1079,47 +1083,50 @@ class JobAccess(BaseAccess):
|
||||
qs = qs.prefetch_related('unified_job_template')
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
credential_ids = set(self.user.get_queryset(Credential).values_list('id', flat=True))
|
||||
credential_ids = self.user.get_queryset(Credential)
|
||||
base_qs = qs.filter(
|
||||
credential_id__in=credential_ids,
|
||||
)
|
||||
org_admin_ids = set(base_qs.filter(
|
||||
org_admin_ids = base_qs.filter(
|
||||
Q(project__organizations__admins__in=[self.user]) |
|
||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__admins__in=[self.user]))
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
||||
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||
team_ids = set(Team.objects.filter(users__in=[self.user]).values_list('id', flat=True))
|
||||
team_ids = Team.objects.filter(users__in=[self.user])
|
||||
|
||||
# TODO: I think the below queries can be combined
|
||||
deploy_permissions_ids = set(Permission.objects.filter(
|
||||
deploy_permissions_ids = Permission.objects.filter(
|
||||
Q(user=self.user) | Q(team__in=team_ids),
|
||||
active=True,
|
||||
permission_type__in=allowed_deploy,
|
||||
).values_list('id', flat=True))
|
||||
check_permissions_ids = set(Permission.objects.filter(
|
||||
)
|
||||
check_permissions_ids = Permission.objects.filter(
|
||||
Q(user=self.user) | Q(team__in=team_ids),
|
||||
active=True,
|
||||
permission_type__in=allowed_check,
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
perm_deploy_ids = set(base_qs.filter(
|
||||
perm_deploy_ids = base_qs.filter(
|
||||
job_type=PERM_INVENTORY_DEPLOY,
|
||||
inventory__permissions__in=deploy_permissions_ids,
|
||||
project__permissions__in=deploy_permissions_ids,
|
||||
inventory__permissions__pk=F('project__permissions__pk'),
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
perm_check_ids = set(base_qs.filter(
|
||||
perm_check_ids = base_qs.filter(
|
||||
job_type=PERM_INVENTORY_CHECK,
|
||||
inventory__permissions__in=check_permissions_ids,
|
||||
project__permissions__in=check_permissions_ids,
|
||||
inventory__permissions__pk=F('project__permissions__pk'),
|
||||
).values_list('id', flat=True))
|
||||
)
|
||||
|
||||
base_ids = org_admin_ids.union(perm_deploy_ids).union(perm_check_ids)
|
||||
return base_qs.filter(id__in=base_ids)
|
||||
return base_qs.filter(
|
||||
Q(id__in=org_admin_ids) |
|
||||
Q(id__in=perm_deploy_ids) |
|
||||
Q(id__in=perm_check_ids)
|
||||
)
|
||||
|
||||
def can_add(self, data):
|
||||
if not data or '_method' in data: # So the browseable API will work?
|
||||
@ -1599,7 +1606,7 @@ class CustomInventoryScriptAccess(BaseAccess):
|
||||
model = CustomInventoryScript
|
||||
|
||||
def get_queryset(self):
|
||||
qs = self.model.objects.filter(active=True, organization__active=True).distinct()
|
||||
qs = self.model.objects.filter(active=True).distinct()
|
||||
if not self.user.is_superuser:
|
||||
qs = qs.filter(Q(organization__admins__in=[self.user]) | Q(organization__users__in=[self.user]))
|
||||
return qs
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Django
|
||||
from django.dispatch import receiver
|
||||
|
||||
# django-auth-ldap
|
||||
from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings
|
||||
from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend
|
||||
from django_auth_ldap.backend import populate_user
|
||||
|
||||
# Ansible Tower
|
||||
from awx.api.license import feature_enabled
|
||||
|
||||
class LDAPSettings(BaseLDAPSettings):
|
||||
|
||||
defaults = dict(BaseLDAPSettings.defaults.items() + {
|
||||
'ORGANIZATION_MAP': {},
|
||||
'TEAM_MAP': {},
|
||||
}.items())
|
||||
|
||||
class LDAPBackend(BaseLDAPBackend):
|
||||
'''
|
||||
Custom LDAP backend for AWX.
|
||||
'''
|
||||
|
||||
settings_prefix = 'AUTH_LDAP_'
|
||||
|
||||
def _get_settings(self):
|
||||
if self._settings is None:
|
||||
self._settings = LDAPSettings(self.settings_prefix)
|
||||
return self._settings
|
||||
|
||||
def _set_settings(self, settings):
|
||||
self._settings = settings
|
||||
|
||||
settings = property(_get_settings, _set_settings)
|
||||
|
||||
def authenticate(self, username, password):
|
||||
if not self.settings.SERVER_URI or not feature_enabled('ldap'):
|
||||
return None
|
||||
return super(LDAPBackend, self).authenticate(username, password)
|
||||
|
||||
def get_user(self, user_id):
|
||||
if not self.settings.SERVER_URI or not feature_enabled('ldap'):
|
||||
return None
|
||||
return super(LDAPBackend, self).get_user(user_id)
|
||||
|
||||
# Disable any LDAP based authorization / permissions checking.
|
||||
|
||||
def has_perm(self, user, perm, obj=None):
|
||||
return False
|
||||
|
||||
def has_module_perms(self, user, app_label):
|
||||
return False
|
||||
|
||||
def get_all_permissions(self, user, obj=None):
|
||||
return set()
|
||||
|
||||
def get_group_permissions(self, user, obj=None):
|
||||
return set()
|
||||
|
||||
def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=False):
|
||||
'''
|
||||
Hepler function to update m2m relationship based on LDAP group membership.
|
||||
'''
|
||||
should_add = False
|
||||
if opts is None:
|
||||
return
|
||||
elif not opts:
|
||||
pass
|
||||
elif opts is True:
|
||||
should_add = True
|
||||
else:
|
||||
if isinstance(opts, basestring):
|
||||
opts = [opts]
|
||||
for group_dn in opts:
|
||||
if not isinstance(group_dn, basestring):
|
||||
continue
|
||||
if ldap_user._get_groups().is_member_of(group_dn):
|
||||
should_add = True
|
||||
if should_add:
|
||||
rel.add(user)
|
||||
elif remove:
|
||||
rel.remove(user)
|
||||
|
||||
@receiver(populate_user)
|
||||
def on_populate_user(sender, **kwargs):
|
||||
'''
|
||||
Handle signal from LDAP backend to populate the user object. Update user
|
||||
organization/team memberships according to their LDAP groups.
|
||||
'''
|
||||
from awx.main.models import Organization, Team
|
||||
user = kwargs['user']
|
||||
ldap_user = kwargs['ldap_user']
|
||||
backend = ldap_user.backend
|
||||
|
||||
# Update organization membership based on group memberships.
|
||||
org_map = getattr(backend.settings, 'ORGANIZATION_MAP', {})
|
||||
for org_name, org_opts in org_map.items():
|
||||
org, created = Organization.objects.get_or_create(name=org_name)
|
||||
remove = bool(org_opts.get('remove', False))
|
||||
admins_opts = org_opts.get('admins', None)
|
||||
remove_admins = bool(org_opts.get('remove_admins', remove))
|
||||
_update_m2m_from_groups(user, ldap_user, org.admins, admins_opts,
|
||||
remove_admins)
|
||||
users_opts = org_opts.get('users', None)
|
||||
remove_users = bool(org_opts.get('remove_users', remove))
|
||||
_update_m2m_from_groups(user, ldap_user, org.users, users_opts,
|
||||
remove_users)
|
||||
|
||||
# Update team membership based on group memberships.
|
||||
team_map = getattr(backend.settings, 'TEAM_MAP', {})
|
||||
for team_name, team_opts in team_map.items():
|
||||
if 'organization' not in team_opts:
|
||||
continue
|
||||
org, created = Organization.objects.get_or_create(name=team_opts['organization'])
|
||||
team, created = Team.objects.get_or_create(name=team_name, organization=org)
|
||||
users_opts = team_opts.get('users', None)
|
||||
remove = bool(team_opts.get('remove', False))
|
||||
_update_m2m_from_groups(user, ldap_user, team.users, users_opts,
|
||||
remove)
|
||||
|
||||
# Update user profile to store LDAP DN.
|
||||
profile = user.profile
|
||||
if profile.ldap_dn != ldap_user.dn:
|
||||
profile.ldap_dn = ldap_user.dn
|
||||
profile.save()
|
||||
@ -21,6 +21,7 @@ class BaseCommandInstance(BaseCommand):
|
||||
|
||||
def __init__(self):
|
||||
super(BaseCommandInstance, self).__init__()
|
||||
self.enforce_primary_role = False
|
||||
self.enforce_roles = False
|
||||
self.enforce_hostname_set = False
|
||||
self.enforce_unique_find = False
|
||||
@ -70,8 +71,13 @@ class BaseCommandInstance(BaseCommand):
|
||||
default='',
|
||||
help='Find instance by specified uuid.')
|
||||
|
||||
def include_option_primary_role(self):
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_primary(), )
|
||||
self.enforce_primary_role = True
|
||||
|
||||
def include_options_roles(self):
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_primary(), BaseCommandInstance.generate_option_secondary(), )
|
||||
self.include_option_primary_role()
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_secondary(), )
|
||||
self.enforce_roles = True
|
||||
|
||||
def include_option_hostname_set(self):
|
||||
@ -107,6 +113,8 @@ class BaseCommandInstance(BaseCommand):
|
||||
return CommandError('--hostname and one of --primary or --secondary is required.')
|
||||
elif self.enforce_hostname_set:
|
||||
return CommandError('--hostname is required.')
|
||||
elif self.enforce_primary_role:
|
||||
return CommandError('--primary is required.')
|
||||
elif self.enforce_roles:
|
||||
return CommandError('One of --primary or --secondary is required.')
|
||||
|
||||
@ -120,6 +128,11 @@ class BaseCommandInstance(BaseCommand):
|
||||
|
||||
if self.is_option_primary() and self.is_option_secondary() or not (self.is_option_primary() or self.is_option_secondary()):
|
||||
raise self.usage_error
|
||||
elif self.enforce_primary_role:
|
||||
if options['primary']:
|
||||
self.option_primary = options['primary']
|
||||
else:
|
||||
raise self.usage_error
|
||||
|
||||
if self.enforce_hostname_set:
|
||||
if options['hostname']:
|
||||
|
||||
34
awx/main/management/commands/cleanup_authtokens.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import logging
|
||||
|
||||
# Django
|
||||
from django.db import transaction
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.timezone import now
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
Management command to cleanup expired auth tokens
|
||||
'''
|
||||
|
||||
help = 'Cleanup expired auth tokens.'
|
||||
|
||||
def init_logging(self):
|
||||
self.logger = logging.getLogger('awx.main.commands.cleanup_authtokens')
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(logging.Formatter('%(message)s'))
|
||||
self.logger.addHandler(handler)
|
||||
self.logger.propagate = False
|
||||
|
||||
@transaction.atomic
|
||||
def handle(self, *args, **options):
|
||||
self.init_logging()
|
||||
tokens_removed = AuthToken.objects.filter(expires__lt=now())
|
||||
self.logger.log(99, "Removing %d expired auth tokens" % tokens_removed.count())
|
||||
tokens_removed.delete()
|
||||
@ -118,3 +118,10 @@ class Command(BaseCommand):
|
||||
self.logger.log(99, "Removed %d items", n_deleted_items)
|
||||
else:
|
||||
self.logger.log(99, "Would have removed %d items", n_deleted_items)
|
||||
|
||||
tokens_removed = AuthToken.objects.filter(expires__lt=now())
|
||||
if not self.dry_run:
|
||||
self.logger.log(99, "Removed %d expired auth tokens" % tokens_removed.count())
|
||||
tokens_removed.delete()
|
||||
else:
|
||||
self.logger.log(99, "Would have removed %d expired auth tokens" % tokens_removed.count())
|
||||
|
||||
@ -883,14 +883,14 @@ class Command(NoArgsCommand):
|
||||
all_obj = self.inventory
|
||||
all_name = 'inventory'
|
||||
db_variables = all_obj.variables_dict
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
db_variables = self.all_group.variables
|
||||
else:
|
||||
db_variables.update(self.all_group.variables)
|
||||
if db_variables != all_obj.variables_dict:
|
||||
all_obj.variables = json.dumps(db_variables)
|
||||
all_obj.save(update_fields=['variables'])
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
self.logger.info('%s variables replaced from "all" group', all_name.capitalize())
|
||||
else:
|
||||
self.logger.info('%s variables updated from "all" group', all_name.capitalize())
|
||||
@ -920,14 +920,14 @@ class Command(NoArgsCommand):
|
||||
for group in self.inventory.groups.filter(name__in=group_names):
|
||||
mem_group = self.all_group.all_groups[group.name]
|
||||
db_variables = group.variables_dict
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
db_variables = mem_group.variables
|
||||
else:
|
||||
db_variables.update(mem_group.variables)
|
||||
if db_variables != group.variables_dict:
|
||||
group.variables = json.dumps(db_variables)
|
||||
group.save(update_fields=['variables'])
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
self.logger.info('Group "%s" variables replaced', group.name)
|
||||
else:
|
||||
self.logger.info('Group "%s" variables updated', group.name)
|
||||
@ -959,7 +959,7 @@ class Command(NoArgsCommand):
|
||||
def _update_db_host_from_mem_host(self, db_host, mem_host):
|
||||
# Update host variables.
|
||||
db_variables = db_host.variables_dict
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
db_variables = mem_host.variables
|
||||
else:
|
||||
db_variables.update(mem_host.variables)
|
||||
@ -994,7 +994,7 @@ class Command(NoArgsCommand):
|
||||
else:
|
||||
self.logger.info('Host "%s" instance_id added', mem_host.name)
|
||||
if 'variables' in update_fields:
|
||||
if self.overwrite_vars or self.overwrite:
|
||||
if self.overwrite_vars:
|
||||
self.logger.info('Host "%s" variables replaced', mem_host.name)
|
||||
else:
|
||||
self.logger.info('Host "%s" variables updated', mem_host.name)
|
||||
|
||||
@ -130,6 +130,7 @@ class CallbackReceiver(object):
|
||||
'playbook_on_task_start',
|
||||
'playbook_on_no_hosts_matched',
|
||||
'playbook_on_no_hosts_remaining',
|
||||
'playbook_on_include',
|
||||
'playbook_on_import_for_host',
|
||||
'playbook_on_not_import_for_host'):
|
||||
parent = job_parent_events.get('playbook_on_play_start', None)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import os
|
||||
import logging
|
||||
import urllib
|
||||
import weakref
|
||||
from optparse import make_option
|
||||
from threading import Thread
|
||||
|
||||
@ -24,51 +25,157 @@ from socketio.namespace import BaseNamespace
|
||||
|
||||
logger = logging.getLogger('awx.main.commands.run_socketio_service')
|
||||
|
||||
valid_sockets = []
|
||||
class SocketSession(object):
|
||||
def __init__(self, session_id, token_key, socket):
|
||||
self.socket = weakref.ref(socket)
|
||||
self.session_id = session_id
|
||||
self.token_key = token_key
|
||||
self._valid = True
|
||||
|
||||
def is_valid(self):
|
||||
return bool(self._valid)
|
||||
|
||||
def invalidate(self):
|
||||
self._valid = False
|
||||
|
||||
def is_db_token_valid(self):
|
||||
auth_token = AuthToken.objects.filter(key=self.token_key, reason='')
|
||||
if not auth_token.exists():
|
||||
return False
|
||||
auth_token = auth_token[0]
|
||||
return bool(not auth_token.is_expired())
|
||||
|
||||
class SocketSessionManager(object):
|
||||
|
||||
def __init__(self):
|
||||
self.SESSIONS_MAX = 1000
|
||||
self.socket_sessions = []
|
||||
self.socket_session_token_key_map = {}
|
||||
|
||||
def _prune(self):
|
||||
if len(self.socket_sessions) > self.SESSIONS_MAX:
|
||||
session = self.socket_sessions[0]
|
||||
entries = self.socket_session_token_key_map[session.token_key]
|
||||
del entries[session.session_id]
|
||||
if len(entries) == 0:
|
||||
del self.socket_session_token_key_map[session.token_key]
|
||||
self.socket_sessions.pop(0)
|
||||
|
||||
'''
|
||||
Returns an dict of sessions <session_id, session>
|
||||
'''
|
||||
def lookup(self, token_key=None):
|
||||
if not token_key:
|
||||
raise ValueError("token_key required")
|
||||
return self.socket_session_token_key_map.get(token_key, None)
|
||||
|
||||
def add_session(self, session):
|
||||
self.socket_sessions.append(session)
|
||||
entries = self.socket_session_token_key_map.get(session.token_key, None)
|
||||
if not entries:
|
||||
entries = {}
|
||||
self.socket_session_token_key_map[session.token_key] = entries
|
||||
entries[session.session_id] = session
|
||||
self._prune()
|
||||
return session
|
||||
|
||||
class SocketController(object):
|
||||
|
||||
def __init__(self, SocketSessionManager):
|
||||
self.server = None
|
||||
self.SocketSessionManager = SocketSessionManager
|
||||
|
||||
def add_session(self, session):
|
||||
return self.SocketSessionManager.add_session(session)
|
||||
|
||||
def broadcast_packet(self, packet):
|
||||
# Broadcast message to everyone at endpoint
|
||||
# Loop over the 'raw' list of sockets (don't trust our list)
|
||||
for session_id, socket in list(self.server.sockets.iteritems()):
|
||||
socket_session = socket.session.get('socket_session', None)
|
||||
if socket_session and socket_session.is_valid():
|
||||
try:
|
||||
socket.send_packet(packet)
|
||||
except Exception, e:
|
||||
logger.error("Error sending client packet to %s: %s" % (str(session_id), str(packet)))
|
||||
logger.error("Error was: " + str(e))
|
||||
|
||||
def send_packet(self, packet, token_key):
|
||||
if not token_key:
|
||||
raise ValueError("token_key is required")
|
||||
socket_sessions = self.SocketSessionManager.lookup(token_key=token_key)
|
||||
# We may not find the socket_session if the user disconnected
|
||||
# (it's actually more compliciated than that because of our prune logic)
|
||||
if not socket_sessions:
|
||||
return None
|
||||
for session_id, socket_session in socket_sessions.iteritems():
|
||||
logger.warn("Maybe sending packet to %s" % session_id)
|
||||
if socket_session and socket_session.is_valid():
|
||||
logger.warn("Sending packet to %s" % session_id)
|
||||
socket = socket_session.socket()
|
||||
if socket:
|
||||
try:
|
||||
socket.send_packet(packet)
|
||||
except Exception, e:
|
||||
logger.error("Error sending client packet to %s: %s" % (str(socket_session.session_id), str(packet)))
|
||||
logger.error("Error was: " + str(e))
|
||||
|
||||
def set_server(self, server):
|
||||
self.server = server
|
||||
return server
|
||||
|
||||
socketController = SocketController(SocketSessionManager())
|
||||
|
||||
#
|
||||
# Socket session is attached to self.session['socket_session']
|
||||
# self.session and self.socket.session point to the same dict
|
||||
#
|
||||
class TowerBaseNamespace(BaseNamespace):
|
||||
|
||||
def get_allowed_methods(self):
|
||||
return ['recv_disconnect']
|
||||
|
||||
def get_initial_acl(self):
|
||||
global valid_sockets
|
||||
v_user = self.valid_user()
|
||||
self.is_valid_connection = False
|
||||
if v_user:
|
||||
if self.socket.sessid not in valid_sockets:
|
||||
valid_sockets.append(self.socket.sessid)
|
||||
self.is_valid_connection = True
|
||||
if len(valid_sockets) > 1000:
|
||||
valid_sockets = valid_sockets[1:]
|
||||
request_token = self._get_request_token()
|
||||
if request_token:
|
||||
# (1) This is the first time the socket has been seen (first
|
||||
# namespace joined).
|
||||
# (2) This socket has already been seen (already joined and maybe
|
||||
# left a namespace)
|
||||
#
|
||||
# Note: Assume that the user token is valid if the session is found
|
||||
socket_session = self.session.get('socket_session', None)
|
||||
if not socket_session:
|
||||
socket_session = SocketSession(self.socket.sessid, request_token, self.socket)
|
||||
if socket_session.is_db_token_valid():
|
||||
self.session['socket_session'] = socket_session
|
||||
socketController.add_session(socket_session)
|
||||
else:
|
||||
socket_session.invalidate()
|
||||
|
||||
return set(['recv_connect'] + self.get_allowed_methods())
|
||||
else:
|
||||
logger.warn("Authentication Failure validating user")
|
||||
self.emit("connect_failed", "Authentication failed")
|
||||
return set(['recv_connect'])
|
||||
|
||||
def valid_user(self):
|
||||
def _get_request_token(self):
|
||||
if 'QUERY_STRING' not in self.environ:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
k, v = self.environ['QUERY_STRING'].split("=")
|
||||
if k == "Token":
|
||||
token_actual = urllib.unquote_plus(v).decode().replace("\"","")
|
||||
auth_token = AuthToken.objects.filter(key=token_actual)
|
||||
if not auth_token.exists():
|
||||
return False
|
||||
auth_token = auth_token[0]
|
||||
if not auth_token.expired:
|
||||
return auth_token.user
|
||||
else:
|
||||
return False
|
||||
except Exception, e:
|
||||
logger.error("Exception validating user: " + str(e))
|
||||
return False
|
||||
|
||||
try:
|
||||
k, v = self.environ['QUERY_STRING'].split("=")
|
||||
if k == "Token":
|
||||
token_actual = urllib.unquote_plus(v).decode().replace("\"","")
|
||||
return token_actual
|
||||
except Exception, e:
|
||||
logger.error("Exception validating user: " + str(e))
|
||||
return False
|
||||
return False
|
||||
|
||||
def recv_connect(self):
|
||||
if not self.is_valid_connection:
|
||||
socket_session = self.session.get('socket_session', None)
|
||||
if socket_session and not socket_session.is_valid():
|
||||
self.disconnect(silent=False)
|
||||
|
||||
class TestNamespace(TowerBaseNamespace):
|
||||
@ -106,6 +213,14 @@ class ScheduleNamespace(TowerBaseNamespace):
|
||||
logger.info("Received client connect for schedule namespace from %s" % str(self.environ['REMOTE_ADDR']))
|
||||
super(ScheduleNamespace, self).recv_connect()
|
||||
|
||||
# Catch-all namespace.
|
||||
# Deliver 'global' events over this namespace
|
||||
class ControlNamespace(TowerBaseNamespace):
|
||||
|
||||
def recv_connect(self):
|
||||
logger.warn("Received client connect for control namespace from %s" % str(self.environ['REMOTE_ADDR']))
|
||||
super(ControlNamespace, self).recv_connect()
|
||||
|
||||
class TowerSocket(object):
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
@ -115,7 +230,8 @@ class TowerSocket(object):
|
||||
'/socket.io/jobs': JobNamespace,
|
||||
'/socket.io/job_events': JobEventNamespace,
|
||||
'/socket.io/ad_hoc_command_events': AdHocCommandEventNamespace,
|
||||
'/socket.io/schedules': ScheduleNamespace})
|
||||
'/socket.io/schedules': ScheduleNamespace,
|
||||
'/socket.io/control': ControlNamespace})
|
||||
else:
|
||||
logger.warn("Invalid connect path received: " + path)
|
||||
start_response('404 Not Found', [])
|
||||
@ -130,13 +246,12 @@ def notification_handler(server):
|
||||
'name': message['event'],
|
||||
'type': 'event',
|
||||
}
|
||||
for session_id, socket in list(server.sockets.iteritems()):
|
||||
if session_id in valid_sockets:
|
||||
try:
|
||||
socket.send_packet(packet)
|
||||
except Exception, e:
|
||||
logger.error("Error sending client packet to %s: %s" % (str(session_id), str(packet)))
|
||||
logger.error("Error was: " + str(e))
|
||||
|
||||
if 'token_key' in message:
|
||||
# Best practice not to send the token over the socket
|
||||
socketController.send_packet(packet, message.pop('token_key'))
|
||||
else:
|
||||
socketController.broadcast_packet(packet)
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
'''
|
||||
@ -164,6 +279,7 @@ class Command(NoArgsCommand):
|
||||
logger.info('Listening on port http://0.0.0.0:' + str(socketio_listen_port))
|
||||
server = SocketIOServer(('0.0.0.0', socketio_listen_port), TowerSocket(), resource='socket.io')
|
||||
|
||||
socketController.set_server(server)
|
||||
handler_thread = Thread(target=notification_handler, args=(server,))
|
||||
handler_thread.daemon = True
|
||||
handler_thread.start()
|
||||
|
||||
@ -26,7 +26,7 @@ class Command(BaseCommandInstance):
|
||||
def __init__(self):
|
||||
super(Command, self).__init__()
|
||||
|
||||
self.include_options_roles()
|
||||
self.include_option_primary_role()
|
||||
self.include_option_hostname_uuid_find()
|
||||
|
||||
@transaction.atomic
|
||||
|
||||
@ -18,6 +18,7 @@ import mongoengine
|
||||
# awx
|
||||
from awx.fact.models.fact import * # noqa
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.utils import timedelta_total_seconds
|
||||
|
||||
TEST_FACT_ANSIBLE = {
|
||||
"ansible_swapfree_mb" : 4092,
|
||||
@ -181,7 +182,7 @@ FACT_FIXTURES = {
|
||||
'ansible': TEST_FACT_ANSIBLE,
|
||||
'packages': TEST_FACT_PACKAGES,
|
||||
'services': TEST_FACT_SERVICES,
|
||||
'files': TEST_FACT_ANSIBLE,
|
||||
'files': TEST_FACT_FILES,
|
||||
}
|
||||
|
||||
EXPERIMENT_DEFAULT = {
|
||||
@ -198,12 +199,6 @@ EXPERIMENT_DEFAULT = {
|
||||
]
|
||||
}
|
||||
|
||||
# damn you python 2.6
|
||||
def timedelta_total_seconds(timedelta):
|
||||
return (
|
||||
timedelta.microseconds + 0.0 +
|
||||
(timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
|
||||
|
||||
class Experiment(object):
|
||||
def __init__(self, exp, fact_fixtures, raw_db, mongoengine_db):
|
||||
self.db = raw_db
|
||||
|
||||
@ -5,15 +5,17 @@ import logging
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.db.models.signals import post_save
|
||||
from django.db import IntegrityError
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.functional import curry
|
||||
from django.conf import settings
|
||||
|
||||
from awx import __version__ as version
|
||||
from awx.main.models import ActivityStream, Instance
|
||||
from awx.api.authentication import TokenAuthentication
|
||||
|
||||
|
||||
logger = logging.getLogger('awx.main.middleware')
|
||||
@ -60,8 +62,11 @@ class ActivityStreamMiddleware(threading.local):
|
||||
def set_actor(self, user, sender, instance, **kwargs):
|
||||
if sender == ActivityStream:
|
||||
if isinstance(user, User) and instance.actor is None:
|
||||
instance.actor = user
|
||||
instance.save(update_fields=['actor'])
|
||||
user = User.objects.filter(id=user.id)
|
||||
if user.exists():
|
||||
user = user[0]
|
||||
instance.actor = user
|
||||
instance.save(update_fields=['actor'])
|
||||
else:
|
||||
if instance.id not in self.instance_ids:
|
||||
self.instance_ids.append(instance.id)
|
||||
@ -100,3 +105,18 @@ class HAMiddleware(object):
|
||||
|
||||
# Redirect to the base page of the primary instance.
|
||||
return HttpResponseRedirect('http://%s%s' % (primary.hostname, request.path))
|
||||
|
||||
class AuthTokenTimeoutMiddleware(object):
|
||||
"""Presume that when the user includes the auth header, they go through the
|
||||
authentication mechanism. Further, that mechanism is presumed to extend
|
||||
the users session validity time by AUTH_TOKEN_EXPIRATION.
|
||||
|
||||
If the auth token is not supplied, then don't include the header
|
||||
"""
|
||||
def process_response(self, request, response):
|
||||
if not TokenAuthentication._get_x_auth_token_header(request):
|
||||
return response
|
||||
|
||||
response['Auth-Token-Timeout'] = int(settings.AUTH_TOKEN_EXPIRATION)
|
||||
return response
|
||||
|
||||
|
||||
538
awx/main/migrations/0071_v240_changes.py
Normal file
@ -0,0 +1,538 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from awx.api.license import feature_enabled
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
nowtime_actual = now()
|
||||
nowtime = nowtime_actual.strftime("%Y%m%dT%H%M%SZ")
|
||||
for stj in orm.SystemJobTemplate.objects.all():
|
||||
if not stj.schedules.count():
|
||||
if stj.name == "Cleanup Job Details":
|
||||
sched = orm.Schedule(name="Cleanup Job Schedule", rrule="DTSTART:%s RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU" % nowtime,
|
||||
description="Automatically Generated Schedule", enabled=True, extra_data={"days": "120"})
|
||||
elif stj.name == "Cleanup Deleted Data":
|
||||
sched = orm.Schedule(name="Cleanup Deleted Data Schedule", rrule="DTSTART:%s RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO" % nowtime,
|
||||
description="Automatically Generated Schedule", enabled=True, extra_data={"days": "30"})
|
||||
elif stj.name == "Cleanup Activity Stream":
|
||||
sched = orm.Schedule(name="Cleanup Activity Schedule", rrule="DTSTART:%s RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU" % nowtime,
|
||||
description="Automatically Generated Schedule", enabled=True, extra_data={"days": "355"})
|
||||
elif stj.name == "Cleanup Fact Details" and feature_enabled('system_tracking'):
|
||||
sched = orm.Schedule(name="Cleanup Fact Schedule", rrule="DTSTART:%s RRULE:FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1" % nowtime,
|
||||
description="Automatically Generated Schedule", enabled=True, extra_data={'older_than': '120d', 'granularity': '1w'})
|
||||
else:
|
||||
continue
|
||||
sched.unified_job_template = stj
|
||||
sched.created = nowtime_actual
|
||||
sched.modified = nowtime_actual
|
||||
sched.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
"Write your backwards methods here."
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'main.activitystream': {
|
||||
'Meta': {'object_name': 'ActivityStream'},
|
||||
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.AdHocCommand']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'custom_inventory_script': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.CustomInventoryScript']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'object1': ('django.db.models.fields.TextField', [], {}),
|
||||
'object2': ('django.db.models.fields.TextField', [], {}),
|
||||
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
|
||||
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
|
||||
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommand': {
|
||||
'Meta': {'object_name': 'AdHocCommand', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ad_hoc_commands'", 'symmetrical': 'False', 'through': "orm['main.AdHocCommandEvent']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'module_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'module_name': ('django.db.models.fields.CharField', [], {'default': "'command'", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommandevent': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('ad_hoc_command', 'host_name')]", 'object_name': 'AdHocCommandEvent'},
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_command_events'", 'to': "orm['main.AdHocCommand']"}),
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_command_events'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'})
|
||||
},
|
||||
'main.authtoken': {
|
||||
'Meta': {'object_name': 'AuthToken'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.credential': {
|
||||
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'become_method': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'become_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'become_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
|
||||
},
|
||||
'main.custominventoryscript': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'CustomInventoryScript'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'custom_inventory_scripts'", 'to': "orm['main.Organization']"}),
|
||||
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.host': {
|
||||
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
|
||||
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.instance': {
|
||||
'Meta': {'object_name': 'Instance'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
|
||||
},
|
||||
'main.inventory': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.inventorysource': {
|
||||
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'main.inventoryupdate': {
|
||||
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
|
||||
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.job': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.jobevent': {
|
||||
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
|
||||
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
|
||||
},
|
||||
'main.jobhostsummary': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
|
||||
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'main.joborigin': {
|
||||
'Meta': {'object_name': 'JobOrigin'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
|
||||
},
|
||||
'main.jobtemplate': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.organization': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'run_ad_hoc_commands': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.profile': {
|
||||
'Meta': {'object_name': 'Profile'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.project': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.schedule': {
|
||||
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.systemjob': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.systemjobtemplate': {
|
||||
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.team': {
|
||||
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.unifiedjob': {
|
||||
'Meta': {'object_name': 'UnifiedJob'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
|
||||
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.unifiedjobtemplate': {
|
||||
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['main']
|
||||
symmetrical = True
|
||||
522
awx/main/migrations/0072_v240_changes.py
Normal file
@ -0,0 +1,522 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'AuthToken.reason'
|
||||
db.add_column(u'main_authtoken', 'reason',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'AuthToken.reason'
|
||||
db.delete_column(u'main_authtoken', 'reason')
|
||||
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'main.activitystream': {
|
||||
'Meta': {'object_name': 'ActivityStream'},
|
||||
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.AdHocCommand']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'custom_inventory_script': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.CustomInventoryScript']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'object1': ('django.db.models.fields.TextField', [], {}),
|
||||
'object2': ('django.db.models.fields.TextField', [], {}),
|
||||
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
|
||||
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
|
||||
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommand': {
|
||||
'Meta': {'object_name': 'AdHocCommand', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ad_hoc_commands'", 'symmetrical': 'False', 'through': "orm['main.AdHocCommandEvent']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'module_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'module_name': ('django.db.models.fields.CharField', [], {'default': "'command'", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommandevent': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('ad_hoc_command', 'host_name')]", 'object_name': 'AdHocCommandEvent'},
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_command_events'", 'to': "orm['main.AdHocCommand']"}),
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_command_events'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'})
|
||||
},
|
||||
'main.authtoken': {
|
||||
'Meta': {'object_name': 'AuthToken'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'reason': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.credential': {
|
||||
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'become_method': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'become_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'become_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
|
||||
},
|
||||
'main.custominventoryscript': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'CustomInventoryScript'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'custom_inventory_scripts'", 'to': "orm['main.Organization']"}),
|
||||
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.host': {
|
||||
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
|
||||
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.instance': {
|
||||
'Meta': {'object_name': 'Instance'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
|
||||
},
|
||||
'main.inventory': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.inventorysource': {
|
||||
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'main.inventoryupdate': {
|
||||
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
|
||||
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.job': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.jobevent': {
|
||||
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
|
||||
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
|
||||
},
|
||||
'main.jobhostsummary': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
|
||||
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'main.joborigin': {
|
||||
'Meta': {'object_name': 'JobOrigin'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
|
||||
},
|
||||
'main.jobtemplate': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.organization': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'run_ad_hoc_commands': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.profile': {
|
||||
'Meta': {'object_name': 'Profile'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.project': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.schedule': {
|
||||
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.systemjob': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.systemjobtemplate': {
|
||||
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.team': {
|
||||
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.unifiedjob': {
|
||||
'Meta': {'object_name': 'UnifiedJob'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
|
||||
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.unifiedjobtemplate': {
|
||||
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['main']
|
||||
523
awx/main/migrations/0073_v240_changes.py
Normal file
@ -0,0 +1,523 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'Credential.security_token'
|
||||
db.add_column(u'main_credential', 'security_token',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'Credential.security_token'
|
||||
db.delete_column(u'main_credential', 'security_token')
|
||||
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'main.activitystream': {
|
||||
'Meta': {'object_name': 'ActivityStream'},
|
||||
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.AdHocCommand']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'custom_inventory_script': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.CustomInventoryScript']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'object1': ('django.db.models.fields.TextField', [], {}),
|
||||
'object2': ('django.db.models.fields.TextField', [], {}),
|
||||
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
|
||||
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
|
||||
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommand': {
|
||||
'Meta': {'object_name': 'AdHocCommand', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ad_hoc_commands'", 'symmetrical': 'False', 'through': "orm['main.AdHocCommandEvent']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'module_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'module_name': ('django.db.models.fields.CharField', [], {'default': "'command'", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommandevent': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('ad_hoc_command', 'host_name')]", 'object_name': 'AdHocCommandEvent'},
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_command_events'", 'to': "orm['main.AdHocCommand']"}),
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_command_events'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'})
|
||||
},
|
||||
'main.authtoken': {
|
||||
'Meta': {'object_name': 'AuthToken'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'reason': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.credential': {
|
||||
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'become_method': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'become_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'become_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'security_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
|
||||
},
|
||||
'main.custominventoryscript': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'CustomInventoryScript'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'custom_inventory_scripts'", 'to': "orm['main.Organization']"}),
|
||||
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.host': {
|
||||
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
|
||||
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.instance': {
|
||||
'Meta': {'object_name': 'Instance'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
|
||||
},
|
||||
'main.inventory': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.inventorysource': {
|
||||
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'main.inventoryupdate': {
|
||||
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
|
||||
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.job': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.jobevent': {
|
||||
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
|
||||
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
|
||||
},
|
||||
'main.jobhostsummary': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
|
||||
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'main.joborigin': {
|
||||
'Meta': {'object_name': 'JobOrigin'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
|
||||
},
|
||||
'main.jobtemplate': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.organization': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'run_ad_hoc_commands': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.profile': {
|
||||
'Meta': {'object_name': 'Profile'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.project': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.schedule': {
|
||||
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.systemjob': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.systemjobtemplate': {
|
||||
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.team': {
|
||||
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.unifiedjob': {
|
||||
'Meta': {'object_name': 'UnifiedJob'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
|
||||
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.unifiedjobtemplate': {
|
||||
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['main']
|
||||
519
awx/main/migrations/0074_v240_changes.py
Normal file
@ -0,0 +1,519 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Changing field 'CustomInventoryScript.organization'
|
||||
db.alter_column(u'main_custominventoryscript', 'organization_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['main.Organization']))
|
||||
|
||||
def backwards(self, orm):
|
||||
# Changing field 'CustomInventoryScript.organization'
|
||||
db.alter_column(u'main_custominventoryscript', 'organization_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['main.Organization']))
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'main.activitystream': {
|
||||
'Meta': {'object_name': 'ActivityStream'},
|
||||
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.AdHocCommand']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'custom_inventory_script': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.CustomInventoryScript']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'object1': ('django.db.models.fields.TextField', [], {}),
|
||||
'object2': ('django.db.models.fields.TextField', [], {}),
|
||||
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
|
||||
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
|
||||
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommand': {
|
||||
'Meta': {'object_name': 'AdHocCommand', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ad_hoc_commands'", 'symmetrical': 'False', 'through': "orm['main.AdHocCommandEvent']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'module_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'module_name': ('django.db.models.fields.CharField', [], {'default': "'command'", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.adhoccommandevent': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('ad_hoc_command', 'host_name')]", 'object_name': 'AdHocCommandEvent'},
|
||||
'ad_hoc_command': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_command_events'", 'to': "orm['main.AdHocCommand']"}),
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_command_events'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'})
|
||||
},
|
||||
'main.authtoken': {
|
||||
'Meta': {'object_name': 'AuthToken'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'reason': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.credential': {
|
||||
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'become_method': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'become_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'become_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'security_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
|
||||
},
|
||||
'main.custominventoryscript': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'CustomInventoryScript'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'custom_inventory_scripts'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.host': {
|
||||
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
|
||||
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.instance': {
|
||||
'Meta': {'object_name': 'Instance'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
|
||||
},
|
||||
'main.inventory': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.inventorysource': {
|
||||
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'main.inventoryupdate': {
|
||||
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
|
||||
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.job': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.jobevent': {
|
||||
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
|
||||
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
|
||||
},
|
||||
'main.jobhostsummary': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
|
||||
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'main.joborigin': {
|
||||
'Meta': {'object_name': 'JobOrigin'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
|
||||
},
|
||||
'main.jobtemplate': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.organization': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'run_ad_hoc_commands': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.profile': {
|
||||
'Meta': {'object_name': 'Profile'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.project': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.schedule': {
|
||||
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.systemjob': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.systemjobtemplate': {
|
||||
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.team': {
|
||||
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.unifiedjob': {
|
||||
'Meta': {'object_name': 'UnifiedJob'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
|
||||
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.unifiedjobtemplate': {
|
||||
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['main']
|
||||
@ -46,7 +46,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
||||
#('runas', _('Runas')),
|
||||
]
|
||||
|
||||
PASSWORD_FIELDS = ('password', 'ssh_key_data', 'ssh_key_unlock',
|
||||
PASSWORD_FIELDS = ('password', 'security_token', 'ssh_key_data', 'ssh_key_unlock',
|
||||
'become_password', 'vault_password')
|
||||
|
||||
class Meta:
|
||||
@ -101,6 +101,13 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
||||
help_text=_('Password for this credential (or "ASK" to prompt the '
|
||||
'user for machine credentials).'),
|
||||
)
|
||||
security_token = models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=1024,
|
||||
verbose_name=_('Security Token'),
|
||||
help_text=_('Security Token for this credential'),
|
||||
)
|
||||
project = models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
|
||||
@ -60,7 +60,7 @@ class Inventory(CommonModel):
|
||||
total_hosts = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
help_text=_('Total mumber of hosts in this inventory.'),
|
||||
help_text=_('Total number of hosts in this inventory.'),
|
||||
)
|
||||
hosts_with_active_failures = models.PositiveIntegerField(
|
||||
default=0,
|
||||
@ -1281,7 +1281,9 @@ class CustomInventoryScript(CommonModelNameNotUnique):
|
||||
'Organization',
|
||||
related_name='custom_inventory_scripts',
|
||||
help_text=_('Organization owning this inventory script'),
|
||||
on_delete=models.CASCADE,
|
||||
blank=False,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
|
||||
@ -240,7 +240,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)" %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and len(data[survey_element['variable']]) > survey_element['max']:
|
||||
errors.append("'%s' value %s is too large (must be no more than%s)" %
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)" %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'integer':
|
||||
if survey_element['variable'] in data:
|
||||
@ -250,7 +250,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \
|
||||
data[survey_element['variable']] > survey_element['max']:
|
||||
errors.append("'%s' value %s is too large (must be no more than%s)" %
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)" %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
if type(data[survey_element['variable']]) != int:
|
||||
errors.append("Value %s for %s expected to be an integer" % (data[survey_element['variable']],
|
||||
@ -261,7 +261,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)" %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > survey_element['max']:
|
||||
errors.append("'%s' value %s is too large (must be no more than%s)" %
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)" %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
if type(data[survey_element['variable']]) not in (float, int):
|
||||
errors.append("Value %s for %s expected to be a numeric type" % (data[survey_element['variable']],
|
||||
@ -283,6 +283,39 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
survey_element['choices']))
|
||||
return errors
|
||||
|
||||
def _update_unified_job_kwargs(self, **kwargs):
|
||||
if 'launch_type' in kwargs and kwargs['launch_type'] == 'relaunch':
|
||||
return kwargs
|
||||
|
||||
# Job Template extra_vars
|
||||
extra_vars = self.extra_vars_dict
|
||||
|
||||
# Overwrite with job template extra vars with survey default vars
|
||||
if self.survey_enabled and 'spec' in self.survey_spec:
|
||||
for survey_element in self.survey_spec.get("spec", []):
|
||||
if survey_element['default']:
|
||||
extra_vars[survey_element['variable']] = survey_element['default']
|
||||
|
||||
# transform to dict
|
||||
if 'extra_vars' in kwargs:
|
||||
kwargs_extra_vars = kwargs['extra_vars']
|
||||
if not isinstance(kwargs_extra_vars, dict):
|
||||
try:
|
||||
kwargs_extra_vars = json.loads(kwargs_extra_vars)
|
||||
except Exception:
|
||||
try:
|
||||
yaml.safe_load(kwargs_extra_vars)
|
||||
except:
|
||||
kwargs_extra_vars = {}
|
||||
else:
|
||||
kwargs_extra_vars = {}
|
||||
|
||||
# Overwrite job template extra vars with explicit job extra vars
|
||||
# and add on job extra vars
|
||||
extra_vars.update(kwargs_extra_vars)
|
||||
kwargs['extra_vars'] = json.dumps(extra_vars)
|
||||
return kwargs
|
||||
|
||||
@property
|
||||
def cache_timeout_blocked(self):
|
||||
if Job.objects.filter(job_template=self, status__in=['pending', 'waiting', 'running']).count() > getattr(settings, 'SCHEDULE_MAX_JOBS', 10):
|
||||
@ -826,7 +859,7 @@ class JobEvent(CreatedModifiedModel):
|
||||
try:
|
||||
failures_dict = self.event_data.get('failures', {})
|
||||
dark_dict = self.event_data.get('dark', {})
|
||||
self.failed = bool(sum(failures_dict.values()) +
|
||||
self.failed = bool(sum(failures_dict.values()) +
|
||||
sum(dark_dict.values()))
|
||||
if 'failed' not in update_fields:
|
||||
update_fields.append('failed')
|
||||
@ -1052,4 +1085,3 @@ class SystemJob(UnifiedJob, SystemJobOptions):
|
||||
@property
|
||||
def task_impact(self):
|
||||
return 150
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ from django.conf import settings
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.timezone import now
|
||||
from django.utils.timezone import now as tz_now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# AWX
|
||||
from awx.main.fields import AutoOneToOneField
|
||||
@ -52,6 +53,12 @@ class Organization(CommonModel):
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def mark_inactive(self, save=True):
|
||||
for script in self.custom_inventory_scripts.all():
|
||||
script.organization = None
|
||||
script.save()
|
||||
super(Organization, self).mark_inactive(save=save)
|
||||
|
||||
|
||||
class Team(CommonModelNameNotUnique):
|
||||
'''
|
||||
@ -128,7 +135,8 @@ class Permission(CommonModelNameNotUnique):
|
||||
# the project parameter is not used when dealing with READ, WRITE, or ADMIN permissions.
|
||||
|
||||
permission_type = models.CharField(max_length=64, choices=PERMISSION_TYPE_CHOICES)
|
||||
run_ad_hoc_commands = models.BooleanField(default=False)
|
||||
run_ad_hoc_commands = models.BooleanField(default=False,
|
||||
help_text=_('Execute Commands on the Inventory'))
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode("Permission(name=%s,ON(user=%s,team=%s),FOR(project=%s,inventory=%s,type=%s%s))" % (
|
||||
@ -164,13 +172,29 @@ class Profile(CreatedModifiedModel):
|
||||
default='',
|
||||
)
|
||||
|
||||
"""
|
||||
Since expiration and session expiration is event driven a token could be
|
||||
invalidated for both reasons. Further, we only support a single reason for a
|
||||
session token being invalid. For this case, mark the token as expired.
|
||||
|
||||
Note: Again, because the value of reason is event based. The reason may not be
|
||||
set (i.e. may equal '') even though a session is expired or a limit is reached.
|
||||
"""
|
||||
class AuthToken(BaseModel):
|
||||
'''
|
||||
Custom authentication tokens per user with expiration and request-specific
|
||||
data.
|
||||
'''
|
||||
|
||||
REASON_CHOICES = [
|
||||
('', _('Token not invalidated')),
|
||||
('timeout_reached', _('Token is expired')),
|
||||
('limit_reached', _('Maximum per-user sessions reached')),
|
||||
# invalid_token is not a used data-base value, but is returned by the
|
||||
# api when a token is not found
|
||||
('invalid_token', _('Invalid token')),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -179,8 +203,21 @@ class AuthToken(BaseModel):
|
||||
on_delete=models.CASCADE)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
expires = models.DateTimeField(default=now)
|
||||
expires = models.DateTimeField(default=tz_now)
|
||||
request_hash = models.CharField(max_length=40, blank=True, default='')
|
||||
reason = models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Reason the auth token was invalidated.')
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def reason_long(reason):
|
||||
for x in AuthToken.REASON_CHOICES:
|
||||
if x[0] == reason:
|
||||
return unicode(x[1])
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_request_hash(cls, request):
|
||||
@ -201,25 +238,65 @@ class AuthToken(BaseModel):
|
||||
self.key = self.generate_key()
|
||||
return super(AuthToken, self).save(*args, **kwargs)
|
||||
|
||||
def refresh(self, save=True):
|
||||
if not self.pk or not self.expired:
|
||||
self.expires = now() + datetime.timedelta(seconds=settings.AUTH_TOKEN_EXPIRATION)
|
||||
def refresh(self, now=None, save=True):
|
||||
if not now:
|
||||
now = tz_now()
|
||||
if not self.pk or not self.is_expired(now=now):
|
||||
self.expires = now + datetime.timedelta(seconds=settings.AUTH_TOKEN_EXPIRATION)
|
||||
if save:
|
||||
self.save()
|
||||
|
||||
def invalidate(self, save=True):
|
||||
if not self.expired:
|
||||
self.expires = now() - datetime.timedelta(seconds=1)
|
||||
if save:
|
||||
self.save()
|
||||
def invalidate(self, reason='timeout_reached', save=True):
|
||||
if not AuthToken.reason_long(reason):
|
||||
raise ValueError('Invalid reason specified')
|
||||
self.reason = reason
|
||||
if save:
|
||||
self.save()
|
||||
return reason
|
||||
|
||||
@staticmethod
|
||||
def get_tokens_over_limit(user, now=None):
|
||||
if now is None:
|
||||
now = tz_now()
|
||||
invalid_tokens = AuthToken.objects.none()
|
||||
if settings.AUTH_TOKEN_PER_USER != -1:
|
||||
invalid_tokens = AuthToken.objects.filter(
|
||||
user=user,
|
||||
expires__gt=now,
|
||||
reason='',
|
||||
).order_by('-created')[settings.AUTH_TOKEN_PER_USER:]
|
||||
return invalid_tokens
|
||||
|
||||
def generate_key(self):
|
||||
unique = uuid.uuid4()
|
||||
return hmac.new(unique.bytes, digestmod=hashlib.sha1).hexdigest()
|
||||
|
||||
def is_expired(self, now=None):
|
||||
if not now:
|
||||
now = tz_now()
|
||||
return bool(self.expires < now)
|
||||
|
||||
@property
|
||||
def expired(self):
|
||||
return bool(self.expires < now())
|
||||
def invalidated(self):
|
||||
return bool(self.reason != '')
|
||||
|
||||
"""
|
||||
Token is valid if it's in the set of unexpired tokens.
|
||||
The unexpired token set is:
|
||||
* tokens not expired
|
||||
* limited to number of tokens per-user
|
||||
* sorted by created on date
|
||||
"""
|
||||
def in_valid_tokens(self, now=None):
|
||||
if not now:
|
||||
now = tz_now()
|
||||
valid_n_tokens_qs = self.user.auth_tokens.filter(
|
||||
expires__gt=now,
|
||||
reason='',
|
||||
).order_by('-created')[0:settings.AUTH_TOKEN_PER_USER]
|
||||
valid_n_tokens = valid_n_tokens_qs.values_list('key', flat=True)
|
||||
|
||||
return bool(self.key in valid_n_tokens)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.key
|
||||
@ -231,7 +308,7 @@ def user_mark_inactive(user, save=True):
|
||||
if user.is_active:
|
||||
# Set timestamp to datetime.isoformat() but without the time zone
|
||||
# offset to stay withint the 30 character username limit.
|
||||
dtnow = now()
|
||||
dtnow = tz_now()
|
||||
deleted_ts = dtnow.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||
user.username = '_d_%s' % deleted_ts
|
||||
user.is_active = False
|
||||
|
||||
@ -343,6 +343,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
||||
|
||||
LAUNCH_TYPE_CHOICES = [
|
||||
('manual', _('Manual')), # Job was started manually by a user.
|
||||
('relaunch', _('Relaunch')), # Job was started via relaunch.
|
||||
('callback', _('Callback')), # Job was started via host callback.
|
||||
('scheduled', _('Scheduled')), # Job was started from a schedule.
|
||||
('dependency', _('Dependency')), # Job was started as a dependency of another job.
|
||||
|
||||
@ -162,6 +162,7 @@ def handle_work_error(self, task_id, subtasks=None):
|
||||
print('Executing error task id %s, subtasks: %s' %
|
||||
(str(self.request.id), str(subtasks)))
|
||||
first_task = None
|
||||
first_task_id = None
|
||||
first_task_type = ''
|
||||
first_task_name = ''
|
||||
if subtasks is not None:
|
||||
@ -169,10 +170,10 @@ def handle_work_error(self, task_id, subtasks=None):
|
||||
instance_name = ''
|
||||
if each_task['type'] == 'project_update':
|
||||
instance = ProjectUpdate.objects.get(id=each_task['id'])
|
||||
instance_name = instance.project.name
|
||||
instance_name = instance.name
|
||||
elif each_task['type'] == 'inventory_update':
|
||||
instance = InventoryUpdate.objects.get(id=each_task['id'])
|
||||
instance_name = instance.inventory_source.inventory.name
|
||||
instance_name = instance.name
|
||||
elif each_task['type'] == 'job':
|
||||
instance = Job.objects.get(id=each_task['id'])
|
||||
instance_name = instance.job_template.name
|
||||
@ -184,13 +185,14 @@ def handle_work_error(self, task_id, subtasks=None):
|
||||
break
|
||||
if first_task is None:
|
||||
first_task = instance
|
||||
first_task_id = instance.id
|
||||
first_task_type = each_task['type']
|
||||
first_task_name = instance_name
|
||||
if instance.celery_task_id != task_id:
|
||||
instance.status = 'failed'
|
||||
instance.failed = True
|
||||
instance.job_explanation = "Previous Task Failed: %s for %s with celery task id: %s" % \
|
||||
(first_task_type, first_task_name, task_id)
|
||||
instance.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \
|
||||
(first_task_type, first_task_name, first_task_id)
|
||||
instance.save()
|
||||
instance.socketio_emit_status("failed")
|
||||
|
||||
@ -345,6 +347,8 @@ class BaseTask(Task):
|
||||
if local_site_packages not in python_paths:
|
||||
python_paths.insert(0, local_site_packages)
|
||||
env['PYTHONPATH'] = os.pathsep.join(python_paths)
|
||||
if self.should_use_proot:
|
||||
env['PROOT_TMP_DIR'] = settings.AWX_PROOT_BASE_PATH
|
||||
return env
|
||||
|
||||
def build_safe_env(self, instance, **kwargs):
|
||||
@ -423,6 +427,15 @@ class BaseTask(Task):
|
||||
'''
|
||||
logfile = stdout_handle
|
||||
logfile_pos = logfile.tell()
|
||||
if hasattr(instance, "extra_vars_dict") and "PEXPECT_SLEEP" in instance.extra_vars_dict:
|
||||
pexpect_sleep = int(instance.extra_vars_dict['PEXPECT_SLEEP'])
|
||||
elif 'PEXPECT_SLEEP' in os.environ:
|
||||
pexpect_sleep = int(os.environ['PEXPECT_SLEEP'])
|
||||
else:
|
||||
pexpect_sleep = None
|
||||
if pexpect_sleep is not None:
|
||||
logger.info("Suspending Job Execution for QA Work")
|
||||
time.sleep(pexpect_sleep)
|
||||
child = pexpect.spawnu(args[0], args[1:], cwd=cwd, env=env)
|
||||
child.logfile_read = logfile
|
||||
canceled = False
|
||||
@ -601,6 +614,21 @@ class RunJob(BaseTask):
|
||||
if credential.ssh_key_data not in (None, ''):
|
||||
private_data[cred_name] = decrypt_field(credential, 'ssh_key_data') or ''
|
||||
|
||||
if job.cloud_credential and job.cloud_credential.kind == 'openstack':
|
||||
credential = job.cloud_credential
|
||||
openstack_auth = dict(auth_url=credential.host,
|
||||
username=credential.username,
|
||||
password=decrypt_field(credential, "password"),
|
||||
project_name=credential.project)
|
||||
openstack_data = {
|
||||
'clouds': {
|
||||
'devstack': {
|
||||
'auth': openstack_auth,
|
||||
},
|
||||
},
|
||||
}
|
||||
private_data['cloud_credential'] = yaml.safe_dump(openstack_data, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
return private_data
|
||||
|
||||
def build_passwords(self, job, **kwargs):
|
||||
@ -625,12 +653,17 @@ class RunJob(BaseTask):
|
||||
Build environment dictionary for ansible-playbook.
|
||||
'''
|
||||
plugin_dir = self.get_path_to('..', 'plugins', 'callback')
|
||||
plugin_dirs = [plugin_dir]
|
||||
if hasattr(settings, 'AWX_ANSIBLE_CALLBACK_PLUGINS') and \
|
||||
settings.AWX_ANSIBLE_CALLBACK_PLUGINS:
|
||||
plugin_dirs.append(settings.AWX_ANSIBLE_CALLBACK_PLUGINS)
|
||||
plugin_path = ':'.join(plugin_dirs)
|
||||
env = super(RunJob, self).build_env(job, **kwargs)
|
||||
# Set environment variables needed for inventory and job event
|
||||
# callbacks to work.
|
||||
env['JOB_ID'] = str(job.pk)
|
||||
env['INVENTORY_ID'] = str(job.inventory.pk)
|
||||
env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_dir
|
||||
env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_path
|
||||
env['REST_API_URL'] = settings.INTERNAL_API_URL
|
||||
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
||||
env['CALLBACK_CONSUMER_PORT'] = str(settings.CALLBACK_CONSUMER_PORT)
|
||||
@ -654,6 +687,8 @@ class RunJob(BaseTask):
|
||||
if cloud_cred and cloud_cred.kind == 'aws':
|
||||
env['AWS_ACCESS_KEY'] = cloud_cred.username
|
||||
env['AWS_SECRET_KEY'] = decrypt_field(cloud_cred, 'password')
|
||||
if len(cloud_cred.security_token) > 0:
|
||||
env['AWS_SECURITY_TOKEN'] = decrypt_field(cloud_cred, 'security_token')
|
||||
# FIXME: Add EC2_URL, maybe EC2_REGION!
|
||||
elif cloud_cred and cloud_cred.kind == 'rax':
|
||||
env['RAX_USERNAME'] = cloud_cred.username
|
||||
@ -669,6 +704,8 @@ class RunJob(BaseTask):
|
||||
env['VMWARE_USER'] = cloud_cred.username
|
||||
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
|
||||
env['VMWARE_HOST'] = cloud_cred.host
|
||||
elif cloud_cred and cloud_cred.kind == 'openstack':
|
||||
env['OS_CLIENT_CONFIG_FILE'] = kwargs.get('private_data_files', {}).get('cloud_credential', '')
|
||||
|
||||
# Set environment variables related to scan jobs
|
||||
if job.job_type == PERM_INVENTORY_SCAN:
|
||||
@ -1116,7 +1153,7 @@ class RunInventoryUpdate(BaseTask):
|
||||
if credential:
|
||||
for subkey in ('username', 'host', 'project'):
|
||||
passwords['source_%s' % subkey] = getattr(credential, subkey)
|
||||
for passkey in ('password', 'ssh_key_data'):
|
||||
for passkey in ('password', 'ssh_key_data', 'security_token'):
|
||||
k = 'source_%s' % passkey
|
||||
passwords[k] = decrypt_field(credential, passkey)
|
||||
return passwords
|
||||
@ -1149,6 +1186,8 @@ class RunInventoryUpdate(BaseTask):
|
||||
if passwords.get('source_username', '') and passwords.get('source_password', ''):
|
||||
env['AWS_ACCESS_KEY_ID'] = passwords['source_username']
|
||||
env['AWS_SECRET_ACCESS_KEY'] = passwords['source_password']
|
||||
if len(passwords['source_security_token']) > 0:
|
||||
env['AWS_SECURITY_TOKEN'] = passwords['source_security_token']
|
||||
env['EC2_INI_PATH'] = cloud_credential
|
||||
elif inventory_update.source == 'rax':
|
||||
env['RAX_CREDS_FILE'] = cloud_credential
|
||||
@ -1169,7 +1208,7 @@ class RunInventoryUpdate(BaseTask):
|
||||
env['GCE_PROJECT'] = passwords.get('source_project', '')
|
||||
env['GCE_PEM_FILE_PATH'] = cloud_credential
|
||||
elif inventory_update.source == 'openstack':
|
||||
env['OPENSTACK_CONFIG_FILE'] = cloud_credential
|
||||
env['OS_CLIENT_CONFIG_FILE'] = cloud_credential
|
||||
elif inventory_update.source == 'file':
|
||||
# FIXME: Parse source_env to dict, update env.
|
||||
pass
|
||||
@ -1188,7 +1227,7 @@ class RunInventoryUpdate(BaseTask):
|
||||
inventory = inventory_source.group.inventory
|
||||
|
||||
# Piece together the initial command to run via. the shell.
|
||||
args = ['awx-manage', 'inventory_import']
|
||||
args = ['tower-manage', 'inventory_import']
|
||||
args.extend(['--inventory-id', str(inventory.pk)])
|
||||
|
||||
# Add appropriate arguments for overwrite if the inventory_update
|
||||
@ -1450,7 +1489,7 @@ class RunSystemJob(BaseTask):
|
||||
model = SystemJob
|
||||
|
||||
def build_args(self, system_job, **kwargs):
|
||||
args = ['awx-manage', system_job.job_type]
|
||||
args = ['tower-manage', system_job.job_type]
|
||||
try:
|
||||
json_vars = json.loads(system_job.extra_vars)
|
||||
if 'days' in json_vars and system_job.job_type != 'cleanup_facts':
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from awx.main.tests.organizations import OrganizationsTest # noqa
|
||||
from awx.main.tests.organizations import * # noqa
|
||||
from awx.main.tests.users import * # noqa
|
||||
from awx.main.tests.inventory import * # noqa
|
||||
from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest # noqa
|
||||
|
||||
@ -28,11 +28,11 @@ from django.test.utils import override_settings
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.backend import LDAPSettings
|
||||
from awx.main.management.commands.run_callback_receiver import CallbackReceiver
|
||||
from awx.main.management.commands.run_task_system import run_taskmanager
|
||||
from awx.main.utils import get_ansible_version
|
||||
from awx.main.task_engine import TaskEngager as LicenseWriter
|
||||
from awx.sso.backends import LDAPSettings
|
||||
|
||||
TEST_PLAYBOOK = '''- hosts: mygroup
|
||||
gather_facts: false
|
||||
@ -56,8 +56,12 @@ class QueueTestMixin(object):
|
||||
|
||||
def start_redis(self):
|
||||
if not getattr(self, 'redis_process', None):
|
||||
self.redis_process = Popen('redis-server --port 16379 > /dev/null',
|
||||
shell=True, executable='/bin/bash')
|
||||
# Centos 6.5 redis is runnable by non-root user but is not in a normal users path by default
|
||||
env = dict(os.environ)
|
||||
env['PATH'] = '%s:/usr/sbin/' % env['PATH']
|
||||
self.redis_process = Popen('echo "port 16379" | redis-server - > /dev/null',
|
||||
shell=True, executable='/bin/bash',
|
||||
env=env)
|
||||
|
||||
def stop_redis(self):
|
||||
if getattr(self, 'redis_process', None):
|
||||
@ -188,6 +192,20 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
||||
self._temp_paths.append(license_path)
|
||||
os.environ['AWX_LICENSE_FILE'] = license_path
|
||||
|
||||
def create_basic_license_file(self, instance_count=100, license_date=int(time.time() + 3600)):
|
||||
writer = LicenseWriter(
|
||||
company_name='AWX',
|
||||
contact_name='AWX Admin',
|
||||
contact_email='awx@example.com',
|
||||
license_date=license_date,
|
||||
instance_count=instance_count,
|
||||
license_type='basic')
|
||||
handle, license_path = tempfile.mkstemp(suffix='.json')
|
||||
os.close(handle)
|
||||
writer.write_file(license_path)
|
||||
self._temp_paths.append(license_path)
|
||||
os.environ['AWX_LICENSE_FILE'] = license_path
|
||||
|
||||
def create_expired_license_file(self, instance_count=1000, grace_period=False):
|
||||
license_date = time.time() - 1
|
||||
if not grace_period:
|
||||
@ -456,8 +474,8 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
||||
assert response.status_code == expect, "expected status %s, got %s for url=%s as auth=%s: %s" % (expect, response.status_code, url, auth, response.content)
|
||||
if method_name == 'head':
|
||||
self.assertFalse(response.content)
|
||||
#if return_response_object:
|
||||
# return response
|
||||
if return_response_object:
|
||||
return response
|
||||
if response.status_code not in [204, 405] and method_name != 'head' and response.content:
|
||||
# no JSON responses in these at least for now, 409 should probably return some (FIXME)
|
||||
if response['Content-Type'].startswith('application/json'):
|
||||
@ -700,7 +718,8 @@ class BaseLiveServerTest(BaseTestMixin, django.test.LiveServerTestCase):
|
||||
|
||||
@override_settings(CELERY_ALWAYS_EAGER=True,
|
||||
CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
|
||||
ANSIBLE_TRANSPORT='local')
|
||||
ANSIBLE_TRANSPORT='local',
|
||||
DEBUG=True)
|
||||
class BaseJobExecutionTest(QueueStartStopTestMixin, BaseLiveServerTest):
|
||||
'''
|
||||
Base class for celery task tests.
|
||||
|
||||
@ -7,3 +7,6 @@ from .run_fact_cache_receiver import * # noqa
|
||||
from .commands_monolithic import * # noqa
|
||||
from .cleanup_facts import * # noqa
|
||||
from .age_deleted import * # noqa
|
||||
from .remove_instance import * # noqa
|
||||
from .run_socketio_service import * # noqa
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import urlparse
|
||||
import unittest
|
||||
import unittest2 as unittest
|
||||
|
||||
# Django
|
||||
import django
|
||||
@ -26,9 +26,6 @@ from django.test.utils import override_settings
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTest, BaseLiveServerTest
|
||||
|
||||
if not hasattr(unittest, 'skipIf'):
|
||||
import unittest2 as unittest
|
||||
|
||||
__all__ = ['CreateDefaultOrgTest', 'DumpDataTest', 'CleanupDeletedTest',
|
||||
'CleanupJobsTest', 'CleanupActivityStreamTest',
|
||||
'InventoryImportTest']
|
||||
@ -831,7 +828,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest):
|
||||
host_names = set(new_inv.hosts.filter(active=True).values_list('name', flat=True))
|
||||
self.assertEqual(expected_host_names, host_names)
|
||||
expected_inv_vars = {'vara': 'A', 'varc': 'C'}
|
||||
if overwrite or overwrite_vars:
|
||||
if overwrite_vars:
|
||||
expected_inv_vars.pop('varc')
|
||||
self.assertEqual(new_inv.variables_dict, expected_inv_vars)
|
||||
for host in new_inv.hosts.filter(active=True):
|
||||
@ -849,7 +846,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest):
|
||||
for group in new_inv.groups.filter(active=True):
|
||||
if group.name == 'servers':
|
||||
expected_vars = {'varb': 'B', 'vard': 'D'}
|
||||
if overwrite or overwrite_vars:
|
||||
if overwrite_vars:
|
||||
expected_vars.pop('vard')
|
||||
self.assertEqual(group.variables_dict, expected_vars)
|
||||
children = set(group.children.filter(active=True).values_list('name', flat=True))
|
||||
|
||||
39
awx/main/tests/commands/remove_instance.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
# Python
|
||||
import uuid
|
||||
|
||||
# AWX
|
||||
from awx.main.tests.base import BaseTest
|
||||
from awx.main.tests.commands.base import BaseCommandMixin
|
||||
from awx.main.models import * # noqa
|
||||
|
||||
__all__ = ['RemoveInstanceCommandFunctionalTest']
|
||||
|
||||
class RemoveInstanceCommandFunctionalTest(BaseCommandMixin, BaseTest):
|
||||
uuids = []
|
||||
instances = []
|
||||
|
||||
def setup_instances(self):
|
||||
self.uuids = [uuid.uuid4().hex for x in range(0, 3)]
|
||||
self.instances.append(Instance(uuid=settings.SYSTEM_UUID, primary=True, hostname='127.0.0.1'))
|
||||
self.instances.append(Instance(uuid=self.uuids[0], primary=False, hostname='127.0.0.2'))
|
||||
self.instances.append(Instance(uuid=self.uuids[1], primary=False, hostname='127.0.0.3'))
|
||||
self.instances.append(Instance(uuid=self.uuids[2], primary=False, hostname='127.0.0.4'))
|
||||
for x in self.instances:
|
||||
x.save()
|
||||
|
||||
def setUp(self):
|
||||
super(RemoveInstanceCommandFunctionalTest, self).setUp()
|
||||
self.create_test_license_file()
|
||||
self.setup_instances()
|
||||
self.setup_users()
|
||||
|
||||
def test_default(self):
|
||||
self.assertEqual(Instance.objects.filter(hostname="127.0.0.2").count(), 1)
|
||||
result, stdout, stderr = self.run_command('remove_instance', hostname='127.0.0.2')
|
||||
self.assertIsNone(result)
|
||||
self.assertEqual(stdout, 'Successfully removed instance (uuid="%s",hostname="127.0.0.2",role="secondary").\n' % (self.uuids[0]))
|
||||
self.assertEqual(Instance.objects.filter(hostname="127.0.0.2").count(), 0)
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import time
|
||||
from datetime import datetime
|
||||
import mock
|
||||
import unittest
|
||||
import unittest2 as unittest
|
||||
from copy import deepcopy
|
||||
from mock import MagicMock
|
||||
|
||||
|
||||
116
awx/main/tests/commands/run_socketio_service.py
Normal file
@ -0,0 +1,116 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
# Python
|
||||
from mock import MagicMock, Mock
|
||||
|
||||
# Django
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
# AWX
|
||||
from awx.fact.models.fact import * # noqa
|
||||
from awx.main.management.commands.run_socketio_service import SocketSessionManager, SocketSession, SocketController
|
||||
|
||||
__all__ = ['SocketSessionManagerUnitTest', 'SocketControllerUnitTest',]
|
||||
|
||||
class WeakRefable():
|
||||
pass
|
||||
|
||||
class SocketSessionManagerUnitTest(SimpleTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.session_manager = SocketSessionManager()
|
||||
super(SocketSessionManagerUnitTest, self).setUp()
|
||||
|
||||
def create_sessions(self, count, token_key=None):
|
||||
self.sessions = []
|
||||
self.count = count
|
||||
for i in range(0, count):
|
||||
self.sessions.append(SocketSession(i, token_key or i, WeakRefable()))
|
||||
self.session_manager.add_session(self.sessions[i])
|
||||
|
||||
def test_multiple_session_diff_token(self):
|
||||
self.create_sessions(10)
|
||||
|
||||
for s in self.sessions:
|
||||
self.assertIn(s.token_key, self.session_manager.socket_session_token_key_map)
|
||||
self.assertEqual(s, self.session_manager.socket_session_token_key_map[s.token_key][s.session_id])
|
||||
|
||||
|
||||
def test_multiple_session_same_token(self):
|
||||
self.create_sessions(10, token_key='foo')
|
||||
|
||||
sessions_dict = self.session_manager.lookup("foo")
|
||||
self.assertEqual(len(sessions_dict), 10)
|
||||
for s in self.sessions:
|
||||
self.assertIn(s.session_id, sessions_dict)
|
||||
self.assertEqual(s, sessions_dict[s.session_id])
|
||||
|
||||
def test_prune_sessions_max(self):
|
||||
self.create_sessions(self.session_manager.SESSIONS_MAX + 10)
|
||||
|
||||
self.assertEqual(len(self.session_manager.socket_sessions), self.session_manager.SESSIONS_MAX)
|
||||
|
||||
|
||||
class SocketControllerUnitTest(SimpleTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.socket_controller = SocketController(SocketSessionManager())
|
||||
server = Mock()
|
||||
self.socket_controller.set_server(server)
|
||||
super(SocketControllerUnitTest, self).setUp()
|
||||
|
||||
def create_clients(self, count, token_key=None):
|
||||
self.sessions = []
|
||||
self.sockets =[]
|
||||
self.count = count
|
||||
self.sockets_dict = {}
|
||||
for i in range(0, count):
|
||||
if isinstance(token_key, list):
|
||||
token_key_actual = token_key[i]
|
||||
else:
|
||||
token_key_actual = token_key or i
|
||||
socket = MagicMock(session=dict())
|
||||
socket_session = SocketSession(i, token_key_actual, socket)
|
||||
self.sockets.append(socket)
|
||||
self.sessions.append(socket_session)
|
||||
self.sockets_dict[i] = socket
|
||||
self.socket_controller.add_session(socket_session)
|
||||
|
||||
socket.session['socket_session'] = socket_session
|
||||
socket.send_packet = Mock()
|
||||
self.socket_controller.server.sockets = self.sockets_dict
|
||||
|
||||
def test_broadcast_packet(self):
|
||||
self.create_clients(10)
|
||||
packet = {
|
||||
"hello": "world"
|
||||
}
|
||||
self.socket_controller.broadcast_packet(packet)
|
||||
for s in self.sockets:
|
||||
s.send_packet.assert_called_with(packet)
|
||||
|
||||
def test_send_packet(self):
|
||||
self.create_clients(5, token_key=[0, 1, 2, 3, 4])
|
||||
packet = {
|
||||
"hello": "world"
|
||||
}
|
||||
self.socket_controller.send_packet(packet, 2)
|
||||
self.assertEqual(0, len(self.sockets[0].send_packet.mock_calls))
|
||||
self.assertEqual(0, len(self.sockets[1].send_packet.mock_calls))
|
||||
self.sockets[2].send_packet.assert_called_once_with(packet)
|
||||
self.assertEqual(0, len(self.sockets[3].send_packet.mock_calls))
|
||||
self.assertEqual(0, len(self.sockets[4].send_packet.mock_calls))
|
||||
|
||||
def test_send_packet_multiple_sessions_one_token(self):
|
||||
self.create_clients(5, token_key=[0, 1, 1, 1, 2])
|
||||
packet = {
|
||||
"hello": "world"
|
||||
}
|
||||
self.socket_controller.send_packet(packet, 1)
|
||||
self.assertEqual(0, len(self.sockets[0].send_packet.mock_calls))
|
||||
self.sockets[1].send_packet.assert_called_once_with(packet)
|
||||
self.sockets[2].send_packet.assert_called_once_with(packet)
|
||||
self.sockets[3].send_packet.assert_called_once_with(packet)
|
||||
self.assertEqual(0, len(self.sockets[4].send_packet.mock_calls))
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# All Rights Reserved
|
||||
|
||||
# Python
|
||||
import unittest
|
||||
import unittest2 as unittest
|
||||
|
||||
# Django
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
@ -1665,6 +1665,54 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
inventory_source.save()
|
||||
self.check_inventory_source(inventory_source, initial=False)
|
||||
|
||||
|
||||
def test_update_from_ec2_sts_iam(self):
|
||||
source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '')
|
||||
source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '')
|
||||
source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all')
|
||||
source_token = getattr(settings, 'TEST_AWS_SECURITY_TOKEN', '')
|
||||
if not all([source_username, source_password, source_token]):
|
||||
self.skipTest('no test ec2 sts credentials defined!')
|
||||
self.create_test_license_file()
|
||||
credential = Credential.objects.create(kind='aws',
|
||||
user=self.super_django_user,
|
||||
username=source_username,
|
||||
password=source_password,
|
||||
security_token=source_token)
|
||||
# Set parent group name to one that might be created by the sync.
|
||||
group = self.group
|
||||
group.name = 'ec2'
|
||||
group.save()
|
||||
self.group = group
|
||||
cache_path = tempfile.mkdtemp(prefix='awx_ec2_')
|
||||
self._temp_paths.append(cache_path)
|
||||
inventory_source = self.update_inventory_source(self.group,
|
||||
source='ec2', credential=credential, source_regions=source_regions,
|
||||
source_vars='---\n\nnested_groups: false\ncache_path: %s\n' % cache_path)
|
||||
self.check_inventory_source(inventory_source)
|
||||
|
||||
def test_update_from_ec2_sts_iam_bad_token(self):
|
||||
source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '')
|
||||
source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '')
|
||||
source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all')
|
||||
self.create_test_license_file()
|
||||
credential = Credential.objects.create(kind='aws',
|
||||
user=self.super_django_user,
|
||||
username=source_username,
|
||||
password=source_password,
|
||||
security_token="BADTOKEN")
|
||||
# Set parent group name to one that might be created by the sync.
|
||||
group = self.group
|
||||
group.name = 'ec2'
|
||||
group.save()
|
||||
self.group = group
|
||||
cache_path = tempfile.mkdtemp(prefix='awx_ec2_')
|
||||
self._temp_paths.append(cache_path)
|
||||
inventory_source = self.update_inventory_source(self.group,
|
||||
source='ec2', credential=credential, source_regions=source_regions,
|
||||
source_vars='---\n\nnested_groups: false\ncache_path: %s\n' % cache_path)
|
||||
self.check_inventory_update(inventory_source, should_fail=True)
|
||||
|
||||
def test_update_from_ec2_without_credential(self):
|
||||
self.create_test_license_file()
|
||||
group = self.group
|
||||
|
||||
@ -5,6 +5,7 @@ from __future__ import absolute_import
|
||||
|
||||
from .jobs_monolithic import * # noqa
|
||||
from .job_launch import * # noqa
|
||||
from .job_relaunch import * # noqa
|
||||
from .survey_password import * # noqa
|
||||
from .start_cancel import * # noqa
|
||||
from .base import * # noqa
|
||||
|
||||
75
awx/main/tests/jobs/job_relaunch.py
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
# Python
|
||||
from __future__ import absolute_import
|
||||
import json
|
||||
|
||||
# Django
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseLiveServerTest
|
||||
from .base import BaseJobTestMixin
|
||||
|
||||
__all__ = ['JobRelaunchTest',]
|
||||
|
||||
class JobRelaunchTest(BaseJobTestMixin, BaseLiveServerTest):
|
||||
|
||||
def test_job_relaunch(self):
|
||||
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success')
|
||||
url = reverse('api:job_relaunch', args=(job.pk,))
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.post(url, {}, expect=201)
|
||||
j = Job.objects.get(pk=response['job'])
|
||||
self.assertTrue(j.status == 'successful')
|
||||
self.assertEqual(j.launch_type, 'relaunch')
|
||||
# Test with a job that prompts for SSH and sudo passwords.
|
||||
job = self.make_job(self.jt_sup_run, self.user_sue, 'success')
|
||||
url = reverse('api:job_start', args=(job.pk,))
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.get(url)
|
||||
self.assertEqual(set(response['passwords_needed_to_start']),
|
||||
set(['ssh_password', 'become_password']))
|
||||
data = dict()
|
||||
response = self.post(url, data, expect=400)
|
||||
data['ssh_password'] = 'sshpass'
|
||||
response = self.post(url, data, expect=400)
|
||||
data2 = dict(become_password='sudopass')
|
||||
response = self.post(url, data2, expect=400)
|
||||
data.update(data2)
|
||||
response = self.post(url, data, expect=202)
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
|
||||
# Create jt with no extra_vars
|
||||
# Launch j1 with runtime extra_vars
|
||||
# Assign extra_vars to jt backing job
|
||||
# Relaunch j1
|
||||
# j2 should not contain jt extra_vars
|
||||
def test_relaunch_job_does_not_inherit_jt_extra_vars(self):
|
||||
jt_extra_vars = {
|
||||
"hello": "world"
|
||||
}
|
||||
j_extra_vars = {
|
||||
"goodbye": "cruel universe"
|
||||
}
|
||||
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success', extra_vars=j_extra_vars)
|
||||
url = reverse('api:job_relaunch', args=(job.pk,))
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.post(url, {}, expect=201)
|
||||
j = Job.objects.get(pk=response['job'])
|
||||
self.assertTrue(j.status == 'successful')
|
||||
|
||||
self.jt_ops_east_run.extra_vars = jt_extra_vars
|
||||
self.jt_ops_east_run.save()
|
||||
|
||||
response = self.post(url, {}, expect=201)
|
||||
j = Job.objects.get(pk=response['job'])
|
||||
self.assertTrue(j.status == 'successful')
|
||||
|
||||
resp_extra_vars = json.loads(response['extra_vars'])
|
||||
self.assertNotIn("hello", resp_extra_vars)
|
||||
self.assertEqual(resp_extra_vars, j_extra_vars)
|
||||
|
||||
|
||||
@ -115,30 +115,6 @@ class JobStartCancelTest(BaseJobTestMixin, BaseLiveServerTest):
|
||||
|
||||
# FIXME: Test with other users, test when passwords are required.
|
||||
|
||||
def test_job_relaunch(self):
|
||||
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success')
|
||||
url = reverse('api:job_relaunch', args=(job.pk,))
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.post(url, {}, expect=201)
|
||||
j = Job.objects.get(pk=response['job'])
|
||||
self.assertTrue(j.status == 'successful')
|
||||
# Test with a job that prompts for SSH and sudo passwords.
|
||||
job = self.make_job(self.jt_sup_run, self.user_sue, 'success')
|
||||
url = reverse('api:job_start', args=(job.pk,))
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.get(url)
|
||||
self.assertEqual(set(response['passwords_needed_to_start']),
|
||||
set(['ssh_password', 'become_password']))
|
||||
data = dict()
|
||||
response = self.post(url, data, expect=400)
|
||||
data['ssh_password'] = 'sshpass'
|
||||
response = self.post(url, data, expect=400)
|
||||
data2 = dict(become_password='sudopass')
|
||||
response = self.post(url, data2, expect=400)
|
||||
data.update(data2)
|
||||
response = self.post(url, data, expect=202)
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
|
||||
def test_job_cancel(self):
|
||||
#job = self.job_ops_east_run
|
||||
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new')
|
||||
|
||||
@ -1,10 +1,56 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
from datetime import timedelta
|
||||
|
||||
# Django
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.timezone import now as tz_now
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTest
|
||||
|
||||
__all__ = ['AuthTokenLimitUnitTest', 'OrganizationsTest']
|
||||
|
||||
class AuthTokenLimitUnitTest(BaseTest):
|
||||
|
||||
def setUp(self):
|
||||
self.now = tz_now()
|
||||
# Times are relative to now
|
||||
# (key, created on in seconds , expiration in seconds)
|
||||
self.test_data = [
|
||||
# a is implicitly expired
|
||||
("a", -1000, -10),
|
||||
# b's are invalid due to session limit of 3
|
||||
("b", -100, 60),
|
||||
("bb", -100, 60),
|
||||
("c", -90, 70),
|
||||
("d", -80, 80),
|
||||
("e", -70, 90),
|
||||
]
|
||||
self.user = User.objects.create_superuser('admin', 'foo@bar.com', 'password')
|
||||
for key, t_create, t_expire in self.test_data:
|
||||
AuthToken.objects.create(
|
||||
user=self.user,
|
||||
key=key,
|
||||
request_hash='this_is_a_hash',
|
||||
created=self.now + timedelta(seconds=t_create),
|
||||
expires=self.now + timedelta(seconds=t_expire),
|
||||
)
|
||||
super(AuthTokenLimitUnitTest, self).setUp()
|
||||
|
||||
@override_settings(AUTH_TOKEN_PER_USER=3)
|
||||
def test_get_tokens_over_limit(self):
|
||||
invalid_tokens = AuthToken.get_tokens_over_limit(self.user, now=self.now)
|
||||
invalid_keys = [x.key for x in invalid_tokens]
|
||||
self.assertEqual(len(invalid_keys), 2)
|
||||
self.assertIn('b', invalid_keys)
|
||||
self.assertIn('bb', invalid_keys)
|
||||
|
||||
class OrganizationsTest(BaseTest):
|
||||
|
||||
def collection(self):
|
||||
@ -221,6 +267,11 @@ class OrganizationsTest(BaseTest):
|
||||
# look at what we got back from the post, make sure we added an org
|
||||
last_org = Organization.objects.order_by('-pk')[0]
|
||||
self.assertTrue(data1['url'].endswith("/%d/" % last_org.pk))
|
||||
|
||||
# Test that not even super users can create an organization with a basic license
|
||||
self.create_basic_license_file()
|
||||
cant_org = dict(name='silly user org', description='4815162342')
|
||||
self.post(self.collection(), cant_org, expect=402, auth=self.get_super_credentials())
|
||||
|
||||
def test_post_item_subobjects_projects(self):
|
||||
|
||||
@ -391,6 +442,10 @@ class OrganizationsTest(BaseTest):
|
||||
# also check that DELETE on the collection doesn't work
|
||||
self.delete(self.collection(), expect=405, auth=self.get_super_credentials())
|
||||
|
||||
# Test that not even super users can delete an organization with a basic license
|
||||
self.create_basic_license_file()
|
||||
self.delete(urls[2], expect=402, auth=self.get_super_credentials())
|
||||
|
||||
def test_invalid_post_data(self):
|
||||
url = reverse('api:organization_list')
|
||||
# API should gracefully handle data of an invalid type.
|
||||
|
||||
@ -209,7 +209,7 @@ class ProjectsTest(BaseTransactionTest):
|
||||
self.assertEquals(results['count'], 10)
|
||||
# org admin
|
||||
results = self.get(projects, expect=200, auth=self.get_normal_credentials())
|
||||
self.assertEquals(results['count'], 10)
|
||||
self.assertEquals(results['count'], 9)
|
||||
# user on a team
|
||||
results = self.get(projects, expect=200, auth=self.get_other_credentials())
|
||||
self.assertEquals(results['count'], 5)
|
||||
@ -300,6 +300,17 @@ class ProjectsTest(BaseTransactionTest):
|
||||
got = self.get(proj_orgs, expect=200, auth=self.get_super_credentials())
|
||||
self.assertEquals(got['count'], 2)
|
||||
|
||||
# Verify that creatorship doesn't imply access if access is removed
|
||||
a_new_proj = self.make_project(created_by=self.other_django_user, playbook_content=TEST_PLAYBOOK)
|
||||
self.organizations[0].admins.add(self.other_django_user)
|
||||
self.organizations[0].projects.add(a_new_proj)
|
||||
proj_detail = reverse('api:project_detail', args=(a_new_proj.pk,))
|
||||
self.patch(proj_detail, data=dict(description="test"), expect=200, auth=self.get_other_credentials())
|
||||
self.organizations[0].admins.remove(self.other_django_user)
|
||||
self.patch(proj_detail, data=dict(description="test_now"), expect=403, auth=self.get_other_credentials())
|
||||
self.delete(proj_detail, expect=403, auth=self.get_other_credentials())
|
||||
a_new_proj.delete()
|
||||
|
||||
# =====================================================================
|
||||
# TEAMS
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
import unittest2 as unittest
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
|
||||
@ -4,18 +4,76 @@
|
||||
# Python
|
||||
import datetime
|
||||
import urllib
|
||||
from mock import patch
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db.models import Q
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTest
|
||||
|
||||
__all__ = ['AuthTokenProxyTest', 'UsersTest', 'LdapTest']
|
||||
__all__ = ['AuthTokenTimeoutTest', 'AuthTokenLimitTest', 'AuthTokenProxyTest', 'UsersTest', 'LdapTest']
|
||||
|
||||
|
||||
class AuthTokenTimeoutTest(BaseTest):
|
||||
def setUp(self):
|
||||
super(AuthTokenTimeoutTest, self).setUp()
|
||||
self.setup_users()
|
||||
self.setup_instances()
|
||||
|
||||
def test_auth_token_timeout_exists(self):
|
||||
auth_token_url = reverse('api:auth_token_view')
|
||||
dashboard_url = reverse('api:dashboard_view')
|
||||
|
||||
data = dict(zip(('username', 'password'), self.get_super_credentials()))
|
||||
auth = self.post(auth_token_url, data, expect=200)
|
||||
kwargs = {
|
||||
'HTTP_X_AUTH_TOKEN': 'Token %s' % auth['token']
|
||||
}
|
||||
|
||||
response = self._generic_rest(dashboard_url, expect=200, method='get', return_response_object=True, client_kwargs=kwargs)
|
||||
self.assertIn('Auth-Token-Timeout', response)
|
||||
self.assertEqual(response['Auth-Token-Timeout'], str(settings.AUTH_TOKEN_EXPIRATION))
|
||||
|
||||
class AuthTokenLimitTest(BaseTest):
|
||||
def setUp(self):
|
||||
super(AuthTokenLimitTest, self).setUp()
|
||||
self.setup_users()
|
||||
self.setup_instances()
|
||||
|
||||
@override_settings(AUTH_TOKEN_PER_USER=1)
|
||||
@patch.object(awx.main.models.organization.AuthToken, 'get_request_hash')
|
||||
def test_invalidate_first_session(self, mock_get_request_hash):
|
||||
auth_token_url = reverse('api:auth_token_view')
|
||||
user_me_url = reverse('api:user_me_list')
|
||||
|
||||
data = dict(zip(('username', 'password'), self.get_normal_credentials()))
|
||||
|
||||
mock_get_request_hash.return_value = "session_1"
|
||||
response = self.post(auth_token_url, data, expect=200, auth=None)
|
||||
auth_token1 = {
|
||||
'token': response['token']
|
||||
}
|
||||
self.get(user_me_url, expect=200, auth=auth_token1)
|
||||
|
||||
mock_get_request_hash.return_value = "session_2"
|
||||
response = self.post(auth_token_url, data, expect=200, auth=None)
|
||||
auth_token2 = {
|
||||
'token': response['token']
|
||||
}
|
||||
self.get(user_me_url, expect=200, auth=auth_token2)
|
||||
|
||||
# Ensure our get_request_hash mock is working
|
||||
self.assertNotEqual(auth_token1['token'], auth_token2['token'])
|
||||
|
||||
mock_get_request_hash.return_value = "session_1"
|
||||
response = self.get(user_me_url, expect=401, auth=auth_token1)
|
||||
self.assertEqual(AuthToken.reason_long('limit_reached'), response['detail'])
|
||||
|
||||
'''
|
||||
Ensure ips from the X-Forwarded-For get honored and used in auth tokens
|
||||
@ -204,7 +262,7 @@ class UsersTest(BaseTest):
|
||||
remote_addr = '127.0.0.2'
|
||||
response = self.get(user_me_url, expect=401, auth=auth_token,
|
||||
remote_addr=remote_addr)
|
||||
self.assertEqual(response['detail'], 'Invalid token')
|
||||
self.assertEqual(response['detail'], AuthToken.reason_long('invalid_token'))
|
||||
|
||||
# The WWW-Authenticate header should specify Token auth, since that
|
||||
# auth method was used in the request.
|
||||
|
||||
@ -389,11 +389,13 @@ def get_system_task_capacity():
|
||||
return 50 + ((int(total_mem_value) / 1024) - 2) * 75
|
||||
|
||||
|
||||
def emit_websocket_notification(endpoint, event, payload):
|
||||
def emit_websocket_notification(endpoint, event, payload, token_key=None):
|
||||
from awx.main.socket import Socket
|
||||
|
||||
try:
|
||||
with Socket('websocket', 'w', nowait=True, logger=logger) as websocket:
|
||||
if token_key:
|
||||
payload['token_key'] = token_key
|
||||
payload['event'] = event
|
||||
payload['endpoint'] = endpoint
|
||||
websocket.publish(payload)
|
||||
@ -512,3 +514,11 @@ def timestamp_apiformat(timestamp):
|
||||
if timestamp.endswith('+00:00'):
|
||||
timestamp = timestamp[:-6] + 'Z'
|
||||
return timestamp
|
||||
|
||||
# damn you python 2.6
|
||||
def timedelta_total_seconds(timedelta):
|
||||
return (
|
||||
timedelta.microseconds + 0.0 +
|
||||
(timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
|
||||
|
||||
|
||||
|
||||
@ -426,6 +426,8 @@ class JobCallbackModule(BaseCallbackModule):
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
self.playbook_on_stats(stats)
|
||||
|
||||
def v2_playbook_on_include(self, included_file):
|
||||
self._log_event('playbook_on_include', included_file=included_file)
|
||||
|
||||
class AdHocCommandCallbackModule(BaseCallbackModule):
|
||||
'''
|
||||
|
||||
@ -35,7 +35,10 @@ import time
|
||||
import datetime
|
||||
from copy import deepcopy
|
||||
from ansible import constants as C
|
||||
from ansible.cache.base import BaseCacheModule
|
||||
try:
|
||||
from ansible.cache.base import BaseCacheModule
|
||||
except:
|
||||
from ansible.plugins.cache.base import BaseCacheModule
|
||||
|
||||
try:
|
||||
import zmq
|
||||
@ -46,7 +49,6 @@ except ImportError:
|
||||
class CacheModule(BaseCacheModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# Basic in-memory caching for typical runs
|
||||
self._cache = {}
|
||||
self._cache_prev = {}
|
||||
@ -108,16 +110,15 @@ class CacheModule(BaseCacheModule):
|
||||
module = self.identify_new_module(key, value)
|
||||
# Assume ansible fact triggered the set if no new module found
|
||||
facts = self.filter_ansible_facts(value) if not module else dict({ module : value[module]})
|
||||
|
||||
self._cache_prev = deepcopy(self._cache)
|
||||
self._cache[key] = value
|
||||
|
||||
self._cache_prev = deepcopy(self._cache)
|
||||
packet = {
|
||||
'host': key,
|
||||
'inventory_id': os.environ['INVENTORY_ID'],
|
||||
'facts': facts,
|
||||
'date_key': self.date_key,
|
||||
}
|
||||
|
||||
# Emit fact data to tower for processing
|
||||
self.socket.send_json(packet)
|
||||
self.socket.recv()
|
||||
|
||||
@ -22,6 +22,12 @@ you need to define:
|
||||
|
||||
export EC2_URL=http://hostname_of_your_cc:port/services/Eucalyptus
|
||||
|
||||
If you're using boto profiles (requires boto>=2.24.0) you can choose a profile
|
||||
using the --boto-profile command line argument (e.g. ec2.py --boto-profile prod) or using
|
||||
the AWS_PROFILE variable:
|
||||
|
||||
AWS_PROFILE=prod ansible-playbook -i ec2.py myplaybook.yml
|
||||
|
||||
For more details, see: http://docs.pythonboto.org/en/latest/boto_config_tut.html
|
||||
|
||||
When run against a specific host, this script returns the following variables:
|
||||
@ -121,6 +127,7 @@ from time import time
|
||||
import boto
|
||||
from boto import ec2
|
||||
from boto import rds
|
||||
from boto import elasticache
|
||||
from boto import route53
|
||||
import six
|
||||
|
||||
@ -147,9 +154,18 @@ class Ec2Inventory(object):
|
||||
# Index of hostname (address) to instance ID
|
||||
self.index = {}
|
||||
|
||||
# Boto profile to use (if any)
|
||||
self.boto_profile = None
|
||||
|
||||
# Read settings and parse CLI arguments
|
||||
self.read_settings()
|
||||
self.parse_cli_args()
|
||||
self.read_settings()
|
||||
|
||||
# Make sure that profile_name is not passed at all if not set
|
||||
# as pre 2.24 boto will fall over otherwise
|
||||
if self.boto_profile:
|
||||
if not hasattr(boto.ec2.EC2Connection, 'profile_name'):
|
||||
self.fail_with_error("boto version must be >= 2.24 to use profile")
|
||||
|
||||
# Cache
|
||||
if self.args.refresh_cache:
|
||||
@ -186,12 +202,12 @@ class Ec2Inventory(object):
|
||||
|
||||
def read_settings(self):
|
||||
''' Reads the settings from the ec2.ini file '''
|
||||
if six.PY2:
|
||||
config = configparser.SafeConfigParser()
|
||||
else:
|
||||
if six.PY3:
|
||||
config = configparser.ConfigParser()
|
||||
else:
|
||||
config = configparser.SafeConfigParser()
|
||||
ec2_default_ini_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ec2.ini')
|
||||
ec2_ini_path = os.environ.get('EC2_INI_PATH', ec2_default_ini_path)
|
||||
ec2_ini_path = os.path.expanduser(os.path.expandvars(os.environ.get('EC2_INI_PATH', ec2_default_ini_path)))
|
||||
config.read(ec2_ini_path)
|
||||
|
||||
# is eucalyptus?
|
||||
@ -232,18 +248,72 @@ class Ec2Inventory(object):
|
||||
if config.has_option('ec2', 'rds'):
|
||||
self.rds_enabled = config.getboolean('ec2', 'rds')
|
||||
|
||||
# Return all EC2 and RDS instances (if RDS is enabled)
|
||||
# Include ElastiCache instances?
|
||||
self.elasticache_enabled = True
|
||||
if config.has_option('ec2', 'elasticache'):
|
||||
self.elasticache_enabled = config.getboolean('ec2', 'elasticache')
|
||||
|
||||
# Return all EC2 instances?
|
||||
if config.has_option('ec2', 'all_instances'):
|
||||
self.all_instances = config.getboolean('ec2', 'all_instances')
|
||||
else:
|
||||
self.all_instances = False
|
||||
|
||||
# Instance states to be gathered in inventory. Default is 'running'.
|
||||
# Setting 'all_instances' to 'yes' overrides this option.
|
||||
ec2_valid_instance_states = [
|
||||
'pending',
|
||||
'running',
|
||||
'shutting-down',
|
||||
'terminated',
|
||||
'stopping',
|
||||
'stopped'
|
||||
]
|
||||
self.ec2_instance_states = []
|
||||
if self.all_instances:
|
||||
self.ec2_instance_states = ec2_valid_instance_states
|
||||
elif config.has_option('ec2', 'instance_states'):
|
||||
for instance_state in config.get('ec2', 'instance_states').split(','):
|
||||
instance_state = instance_state.strip()
|
||||
if instance_state not in ec2_valid_instance_states:
|
||||
continue
|
||||
self.ec2_instance_states.append(instance_state)
|
||||
else:
|
||||
self.ec2_instance_states = ['running']
|
||||
|
||||
# Return all RDS instances? (if RDS is enabled)
|
||||
if config.has_option('ec2', 'all_rds_instances') and self.rds_enabled:
|
||||
self.all_rds_instances = config.getboolean('ec2', 'all_rds_instances')
|
||||
else:
|
||||
self.all_rds_instances = False
|
||||
|
||||
# Return all ElastiCache replication groups? (if ElastiCache is enabled)
|
||||
if config.has_option('ec2', 'all_elasticache_replication_groups') and self.elasticache_enabled:
|
||||
self.all_elasticache_replication_groups = config.getboolean('ec2', 'all_elasticache_replication_groups')
|
||||
else:
|
||||
self.all_elasticache_replication_groups = False
|
||||
|
||||
# Return all ElastiCache clusters? (if ElastiCache is enabled)
|
||||
if config.has_option('ec2', 'all_elasticache_clusters') and self.elasticache_enabled:
|
||||
self.all_elasticache_clusters = config.getboolean('ec2', 'all_elasticache_clusters')
|
||||
else:
|
||||
self.all_elasticache_clusters = False
|
||||
|
||||
# Return all ElastiCache nodes? (if ElastiCache is enabled)
|
||||
if config.has_option('ec2', 'all_elasticache_nodes') and self.elasticache_enabled:
|
||||
self.all_elasticache_nodes = config.getboolean('ec2', 'all_elasticache_nodes')
|
||||
else:
|
||||
self.all_elasticache_nodes = False
|
||||
|
||||
# boto configuration profile (prefer CLI argument)
|
||||
self.boto_profile = self.args.boto_profile
|
||||
if config.has_option('ec2', 'boto_profile') and not self.boto_profile:
|
||||
self.boto_profile = config.get('ec2', 'boto_profile')
|
||||
|
||||
# Cache related
|
||||
cache_dir = os.path.expanduser(config.get('ec2', 'cache_path'))
|
||||
if self.boto_profile:
|
||||
cache_dir = os.path.join(cache_dir, 'profile_' + self.boto_profile)
|
||||
if not os.path.exists(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
|
||||
@ -257,6 +327,12 @@ class Ec2Inventory(object):
|
||||
else:
|
||||
self.nested_groups = False
|
||||
|
||||
# Replace dash or not in group names
|
||||
if config.has_option('ec2', 'replace_dash_in_groups'):
|
||||
self.replace_dash_in_groups = config.getboolean('ec2', 'replace_dash_in_groups')
|
||||
else:
|
||||
self.replace_dash_in_groups = True
|
||||
|
||||
# Configure which groups should be created.
|
||||
group_by_options = [
|
||||
'group_by_instance_id',
|
||||
@ -272,6 +348,10 @@ class Ec2Inventory(object):
|
||||
'group_by_route53_names',
|
||||
'group_by_rds_engine',
|
||||
'group_by_rds_parameter_group',
|
||||
'group_by_elasticache_engine',
|
||||
'group_by_elasticache_cluster',
|
||||
'group_by_elasticache_parameter_group',
|
||||
'group_by_elasticache_replication_group',
|
||||
]
|
||||
for option in group_by_options:
|
||||
if config.has_option('ec2', option):
|
||||
@ -286,7 +366,7 @@ class Ec2Inventory(object):
|
||||
self.pattern_include = re.compile(pattern_include)
|
||||
else:
|
||||
self.pattern_include = None
|
||||
except configparser.NoOptionError as e:
|
||||
except configparser.NoOptionError:
|
||||
self.pattern_include = None
|
||||
|
||||
# Do we need to exclude hosts that match a pattern?
|
||||
@ -296,7 +376,7 @@ class Ec2Inventory(object):
|
||||
self.pattern_exclude = re.compile(pattern_exclude)
|
||||
else:
|
||||
self.pattern_exclude = None
|
||||
except configparser.NoOptionError as e:
|
||||
except configparser.NoOptionError:
|
||||
self.pattern_exclude = None
|
||||
|
||||
# Instance filters (see boto and EC2 API docs). Ignore invalid filters.
|
||||
@ -321,6 +401,8 @@ class Ec2Inventory(object):
|
||||
help='Get all the variables about a specific instance')
|
||||
parser.add_argument('--refresh-cache', action='store_true', default=False,
|
||||
help='Force refresh of cache by making API requests to EC2 (default: False - use cache files)')
|
||||
parser.add_argument('--boto-profile', action='store',
|
||||
help='Use boto profile for connections to EC2')
|
||||
self.args = parser.parse_args()
|
||||
|
||||
|
||||
@ -334,6 +416,9 @@ class Ec2Inventory(object):
|
||||
self.get_instances_by_region(region)
|
||||
if self.rds_enabled:
|
||||
self.get_rds_instances_by_region(region)
|
||||
if self.elasticache_enabled:
|
||||
self.get_elasticache_clusters_by_region(region)
|
||||
self.get_elasticache_replication_groups_by_region(region)
|
||||
|
||||
self.write_to_cache(self.inventory, self.cache_path_cache)
|
||||
self.write_to_cache(self.index, self.cache_path_index)
|
||||
@ -344,7 +429,25 @@ class Ec2Inventory(object):
|
||||
conn = boto.connect_euca(host=self.eucalyptus_host)
|
||||
conn.APIVersion = '2010-08-31'
|
||||
else:
|
||||
conn = ec2.connect_to_region(region)
|
||||
conn = self.connect_to_aws(ec2, region)
|
||||
return conn
|
||||
|
||||
def boto_fix_security_token_in_profile(self, connect_args):
|
||||
''' monkey patch for boto issue boto/boto#2100 '''
|
||||
profile = 'profile ' + self.boto_profile
|
||||
if boto.config.has_option(profile, 'aws_security_token'):
|
||||
connect_args['security_token'] = boto.config.get(profile, 'aws_security_token')
|
||||
return connect_args
|
||||
|
||||
def connect_to_aws(self, module, region):
|
||||
connect_args = {}
|
||||
|
||||
# only pass the profile name if it's set (as it is not supported by older boto versions)
|
||||
if self.boto_profile:
|
||||
connect_args['profile_name'] = self.boto_profile
|
||||
self.boto_fix_security_token_in_profile(connect_args)
|
||||
|
||||
conn = module.connect_to_region(region, **connect_args)
|
||||
# connect_to_region will fail "silently" by returning None if the region name is wrong or not supported
|
||||
if conn is None:
|
||||
self.fail_with_error("region name: %s likely not supported, or AWS is down. connection to region failed." % region)
|
||||
@ -373,26 +476,96 @@ class Ec2Inventory(object):
|
||||
else:
|
||||
backend = 'Eucalyptus' if self.eucalyptus else 'AWS'
|
||||
error = "Error connecting to %s backend.\n%s" % (backend, e.message)
|
||||
self.fail_with_error(error)
|
||||
self.fail_with_error(error, 'getting EC2 instances')
|
||||
|
||||
def get_rds_instances_by_region(self, region):
|
||||
''' Makes an AWS API call to the list of RDS instances in a particular
|
||||
region '''
|
||||
|
||||
try:
|
||||
conn = rds.connect_to_region(region)
|
||||
conn = self.connect_to_aws(rds, region)
|
||||
if conn:
|
||||
instances = conn.get_all_dbinstances()
|
||||
for instance in instances:
|
||||
self.add_rds_instance(instance, region)
|
||||
except boto.exception.BotoServerError as e:
|
||||
error = e.reason
|
||||
|
||||
|
||||
if e.error_code == 'AuthFailure':
|
||||
error = self.get_auth_error_message()
|
||||
if not e.reason == "Forbidden":
|
||||
error = "Looks like AWS RDS is down:\n%s" % e.message
|
||||
self.fail_with_error(error)
|
||||
self.fail_with_error(error, 'getting RDS instances')
|
||||
|
||||
def get_elasticache_clusters_by_region(self, region):
|
||||
''' Makes an AWS API call to the list of ElastiCache clusters (with
|
||||
nodes' info) in a particular region.'''
|
||||
|
||||
# ElastiCache boto module doesn't provide a get_all_intances method,
|
||||
# that's why we need to call describe directly (it would be called by
|
||||
# the shorthand method anyway...)
|
||||
try:
|
||||
conn = elasticache.connect_to_region(region)
|
||||
if conn:
|
||||
# show_cache_node_info = True
|
||||
# because we also want nodes' information
|
||||
response = conn.describe_cache_clusters(None, None, None, True)
|
||||
|
||||
except boto.exception.BotoServerError as e:
|
||||
error = e.reason
|
||||
|
||||
if e.error_code == 'AuthFailure':
|
||||
error = self.get_auth_error_message()
|
||||
if not e.reason == "Forbidden":
|
||||
error = "Looks like AWS ElastiCache is down:\n%s" % e.message
|
||||
self.fail_with_error(error, 'getting ElastiCache clusters')
|
||||
|
||||
try:
|
||||
# Boto also doesn't provide wrapper classes to CacheClusters or
|
||||
# CacheNodes. Because of that wo can't make use of the get_list
|
||||
# method in the AWSQueryConnection. Let's do the work manually
|
||||
clusters = response['DescribeCacheClustersResponse']['DescribeCacheClustersResult']['CacheClusters']
|
||||
|
||||
except KeyError as e:
|
||||
error = "ElastiCache query to AWS failed (unexpected format)."
|
||||
self.fail_with_error(error, 'getting ElastiCache clusters')
|
||||
|
||||
for cluster in clusters:
|
||||
self.add_elasticache_cluster(cluster, region)
|
||||
|
||||
def get_elasticache_replication_groups_by_region(self, region):
|
||||
''' Makes an AWS API call to the list of ElastiCache replication groups
|
||||
in a particular region.'''
|
||||
|
||||
# ElastiCache boto module doesn't provide a get_all_intances method,
|
||||
# that's why we need to call describe directly (it would be called by
|
||||
# the shorthand method anyway...)
|
||||
try:
|
||||
conn = elasticache.connect_to_region(region)
|
||||
if conn:
|
||||
response = conn.describe_replication_groups()
|
||||
|
||||
except boto.exception.BotoServerError as e:
|
||||
error = e.reason
|
||||
|
||||
if e.error_code == 'AuthFailure':
|
||||
error = self.get_auth_error_message()
|
||||
if not e.reason == "Forbidden":
|
||||
error = "Looks like AWS ElastiCache [Replication Groups] is down:\n%s" % e.message
|
||||
self.fail_with_error(error, 'getting ElastiCache clusters')
|
||||
|
||||
try:
|
||||
# Boto also doesn't provide wrapper classes to ReplicationGroups
|
||||
# Because of that wo can't make use of the get_list method in the
|
||||
# AWSQueryConnection. Let's do the work manually
|
||||
replication_groups = response['DescribeReplicationGroupsResponse']['DescribeReplicationGroupsResult']['ReplicationGroups']
|
||||
|
||||
except KeyError as e:
|
||||
error = "ElastiCache [Replication Groups] query to AWS failed (unexpected format)."
|
||||
self.fail_with_error(error, 'getting ElastiCache clusters')
|
||||
|
||||
for replication_group in replication_groups:
|
||||
self.add_elasticache_replication_group(replication_group, region)
|
||||
|
||||
def get_auth_error_message(self):
|
||||
''' create an informative error message if there is an issue authenticating'''
|
||||
@ -410,9 +583,12 @@ class Ec2Inventory(object):
|
||||
errors.append(" - No Boto config found at any expected location '%s'" % ', '.join(boto_paths))
|
||||
|
||||
return '\n'.join(errors)
|
||||
|
||||
def fail_with_error(self, err_msg):
|
||||
|
||||
def fail_with_error(self, err_msg, err_operation=None):
|
||||
'''log an error to std err for ansible-playbook to consume and exit'''
|
||||
if err_operation:
|
||||
err_msg = 'ERROR: "{err_msg}", while: {err_operation}'.format(
|
||||
err_msg=err_msg, err_operation=err_operation)
|
||||
sys.stderr.write(err_msg)
|
||||
sys.exit(1)
|
||||
|
||||
@ -428,8 +604,8 @@ class Ec2Inventory(object):
|
||||
''' Adds an instance to the inventory and index, as long as it is
|
||||
addressable '''
|
||||
|
||||
# Only want running instances unless all_instances is True
|
||||
if not self.all_instances and instance.state != 'running':
|
||||
# Only return instances with desired instance states
|
||||
if instance.state not in self.ec2_instance_states:
|
||||
return
|
||||
|
||||
# Select the best destination address
|
||||
@ -633,6 +809,243 @@ class Ec2Inventory(object):
|
||||
|
||||
self.inventory["_meta"]["hostvars"][dest] = self.get_host_info_dict_from_instance(instance)
|
||||
|
||||
def add_elasticache_cluster(self, cluster, region):
|
||||
''' Adds an ElastiCache cluster to the inventory and index, as long as
|
||||
it's nodes are addressable '''
|
||||
|
||||
# Only want available clusters unless all_elasticache_clusters is True
|
||||
if not self.all_elasticache_clusters and cluster['CacheClusterStatus'] != 'available':
|
||||
return
|
||||
|
||||
# Select the best destination address
|
||||
if 'ConfigurationEndpoint' in cluster and cluster['ConfigurationEndpoint']:
|
||||
# Memcached cluster
|
||||
dest = cluster['ConfigurationEndpoint']['Address']
|
||||
is_redis = False
|
||||
else:
|
||||
# Redis sigle node cluster
|
||||
# Because all Redis clusters are single nodes, we'll merge the
|
||||
# info from the cluster with info about the node
|
||||
dest = cluster['CacheNodes'][0]['Endpoint']['Address']
|
||||
is_redis = True
|
||||
|
||||
if not dest:
|
||||
# Skip clusters we cannot address (e.g. private VPC subnet)
|
||||
return
|
||||
|
||||
# Add to index
|
||||
self.index[dest] = [region, cluster['CacheClusterId']]
|
||||
|
||||
# Inventory: Group by instance ID (always a group of 1)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[cluster['CacheClusterId']] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', cluster['CacheClusterId'])
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.group_by_region and not is_redis:
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
if self.group_by_availability_zone and not is_redis:
|
||||
self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])
|
||||
self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])
|
||||
|
||||
# Inventory: Group by node type
|
||||
if self.group_by_instance_type and not is_redis:
|
||||
type_name = self.to_safe('type_' + cluster['CacheNodeType'])
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by VPC (information not available in the current
|
||||
# AWS API version for ElastiCache)
|
||||
|
||||
# Inventory: Group by security group
|
||||
if self.group_by_security_group and not is_redis:
|
||||
|
||||
# Check for the existence of the 'SecurityGroups' key and also if
|
||||
# this key has some value. When the cluster is not placed in a SG
|
||||
# the query can return None here and cause an error.
|
||||
if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:
|
||||
for security_group in cluster['SecurityGroups']:
|
||||
key = self.to_safe("security_group_" + security_group['SecurityGroupId'])
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
|
||||
# Inventory: Group by engine
|
||||
if self.group_by_elasticache_engine and not is_redis:
|
||||
self.push(self.inventory, self.to_safe("elasticache_" + cluster['Engine']), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'elasticache_engines', self.to_safe(cluster['Engine']))
|
||||
|
||||
# Inventory: Group by parameter group
|
||||
if self.group_by_elasticache_parameter_group:
|
||||
self.push(self.inventory, self.to_safe("elasticache_parameter_group_" + cluster['CacheParameterGroup']['CacheParameterGroupName']), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'elasticache_parameter_groups', self.to_safe(cluster['CacheParameterGroup']['CacheParameterGroupName']))
|
||||
|
||||
# Inventory: Group by replication group
|
||||
if self.group_by_elasticache_replication_group and 'ReplicationGroupId' in cluster and cluster['ReplicationGroupId']:
|
||||
self.push(self.inventory, self.to_safe("elasticache_replication_group_" + cluster['ReplicationGroupId']), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'elasticache_replication_groups', self.to_safe(cluster['ReplicationGroupId']))
|
||||
|
||||
# Global Tag: all ElastiCache clusters
|
||||
self.push(self.inventory, 'elasticache_clusters', cluster['CacheClusterId'])
|
||||
|
||||
host_info = self.get_host_info_dict_from_describe_dict(cluster)
|
||||
|
||||
self.inventory["_meta"]["hostvars"][dest] = host_info
|
||||
|
||||
# Add the nodes
|
||||
for node in cluster['CacheNodes']:
|
||||
self.add_elasticache_node(node, cluster, region)
|
||||
|
||||
def add_elasticache_node(self, node, cluster, region):
|
||||
''' Adds an ElastiCache node to the inventory and index, as long as
|
||||
it is addressable '''
|
||||
|
||||
# Only want available nodes unless all_elasticache_nodes is True
|
||||
if not self.all_elasticache_nodes and node['CacheNodeStatus'] != 'available':
|
||||
return
|
||||
|
||||
# Select the best destination address
|
||||
dest = node['Endpoint']['Address']
|
||||
|
||||
if not dest:
|
||||
# Skip nodes we cannot address (e.g. private VPC subnet)
|
||||
return
|
||||
|
||||
node_id = self.to_safe(cluster['CacheClusterId'] + '_' + node['CacheNodeId'])
|
||||
|
||||
# Add to index
|
||||
self.index[dest] = [region, node_id]
|
||||
|
||||
# Inventory: Group by node ID (always a group of 1)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[node_id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', node_id)
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
if self.group_by_availability_zone:
|
||||
self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])
|
||||
self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])
|
||||
|
||||
# Inventory: Group by node type
|
||||
if self.group_by_instance_type:
|
||||
type_name = self.to_safe('type_' + cluster['CacheNodeType'])
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by VPC (information not available in the current
|
||||
# AWS API version for ElastiCache)
|
||||
|
||||
# Inventory: Group by security group
|
||||
if self.group_by_security_group:
|
||||
|
||||
# Check for the existence of the 'SecurityGroups' key and also if
|
||||
# this key has some value. When the cluster is not placed in a SG
|
||||
# the query can return None here and cause an error.
|
||||
if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:
|
||||
for security_group in cluster['SecurityGroups']:
|
||||
key = self.to_safe("security_group_" + security_group['SecurityGroupId'])
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
|
||||
# Inventory: Group by engine
|
||||
if self.group_by_elasticache_engine:
|
||||
self.push(self.inventory, self.to_safe("elasticache_" + cluster['Engine']), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'elasticache_engines', self.to_safe("elasticache_" + cluster['Engine']))
|
||||
|
||||
# Inventory: Group by parameter group (done at cluster level)
|
||||
|
||||
# Inventory: Group by replication group (done at cluster level)
|
||||
|
||||
# Inventory: Group by ElastiCache Cluster
|
||||
if self.group_by_elasticache_cluster:
|
||||
self.push(self.inventory, self.to_safe("elasticache_cluster_" + cluster['CacheClusterId']), dest)
|
||||
|
||||
# Global Tag: all ElastiCache nodes
|
||||
self.push(self.inventory, 'elasticache_nodes', dest)
|
||||
|
||||
host_info = self.get_host_info_dict_from_describe_dict(node)
|
||||
|
||||
if dest in self.inventory["_meta"]["hostvars"]:
|
||||
self.inventory["_meta"]["hostvars"][dest].update(host_info)
|
||||
else:
|
||||
self.inventory["_meta"]["hostvars"][dest] = host_info
|
||||
|
||||
def add_elasticache_replication_group(self, replication_group, region):
|
||||
''' Adds an ElastiCache replication group to the inventory and index '''
|
||||
|
||||
# Only want available clusters unless all_elasticache_replication_groups is True
|
||||
if not self.all_elasticache_replication_groups and replication_group['Status'] != 'available':
|
||||
return
|
||||
|
||||
# Select the best destination address (PrimaryEndpoint)
|
||||
dest = replication_group['NodeGroups'][0]['PrimaryEndpoint']['Address']
|
||||
|
||||
if not dest:
|
||||
# Skip clusters we cannot address (e.g. private VPC subnet)
|
||||
return
|
||||
|
||||
# Add to index
|
||||
self.index[dest] = [region, replication_group['ReplicationGroupId']]
|
||||
|
||||
# Inventory: Group by ID (always a group of 1)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[replication_group['ReplicationGroupId']] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', replication_group['ReplicationGroupId'])
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone (doesn't apply to replication groups)
|
||||
|
||||
# Inventory: Group by node type (doesn't apply to replication groups)
|
||||
|
||||
# Inventory: Group by VPC (information not available in the current
|
||||
# AWS API version for replication groups
|
||||
|
||||
# Inventory: Group by security group (doesn't apply to replication groups)
|
||||
# Check this value in cluster level
|
||||
|
||||
# Inventory: Group by engine (replication groups are always Redis)
|
||||
if self.group_by_elasticache_engine:
|
||||
self.push(self.inventory, 'elasticache_redis', dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'elasticache_engines', 'redis')
|
||||
|
||||
# Global Tag: all ElastiCache clusters
|
||||
self.push(self.inventory, 'elasticache_replication_groups', replication_group['ReplicationGroupId'])
|
||||
|
||||
host_info = self.get_host_info_dict_from_describe_dict(replication_group)
|
||||
|
||||
self.inventory["_meta"]["hostvars"][dest] = host_info
|
||||
|
||||
def get_route53_records(self):
|
||||
''' Get and store the map of resource records to domain names that
|
||||
@ -681,7 +1094,6 @@ class Ec2Inventory(object):
|
||||
|
||||
return list(name_list)
|
||||
|
||||
|
||||
def get_host_info_dict_from_instance(self, instance):
|
||||
instance_vars = {}
|
||||
for key in vars(instance):
|
||||
@ -727,6 +1139,91 @@ class Ec2Inventory(object):
|
||||
|
||||
return instance_vars
|
||||
|
||||
def get_host_info_dict_from_describe_dict(self, describe_dict):
|
||||
''' Parses the dictionary returned by the API call into a flat list
|
||||
of parameters. This method should be used only when 'describe' is
|
||||
used directly because Boto doesn't provide specific classes. '''
|
||||
|
||||
# I really don't agree with prefixing everything with 'ec2'
|
||||
# because EC2, RDS and ElastiCache are different services.
|
||||
# I'm just following the pattern used until now to not break any
|
||||
# compatibility.
|
||||
|
||||
host_info = {}
|
||||
for key in describe_dict:
|
||||
value = describe_dict[key]
|
||||
key = self.to_safe('ec2_' + self.uncammelize(key))
|
||||
|
||||
# Handle complex types
|
||||
|
||||
# Target: Memcached Cache Clusters
|
||||
if key == 'ec2_configuration_endpoint' and value:
|
||||
host_info['ec2_configuration_endpoint_address'] = value['Address']
|
||||
host_info['ec2_configuration_endpoint_port'] = value['Port']
|
||||
|
||||
# Target: Cache Nodes and Redis Cache Clusters (single node)
|
||||
if key == 'ec2_endpoint' and value:
|
||||
host_info['ec2_endpoint_address'] = value['Address']
|
||||
host_info['ec2_endpoint_port'] = value['Port']
|
||||
|
||||
# Target: Redis Replication Groups
|
||||
if key == 'ec2_node_groups' and value:
|
||||
host_info['ec2_endpoint_address'] = value[0]['PrimaryEndpoint']['Address']
|
||||
host_info['ec2_endpoint_port'] = value[0]['PrimaryEndpoint']['Port']
|
||||
replica_count = 0
|
||||
for node in value[0]['NodeGroupMembers']:
|
||||
if node['CurrentRole'] == 'primary':
|
||||
host_info['ec2_primary_cluster_address'] = node['ReadEndpoint']['Address']
|
||||
host_info['ec2_primary_cluster_port'] = node['ReadEndpoint']['Port']
|
||||
host_info['ec2_primary_cluster_id'] = node['CacheClusterId']
|
||||
elif node['CurrentRole'] == 'replica':
|
||||
host_info['ec2_replica_cluster_address_'+ str(replica_count)] = node['ReadEndpoint']['Address']
|
||||
host_info['ec2_replica_cluster_port_'+ str(replica_count)] = node['ReadEndpoint']['Port']
|
||||
host_info['ec2_replica_cluster_id_'+ str(replica_count)] = node['CacheClusterId']
|
||||
replica_count += 1
|
||||
|
||||
# Target: Redis Replication Groups
|
||||
if key == 'ec2_member_clusters' and value:
|
||||
host_info['ec2_member_clusters'] = ','.join([str(i) for i in value])
|
||||
|
||||
# Target: All Cache Clusters
|
||||
elif key == 'ec2_cache_parameter_group':
|
||||
host_info["ec2_cache_node_ids_to_reboot"] = ','.join([str(i) for i in value['CacheNodeIdsToReboot']])
|
||||
host_info['ec2_cache_parameter_group_name'] = value['CacheParameterGroupName']
|
||||
host_info['ec2_cache_parameter_apply_status'] = value['ParameterApplyStatus']
|
||||
|
||||
# Target: Almost everything
|
||||
elif key == 'ec2_security_groups':
|
||||
|
||||
# Skip if SecurityGroups is None
|
||||
# (it is possible to have the key defined but no value in it).
|
||||
if value is not None:
|
||||
sg_ids = []
|
||||
for sg in value:
|
||||
sg_ids.append(sg['SecurityGroupId'])
|
||||
host_info["ec2_security_group_ids"] = ','.join([str(i) for i in sg_ids])
|
||||
|
||||
# Target: Everything
|
||||
# Preserve booleans and integers
|
||||
elif type(value) in [int, bool]:
|
||||
host_info[key] = value
|
||||
|
||||
# Target: Everything
|
||||
# Sanitize string values
|
||||
elif isinstance(value, six.string_types):
|
||||
host_info[key] = value.strip()
|
||||
|
||||
# Target: Everything
|
||||
# Replace None by an empty string
|
||||
elif type(value) == type(None):
|
||||
host_info[key] = ''
|
||||
|
||||
else:
|
||||
# Remove non-processed complex types
|
||||
pass
|
||||
|
||||
return host_info
|
||||
|
||||
def get_host_info(self):
|
||||
''' Get variables about a specific host '''
|
||||
|
||||
@ -790,13 +1287,16 @@ class Ec2Inventory(object):
|
||||
cache.write(json_data)
|
||||
cache.close()
|
||||
|
||||
def uncammelize(self, key):
|
||||
temp = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', key)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', temp).lower()
|
||||
|
||||
def to_safe(self, word):
|
||||
''' Converts 'bad' characters in a string to underscores so they can be
|
||||
used as Ansible groups '''
|
||||
|
||||
return re.sub("[^A-Za-z0-9\_]", "_", word)
|
||||
|
||||
''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups '''
|
||||
regex = "[^A-Za-z0-9\_"
|
||||
if self.replace_dash_in_groups:
|
||||
regex += "\-"
|
||||
return re.sub(regex + "]", "_", word)
|
||||
|
||||
def json_format_dict(self, data, pretty=False):
|
||||
''' Converts a dict to a JSON object and dumps it as a formatted
|
||||
@ -810,3 +1310,4 @@ class Ec2Inventory(object):
|
||||
|
||||
# Run the script
|
||||
Ec2Inventory()
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ Examples:
|
||||
$ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"
|
||||
|
||||
Use the GCE inventory script to print out instance specific information
|
||||
$ plugins/inventory/gce.py --host my_instance
|
||||
$ contrib/inventory/gce.py --host my_instance
|
||||
|
||||
Author: Eric Johnson <erjohnso@google.com>
|
||||
Version: 0.0.1
|
||||
@ -112,9 +112,9 @@ class GceInventory(object):
|
||||
|
||||
# Just display data for specific host
|
||||
if self.args.host:
|
||||
print self.json_format_dict(self.node_to_dict(
|
||||
print(self.json_format_dict(self.node_to_dict(
|
||||
self.get_instance(self.args.host)),
|
||||
pretty=self.args.pretty)
|
||||
pretty=self.args.pretty))
|
||||
sys.exit(0)
|
||||
|
||||
# Otherwise, assume user wants all instances grouped
|
||||
@ -237,7 +237,7 @@ class GceInventory(object):
|
||||
'''Gets details about a specific instance '''
|
||||
try:
|
||||
return self.driver.ex_get_node(instance_name)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
def group_instances(self):
|
||||
@ -257,7 +257,10 @@ class GceInventory(object):
|
||||
|
||||
tags = node.extra['tags']
|
||||
for t in tags:
|
||||
tag = 'tag_%s' % t
|
||||
if t.startswith('group-'):
|
||||
tag = t[6:]
|
||||
else:
|
||||
tag = 'tag_%s' % t
|
||||
if groups.has_key(tag): groups[tag].append(name)
|
||||
else: groups[tag] = [name]
|
||||
|
||||
|
||||
@ -52,8 +52,6 @@ class OpenStackInventory(object):
|
||||
|
||||
def __init__(self, private=False, refresh=False):
|
||||
config_files = os_client_config.config.CONFIG_FILES
|
||||
if os.environ.get('OPENSTACK_CONFIG_FILE', None):
|
||||
config_files.insert(0, os.environ['OPENSTACK_CONFIG_FILE'])
|
||||
config_files.append('/etc/ansible/openstack.yml')
|
||||
self.openstack_config = os_client_config.config.OpenStackConfig(
|
||||
config_files)
|
||||
@ -156,7 +154,8 @@ def main():
|
||||
elif args.host:
|
||||
inventory.get_host(args.host)
|
||||
except shade.OpenStackCloudException as e:
|
||||
sys.exit(e.message)
|
||||
sys.stderr.write('%s\n' % e.message)
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
@ -153,6 +153,8 @@ import warnings
|
||||
import collections
|
||||
import ConfigParser
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from ansible.constants import get_config, mk_boolean
|
||||
|
||||
try:
|
||||
@ -167,6 +169,9 @@ except ImportError:
|
||||
print('pyrax is required for this module')
|
||||
sys.exit(1)
|
||||
|
||||
from time import time
|
||||
|
||||
|
||||
NON_CALLABLES = (basestring, bool, dict, int, list, type(None))
|
||||
|
||||
|
||||
@ -214,7 +219,7 @@ def host(regions, hostname):
|
||||
print(json.dumps(hostvars, sort_keys=True, indent=4))
|
||||
|
||||
|
||||
def _list(regions):
|
||||
def _list_into_cache(regions):
|
||||
groups = collections.defaultdict(list)
|
||||
hostvars = collections.defaultdict(dict)
|
||||
images = {}
|
||||
@ -242,7 +247,7 @@ def _list(regions):
|
||||
if cs is None:
|
||||
warnings.warn(
|
||||
'Connecting to Rackspace region "%s" has caused Pyrax to '
|
||||
'return a NoneType. Is this a valid region?' % region,
|
||||
'return None. Is this a valid region?' % region,
|
||||
RuntimeWarning)
|
||||
continue
|
||||
for server in cs.servers.list():
|
||||
@ -264,7 +269,7 @@ def _list(regions):
|
||||
|
||||
hostvars[server.name]['rax_region'] = region
|
||||
|
||||
for key, value in server.metadata.iteritems():
|
||||
for key, value in iteritems(server.metadata):
|
||||
groups['%s_%s_%s' % (prefix, key, value)].append(server.name)
|
||||
|
||||
groups['instance-%s' % server.id].append(server.name)
|
||||
@ -334,7 +339,31 @@ def _list(regions):
|
||||
|
||||
if hostvars:
|
||||
groups['_meta'] = {'hostvars': hostvars}
|
||||
print(json.dumps(groups, sort_keys=True, indent=4))
|
||||
|
||||
with open(get_cache_file_path(regions), 'w') as cache_file:
|
||||
json.dump(groups, cache_file)
|
||||
|
||||
|
||||
def get_cache_file_path(regions):
|
||||
regions_str = '.'.join([reg.strip().lower() for reg in regions])
|
||||
ansible_tmp_path = os.path.join(os.path.expanduser("~"), '.ansible', 'tmp')
|
||||
if not os.path.exists(ansible_tmp_path):
|
||||
os.makedirs(ansible_tmp_path)
|
||||
return os.path.join(ansible_tmp_path,
|
||||
'ansible-rax-%s-%s.cache' % (
|
||||
pyrax.identity.username, regions_str))
|
||||
|
||||
|
||||
def _list(regions, refresh_cache=True):
|
||||
if (not os.path.exists(get_cache_file_path(regions)) or
|
||||
refresh_cache or
|
||||
(time() - os.stat(get_cache_file_path(regions))[-1]) > 600):
|
||||
# Cache file doesn't exist or older than 10m or refresh cache requested
|
||||
_list_into_cache(regions)
|
||||
|
||||
with open(get_cache_file_path(regions), 'r') as cache_file:
|
||||
groups = json.load(cache_file)
|
||||
print(json.dumps(groups, sort_keys=True, indent=4))
|
||||
|
||||
|
||||
def parse_args():
|
||||
@ -344,6 +373,9 @@ def parse_args():
|
||||
group.add_argument('--list', action='store_true',
|
||||
help='List active servers')
|
||||
group.add_argument('--host', help='List details about the specific host')
|
||||
parser.add_argument('--refresh-cache', action='store_true', default=False,
|
||||
help=('Force refresh of cache, making API requests to'
|
||||
'RackSpace (default: False - use cache files)'))
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@ -382,7 +414,7 @@ def setup():
|
||||
pyrax.keyring_auth(keyring_username, region=region)
|
||||
else:
|
||||
pyrax.set_credential_file(creds_file, region=region)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
sys.stderr.write("%s: %s\n" % (e, e.message))
|
||||
sys.exit(1)
|
||||
|
||||
@ -410,7 +442,7 @@ def main():
|
||||
args = parse_args()
|
||||
regions = setup()
|
||||
if args.list:
|
||||
_list(regions)
|
||||
_list(regions, refresh_cache=args.refresh_cache)
|
||||
elif args.host:
|
||||
host(regions, args.host)
|
||||
sys.exit(0)
|
||||
|
||||
@ -28,6 +28,8 @@ take precedence over options present in the INI file. An INI file is not
|
||||
required if these options are specified using environment variables.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import collections
|
||||
import json
|
||||
import logging
|
||||
@ -37,6 +39,8 @@ import sys
|
||||
import time
|
||||
import ConfigParser
|
||||
|
||||
from six import text_type
|
||||
|
||||
# Disable logging message trigged by pSphere/suds.
|
||||
try:
|
||||
from logging import NullHandler
|
||||
@ -95,7 +99,7 @@ class VMwareInventory(object):
|
||||
Saves the value to cache with the name given.
|
||||
'''
|
||||
if self.config.has_option('defaults', 'cache_dir'):
|
||||
cache_dir = self.config.get('defaults', 'cache_dir')
|
||||
cache_dir = os.path.expanduser(self.config.get('defaults', 'cache_dir'))
|
||||
if not os.path.exists(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
cache_file = os.path.join(cache_dir, name)
|
||||
@ -115,7 +119,7 @@ class VMwareInventory(object):
|
||||
else:
|
||||
cache_max_age = 0
|
||||
cache_stat = os.stat(cache_file)
|
||||
if (cache_stat.st_mtime + cache_max_age) < time.time():
|
||||
if (cache_stat.st_mtime + cache_max_age) >= time.time():
|
||||
with open(cache_file) as cache:
|
||||
return json.load(cache)
|
||||
return default
|
||||
@ -147,7 +151,7 @@ class VMwareInventory(object):
|
||||
seen = seen or set()
|
||||
if isinstance(obj, ManagedObject):
|
||||
try:
|
||||
obj_unicode = unicode(getattr(obj, 'name'))
|
||||
obj_unicode = text_type(getattr(obj, 'name'))
|
||||
except AttributeError:
|
||||
obj_unicode = ()
|
||||
if obj in seen:
|
||||
@ -164,7 +168,7 @@ class VMwareInventory(object):
|
||||
obj_info = self._get_obj_info(val, depth - 1, seen)
|
||||
if obj_info != ():
|
||||
d[attr] = obj_info
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
pass
|
||||
return d
|
||||
elif isinstance(obj, SudsObject):
|
||||
@ -207,8 +211,8 @@ class VMwareInventory(object):
|
||||
host_info[k] = v
|
||||
try:
|
||||
host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress
|
||||
except Exception, e:
|
||||
print >> sys.stderr, e
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
host_info = self._flatten_dict(host_info, prefix)
|
||||
if ('%s_ipAddress' % prefix) in host_info:
|
||||
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix]
|
||||
|
||||
@ -51,7 +51,7 @@ try:
|
||||
from azure import WindowsAzureError
|
||||
from azure.servicemanagement import ServiceManagementService
|
||||
except ImportError as e:
|
||||
print "failed=True msg='`azure` library required for this script'"
|
||||
print("failed=True msg='`azure` library required for this script'")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@ -70,8 +70,8 @@ class AzureInventory(object):
|
||||
# Cache setting defaults.
|
||||
# These can be overridden in settings (see `read_settings`).
|
||||
cache_dir = os.path.expanduser('~')
|
||||
self.cache_path_cache = '%s/.ansible-azure.cache' % cache_dir
|
||||
self.cache_path_index = '%s/.ansible-azure.index' % cache_dir
|
||||
self.cache_path_cache = os.path.join(cache_dir, '.ansible-azure.cache')
|
||||
self.cache_path_index = os.path.join(cache_dir, '.ansible-azure.index')
|
||||
self.cache_max_age = 0
|
||||
|
||||
# Read settings and parse CLI arguments
|
||||
@ -103,15 +103,13 @@ class AzureInventory(object):
|
||||
# Add the `['_meta']['hostvars']` information.
|
||||
hostvars = {}
|
||||
if len(data) > 0:
|
||||
for host in set(reduce(lambda x, y: x + y,
|
||||
[i for i in data.values()])):
|
||||
if host is not None:
|
||||
hostvars[host] = self.get_host(host, jsonify=False)
|
||||
for host in set([h for hosts in data.values() for h in hosts if h]):
|
||||
hostvars[host] = self.get_host(host, jsonify=False)
|
||||
data['_meta'] = {'hostvars': hostvars}
|
||||
|
||||
# JSONify the data.
|
||||
data_to_print = self.json_format_dict(data, pretty=True)
|
||||
print data_to_print
|
||||
print(data_to_print)
|
||||
|
||||
def get_host(self, hostname, jsonify=True):
|
||||
"""Return information about the given hostname, based on what
|
||||
@ -153,9 +151,9 @@ class AzureInventory(object):
|
||||
|
||||
# Cache related
|
||||
if config.has_option('azure', 'cache_path'):
|
||||
cache_path = os.path.expanduser(config.get('azure', 'cache_path'))
|
||||
self.cache_path_cache = cache_path + '/ansible-azure.cache'
|
||||
self.cache_path_index = cache_path + '/ansible-azure.index'
|
||||
cache_path = os.path.expandvars(os.path.expanduser(config.get('azure', 'cache_path')))
|
||||
self.cache_path_cache = os.path.join(cache_path, 'ansible-azure.cache')
|
||||
self.cache_path_index = os.path.join(cache_path, 'ansible-azure.index')
|
||||
if config.has_option('azure', 'cache_max_age'):
|
||||
self.cache_max_age = config.getint('azure', 'cache_max_age')
|
||||
|
||||
@ -197,9 +195,9 @@ class AzureInventory(object):
|
||||
for cloud_service in self.sms.list_hosted_services():
|
||||
self.add_deployments(cloud_service)
|
||||
except WindowsAzureError as e:
|
||||
print "Looks like Azure's API is down:"
|
||||
print
|
||||
print e
|
||||
print("Looks like Azure's API is down:")
|
||||
print("")
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
def add_deployments(self, cloud_service):
|
||||
@ -209,12 +207,10 @@ class AzureInventory(object):
|
||||
try:
|
||||
for deployment in self.sms.get_hosted_service_properties(cloud_service.service_name,embed_detail=True).deployments.deployments:
|
||||
self.add_deployment(cloud_service, deployment)
|
||||
#if deployment.deployment_slot == "Production":
|
||||
# self.add_deployment(cloud_service, deployment)
|
||||
except WindowsAzureError as e:
|
||||
print "Looks like Azure's API is down:"
|
||||
print
|
||||
print e
|
||||
print("Looks like Azure's API is down:")
|
||||
print("")
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
def add_deployment(self, cloud_service, deployment):
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
import os
|
||||
import re # noqa
|
||||
import sys
|
||||
import djcelery
|
||||
from datetime import timedelta
|
||||
@ -63,7 +64,7 @@ USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, 'ui', 'dist'),
|
||||
os.path.join(BASE_DIR, 'ui', 'static'),
|
||||
os.path.join(BASE_DIR, 'static'),
|
||||
)
|
||||
|
||||
@ -118,16 +119,32 @@ REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
||||
|
||||
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS += ( # NOQA
|
||||
TEMPLATE_CONTEXT_PROCESSORS = ( # NOQA
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
'django.core.context_processors.i18n',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'django.core.context_processors.tz',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.core.context_processors.request',
|
||||
'awx.ui.context_processors.settings',
|
||||
'awx.ui.context_processors.version',
|
||||
'social.apps.django_app.context_processors.backends',
|
||||
'social.apps.django_app.context_processors.login_redirect',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES += ( # NOQA
|
||||
MIDDLEWARE_CLASSES = ( # NOQA
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'awx.main.middleware.HAMiddleware',
|
||||
'awx.main.middleware.ActivityStreamMiddleware',
|
||||
'awx.sso.middleware.SocialAuthMiddleware',
|
||||
'crum.CurrentRequestUserMiddleware',
|
||||
'awx.main.middleware.AuthTokenTimeoutMiddleware',
|
||||
)
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
@ -159,10 +176,12 @@ INSTALLED_APPS = (
|
||||
'kombu.transport.django',
|
||||
'polymorphic',
|
||||
'taggit',
|
||||
'social.apps.django_app.default',
|
||||
'awx.main',
|
||||
'awx.api',
|
||||
'awx.ui',
|
||||
'awx.fact',
|
||||
'awx.sso',
|
||||
)
|
||||
|
||||
INTERNAL_IPS = ('127.0.0.1',)
|
||||
@ -199,16 +218,35 @@ REST_FRAMEWORK = {
|
||||
}
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'awx.main.backend.LDAPBackend',
|
||||
'awx.sso.backends.LDAPBackend',
|
||||
'awx.sso.backends.RADIUSBackend',
|
||||
'social.backends.google.GoogleOAuth2',
|
||||
'social.backends.github.GithubOAuth2',
|
||||
'social.backends.github.GithubOrganizationOAuth2',
|
||||
'social.backends.github.GithubTeamOAuth2',
|
||||
'awx.sso.backends.SAMLAuth',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
# LDAP server (default to None to skip using LDAP authentication).
|
||||
AUTH_LDAP_SERVER_URI = None
|
||||
|
||||
# Radius server settings (default to empty string to skip using Radius auth).
|
||||
RADIUS_SERVER = ''
|
||||
RADIUS_PORT = 1812
|
||||
RADIUS_SECRET = ''
|
||||
|
||||
# Seconds before auth tokens expire.
|
||||
AUTH_TOKEN_EXPIRATION = 1800
|
||||
|
||||
# Maximum number of per-user valid, concurrent tokens.
|
||||
# -1 is unlimited
|
||||
AUTH_TOKEN_PER_USER = -1
|
||||
|
||||
# Enable / Disable HTTP Basic Authentication used in the API browser
|
||||
# Note: Session limits are not enforced when using HTTP Basic Authentication.
|
||||
AUTH_BASIC_ENABLED = True
|
||||
|
||||
# If set, serve only minified JS for UI.
|
||||
USE_MINIFIED_JS = False
|
||||
|
||||
@ -307,6 +345,70 @@ CELERYBEAT_SCHEDULE = {
|
||||
},
|
||||
}
|
||||
|
||||
# Social Auth configuration.
|
||||
SOCIAL_AUTH_STRATEGY = 'social.strategies.django_strategy.DjangoStrategy'
|
||||
SOCIAL_AUTH_STORAGE = 'social.apps.django_app.default.models.DjangoStorage'
|
||||
SOCIAL_AUTH_USER_MODEL = AUTH_USER_MODEL # noqa
|
||||
SOCIAL_AUTH_PIPELINE = (
|
||||
'social.pipeline.social_auth.social_details',
|
||||
'social.pipeline.social_auth.social_uid',
|
||||
'social.pipeline.social_auth.auth_allowed',
|
||||
'social.pipeline.social_auth.social_user',
|
||||
'social.pipeline.user.get_username',
|
||||
'social.pipeline.social_auth.associate_by_email',
|
||||
'social.pipeline.user.create_user',
|
||||
'awx.sso.pipeline.check_user_found_or_created',
|
||||
'social.pipeline.social_auth.associate_user',
|
||||
'social.pipeline.social_auth.load_extra_data',
|
||||
'awx.sso.pipeline.set_is_active_for_new_user',
|
||||
'social.pipeline.user.user_details',
|
||||
'awx.sso.pipeline.prevent_inactive_login',
|
||||
'awx.sso.pipeline.update_user_orgs',
|
||||
'awx.sso.pipeline.update_user_teams',
|
||||
)
|
||||
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile']
|
||||
|
||||
SOCIAL_AUTH_GITHUB_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_SCOPE = ['user:email', 'read:org']
|
||||
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_SCOPE = ['user:email', 'read:org']
|
||||
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SCOPE = ['user:email', 'read:org']
|
||||
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID = ''
|
||||
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = ''
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = ''
|
||||
SOCIAL_AUTH_SAML_ORG_INFO = {}
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {}
|
||||
SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {}
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS = {}
|
||||
|
||||
SOCIAL_AUTH_LOGIN_URL = '/'
|
||||
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/'
|
||||
SOCIAL_AUTH_LOGIN_ERROR_URL = '/sso/error/'
|
||||
SOCIAL_AUTH_INACTIVE_USER_URL = '/sso/inactive/'
|
||||
|
||||
SOCIAL_AUTH_RAISE_EXCEPTIONS = False
|
||||
SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = False
|
||||
SOCIAL_AUTH_SLUGIFY_USERNAMES = True
|
||||
SOCIAL_AUTH_CLEAN_USERNAMES = True
|
||||
|
||||
SOCIAL_AUTH_SANITIZE_REDIRECTS = True
|
||||
SOCIAL_AUTH_REDIRECT_IS_HTTPS = False
|
||||
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP = {}
|
||||
SOCIAL_AUTH_TEAM_MAP = {}
|
||||
|
||||
# Any ANSIBLE_* settings will be passed to the subprocess environment by the
|
||||
# celery task.
|
||||
|
||||
@ -355,6 +457,12 @@ AWX_JOB_TEMPLATE_HISTORY = 10
|
||||
# The directory in which proot will create new temporary directories for its root
|
||||
AWX_PROOT_BASE_PATH = "/tmp"
|
||||
|
||||
# User definable ansible callback plugins
|
||||
AWX_ANSIBLE_CALLBACK_PLUGINS = ""
|
||||
|
||||
# Enable Pendo on the UI, possible values are 'off', 'anonymous', and 'detailed'
|
||||
PENDO_TRACKING_STATE = "off"
|
||||
|
||||
# Default list of modules allowed for ad hoc commands.
|
||||
AD_HOC_COMMANDS = [
|
||||
'command',
|
||||
@ -473,13 +581,19 @@ VMWARE_EXCLUDE_EMPTY_GROUPS = True
|
||||
# provide a list here.
|
||||
# Source: https://developers.google.com/compute/docs/zones
|
||||
GCE_REGION_CHOICES = [
|
||||
('us-east1-b', 'US East (B)'),
|
||||
('us-east1-c', 'US East (C)'),
|
||||
('us-east1-d', 'US East (D)'),
|
||||
('us-central1-a', 'US Central (A)'),
|
||||
('us-central1-b', 'US Central (B)'),
|
||||
('us-central1-c', 'US Central (C)'),
|
||||
('us-central1-f', 'US Central (F)'),
|
||||
('europe-west1-a', 'Europe West (A)'),
|
||||
('europe-west1-b', 'Europe West (B)'),
|
||||
('europe-west1-c', 'Europe West (C)'),
|
||||
('europe-west1-d', 'Europe West (D)'),
|
||||
('asia-east1-a', 'Asia East (A)'),
|
||||
('asia-east1-b', 'Asia East (B)'),
|
||||
('asia-east1-c', 'Asia East (C)'),
|
||||
]
|
||||
GCE_REGIONS_BLACKLIST = []
|
||||
|
||||
@ -616,7 +730,7 @@ LOGGING = {
|
||||
'level': 'WARNING',
|
||||
'class':'logging.handlers.RotatingFileHandler',
|
||||
'filters': ['require_debug_false'],
|
||||
'filename': os.path.join(LOG_ROOT, 'tower_warnings.log'),
|
||||
'filename': os.path.join(LOG_ROOT, 'tower.log'),
|
||||
'maxBytes': 1024 * 1024 * 5, # 5 MB
|
||||
'backupCount': 5,
|
||||
'formatter':'simple',
|
||||
@ -708,7 +822,12 @@ LOGGING = {
|
||||
'propagate': False,
|
||||
},
|
||||
'django_auth_ldap': {
|
||||
'handlers': ['null'],
|
||||
'handlers': ['console', 'file', 'tower_warnings'],
|
||||
'level': 'DEBUG',
|
||||
},
|
||||
'social': {
|
||||
'handlers': ['console', 'file', 'tower_warnings'],
|
||||
'level': 'DEBUG',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,8 @@ CALLBACK_QUEUE_PORT = "ipc:///tmp/callback_receiver_dev.ipc"
|
||||
# Enable PROOT for tower-qa integration tests
|
||||
AWX_PROOT_ENABLED = True
|
||||
|
||||
PENDO_TRACKING_STATE = "off"
|
||||
|
||||
# Use Django-Jenkins if installed. Only run tests for awx.main app.
|
||||
try:
|
||||
import django_jenkins
|
||||
@ -74,10 +76,8 @@ include(optional('/etc/tower/conf.d/*.py'), scope=locals())
|
||||
# default settings for development. If not present, we can still run using
|
||||
# only the defaults.
|
||||
try:
|
||||
include(
|
||||
optional('local_*.py'),
|
||||
scope=locals(),
|
||||
)
|
||||
include(optional('local_*.py'), scope=locals())
|
||||
include('postprocess.py', scope=locals())
|
||||
except ImportError:
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
629
awx/settings/local_settings.py.docker_compose
Normal file
@ -0,0 +1,629 @@
|
||||
# Copyright (c) 2015 Ansible, Inc. (formerly AnsibleWorks, Inc.)
|
||||
# All Rights Reserved.
|
||||
|
||||
# Local Django settings for AWX project. Rename to "local_settings.py" and
|
||||
# edit as needed for your development environment.
|
||||
|
||||
# All variables defined in awx/settings/development.py will already be loaded
|
||||
# into the global namespace before this file is loaded, to allow for reading
|
||||
# and updating the default settings as needed.
|
||||
|
||||
###############################################################################
|
||||
# MISC PROJECT SETTINGS
|
||||
###############################################################################
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@domain.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
# Database settings to use PostgreSQL for development.
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'awx-dev',
|
||||
'USER': 'awx-dev',
|
||||
'PASSWORD': 'AWXsome1',
|
||||
'HOST': 'postgres',
|
||||
'PORT': '',
|
||||
}
|
||||
}
|
||||
|
||||
# Use SQLite for unit tests instead of PostgreSQL. If the lines below are
|
||||
# commented out, Django will create the test_awx-dev database in PostgreSQL to
|
||||
# run unit tests.
|
||||
if len(sys.argv) >= 2 and sys.argv[1] == 'test':
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'awx.sqlite3'),
|
||||
# Test database cannot be :memory: for celery/inventory tests.
|
||||
'TEST_NAME': os.path.join(BASE_DIR, 'awx_test.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
MONGO_DB = 'system_tracking_test'
|
||||
|
||||
# Celery AMQP configuration.
|
||||
BROKER_URL = 'redis://redis/'
|
||||
|
||||
# Mongo host configuration
|
||||
MONGO_HOST = 'mongo'
|
||||
|
||||
# Set True to enable additional logging from the job_event_callback plugin
|
||||
JOB_CALLBACK_DEBUG = False
|
||||
|
||||
# Absolute filesystem path to the directory to host projects (with playbooks).
|
||||
# This directory should NOT be web-accessible.
|
||||
PROJECTS_ROOT = os.path.join(BASE_DIR, 'projects')
|
||||
|
||||
# Absolute filesystem path to the directory for job status stdout
|
||||
# This directory should not be web-accessible
|
||||
JOBOUTPUT_ROOT = os.path.join(BASE_DIR, 'job_status')
|
||||
|
||||
# The UUID of the system, for HA.
|
||||
SYSTEM_UUID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# On Unix systems, a value of None will cause Django to use the same
|
||||
# timezone as the operating system.
|
||||
# If running in a Windows environment this must be set to the same as your
|
||||
# system time zone.
|
||||
TIME_ZONE = 'America/New_York'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# Hardcoded values can leak through source control. Consider loading
|
||||
# the secret key from an environment variable or a file instead.
|
||||
SECRET_KEY = 'p7z7g1ql4%6+(6nlebb6hdk7sd^&fnjpal308%n%+p^_e6vo1y'
|
||||
|
||||
# HTTP headers and meta keys to search to determine remote host name or IP. Add
|
||||
# additional items to this list, such as "HTTP_X_FORWARDED_FOR", if behind a
|
||||
# reverse proxy.
|
||||
REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
||||
|
||||
# Define additional environment variables to be passed to subprocess started by
|
||||
# the celery task.
|
||||
#AWX_TASK_ENV['FOO'] = 'BAR'
|
||||
|
||||
# If set, use -vvv for project updates instead of -v for more output.
|
||||
# PROJECT_UPDATE_VVV=True
|
||||
|
||||
# Set verbosity for inventory import command when running inventory updates.
|
||||
# INVENTORY_UPDATE_VERBOSITY=1
|
||||
|
||||
###############################################################################
|
||||
# EMAIL SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Email address that error messages come from.
|
||||
SERVER_EMAIL = 'root@localhost'
|
||||
|
||||
# The email backend to use. For possible shortcuts see django.core.mail.
|
||||
# The default is to use the SMTP backend.
|
||||
# Third-party backends can be specified by providing a Python path
|
||||
# to a module that defines an EmailBackend class.
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
# Host for sending email.
|
||||
EMAIL_HOST = 'localhost'
|
||||
|
||||
# Port for sending email.
|
||||
EMAIL_PORT = 25
|
||||
|
||||
# Optional SMTP authentication information for EMAIL_HOST.
|
||||
EMAIL_HOST_USER = ''
|
||||
EMAIL_HOST_PASSWORD = ''
|
||||
EMAIL_USE_TLS = False
|
||||
|
||||
# Default email address to use for various automated correspondence from
|
||||
# the site managers.
|
||||
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
|
||||
|
||||
# Subject-line prefix for email messages send with django.core.mail.mail_admins
|
||||
# or ...mail_managers. Make sure to include the trailing space.
|
||||
EMAIL_SUBJECT_PREFIX = '[AWX] '
|
||||
|
||||
###############################################################################
|
||||
# LOGGING SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Enable logging to syslog. Setting level to ERROR captures 500 errors,
|
||||
# WARNING also logs 4xx responses.
|
||||
|
||||
LOGGING['handlers']['syslog'] = {
|
||||
'level': 'WARNING',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.NullHandler',
|
||||
'formatter': 'simple',
|
||||
}
|
||||
|
||||
# Enable the following lines to also log to a file.
|
||||
#LOGGING['handlers']['file'] = {
|
||||
# 'class': 'logging.FileHandler',
|
||||
# 'filename': os.path.join(BASE_DIR, 'awx.log'),
|
||||
# 'formatter': 'simple',
|
||||
#}
|
||||
|
||||
# Enable the following lines to turn on lots of permissions-related logging.
|
||||
#LOGGING['loggers']['awx.main.access']['propagate'] = True
|
||||
#LOGGING['loggers']['awx.main.signals']['propagate'] = True
|
||||
#LOGGING['loggers']['awx.main.permissions']['propagate'] = True
|
||||
|
||||
# Enable the following lines to turn on LDAP auth logging.
|
||||
#LOGGING['loggers']['django_auth_ldap']['handlers'] = ['console']
|
||||
#LOGGING['loggers']['django_auth_ldap']['level'] = 'DEBUG'
|
||||
|
||||
###############################################################################
|
||||
# LDAP AUTHENTICATION SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Refer to django-auth-ldap docs for more details:
|
||||
# http://pythonhosted.org/django-auth-ldap/authentication.html
|
||||
|
||||
# LDAP server URI, such as "ldap://ldap.example.com:389" (non-SSL) or
|
||||
# "ldaps://ldap.example.com:636" (SSL). LDAP authentication is disable if this
|
||||
# parameter is empty.
|
||||
AUTH_LDAP_SERVER_URI = ''
|
||||
|
||||
# DN of user to bind for all search queries. Normally in the format
|
||||
# "CN=Some User,OU=Users,DC=example,DC=com" but may also be specified as
|
||||
# "DOMAIN\username" for Active Directory.
|
||||
AUTH_LDAP_BIND_DN = ''
|
||||
|
||||
# Password using to bind above user account.
|
||||
AUTH_LDAP_BIND_PASSWORD = ''
|
||||
|
||||
# Enable TLS when the connection is not using SSL.
|
||||
AUTH_LDAP_START_TLS = False
|
||||
|
||||
# Imports needed for remaining LDAP configuration.
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
|
||||
from django_auth_ldap.config import ActiveDirectoryGroupType
|
||||
|
||||
# LDAP search query to find users.
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
||||
'OU=Users,DC=example,DC=com', # Base DN
|
||||
ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE
|
||||
'(sAMAccountName=%(user)s)', # Query
|
||||
)
|
||||
|
||||
# Alternative to user search, if user DNs are all of the same format.
|
||||
#AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,OU=Users,DC=example,DC=com'
|
||||
|
||||
# Mapping of LDAP to user atrributes (key is user attribute name, value is LDAP
|
||||
# attribute name).
|
||||
AUTH_LDAP_USER_ATTR_MAP = {
|
||||
'first_name': 'givenName',
|
||||
'last_name': 'sn',
|
||||
'email': 'mail',
|
||||
}
|
||||
|
||||
# LDAP search query to find groups. Does not support LDAPSearchUnion.
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
|
||||
'DC=example,DC=com', # Base DN
|
||||
ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE
|
||||
'(objectClass=group)', # Query
|
||||
)
|
||||
# Type of group returned by the search above. Should be one of the types
|
||||
# listed at: http://pythonhosted.org/django-auth-ldap/groups.html#types-of-groups
|
||||
AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType()
|
||||
|
||||
# Group DN required to login. If specified, user must be a member of this
|
||||
# group to login via LDAP.
|
||||
#AUTH_LDAP_REQUIRE_GROUP = ''
|
||||
|
||||
# Group DN denied from login. If specified, user will not be allowed to login
|
||||
# if a member of this group.
|
||||
#AUTH_LDAP_DENY_GROUP = ''
|
||||
|
||||
# User profile flags updated from group membership (key is user attribute name,
|
||||
# value is group DN).
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
#'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
}
|
||||
|
||||
# Mapping between organization admins/users and LDAP groups. Keys are
|
||||
# organization names (will be created if not present). Values are dictionaries
|
||||
# of options for each organization's membership, where each can contain the
|
||||
# following parameters:
|
||||
# - remove: True/False. Defaults to False. Specifies the default for
|
||||
# remove_admins or remove_users if those parameters aren't explicitly set.
|
||||
# - admins: None, True/False, string or list/tuple of strings.
|
||||
# If None, organization admins will not be updated.
|
||||
# If True/False, all LDAP users will be added/removed as admins.
|
||||
# If a string or list of strings, specifies the group DN(s). User will be
|
||||
# added as an org admin if the user is a member of ANY of these groups.
|
||||
# - remove_admins: True/False. Defaults to False. If True, a user who is not a
|
||||
# member of the given groups will be removed from the organization's admins.
|
||||
# - users: None, True/False, string or list/tuple of strings. Same rules apply
|
||||
# as for admins.
|
||||
# - remove_users: True/False. Defaults to False. If True, a user who is not a
|
||||
# member of the given groups will be removed from the organization's users.
|
||||
AUTH_LDAP_ORGANIZATION_MAP = {
|
||||
#'Test Org': {
|
||||
# 'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
# 'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||
#},
|
||||
#'Test Org 2': {
|
||||
# 'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'],
|
||||
# 'users': True,
|
||||
#},
|
||||
}
|
||||
|
||||
# Mapping between team members (users) and LDAP groups. Keys are team names
|
||||
# (will be created if not present). Values are dictionaries of options for
|
||||
# each team's membership, where each can contain the following parameters:
|
||||
# - organization: string. The name of the organization to which the team
|
||||
# belongs. The team will be created if the combination of organization and
|
||||
# team name does not exist. The organization will first be created if it
|
||||
# does not exist.
|
||||
# - users: None, True/False, string or list/tuple of strings.
|
||||
# If None, team members will not be updated.
|
||||
# If True/False, all LDAP users will be added/removed as team members.
|
||||
# If a string or list of strings, specifies the group DN(s). User will be
|
||||
# added as a team member if the user is a member of ANY of these groups.
|
||||
# - remove: True/False. Defaults to False. If True, a user who is not a member
|
||||
# of the given groups will be removed from the team.
|
||||
AUTH_LDAP_TEAM_MAP = {
|
||||
'My Team': {
|
||||
'organization': 'Test Org',
|
||||
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||
'remove': True,
|
||||
},
|
||||
'Other Team': {
|
||||
'organization': 'Test Org 2',
|
||||
'users': 'CN=Other Users,CN=Users,DC=example,DC=com',
|
||||
'remove': False,
|
||||
},
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# SCM TEST SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Define these variables to enable more complete testing of project support for
|
||||
# SCM updates. The test repositories listed do not have to contain any valid
|
||||
# playbooks.
|
||||
|
||||
try:
|
||||
path = os.path.expanduser(os.path.expandvars('~/.ssh/id_rsa'))
|
||||
TEST_SSH_KEY_DATA = file(path, 'rb').read()
|
||||
except IOError:
|
||||
TEST_SSH_KEY_DATA = ''
|
||||
|
||||
TEST_GIT_USERNAME = ''
|
||||
TEST_GIT_PASSWORD = ''
|
||||
TEST_GIT_KEY_DATA = TEST_SSH_KEY_DATA
|
||||
TEST_GIT_PUBLIC_HTTPS = 'https://github.com/ansible/ansible.github.com.git'
|
||||
TEST_GIT_PRIVATE_HTTPS = 'https://github.com/ansible/product-docs.git'
|
||||
TEST_GIT_PRIVATE_SSH = 'git@github.com:ansible/product-docs.git'
|
||||
|
||||
TEST_HG_USERNAME = ''
|
||||
TEST_HG_PASSWORD = ''
|
||||
TEST_HG_KEY_DATA = TEST_SSH_KEY_DATA
|
||||
TEST_HG_PUBLIC_HTTPS = 'https://bitbucket.org/cchurch/django-hotrunner'
|
||||
TEST_HG_PRIVATE_HTTPS = ''
|
||||
TEST_HG_PRIVATE_SSH = ''
|
||||
|
||||
TEST_SVN_USERNAME = ''
|
||||
TEST_SVN_PASSWORD = ''
|
||||
TEST_SVN_PUBLIC_HTTPS = 'https://github.com/ansible/ansible.github.com'
|
||||
TEST_SVN_PRIVATE_HTTPS = 'https://github.com/ansible/product-docs'
|
||||
|
||||
# To test repo access via SSH login to localhost.
|
||||
import getpass
|
||||
TEST_SSH_LOOPBACK_USERNAME = getpass.getuser()
|
||||
TEST_SSH_LOOPBACK_PASSWORD = ''
|
||||
|
||||
###############################################################################
|
||||
# LDAP TEST SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# LDAP connection and authentication settings for unit tests only. LDAP tests
|
||||
# will be skipped if TEST_AUTH_LDAP_SERVER_URI is not configured.
|
||||
|
||||
TEST_AUTH_LDAP_SERVER_URI = ''
|
||||
TEST_AUTH_LDAP_BIND_DN = ''
|
||||
TEST_AUTH_LDAP_BIND_PASSWORD = ''
|
||||
TEST_AUTH_LDAP_START_TLS = False
|
||||
|
||||
# LDAP username/password for testing authentication.
|
||||
TEST_AUTH_LDAP_USERNAME = ''
|
||||
TEST_AUTH_LDAP_PASSWORD = ''
|
||||
|
||||
# LDAP search query to find users.
|
||||
TEST_AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
||||
'CN=Users,DC=example,DC=com',
|
||||
ldap.SCOPE_SUBTREE,
|
||||
'(sAMAccountName=%(user)s)',
|
||||
)
|
||||
|
||||
# Alternative to user search.
|
||||
#TEST_AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com'
|
||||
|
||||
# Mapping of LDAP attributes to user attributes.
|
||||
TEST_AUTH_LDAP_USER_ATTR_MAP = {
|
||||
'first_name': 'givenName',
|
||||
'last_name': 'sn',
|
||||
'email': 'mail',
|
||||
}
|
||||
|
||||
# LDAP search query for finding groups.
|
||||
TEST_AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
|
||||
'DC=example,DC=com',
|
||||
ldap.SCOPE_SUBTREE,
|
||||
'(objectClass=group)',
|
||||
)
|
||||
# Type of group returned by the search above.
|
||||
TEST_AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType()
|
||||
|
||||
# Test DNs for a group required to login. User should be a member of the first
|
||||
# group, but not a member of the second.
|
||||
TEST_AUTH_LDAP_REQUIRE_GROUP = 'CN=Domain Admins,CN=Users,DC=example,DC=com'
|
||||
TEST_AUTH_LDAP_REQUIRE_GROUP_FAIL = 'CN=Guest,CN=Users,DC=example,DC=com'
|
||||
|
||||
# Test DNs for a group denied from login. User should not be a member of the
|
||||
# first group, but should be a member of the second.
|
||||
TEST_AUTH_LDAP_DENY_GROUP = 'CN=Guest,CN=Users,DC=example,DC=com'
|
||||
TEST_AUTH_LDAP_DENY_GROUP_FAIL = 'CN=Domain Admins,CN=Users,DC=example,DC=com'
|
||||
|
||||
# User profile flags updated from group membership. Test user should be a
|
||||
# member of the group.
|
||||
TEST_AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
}
|
||||
|
||||
# Test mapping between organization admins/users and LDAP groups.
|
||||
TEST_AUTH_LDAP_ORGANIZATION_MAP = {
|
||||
'Test Org': {
|
||||
'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||
},
|
||||
'Test Org 2': {
|
||||
'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'],
|
||||
'users': True,
|
||||
},
|
||||
}
|
||||
# Expected results from organization mapping. After login, should user be an
|
||||
# admin/user in the given organization?
|
||||
TEST_AUTH_LDAP_ORGANIZATION_MAP_RESULT = {
|
||||
'Test Org': {'admins': True, 'users': False},
|
||||
'Test Org 2': {'admins': False, 'users': True},
|
||||
}
|
||||
|
||||
# Second test mapping to test remove parameters.
|
||||
TEST_AUTH_LDAP_ORGANIZATION_MAP_2 = {
|
||||
'Test Org': {
|
||||
'admins': 'CN=Domain Users,CN=Users,DC=example,DC=com',
|
||||
'users': True,
|
||||
'remove_admins': True,
|
||||
'remove_users': False,
|
||||
},
|
||||
'Test Org 2': {
|
||||
'admins': ['CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
'CN=Administrators,CN=Builtin,DC=example,DC=com'],
|
||||
'users': False,
|
||||
'remove': True,
|
||||
},
|
||||
}
|
||||
|
||||
# Expected results from second organization mapping.
|
||||
TEST_AUTH_LDAP_ORGANIZATION_MAP_2_RESULT = {
|
||||
'Test Org': {'admins': False, 'users': True},
|
||||
'Test Org 2': {'admins': True, 'users': False},
|
||||
}
|
||||
|
||||
# Test mapping between team users and LDAP groups.
|
||||
TEST_AUTH_LDAP_TEAM_MAP = {
|
||||
'Domain Users Team': {
|
||||
'organization': 'Test Org',
|
||||
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||
'remove': False,
|
||||
},
|
||||
'Admins Team': {
|
||||
'organization': 'Admins Org',
|
||||
'users': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||
'remove': True,
|
||||
},
|
||||
'Everyone Team': {
|
||||
'organization': 'Test Org 2',
|
||||
'users': True,
|
||||
},
|
||||
}
|
||||
# Expected results from team mapping. After login, should user be a member of
|
||||
# the given team?
|
||||
TEST_AUTH_LDAP_TEAM_MAP_RESULT = {
|
||||
'Domain Users Team': {'users': False},
|
||||
'Admins Team': {'users': True},
|
||||
'Everyone Team': {'users': True},
|
||||
}
|
||||
|
||||
# Second test mapping for teams to remove user.
|
||||
TEST_AUTH_LDAP_TEAM_MAP_2 = {
|
||||
'Domain Users Team': {
|
||||
'organization': 'Test Org',
|
||||
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||
'remove': False,
|
||||
},
|
||||
'Admins Team': {
|
||||
'organization': 'Admins Org',
|
||||
'users': 'CN=Administrators,CN=Builtin,DC=example,DC=com',
|
||||
'remove': True,
|
||||
},
|
||||
'Everyone Team': {
|
||||
'organization': 'Test Org 2',
|
||||
'users': False,
|
||||
'remove': False,
|
||||
},
|
||||
}
|
||||
# Expected results from second team mapping. After login, should user be a
|
||||
# member of the given team?
|
||||
TEST_AUTH_LDAP_TEAM_MAP_2_RESULT = {
|
||||
'Domain Users Team': {'users': False},
|
||||
'Admins Team': {'users': False},
|
||||
'Everyone Team': {'users': True},
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# RADIUS AUTH SETTINGS
|
||||
###############################################################################
|
||||
|
||||
RADIUS_SERVER = ''
|
||||
RADIUS_PORT = 1812
|
||||
RADIUS_SECRET = ''
|
||||
|
||||
###############################################################################
|
||||
# SOCIAL AUTH SETTINGS
|
||||
###############################################################################
|
||||
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile']
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['example.com']
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'example.com'}
|
||||
|
||||
SOCIAL_AUTH_GITHUB_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_SECRET = ''
|
||||
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME = ''
|
||||
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID = ''
|
||||
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID = ''
|
||||
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = ''
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = ''
|
||||
SOCIAL_AUTH_SAML_ORG_INFO = {
|
||||
'en-US': {
|
||||
'name': 'example',
|
||||
'displayname': 'Example',
|
||||
'url': 'http://www.example.com',
|
||||
},
|
||||
}
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {
|
||||
'givenName': 'Some User',
|
||||
'emailAddress': 'suser@example.com',
|
||||
}
|
||||
SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {
|
||||
'givenName': 'Some User',
|
||||
'emailAddress': 'suser@example.com',
|
||||
}
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS = {
|
||||
#'myidp': {
|
||||
# 'entity_id': 'https://idp.example.com',
|
||||
# 'url': 'https://myidp.example.com/sso',
|
||||
# 'x509cert': '',
|
||||
#},
|
||||
#'onelogin': {
|
||||
# 'entity_id': 'https://app.onelogin.com/saml/metadata/123456',
|
||||
# 'url': 'https://example.onelogin.com/trust/saml2/http-post/sso/123456',
|
||||
# 'x509cert': '',
|
||||
# 'attr_user_permanent_id': 'name_id',
|
||||
# 'attr_first_name': 'User.FirstName',
|
||||
# 'attr_last_name': 'User.LastName',
|
||||
# 'attr_username': 'User.email',
|
||||
# 'attr_email': 'User.email',
|
||||
#},
|
||||
}
|
||||
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP = {
|
||||
# Add all users to the default organization.
|
||||
'Default': {
|
||||
'users': True,
|
||||
},
|
||||
#'Test Org': {
|
||||
# 'admins': ['admin@example.com'],
|
||||
# 'users': True,
|
||||
#},
|
||||
#'Test Org 2': {
|
||||
# 'admins': ['admin@example.com', re.compile(r'^tower-[^@]+*?@.*$],
|
||||
# 'users': re.compile(r'^[^@].*?@example\.com$'),
|
||||
#},
|
||||
}
|
||||
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_SAML_ORGANIZATION_MAP = {}
|
||||
|
||||
SOCIAL_AUTH_TEAM_MAP = {
|
||||
#'My Team': {
|
||||
# 'organization': 'Test Org',
|
||||
# 'users': ['re.compile(r'^[^@]+?@test\.example\.com$')'],
|
||||
# 'remove': True,
|
||||
#},
|
||||
#'Other Team': {
|
||||
# 'organization': 'Test Org 2',
|
||||
# 'users': re.compile(r'^[^@]+?@test2\.example\.com$'),
|
||||
# 'remove': False,
|
||||
#},
|
||||
}
|
||||
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_SAML_TEAM_MAP = {}
|
||||
|
||||
# Uncomment the line below (i.e. set SOCIAL_AUTH_USER_FIELDS to an empty list)
|
||||
# to prevent new user accounts from being created. Only users who have
|
||||
# previously logged in using social auth or have a user account with a matching
|
||||
# email address will be able to login.
|
||||
|
||||
#SOCIAL_AUTH_USER_FIELDS = []
|
||||
|
||||
# It is also possible to add custom functions to the social auth pipeline for
|
||||
# more advanced organization and team mapping. Use at your own risk.
|
||||
|
||||
#def custom_social_auth_pipeline_function(backend, details, user=None, *args, **kwargs):
|
||||
# print 'custom:', backend, details, user, args, kwargs
|
||||
|
||||
#SOCIAL_AUTH_PIPELINE += (
|
||||
# 'awx.settings.development.custom_social_auth_pipeline_function',
|
||||
#)
|
||||
|
||||
###############################################################################
|
||||
# INVENTORY IMPORT TEST SETTINGS
|
||||
###############################################################################
|
||||
|
||||
# Define these variables to enable more complete testing of inventory import
|
||||
# from cloud providers.
|
||||
|
||||
# EC2 credentials
|
||||
TEST_AWS_ACCESS_KEY_ID = ''
|
||||
TEST_AWS_SECRET_ACCESS_KEY = ''
|
||||
TEST_AWS_REGIONS = 'all'
|
||||
# Check IAM STS credentials
|
||||
TEST_AWS_SECURITY_TOKEN = ''
|
||||
|
||||
# Rackspace credentials
|
||||
TEST_RACKSPACE_USERNAME = ''
|
||||
TEST_RACKSPACE_API_KEY = ''
|
||||
TEST_RACKSPACE_REGIONS = 'all'
|
||||
|
||||
# VMware credentials
|
||||
TEST_VMWARE_HOST = ''
|
||||
TEST_VMWARE_USER = ''
|
||||
TEST_VMWARE_PASSWORD = ''
|
||||
|
||||
# OpenStack credentials
|
||||
TEST_OPENSTACK_HOST = ''
|
||||
TEST_OPENSTACK_USER = ''
|
||||
TEST_OPENSTACK_PASSWORD = ''
|
||||
TEST_OPENSTACK_PROJECT = ''
|
||||
|
||||
# Azure credentials.
|
||||
TEST_AZURE_USERNAME = ''
|
||||
TEST_AZURE_KEY_DATA = ''
|
||||
@ -470,6 +470,128 @@ TEST_AUTH_LDAP_TEAM_MAP_2_RESULT = {
|
||||
'Everyone Team': {'users': True},
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# RADIUS AUTH SETTINGS
|
||||
###############################################################################
|
||||
|
||||
RADIUS_SERVER = ''
|
||||
RADIUS_PORT = 1812
|
||||
RADIUS_SECRET = ''
|
||||
|
||||
###############################################################################
|
||||
# SOCIAL AUTH SETTINGS
|
||||
###############################################################################
|
||||
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile']
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['example.com']
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'example.com'}
|
||||
|
||||
SOCIAL_AUTH_GITHUB_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_SECRET = ''
|
||||
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME = ''
|
||||
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET = ''
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID = ''
|
||||
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID = ''
|
||||
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = ''
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = ''
|
||||
SOCIAL_AUTH_SAML_ORG_INFO = {
|
||||
'en-US': {
|
||||
'name': 'example',
|
||||
'displayname': 'Example',
|
||||
'url': 'http://www.example.com',
|
||||
},
|
||||
}
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {
|
||||
'givenName': 'Some User',
|
||||
'emailAddress': 'suser@example.com',
|
||||
}
|
||||
SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {
|
||||
'givenName': 'Some User',
|
||||
'emailAddress': 'suser@example.com',
|
||||
}
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS = {
|
||||
#'myidp': {
|
||||
# 'entity_id': 'https://idp.example.com',
|
||||
# 'url': 'https://myidp.example.com/sso',
|
||||
# 'x509cert': '',
|
||||
#},
|
||||
#'onelogin': {
|
||||
# 'entity_id': 'https://app.onelogin.com/saml/metadata/123456',
|
||||
# 'url': 'https://example.onelogin.com/trust/saml2/http-post/sso/123456',
|
||||
# 'x509cert': '',
|
||||
# 'attr_user_permanent_id': 'name_id',
|
||||
# 'attr_first_name': 'User.FirstName',
|
||||
# 'attr_last_name': 'User.LastName',
|
||||
# 'attr_username': 'User.email',
|
||||
# 'attr_email': 'User.email',
|
||||
#},
|
||||
}
|
||||
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP = {
|
||||
# Add all users to the default organization.
|
||||
'Default': {
|
||||
'users': True,
|
||||
},
|
||||
#'Test Org': {
|
||||
# 'admins': ['admin@example.com'],
|
||||
# 'users': True,
|
||||
#},
|
||||
#'Test Org 2': {
|
||||
# 'admins': ['admin@example.com', re.compile(r'^tower-[^@]+*?@.*$],
|
||||
# 'users': re.compile(r'^[^@].*?@example\.com$'),
|
||||
#},
|
||||
}
|
||||
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP = {}
|
||||
#SOCIAL_AUTH_SAML_ORGANIZATION_MAP = {}
|
||||
|
||||
SOCIAL_AUTH_TEAM_MAP = {
|
||||
#'My Team': {
|
||||
# 'organization': 'Test Org',
|
||||
# 'users': ['re.compile(r'^[^@]+?@test\.example\.com$')'],
|
||||
# 'remove': True,
|
||||
#},
|
||||
#'Other Team': {
|
||||
# 'organization': 'Test Org 2',
|
||||
# 'users': re.compile(r'^[^@]+?@test2\.example\.com$'),
|
||||
# 'remove': False,
|
||||
#},
|
||||
}
|
||||
|
||||
#SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP = {}
|
||||
#SOCIAL_AUTH_SAML_TEAM_MAP = {}
|
||||
|
||||
# Uncomment the line below (i.e. set SOCIAL_AUTH_USER_FIELDS to an empty list)
|
||||
# to prevent new user accounts from being created. Only users who have
|
||||
# previously logged in using social auth or have a user account with a matching
|
||||
# email address will be able to login.
|
||||
|
||||
#SOCIAL_AUTH_USER_FIELDS = []
|
||||
|
||||
# It is also possible to add custom functions to the social auth pipeline for
|
||||
# more advanced organization and team mapping. Use at your own risk.
|
||||
|
||||
#def custom_social_auth_pipeline_function(backend, details, user=None, *args, **kwargs):
|
||||
# print 'custom:', backend, details, user, args, kwargs
|
||||
|
||||
#SOCIAL_AUTH_PIPELINE += (
|
||||
# 'awx.settings.development.custom_social_auth_pipeline_function',
|
||||
#)
|
||||
|
||||
###############################################################################
|
||||
# INVENTORY IMPORT TEST SETTINGS
|
||||
###############################################################################
|
||||
@ -481,6 +603,9 @@ TEST_AUTH_LDAP_TEAM_MAP_2_RESULT = {
|
||||
TEST_AWS_ACCESS_KEY_ID = ''
|
||||
TEST_AWS_SECRET_ACCESS_KEY = ''
|
||||
TEST_AWS_REGIONS = 'all'
|
||||
# Check IAM STS credentials
|
||||
TEST_AWS_SECURITY_TOKEN = ''
|
||||
|
||||
|
||||
# Rackspace credentials
|
||||
TEST_RACKSPACE_USERNAME = ''
|
||||
|
||||
34
awx/settings/postprocess.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# flake8: noqa
|
||||
|
||||
# Runs after all configuration files have been loaded to fix/check/update
|
||||
# settings as needed.
|
||||
|
||||
if not AUTH_LDAP_SERVER_URI:
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.sso.backends.LDAPBackend']
|
||||
|
||||
if not RADIUS_SERVER:
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.sso.backends.RADIUSBackend']
|
||||
|
||||
if not all([SOCIAL_AUTH_GOOGLE_OAUTH2_KEY, SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET]):
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.google.GoogleOAuth2']
|
||||
|
||||
if not all([SOCIAL_AUTH_GITHUB_KEY, SOCIAL_AUTH_GITHUB_SECRET]):
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.github.GithubOAuth2']
|
||||
|
||||
if not all([SOCIAL_AUTH_GITHUB_ORG_KEY, SOCIAL_AUTH_GITHUB_ORG_SECRET, SOCIAL_AUTH_GITHUB_ORG_NAME]):
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.github.GithubOrganizationOAuth2']
|
||||
|
||||
if not all([SOCIAL_AUTH_GITHUB_TEAM_KEY, SOCIAL_AUTH_GITHUB_TEAM_SECRET, SOCIAL_AUTH_GITHUB_TEAM_ID]):
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.github.GithubTeamOAuth2']
|
||||
|
||||
if not all([SOCIAL_AUTH_SAML_SP_ENTITY_ID, SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, SOCIAL_AUTH_SAML_ORG_INFO,
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, SOCIAL_AUTH_SAML_SUPPORT_CONTACT,
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS]):
|
||||
AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.sso.backends.SAMLAuth']
|
||||
|
||||
if not AUTH_BASIC_ENABLED:
|
||||
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [x for x in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] if x != 'rest_framework.authentication.BasicAuthentication']
|
||||
@ -108,11 +108,8 @@ settings_file = os.environ.get('AWX_SETTINGS_FILE',
|
||||
# Attempt to load settings from /etc/tower/settings.py first, followed by
|
||||
# /etc/tower/conf.d/*.py.
|
||||
try:
|
||||
include(
|
||||
settings_file,
|
||||
optional(settings_files),
|
||||
scope=locals(),
|
||||
)
|
||||
include(settings_file, optional(settings_files), scope=locals())
|
||||
include('postprocess.py', scope=locals())
|
||||
except ImportError:
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
2
awx/sso/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
232
awx/sso/backends.py
Normal file
@ -0,0 +1,232 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import logging
|
||||
|
||||
# Django
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings as django_settings
|
||||
|
||||
# django-auth-ldap
|
||||
from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings
|
||||
from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend
|
||||
from django_auth_ldap.backend import populate_user
|
||||
|
||||
# radiusauth
|
||||
from radiusauth.backends import RADIUSBackend as BaseRADIUSBackend
|
||||
|
||||
# social
|
||||
from social.backends.saml import OID_USERID
|
||||
from social.backends.saml import SAMLAuth as BaseSAMLAuth
|
||||
from social.backends.saml import SAMLIdentityProvider as BaseSAMLIdentityProvider
|
||||
|
||||
# Ansible Tower
|
||||
from awx.api.license import feature_enabled
|
||||
|
||||
logger = logging.getLogger('awx.sso.backends')
|
||||
|
||||
|
||||
class LDAPSettings(BaseLDAPSettings):
|
||||
|
||||
defaults = dict(BaseLDAPSettings.defaults.items() + {
|
||||
'ORGANIZATION_MAP': {},
|
||||
'TEAM_MAP': {},
|
||||
}.items())
|
||||
|
||||
|
||||
class LDAPBackend(BaseLDAPBackend):
|
||||
'''
|
||||
Custom LDAP backend for AWX.
|
||||
'''
|
||||
|
||||
settings_prefix = 'AUTH_LDAP_'
|
||||
|
||||
def _get_settings(self):
|
||||
if self._settings is None:
|
||||
self._settings = LDAPSettings(self.settings_prefix)
|
||||
return self._settings
|
||||
|
||||
def _set_settings(self, settings):
|
||||
self._settings = settings
|
||||
|
||||
settings = property(_get_settings, _set_settings)
|
||||
|
||||
def authenticate(self, username, password):
|
||||
if not self.settings.SERVER_URI:
|
||||
return None
|
||||
if not feature_enabled('ldap'):
|
||||
logger.error("Unable to authenticate, license does not support LDAP authentication")
|
||||
return None
|
||||
return super(LDAPBackend, self).authenticate(username, password)
|
||||
|
||||
def get_user(self, user_id):
|
||||
if not self.settings.SERVER_URI:
|
||||
return None
|
||||
if not feature_enabled('ldap'):
|
||||
logger.error("Unable to get_user, license does not support LDAP authentication")
|
||||
return None
|
||||
return super(LDAPBackend, self).get_user(user_id)
|
||||
|
||||
# Disable any LDAP based authorization / permissions checking.
|
||||
|
||||
def has_perm(self, user, perm, obj=None):
|
||||
return False
|
||||
|
||||
def has_module_perms(self, user, app_label):
|
||||
return False
|
||||
|
||||
def get_all_permissions(self, user, obj=None):
|
||||
return set()
|
||||
|
||||
def get_group_permissions(self, user, obj=None):
|
||||
return set()
|
||||
|
||||
|
||||
class RADIUSBackend(BaseRADIUSBackend):
|
||||
'''
|
||||
Custom Radius backend to verify license status
|
||||
'''
|
||||
|
||||
def authenticate(self, username, password):
|
||||
if not django_settings.RADIUS_SERVER:
|
||||
return None
|
||||
if not feature_enabled('enterprise_auth'):
|
||||
logger.error("Unable to authenticate, license does not support RADIUS authentication")
|
||||
return None
|
||||
return super(RADIUSBackend, self).authenticate(username, password)
|
||||
|
||||
def get_user(self, user_id):
|
||||
if not django_settings.RADIUS_SERVER:
|
||||
return None
|
||||
if not feature_enabled('enterprise_auth'):
|
||||
logger.error("Unable to get_user, license does not support RADIUS authentication")
|
||||
return None
|
||||
return super(RADIUSBackend, self).get_user(user_id)
|
||||
|
||||
|
||||
class TowerSAMLIdentityProvider(BaseSAMLIdentityProvider):
|
||||
'''
|
||||
Custom Identity Provider to make attributes to what we expect.
|
||||
'''
|
||||
|
||||
def get_user_permanent_id(self, attributes):
|
||||
uid = attributes[self.conf.get('attr_user_permanent_id', OID_USERID)]
|
||||
if isinstance(uid, basestring):
|
||||
return uid
|
||||
return uid[0]
|
||||
|
||||
def get_attr(self, attributes, conf_key, default_attribute):
|
||||
"""
|
||||
Get the attribute 'default_attribute' out of the attributes,
|
||||
unless self.conf[conf_key] overrides the default by specifying
|
||||
another attribute to use.
|
||||
"""
|
||||
key = self.conf.get(conf_key, default_attribute)
|
||||
value = attributes[key][0] if key in attributes else None
|
||||
if conf_key in ('attr_first_name', 'attr_last_name', 'attr_username', 'attr_email') and value is None:
|
||||
logger.warn("Could not map user detail '%s' from SAML attribute '%s'; "
|
||||
"update SOCIAL_AUTH_SAML_ENABLED_IDPS['%s']['%s'] with the correct SAML attribute.",
|
||||
conf_key[5:], key, self.name, conf_key)
|
||||
return unicode(value) if value is not None else value
|
||||
|
||||
|
||||
class SAMLAuth(BaseSAMLAuth):
|
||||
'''
|
||||
Custom SAMLAuth backend to verify license status
|
||||
'''
|
||||
|
||||
def get_idp(self, idp_name):
|
||||
idp_config = self.setting('ENABLED_IDPS')[idp_name]
|
||||
return TowerSAMLIdentityProvider(idp_name, **idp_config)
|
||||
|
||||
def authenticate(self, *args, **kwargs):
|
||||
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
|
||||
django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO,
|
||||
django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT,
|
||||
django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]):
|
||||
return None
|
||||
if not feature_enabled('enterprise_auth'):
|
||||
logger.error("Unable to authenticate, license does not support SAML authentication")
|
||||
return None
|
||||
return super(SAMLAuth, self).authenticate(*args, **kwargs)
|
||||
|
||||
def get_user(self, user_id):
|
||||
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
|
||||
django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO,
|
||||
django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT,
|
||||
django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]):
|
||||
return None
|
||||
if not feature_enabled('enterprise_auth'):
|
||||
logger.error("Unable to get_user, license does not support SAML authentication")
|
||||
return None
|
||||
return super(SAMLAuth, self).get_user(user_id)
|
||||
|
||||
|
||||
def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=False):
|
||||
'''
|
||||
Hepler function to update m2m relationship based on LDAP group membership.
|
||||
'''
|
||||
should_add = False
|
||||
if opts is None:
|
||||
return
|
||||
elif not opts:
|
||||
pass
|
||||
elif opts is True:
|
||||
should_add = True
|
||||
else:
|
||||
if isinstance(opts, basestring):
|
||||
opts = [opts]
|
||||
for group_dn in opts:
|
||||
if not isinstance(group_dn, basestring):
|
||||
continue
|
||||
if ldap_user._get_groups().is_member_of(group_dn):
|
||||
should_add = True
|
||||
if should_add:
|
||||
rel.add(user)
|
||||
elif remove:
|
||||
rel.remove(user)
|
||||
|
||||
|
||||
@receiver(populate_user)
|
||||
def on_populate_user(sender, **kwargs):
|
||||
'''
|
||||
Handle signal from LDAP backend to populate the user object. Update user
|
||||
organization/team memberships according to their LDAP groups.
|
||||
'''
|
||||
from awx.main.models import Organization, Team
|
||||
user = kwargs['user']
|
||||
ldap_user = kwargs['ldap_user']
|
||||
backend = ldap_user.backend
|
||||
|
||||
# Update organization membership based on group memberships.
|
||||
org_map = getattr(backend.settings, 'ORGANIZATION_MAP', {})
|
||||
for org_name, org_opts in org_map.items():
|
||||
org, created = Organization.objects.get_or_create(name=org_name)
|
||||
remove = bool(org_opts.get('remove', False))
|
||||
admins_opts = org_opts.get('admins', None)
|
||||
remove_admins = bool(org_opts.get('remove_admins', remove))
|
||||
_update_m2m_from_groups(user, ldap_user, org.admins, admins_opts,
|
||||
remove_admins)
|
||||
users_opts = org_opts.get('users', None)
|
||||
remove_users = bool(org_opts.get('remove_users', remove))
|
||||
_update_m2m_from_groups(user, ldap_user, org.users, users_opts,
|
||||
remove_users)
|
||||
|
||||
# Update team membership based on group memberships.
|
||||
team_map = getattr(backend.settings, 'TEAM_MAP', {})
|
||||
for team_name, team_opts in team_map.items():
|
||||
if 'organization' not in team_opts:
|
||||
continue
|
||||
org, created = Organization.objects.get_or_create(name=team_opts['organization'])
|
||||
team, created = Team.objects.get_or_create(name=team_name, organization=org)
|
||||
users_opts = team_opts.get('users', None)
|
||||
remove = bool(team_opts.get('remove', False))
|
||||
_update_m2m_from_groups(user, ldap_user, team.users, users_opts,
|
||||
remove)
|
||||
|
||||
# Update user profile to store LDAP DN.
|
||||
profile = user.profile
|
||||
if profile.ldap_dn != ldap_user.dn:
|
||||
profile.ldap_dn = ldap_user.dn
|
||||
profile.save()
|
||||
91
awx/sso/middleware.py
Normal file
@ -0,0 +1,91 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import urllib
|
||||
|
||||
# Six
|
||||
import six
|
||||
|
||||
# Django
|
||||
from django.contrib.auth import login, logout
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.timezone import now
|
||||
|
||||
# Python Social Auth
|
||||
from social.exceptions import SocialAuthBaseException
|
||||
from social.utils import social_logger
|
||||
from social.apps.django_app.middleware import SocialAuthExceptionMiddleware
|
||||
|
||||
# Ansible Tower
|
||||
from awx.main.models import AuthToken
|
||||
|
||||
|
||||
class SocialAuthMiddleware(SocialAuthExceptionMiddleware):
|
||||
|
||||
def process_request(self, request):
|
||||
token_key = request.COOKIES.get('token', '')
|
||||
token_key = urllib.quote(urllib.unquote(token_key).strip('"'))
|
||||
|
||||
if not hasattr(request, 'successful_authenticator'):
|
||||
request.successful_authenticator = None
|
||||
|
||||
if not request.path.startswith('/sso/'):
|
||||
|
||||
# If token isn't present but we still have a user logged in via Django
|
||||
# sessions, log them out.
|
||||
if not token_key and request.user and request.user.is_authenticated():
|
||||
logout(request)
|
||||
|
||||
# If a token is present, make sure it matches a valid one in the
|
||||
# database, and log the user via Django session if necessary.
|
||||
# Otherwise, log the user out via Django sessions.
|
||||
elif token_key:
|
||||
|
||||
try:
|
||||
auth_token = AuthToken.objects.filter(key=token_key, expires__gt=now())[0]
|
||||
except IndexError:
|
||||
auth_token = None
|
||||
|
||||
if not auth_token and request.user and request.user.is_authenticated():
|
||||
logout(request)
|
||||
elif auth_token and request.user != auth_token.user:
|
||||
logout(request)
|
||||
auth_token.user.backend = ''
|
||||
login(request, auth_token.user)
|
||||
auth_token.refresh()
|
||||
|
||||
if auth_token and request.user and request.user.is_authenticated():
|
||||
request.session.pop('social_auth_error', None)
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
strategy = getattr(request, 'social_strategy', None)
|
||||
if strategy is None or self.raise_exception(request, exception):
|
||||
return
|
||||
|
||||
if isinstance(exception, SocialAuthBaseException) or request.path.startswith('/sso/'):
|
||||
backend = getattr(request, 'backend', None)
|
||||
backend_name = getattr(backend, 'name', 'unknown-backend')
|
||||
full_backend_name = backend_name
|
||||
try:
|
||||
idp_name = strategy.request_data()['RelayState']
|
||||
full_backend_name = '%s:%s' % (backend_name, idp_name)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
message = self.get_message(request, exception)
|
||||
social_logger.error(message)
|
||||
|
||||
url = self.get_redirect_uri(request, exception)
|
||||
request.session['social_auth_error'] = (full_backend_name, message)
|
||||
return redirect(url)
|
||||
|
||||
def get_message(self, request, exception):
|
||||
msg = six.text_type(exception)
|
||||
if msg and msg[-1] not in '.?!':
|
||||
msg = msg + '.'
|
||||
return msg
|
||||
|
||||
def get_redirect_uri(self, request, exception):
|
||||
strategy = getattr(request, 'social_strategy', None)
|
||||
return strategy.session_get('next', '') or strategy.setting('LOGIN_ERROR_URL')
|
||||
2
awx/sso/models.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
137
awx/sso/pipeline.py
Normal file
@ -0,0 +1,137 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import re
|
||||
|
||||
# Python Social Auth
|
||||
from social.exceptions import AuthException
|
||||
|
||||
# Tower
|
||||
from awx.api.license import feature_enabled
|
||||
|
||||
|
||||
class AuthNotFound(AuthException):
|
||||
|
||||
def __init__(self, backend, email_or_uid, *args, **kwargs):
|
||||
self.email_or_uid = email_or_uid
|
||||
super(AuthNotFound, self).__init__(backend, *args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return 'An account cannot be found for {0}'.format(self.email_or_uid)
|
||||
|
||||
|
||||
class AuthInactive(AuthException):
|
||||
|
||||
def __str__(self):
|
||||
return 'Your account is inactive'
|
||||
|
||||
|
||||
def check_user_found_or_created(backend, details, user=None, *args, **kwargs):
|
||||
if not user:
|
||||
email_or_uid = details.get('email') or kwargs.get('email') or kwargs.get('uid') or '???'
|
||||
raise AuthNotFound(backend, email_or_uid)
|
||||
|
||||
|
||||
def set_is_active_for_new_user(strategy, details, user=None, *args, **kwargs):
|
||||
if kwargs.get('is_new', False):
|
||||
details['is_active'] = True
|
||||
return {'details': details}
|
||||
|
||||
|
||||
def prevent_inactive_login(backend, details, user=None, *args, **kwargs):
|
||||
if user and not user.is_active:
|
||||
raise AuthInactive(backend)
|
||||
|
||||
|
||||
def _update_m2m_from_expression(user, rel, expr, remove=False):
|
||||
'''
|
||||
Helper function to update m2m relationship based on user matching one or
|
||||
more expressions.
|
||||
'''
|
||||
should_add = False
|
||||
if expr is None:
|
||||
return
|
||||
elif not expr:
|
||||
pass
|
||||
elif expr is True:
|
||||
should_add = True
|
||||
else:
|
||||
if isinstance(expr, (basestring, type(re.compile('')))):
|
||||
expr = [expr]
|
||||
for ex in expr:
|
||||
if isinstance(ex, basestring):
|
||||
if user.username == ex or user.email == ex:
|
||||
should_add = True
|
||||
elif isinstance(ex, type(re.compile(''))):
|
||||
if ex.match(user.username) or ex.match(user.email):
|
||||
should_add = True
|
||||
if should_add:
|
||||
rel.add(user)
|
||||
elif remove:
|
||||
rel.remove(user)
|
||||
|
||||
|
||||
def update_user_orgs(backend, details, user=None, *args, **kwargs):
|
||||
'''
|
||||
Update organization memberships for the given user based on mapping rules
|
||||
defined in settings.
|
||||
'''
|
||||
if not user:
|
||||
return
|
||||
from awx.main.models import Organization
|
||||
multiple_orgs = feature_enabled('multiple_organizations')
|
||||
org_map = backend.setting('ORGANIZATION_MAP') or {}
|
||||
for org_name, org_opts in org_map.items():
|
||||
|
||||
# Get or create the org to update. If the license only allows for one
|
||||
# org, always use the first active org, unless no org exists.
|
||||
if multiple_orgs:
|
||||
org = Organization.objects.get_or_create(name=org_name)[0]
|
||||
else:
|
||||
try:
|
||||
org = Organization.objects.filter(active=True).order_by('pk')[0]
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
# Update org admins from expression(s).
|
||||
remove = bool(org_opts.get('remove', False))
|
||||
admins_expr = org_opts.get('admins', None)
|
||||
remove_admins = bool(org_opts.get('remove_admins', remove))
|
||||
_update_m2m_from_expression(user, org.admins, admins_expr, remove_admins)
|
||||
|
||||
# Update org users from expression(s).
|
||||
users_expr = org_opts.get('users', None)
|
||||
remove_users = bool(org_opts.get('remove_users', remove))
|
||||
_update_m2m_from_expression(user, org.users, users_expr, remove_users)
|
||||
|
||||
|
||||
def update_user_teams(backend, details, user=None, *args, **kwargs):
|
||||
'''
|
||||
Update team memberships for the given user based on mapping rules defined
|
||||
in settings.
|
||||
'''
|
||||
if not user:
|
||||
return
|
||||
from awx.main.models import Organization, Team
|
||||
multiple_orgs = feature_enabled('multiple_organizations')
|
||||
team_map = backend.setting('TEAM_MAP') or {}
|
||||
for team_name, team_opts in team_map.items():
|
||||
|
||||
# Get or create the org to update. If the license only allows for one
|
||||
# org, always use the first active org, unless no org exists.
|
||||
if multiple_orgs:
|
||||
if 'organization' not in team_opts:
|
||||
continue
|
||||
org = Organization.objects.get_or_create(name=team_opts['organization'])[0]
|
||||
else:
|
||||
try:
|
||||
org = Organization.objects.filter(active=True).order_by('pk')[0]
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
# Update team members from expression(s).
|
||||
team = Team.objects.get_or_create(name=team_name, organization=org)[0]
|
||||
users_expr = team_opts.get('users', None)
|
||||
remove = bool(team_opts.get('remove', False))
|
||||
_update_m2m_from_expression(user, team.users, users_expr, remove)
|
||||
13
awx/sso/urls.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Django
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
urlpatterns = patterns(
|
||||
'awx.sso.views',
|
||||
url(r'^complete/$', 'sso_complete', name='sso_complete'),
|
||||
url(r'^error/$', 'sso_error', name='sso_error'),
|
||||
url(r'^inactive/$', 'sso_inactive', name='sso_inactive'),
|
||||
url(r'^metadata/saml/$', 'saml_metadata', name='saml_metadata'),
|
||||
)
|
||||
84
awx/sso/views.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import urllib
|
||||
|
||||
# Django
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse
|
||||
from django.utils.timezone import now, utc
|
||||
from django.views.generic import View
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
# Django REST Framework
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
|
||||
# Ansible Tower
|
||||
from awx.main.models import AuthToken
|
||||
from awx.api.serializers import UserSerializer
|
||||
|
||||
|
||||
class BaseRedirectView(RedirectView):
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
last_path = self.request.COOKIES.get('lastPath', '')
|
||||
last_path = urllib.quote(urllib.unquote(last_path).strip('"'))
|
||||
url = reverse('ui:index')
|
||||
if last_path:
|
||||
return '%s#%s' % (url, last_path)
|
||||
else:
|
||||
return url
|
||||
|
||||
sso_error = BaseRedirectView.as_view()
|
||||
sso_inactive = BaseRedirectView.as_view()
|
||||
|
||||
|
||||
class CompleteView(BaseRedirectView):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
response = super(CompleteView, self).dispatch(request, *args, **kwargs)
|
||||
if self.request.user and self.request.user.is_authenticated():
|
||||
request_hash = AuthToken.get_request_hash(self.request)
|
||||
try:
|
||||
token = AuthToken.objects.filter(user=request.user,
|
||||
request_hash=request_hash,
|
||||
expires__gt=now())[0]
|
||||
token.refresh()
|
||||
except IndexError:
|
||||
token = AuthToken.objects.create(user=request.user,
|
||||
request_hash=request_hash)
|
||||
request.session['auth_token_key'] = token.key
|
||||
token_key = urllib.quote('"%s"' % token.key)
|
||||
response.set_cookie('token', token_key)
|
||||
token_expires = token.expires.astimezone(utc).strftime('%Y-%m-%dT%H:%M:%S')
|
||||
token_expires = '%s.%03dZ' % (token_expires, token.expires.microsecond / 1000)
|
||||
token_expires = urllib.quote('"%s"' % token_expires)
|
||||
response.set_cookie('token_expires', token_expires)
|
||||
response.set_cookie('userLoggedIn', 'true')
|
||||
current_user = UserSerializer(self.request.user)
|
||||
current_user = JSONRenderer().render(current_user.data)
|
||||
current_user = urllib.quote('%s' % current_user, '')
|
||||
response.set_cookie('current_user', current_user)
|
||||
return response
|
||||
|
||||
sso_complete = CompleteView.as_view()
|
||||
|
||||
|
||||
class MetadataView(View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
from social.apps.django_app.utils import load_backend, load_strategy
|
||||
complete_url = reverse('social:complete', args=('saml', ))
|
||||
saml_backend = load_backend(
|
||||
load_strategy(request),
|
||||
'saml',
|
||||
redirect_uri=complete_url,
|
||||
)
|
||||
metadata, errors = saml_backend.generate_metadata_xml()
|
||||
if not errors:
|
||||
return HttpResponse(content=metadata, content_type='text/xml')
|
||||
else:
|
||||
return HttpResponse(content=str(errors), content_type='text/plain')
|
||||
|
||||
saml_metadata = MetadataView.as_view()
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@ -5,7 +5,7 @@
|
||||
|
||||
{% block style %}
|
||||
{{ block.super }}
|
||||
<link href="{{ STATIC_URL }}img/favicon.ico" rel="shortcut icon" />
|
||||
<link href="{{ STATIC_URL }}assets/favicon.ico" rel="shortcut icon" />
|
||||
<style type="text/css">
|
||||
html body {
|
||||
background: #ddd;
|
||||
@ -183,7 +183,7 @@ html body .dropdown-submenu:hover>a {
|
||||
{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
<a class="brand" href="/api/"><img class="logo" src="{{ STATIC_URL }}img/tower_console_logo.png">{% block branding_title %}{% trans 'REST API' %}{% endblock %}</a>
|
||||
<a class="brand" href="/api/"><img class="logo" src="{{ STATIC_URL }}assets/tower_console_logo.png">{% block branding_title %}{% trans 'REST API' %}{% endblock %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block userlinks %}
|
||||
@ -196,7 +196,7 @@ html body .dropdown-submenu:hover>a {
|
||||
|
||||
{% block footer %}
|
||||
<div id="footer">
|
||||
<a href="http://www.ansible.com" target="_blank"><img class="towerlogo" src="{{ STATIC_URL }}img/tower_console_bug.png" /></a><br/>
|
||||
<a href="http://www.ansible.com" target="_blank"><img class="towerlogo" src="{{ STATIC_URL }}assets/tower_console_bug.png" /></a><br/>
|
||||
Copyright © 2015 <a href="http://www.ansible.com" target="_blank">Ansible, Inc.</a> All rights reserved.
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 898 B After Width: | Height: | Size: 898 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |