Merge branch 'release_2.4.0' into stable
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"directory": "awx/ui/static/lib"
|
"directory": "awx/ui/client/lib"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,19 @@ awx/public/media
|
|||||||
awx/public/static
|
awx/public/static
|
||||||
awx/ui/tests/test-results.xml
|
awx/ui/tests/test-results.xml
|
||||||
awx/ui/static/js/awx.min.js
|
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/ui/static/css/awx.min.css
|
||||||
awx/main/fixtures
|
awx/main/fixtures
|
||||||
awx/*.log
|
awx/*.log
|
||||||
tower/tower_warnings.log
|
tower/tower_warnings.log
|
||||||
celerybeat-schedule
|
celerybeat-schedule
|
||||||
awx/ui/static/docs
|
awx/ui/static
|
||||||
awx/ui/dist
|
awx/ui/build_test
|
||||||
|
|
||||||
# Python & setuptools
|
# Python & setuptools
|
||||||
__pycache__
|
__pycache__
|
||||||
build
|
/build
|
||||||
/deb-build
|
/deb-build
|
||||||
/rpm-build
|
/rpm-build
|
||||||
/tar-build
|
/tar-build
|
||||||
@@ -37,12 +38,13 @@ build
|
|||||||
/Brocfile.js
|
/Brocfile.js
|
||||||
/bower.json
|
/bower.json
|
||||||
/package.json
|
/package.json
|
||||||
|
/testem.yml
|
||||||
node_modules/**
|
node_modules/**
|
||||||
/tmp
|
/tmp
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
# UI build debugging
|
# UI build debugging
|
||||||
/DEBUG-*
|
/DEBUG
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
.coverage
|
.coverage
|
||||||
@@ -50,6 +52,8 @@ npm-debug.log
|
|||||||
coverage.xml
|
coverage.xml
|
||||||
htmlcov
|
htmlcov
|
||||||
pep8.txt
|
pep8.txt
|
||||||
|
scratch
|
||||||
|
testem.log
|
||||||
|
|
||||||
# Mac OS X
|
# Mac OS X
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ recursive-include awx/static *.ico
|
|||||||
recursive-include awx/templates *.html
|
recursive-include awx/templates *.html
|
||||||
recursive-include awx/api/templates *.md *.html
|
recursive-include awx/api/templates *.md *.html
|
||||||
recursive-include awx/ui/templates *.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/playbooks *.yml
|
||||||
recursive-include awx/lib/site-packages *
|
recursive-include awx/lib/site-packages *
|
||||||
recursive-include requirements *.txt
|
recursive-include requirements *.txt
|
||||||
@@ -12,8 +12,10 @@ recursive-include docs/licenses *
|
|||||||
recursive-exclude awx devonly.py*
|
recursive-exclude awx devonly.py*
|
||||||
recursive-exclude awx/api/tests *
|
recursive-exclude awx/api/tests *
|
||||||
recursive-exclude awx/main/tests *
|
recursive-exclude awx/main/tests *
|
||||||
|
recursive-exclude awx/ui/client *
|
||||||
recursive-exclude awx/settings local_settings.py*
|
recursive-exclude awx/settings local_settings.py*
|
||||||
include tools/scripts/request_tower_configuration.sh
|
include tools/scripts/request_tower_configuration.sh
|
||||||
|
include tools/scripts/request_tower_configuration.ps1
|
||||||
include tools/scripts/ansible-tower-service
|
include tools/scripts/ansible-tower-service
|
||||||
include tools/munin_monitors/*
|
include tools/munin_monitors/*
|
||||||
include tools/sosreport/*
|
include tools/sosreport/*
|
||||||
|
|||||||
@@ -2,12 +2,18 @@ PYTHON = python
|
|||||||
SITELIB=$(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
|
SITELIB=$(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
|
||||||
OFFICIAL ?= no
|
OFFICIAL ?= no
|
||||||
PACKER ?= packer
|
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")
|
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
|
NODE ?= node
|
||||||
NPM_BIN ?= npm
|
NPM_BIN ?= npm
|
||||||
DEPS_SCRIPT ?= packaging/bundle/deps.py
|
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
|
# Determine appropriate shasum command
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
@@ -28,8 +34,10 @@ GIT_REMOTE_URL = $(shell git config --get remote.origin.url)
|
|||||||
BUILD = 0.git$(DATE)
|
BUILD = 0.git$(DATE)
|
||||||
ifeq ($(OFFICIAL),yes)
|
ifeq ($(OFFICIAL),yes)
|
||||||
RELEASE ?= 1
|
RELEASE ?= 1
|
||||||
|
AW_REPO_URL ?= http://releases.ansible.com/ansible-tower
|
||||||
else
|
else
|
||||||
RELEASE ?= $(BUILD)
|
RELEASE ?= $(BUILD)
|
||||||
|
AW_REPO_URL ?= http://jenkins.testing.ansible.com/ansible-tower_nightlies_RTYUIOPOIUYTYU/$(GIT_BRANCH)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Allow AMI license customization
|
# Allow AMI license customization
|
||||||
@@ -52,11 +60,9 @@ endif
|
|||||||
ifeq ($(OFFICIAL),yes)
|
ifeq ($(OFFICIAL),yes)
|
||||||
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)
|
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)
|
||||||
SDIST_TAR_NAME=$(NAME)-$(VERSION)
|
SDIST_TAR_NAME=$(NAME)-$(VERSION)
|
||||||
PACKER_BUILD_OPTS=-var-file=vars-release.json
|
|
||||||
else
|
else
|
||||||
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)-$(RELEASE)
|
SETUP_TAR_NAME=$(NAME)-setup-$(VERSION)-$(RELEASE)
|
||||||
SDIST_TAR_NAME=$(NAME)-$(VERSION)-$(RELEASE)
|
SDIST_TAR_NAME=$(NAME)-$(VERSION)-$(RELEASE)
|
||||||
PACKER_BUILD_OPTS=-var-file=vars-nightly.json
|
|
||||||
endif
|
endif
|
||||||
SDIST_TAR_FILE=$(SDIST_TAR_NAME).tar.gz
|
SDIST_TAR_FILE=$(SDIST_TAR_NAME).tar.gz
|
||||||
SETUP_TAR_FILE=$(SETUP_TAR_NAME).tar.gz
|
SETUP_TAR_FILE=$(SETUP_TAR_NAME).tar.gz
|
||||||
@@ -98,14 +104,15 @@ MOCK_BIN ?= mock
|
|||||||
MOCK_CFG ?=
|
MOCK_CFG ?=
|
||||||
RPM_SPECDIR= packaging/rpm
|
RPM_SPECDIR= packaging/rpm
|
||||||
RPM_SPEC = $(RPM_SPECDIR)/$(NAME).spec
|
RPM_SPEC = $(RPM_SPECDIR)/$(NAME).spec
|
||||||
# Provide a fallback value for RPM_DIST
|
|
||||||
RPM_DIST ?= $(shell rpm --eval '%{?dist}' 2>/dev/null)
|
RPM_DIST ?= $(shell rpm --eval '%{?dist}' 2>/dev/null)
|
||||||
|
# Provide a fallback value for RPM_DIST
|
||||||
ifeq ($(RPM_DIST),)
|
ifeq ($(RPM_DIST),)
|
||||||
RPM_DIST = .el6
|
RPM_DIST = .el6
|
||||||
endif
|
endif
|
||||||
RPM_ARCH ?= $(shell rpm --eval '%{_arch}' 2>/dev/null)
|
RPM_ARCH ?= $(shell rpm --eval '%{_arch}' 2>/dev/null)
|
||||||
|
# Provide a fallback value for RPM_ARCH
|
||||||
ifeq ($(RPM_ARCH),)
|
ifeq ($(RPM_ARCH),)
|
||||||
RPM_ARCH = $(shell uname -m)
|
RPM_ARCH = $(shell uname -m)
|
||||||
endif
|
endif
|
||||||
RPM_NVR = $(NAME)-$(VERSION)-$(RELEASE)$(RPM_DIST)
|
RPM_NVR = $(NAME)-$(VERSION)-$(RELEASE)$(RPM_DIST)
|
||||||
|
|
||||||
@@ -147,7 +154,7 @@ endif
|
|||||||
.PHONY: clean rebase push requirements requirements_dev requirements_jenkins \
|
.PHONY: clean rebase push requirements requirements_dev requirements_jenkins \
|
||||||
real-requirements real-requirements_dev real-requirements_jenkins \
|
real-requirements real-requirements_dev real-requirements_jenkins \
|
||||||
develop refresh adduser syncdb migrate dbchange dbshell runserver celeryd \
|
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 \
|
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 \
|
devjs minjs testjs testjs_ci node-tests browser-tests jshint ngdocs sync_ui \
|
||||||
deb deb-src debian debsign pbuilder reprepro setup_tarball \
|
deb deb-src debian debsign pbuilder reprepro setup_tarball \
|
||||||
@@ -173,17 +180,20 @@ clean-grunt:
|
|||||||
|
|
||||||
# Remove UI build files
|
# Remove UI build files
|
||||||
clean-ui:
|
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/dist
|
||||||
rm -rf awx/ui/static/docs
|
|
||||||
|
|
||||||
# Remove packer artifacts
|
# Remove packer artifacts
|
||||||
clean-packer:
|
clean-packer:
|
||||||
rm -rf packer_cache
|
rm -rf packer_cache
|
||||||
rm -rf packaging/packer/packer_cache
|
rm -rf packaging/packer/packer_cache
|
||||||
rm -rf packaging/packer/output-virtualbox-iso/
|
rm -rf packaging/packer/output-virtualbox-iso/
|
||||||
|
rm -rf packaging/packer/output-vmware-iso
|
||||||
rm -f packaging/packer/ansible-tower-*.box
|
rm -f packaging/packer/ansible-tower-*.box
|
||||||
rm -rf packaging/packer/ansible-tower*-ova
|
rm -rf packaging/packer/ansible-tower*-ova
|
||||||
|
rm -rf packaging/packer/ansible-tower*-vmx
|
||||||
rm -f Vagrantfile
|
rm -f Vagrantfile
|
||||||
|
|
||||||
clean-bundle:
|
clean-bundle:
|
||||||
@@ -192,7 +202,10 @@ clean-bundle:
|
|||||||
# Remove temporary build files, compiled Python files.
|
# Remove temporary build files, compiled Python files.
|
||||||
clean: clean-rpm clean-deb clean-grunt clean-ui clean-tar clean-packer clean-bundle
|
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/site-packages
|
||||||
|
rm -rf awx/lib/.deps_built
|
||||||
rm -rf dist/*
|
rm -rf dist/*
|
||||||
|
rm -rf tmp
|
||||||
|
mkdir tmp
|
||||||
rm -rf build $(NAME)-$(VERSION) *.egg-info
|
rm -rf build $(NAME)-$(VERSION) *.egg-info
|
||||||
find . -type f -regex ".*\.py[co]$$" -delete
|
find . -type f -regex ".*\.py[co]$$" -delete
|
||||||
|
|
||||||
@@ -295,6 +308,11 @@ server: server_noattach
|
|||||||
servercc: server_noattach
|
servercc: server_noattach
|
||||||
tmux -2 -CC attach-session -t tower
|
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).
|
# Run the built-in development webserver (by default on http://localhost:8013).
|
||||||
runserver:
|
runserver:
|
||||||
$(PYTHON) manage.py runserver
|
$(PYTHON) manage.py runserver
|
||||||
@@ -345,17 +363,6 @@ test_coverage:
|
|||||||
coverage_html:
|
coverage_html:
|
||||||
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.
|
# Run API unit tests across multiple Python/Django versions with Tox.
|
||||||
test_tox:
|
test_tox:
|
||||||
tox -v
|
tox -v
|
||||||
@@ -364,44 +371,90 @@ test_tox:
|
|||||||
test_jenkins:
|
test_jenkins:
|
||||||
$(PYTHON) manage.py jenkins -v2 --enable-coverage --project-apps-tests
|
$(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 $< $@
|
cp $< $@
|
||||||
|
|
||||||
Brocfile.js: packaging/grunt/Brocfile.js
|
Brocfile.js: packaging/node/Brocfile.js
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
bower.json: packaging/grunt/bower.json
|
bower.json: packaging/node/bower.json
|
||||||
cp $< $@
|
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)#;' $< > $@
|
sed -e 's#%NAME%#$(NAME)#;s#%VERSION%#$(VERSION)#;s#%GIT_REMOTE_URL%#$(GIT_REMOTE_URL)#;' $< > $@
|
||||||
|
|
||||||
sync_ui: node_modules Brocfile.js
|
testem.yml: packaging/node/testem.yml
|
||||||
$(NODE) tools/ui/timepiece.js awx/ui/dist -- --debug
|
cp $< $@
|
||||||
|
|
||||||
# Update local npm install
|
# Update local npm install
|
||||||
node_modules: package.json
|
node_modules: package.json
|
||||||
$(NPM_BIN) install
|
$(NPM_BIN) install
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
devjs: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
awx/ui/%: node_modules clean-ui Brocfile.js bower.json
|
||||||
$(BROCCOLI) build awx/ui/dist -- --debug
|
$(BROCCOLI_BIN) build $@ -- $(UI_FLAGS)
|
||||||
|
|
||||||
# Build minified JS/CSS.
|
# Concatenated, non-minified build; contains debug code and sourcemaps; does not include any tests
|
||||||
minjs: awx/ui/dist/tower.min.css.gz
|
devjs: awx/ui/static
|
||||||
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
|
|
||||||
|
|
||||||
minjs_ci: node_modules clean-ui Brocfile.js
|
# Concatenated, minified, compressed (production) build with no sourcemaps or tests
|
||||||
$(BROCCOLI) build awx/ui/dist -- --no-debug --compress --no-docs
|
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
|
# Check .js files for errors and lint
|
||||||
jshint: node_modules Gruntfile.js
|
jshint: node_modules Gruntfile.js
|
||||||
$(GRUNT) $@
|
$(GRUNT) $@
|
||||||
|
|
||||||
|
# Generate UI code documentation
|
||||||
ngdocs: devjs Gruntfile.js
|
ngdocs: devjs Gruntfile.js
|
||||||
$(GRUNT) $@
|
$(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.
|
# Build a pip-installable package into dist/ with a timestamped version number.
|
||||||
dev_build:
|
dev_build:
|
||||||
$(PYTHON) setup.py dev_build
|
$(PYTHON) setup.py dev_build
|
||||||
@@ -438,7 +491,7 @@ release_clean:
|
|||||||
-(rm *.tar)
|
-(rm *.tar)
|
||||||
-(rm -rf ($RELEASE))
|
-(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
|
BUILD="$(BUILD)" $(PYTHON) setup.py sdist
|
||||||
|
|
||||||
sdist: dist/$(SDIST_TAR_FILE)
|
sdist: dist/$(SDIST_TAR_FILE)
|
||||||
@@ -598,7 +651,7 @@ reprepro: deb-build/$(DEB_NVRA).deb reprepro/conf
|
|||||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) clearvanished
|
$(REPREPRO_BIN) $(REPREPRO_OPTS) clearvanished
|
||||||
for COMPONENT in non-free $(VERSION); do \
|
for COMPONENT in non-free $(VERSION); do \
|
||||||
$(REPREPRO_BIN) $(REPREPRO_OPTS) -C $$COMPONENT remove $(DEB_DIST) $(NAME) ; \
|
$(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
|
done
|
||||||
|
|
||||||
|
|
||||||
@@ -609,6 +662,7 @@ reprepro: deb-build/$(DEB_NVRA).deb reprepro/conf
|
|||||||
amazon-ebs:
|
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
|
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
|
virtualbox-ovf: packaging/packer/ansible-tower-$(VERSION)-virtualbox.box
|
||||||
|
|
||||||
packaging/packer/ansible-tower-$(VERSION)-virtualbox.box: packaging/packer/output-virtualbox-iso/centos-7.ovf
|
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:
|
packaging/packer/output-virtualbox-iso/centos-6.ovf:
|
||||||
cd packaging/packer && $(PACKER) build packer-centos-6.json
|
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:
|
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
|
# TODO - figure out how to build the front-end and python requirements with
|
||||||
# 'build'
|
# 'build'
|
||||||
@@ -635,3 +699,15 @@ install:
|
|||||||
# Docker Compose Development environment
|
# Docker Compose Development environment
|
||||||
docker-compose:
|
docker-compose:
|
||||||
docker-compose -f tools/docker-compose.yml up --no-recreate
|
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'
|
||||||
|
|||||||
@@ -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
|
Tower provides a web-based user interface, REST API and task engine built on top of
|
||||||
Ansible.
|
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.
|
Development releases always use the 'master' branch.
|
||||||
|
|
||||||
@@ -31,10 +31,14 @@ Release History
|
|||||||
* 2.1.2, March 25, 2015
|
* 2.1.2, March 25, 2015
|
||||||
* 2.1.3, April 15, 2015
|
* 2.1.3, April 15, 2015
|
||||||
* 2.1.4, June 12, 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.0, July 14, 2015
|
||||||
* 2.2.1, August 12, 2015
|
* 2.2.1, August 12, 2015
|
||||||
* 2.2.2, August 19, 2015
|
* 2.2.2, August 19, 2015
|
||||||
* 2.3.0, September 22, 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
|
Any fixes should be applied on the appropriate release branch and be cherry-picked to
|
||||||
master.
|
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/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 `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 warnings
|
||||||
import site
|
import site
|
||||||
|
|
||||||
__version__ = '2.3.1'
|
__version__ = '2.4.0'
|
||||||
|
|
||||||
__all__ = ['__version__']
|
__all__ = ['__version__']
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
# Python
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
# Django
|
||||||
|
from django.utils.timezone import now as tz_now
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework import authentication
|
from rest_framework import authentication
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
@@ -18,24 +25,38 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
|||||||
|
|
||||||
model = AuthToken
|
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', '')
|
auth = request.META.get('HTTP_X_AUTH_TOKEN', '')
|
||||||
if isinstance(auth, type('')):
|
if isinstance(auth, type('')):
|
||||||
# Work around django test client oddness
|
# Work around django test client oddness
|
||||||
auth = auth.encode(HTTP_HEADER_ENCODING)
|
auth = auth.encode(HTTP_HEADER_ENCODING)
|
||||||
return auth
|
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):
|
def authenticate(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
# Prefer the custom X-Auth-Token header over the Authorization header,
|
# Prefer the custom X-Auth-Token header over the Authorization header,
|
||||||
# to handle cases where the browser submits saved Basic auth and
|
# to handle cases where the browser submits saved Basic auth and
|
||||||
# overrides the UI's normal use of the Authorization header.
|
# 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':
|
if not auth or auth[0].lower() != 'token':
|
||||||
auth = authentication.get_authorization_header(request).split()
|
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
|
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:
|
if len(auth) == 1:
|
||||||
msg = 'Invalid token header. No credentials provided.'
|
msg = 'Invalid token header. No credentials provided.'
|
||||||
@@ -47,6 +68,7 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
|||||||
return self.authenticate_credentials(auth[1])
|
return self.authenticate_credentials(auth[1])
|
||||||
|
|
||||||
def authenticate_credentials(self, key):
|
def authenticate_credentials(self, key):
|
||||||
|
now = tz_now()
|
||||||
# Retrieve the request hash and token.
|
# Retrieve the request hash and token.
|
||||||
try:
|
try:
|
||||||
request_hash = self.model.get_request_hash(self.request)
|
request_hash = self.model.get_request_hash(self.request)
|
||||||
@@ -55,25 +77,46 @@ class TokenAuthentication(authentication.TokenAuthentication):
|
|||||||
request_hash=request_hash,
|
request_hash=request_hash,
|
||||||
)
|
)
|
||||||
except self.model.DoesNotExist:
|
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.
|
# Tell the user why their token was previously invalidated.
|
||||||
# Tokens expire if they are not used for 30 minutes.
|
if token.invalidated:
|
||||||
if token.expired:
|
raise exceptions.AuthenticationFailed(AuthToken.reason_long(token.reason))
|
||||||
raise exceptions.AuthenticationFailed('Token is expired')
|
|
||||||
|
|
||||||
# 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:
|
if not token.user.is_active:
|
||||||
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
||||||
|
|
||||||
# Refresh the token.
|
# Refresh the token.
|
||||||
# This updates the time that the token was last used, meaning that
|
# The token is extended from "right now" + configurable setting amount.
|
||||||
# now the token is valid for 30 minutes from "right now".
|
token.refresh(now=now)
|
||||||
token.refresh()
|
|
||||||
|
|
||||||
# Return the user object and the token.
|
# Return the user object and the token.
|
||||||
return (token.user, 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):
|
class TaskAuthentication(authentication.BaseAuthentication):
|
||||||
'''
|
'''
|
||||||
Custom authentication used for views accessed by the inventory and callback
|
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_200': getattr(self, 'new_in_200', False),
|
||||||
'new_in_210': getattr(self, 'new_in_210', False),
|
'new_in_210': getattr(self, 'new_in_210', False),
|
||||||
'new_in_220': getattr(self, 'new_in_220', 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):
|
def get_description(self, html=False):
|
||||||
@@ -158,7 +160,7 @@ class APIView(views.APIView):
|
|||||||
'''
|
'''
|
||||||
ret = super(APIView, self).metadata(request)
|
ret = super(APIView, self).metadata(request)
|
||||||
added_in_version = '1.2'
|
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):
|
if getattr(self, 'new_in_%s' % version.replace('.', ''), False):
|
||||||
added_in_version = version
|
added_in_version = version
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -31,4 +31,17 @@ def feature_enabled(name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Return the correct feature flag.
|
# 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 = super(UserSerializer, self).to_native(obj)
|
||||||
ret.pop('password', None)
|
ret.pop('password', None)
|
||||||
ret.fields.pop('password', None)
|
ret.fields.pop('password', None)
|
||||||
|
if obj:
|
||||||
|
ret['auth'] = obj.social_auth.values('provider', 'uid')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_validation_exclusions(self):
|
def get_validation_exclusions(self):
|
||||||
@@ -628,6 +630,12 @@ class UserSerializer(BaseSerializer):
|
|||||||
new_password = None
|
new_password = None
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
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:
|
if new_password:
|
||||||
obj.set_password(new_password)
|
obj.set_password(new_password)
|
||||||
if not obj.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
|
# FIXME: may want to make some of these filtered based on user accessing
|
||||||
|
|
||||||
password = serializers.CharField(required=False, default='')
|
password = serializers.CharField(required=False, default='')
|
||||||
|
security_token = serializers.CharField(required=False, default='')
|
||||||
ssh_key_data = serializers.CharField(required=False, default='')
|
ssh_key_data = serializers.CharField(required=False, default='')
|
||||||
ssh_key_unlock = serializers.CharField(required=False, default='')
|
ssh_key_unlock = serializers.CharField(required=False, default='')
|
||||||
become_password = serializers.CharField(required=False, default='')
|
become_password = serializers.CharField(required=False, default='')
|
||||||
@@ -1357,7 +1366,7 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Credential
|
model = Credential
|
||||||
fields = ('*', 'user', 'team', 'kind', 'cloud', 'host', 'username',
|
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',
|
'become_method', 'become_username', 'become_password',
|
||||||
'vault_password')
|
'vault_password')
|
||||||
|
|
||||||
@@ -2001,11 +2010,12 @@ class ScheduleSerializer(BaseSerializer):
|
|||||||
class ActivityStreamSerializer(BaseSerializer):
|
class ActivityStreamSerializer(BaseSerializer):
|
||||||
|
|
||||||
changes = serializers.SerializerMethodField('get_changes')
|
changes = serializers.SerializerMethodField('get_changes')
|
||||||
|
object_association = serializers.SerializerMethodField('get_object_association')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ActivityStream
|
model = ActivityStream
|
||||||
fields = ('*', '-name', '-description', '-created', '-modified',
|
fields = ('*', '-name', '-description', '-created', '-modified',
|
||||||
'timestamp', 'operation', 'changes', 'object1', 'object2')
|
'timestamp', 'operation', 'changes', 'object1', 'object2', 'object_association')
|
||||||
|
|
||||||
def get_fields(self):
|
def get_fields(self):
|
||||||
ret = super(ActivityStreamSerializer, self).get_fields()
|
ret = super(ActivityStreamSerializer, self).get_fields()
|
||||||
@@ -2030,6 +2040,13 @@ class ActivityStreamSerializer(BaseSerializer):
|
|||||||
logger.warn("Error deserializing activity stream json changes")
|
logger.warn("Error deserializing activity stream json changes")
|
||||||
return {}
|
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):
|
def get_related(self, obj):
|
||||||
rel = {}
|
rel = {}
|
||||||
if obj.actor is not None:
|
if obj.actor is not None:
|
||||||
|
|||||||
@@ -4,3 +4,5 @@
|
|||||||
{% if new_in_148 %}> _Added in Ansible Tower 1.4.8_{% 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_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'^$', 'api_v1_root_view'),
|
||||||
url(r'^ping/$', 'api_v1_ping_view'),
|
url(r'^ping/$', 'api_v1_ping_view'),
|
||||||
url(r'^config/$', 'api_v1_config_view'),
|
url(r'^config/$', 'api_v1_config_view'),
|
||||||
|
url(r'^auth/$', 'auth_view'),
|
||||||
url(r'^authtoken/$', 'auth_token_view'),
|
url(r'^authtoken/$', 'auth_token_view'),
|
||||||
url(r'^me/$', 'user_me_list'),
|
url(r'^me/$', 'user_me_list'),
|
||||||
url(r'^dashboard/$', 'dashboard_view'),
|
url(r'^dashboard/$', 'dashboard_view'),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import time
|
|||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import errno
|
import errno
|
||||||
|
from base64 import b64encode
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -47,23 +48,27 @@ import qsstats
|
|||||||
# ANSIConv
|
# ANSIConv
|
||||||
import ansiconv
|
import ansiconv
|
||||||
|
|
||||||
|
# Python Social Auth
|
||||||
|
from social.backends.utils import load_backends
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.main.task_engine import TaskSerializer, TASK_FILE, TEMPORARY_TASK_FILE
|
from awx.main.task_engine import TaskSerializer, TASK_FILE, TEMPORARY_TASK_FILE
|
||||||
from awx.main.tasks import mongodb_control
|
from awx.main.tasks import mongodb_control
|
||||||
from awx.main.access import get_user_queryset
|
from awx.main.access import get_user_queryset
|
||||||
from awx.main.ha import is_ha_environment
|
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.utils.decorators import paginated
|
||||||
from awx.api.filters import MongoFilterBackend
|
from awx.api.filters import MongoFilterBackend
|
||||||
from awx.api.generics import get_view_name
|
from awx.api.generics import get_view_name
|
||||||
from awx.api.generics import * # noqa
|
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.models import * # noqa
|
||||||
from awx.main.utils import * # noqa
|
from awx.main.utils import * # noqa
|
||||||
from awx.api.permissions import * # noqa
|
from awx.api.permissions import * # noqa
|
||||||
from awx.api.renderers import * # noqa
|
from awx.api.renderers import * # noqa
|
||||||
from awx.api.serializers import * # noqa
|
from awx.api.serializers import * # noqa
|
||||||
from awx.fact.models import * # noqa
|
from awx.fact.models import * # noqa
|
||||||
|
from awx.main.utils import emit_websocket_notification
|
||||||
|
|
||||||
def api_exception_handler(exc):
|
def api_exception_handler(exc):
|
||||||
'''
|
'''
|
||||||
@@ -186,12 +191,15 @@ class ApiV1ConfigView(APIView):
|
|||||||
license_reader = TaskSerializer()
|
license_reader = TaskSerializer()
|
||||||
license_data = license_reader.from_file(show_key=request.user.is_superuser)
|
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(
|
data = dict(
|
||||||
time_zone=settings.TIME_ZONE,
|
time_zone=settings.TIME_ZONE,
|
||||||
license_info=license_data,
|
license_info=license_data,
|
||||||
version=get_awx_version(),
|
version=get_awx_version(),
|
||||||
ansible_version=get_ansible_version(),
|
ansible_version=get_ansible_version(),
|
||||||
eula=render_to_string("eula.md"),
|
eula=render_to_string("eula.md"),
|
||||||
|
analytics_status=pendo_state
|
||||||
)
|
)
|
||||||
|
|
||||||
# If LDAP is enabled, user_ldap_fields will return a list of field
|
# 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'
|
view_name = 'Schedule Jobs List'
|
||||||
new_in_148 = True
|
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):
|
class AuthTokenView(APIView):
|
||||||
|
|
||||||
authentication_classes = []
|
authentication_classes = []
|
||||||
@@ -524,12 +571,30 @@ class AuthTokenView(APIView):
|
|||||||
try:
|
try:
|
||||||
token = AuthToken.objects.filter(user=serializer.object['user'],
|
token = AuthToken.objects.filter(user=serializer.object['user'],
|
||||||
request_hash=request_hash,
|
request_hash=request_hash,
|
||||||
expires__gt=now())[0]
|
expires__gt=now(),
|
||||||
|
reason='')[0]
|
||||||
token.refresh()
|
token.refresh()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
token = AuthToken.objects.create(user=serializer.object['user'],
|
token = AuthToken.objects.create(user=serializer.object['user'],
|
||||||
request_hash=request_hash)
|
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)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class OrganizationList(ListCreateAPIView):
|
class OrganizationList(ListCreateAPIView):
|
||||||
@@ -2092,12 +2157,6 @@ class SystemJobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
|
|||||||
relationship = 'schedules'
|
relationship = 'schedules'
|
||||||
parent_key = 'unified_job_template'
|
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):
|
class SystemJobTemplateJobsList(SubListAPIView):
|
||||||
|
|
||||||
model = SystemJob
|
model = SystemJob
|
||||||
@@ -2207,6 +2266,7 @@ class JobRelaunch(RetrieveAPIView, GenericAPIView):
|
|||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
obj.launch_type = 'relaunch'
|
||||||
new_job = obj.copy()
|
new_job = obj.copy()
|
||||||
result = new_job.signal_start(**request.DATA)
|
result = new_job.signal_start(**request.DATA)
|
||||||
if not result:
|
if not result:
|
||||||
@@ -2788,6 +2848,7 @@ class UnifiedJobList(ListAPIView):
|
|||||||
|
|
||||||
class UnifiedJobStdout(RetrieveAPIView):
|
class UnifiedJobStdout(RetrieveAPIView):
|
||||||
|
|
||||||
|
authentication_classes = [TokenGetAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
|
||||||
serializer_class = UnifiedJobStdoutSerializer
|
serializer_class = UnifiedJobStdoutSerializer
|
||||||
renderer_classes = [BrowsableAPIRenderer, renderers.StaticHTMLRenderer,
|
renderer_classes = [BrowsableAPIRenderer, renderers.StaticHTMLRenderer,
|
||||||
PlainTextRenderer, AnsiTextRenderer,
|
PlainTextRenderer, AnsiTextRenderer,
|
||||||
@@ -2806,6 +2867,8 @@ class UnifiedJobStdout(RetrieveAPIView):
|
|||||||
return Response(response_message)
|
return Response(response_message)
|
||||||
|
|
||||||
if request.accepted_renderer.format in ('html', 'api', 'json'):
|
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)
|
start_line = request.QUERY_PARAMS.get('start_line', 0)
|
||||||
end_line = request.QUERY_PARAMS.get('end_line', None)
|
end_line = request.QUERY_PARAMS.get('end_line', None)
|
||||||
dark_val = request.QUERY_PARAMS.get('dark', '')
|
dark_val = request.QUERY_PARAMS.get('dark', '')
|
||||||
@@ -2826,7 +2889,10 @@ class UnifiedJobStdout(RetrieveAPIView):
|
|||||||
if request.accepted_renderer.format == 'api':
|
if request.accepted_renderer.format == 'api':
|
||||||
return Response(mark_safe(data))
|
return Response(mark_safe(data))
|
||||||
if request.accepted_renderer.format == 'json':
|
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)
|
return Response(data)
|
||||||
elif request.accepted_renderer.format == 'ansi':
|
elif request.accepted_renderer.format == 'ansi':
|
||||||
return Response(unified_job.result_stdout_raw)
|
return Response(unified_job.result_stdout_raw)
|
||||||
|
|||||||
@@ -1,31 +1,2 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# 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.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
|
|
||||||
|
from mongoengine import connect
|
||||||
from mongoengine.base import BaseField
|
from mongoengine.base import BaseField
|
||||||
from mongoengine import Document, DateTimeField, ReferenceField, StringField, IntField
|
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')])
|
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):
|
class TransformField(BaseField):
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
return key_transform.transform_outgoing(value, None)
|
return key_transform.transform_outgoing(value, None)
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
# Python
|
# Python
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from django.utils.timezone import now
|
import os
|
||||||
from dateutil.relativedelta import relativedelta
|
import json
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.fact.models.fact import * # noqa
|
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']
|
__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):
|
class FactHostTest(BaseFactTest):
|
||||||
def test_create_host(self):
|
def test_create_host(self):
|
||||||
host = FactHost(hostname='hosty', inventory_id=1)
|
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.id, f_obj.id)
|
||||||
self.assertEqual(v.fact.module, 'packages')
|
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):
|
class FactGetHostVersionTest(BaseFactTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(FactGetHostVersionTest, self).setUp()
|
super(FactGetHostVersionTest, self).setUp()
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ class BaseAccess(object):
|
|||||||
def can_unattach(self, obj, sub_obj, relationship):
|
def can_unattach(self, obj, sub_obj, relationship):
|
||||||
return self.can_change(obj, None)
|
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()
|
reader = TaskSerializer()
|
||||||
validation_info = reader.from_file()
|
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', ''):
|
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['time_remaining'] = 99999999
|
||||||
validation_info['grace_period_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")
|
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")
|
raise PermissionDenied("license has expired")
|
||||||
|
|
||||||
free_instances = validation_info.get('free_instances', 0)
|
free_instances = validation_info.get('free_instances', 0)
|
||||||
@@ -262,6 +262,7 @@ class OrganizationAccess(BaseAccess):
|
|||||||
self.user in obj.admins.all())
|
self.user in obj.admins.all())
|
||||||
|
|
||||||
def can_delete(self, obj):
|
def can_delete(self, obj):
|
||||||
|
self.check_license(feature='multiple_organizations', check_expiration=False)
|
||||||
return self.can_change(obj, None)
|
return self.can_change(obj, None)
|
||||||
|
|
||||||
class InventoryAccess(BaseAccess):
|
class InventoryAccess(BaseAccess):
|
||||||
@@ -672,22 +673,22 @@ class ProjectAccess(BaseAccess):
|
|||||||
- I am on a team associated with the project.
|
- I am on a team associated with the project.
|
||||||
- I have been explicitly granted permission to run/check jobs using the
|
- I have been explicitly granted permission to run/check jobs using the
|
||||||
project.
|
project.
|
||||||
- I created it (for now?).
|
- I created the project but it isn't associated with an organization
|
||||||
I can change/delete when:
|
I can change/delete when:
|
||||||
- I am a superuser.
|
- I am a superuser.
|
||||||
- I am an admin in an organization associated with the project.
|
- 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
|
model = Project
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = Project.objects.filter(active=True).distinct()
|
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:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
team_ids = set(Team.objects.filter(users__in=[self.user]).values_list('id', flat=True))
|
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__admins__in=[self.user], organizations__active=True) |
|
||||||
Q(organizations__users__in=[self.user], organizations__active=True) |
|
Q(organizations__users__in=[self.user], organizations__active=True) |
|
||||||
Q(teams__in=team_ids))
|
Q(teams__in=team_ids))
|
||||||
@@ -719,7 +720,7 @@ class ProjectAccess(BaseAccess):
|
|||||||
def can_change(self, obj, data):
|
def can_change(self, obj, data):
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if obj.created_by == self.user:
|
if obj.created_by == self.user and not obj.organizations.filter(active=True).count():
|
||||||
return True
|
return True
|
||||||
if obj.organizations.filter(active=True, admins__in=[self.user]).exists():
|
if obj.organizations.filter(active=True, admins__in=[self.user]).exists():
|
||||||
return True
|
return True
|
||||||
@@ -863,52 +864,55 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
'credential', 'cloud_credential', 'next_schedule')
|
'credential', 'cloud_credential', 'next_schedule')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
credential_ids = set(self.user.get_queryset(Credential).values_list('id', flat=True))
|
credential_ids = self.user.get_queryset(Credential)
|
||||||
inventory_ids = set(self.user.get_queryset(Inventory).values_list('id', flat=True))
|
inventory_ids = self.user.get_queryset(Inventory)
|
||||||
base_qs = qs.filter(
|
base_qs = qs.filter(
|
||||||
Q(credential_id__in=credential_ids) | Q(credential__isnull=True),
|
Q(credential_id__in=credential_ids) | Q(credential__isnull=True),
|
||||||
Q(cloud_credential_id__in=credential_ids) | Q(cloud_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__organizations__admins__in=[self.user]) |
|
||||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__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_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
||||||
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
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
|
# 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),
|
Q(user=self.user) | Q(team_id__in=team_ids),
|
||||||
active=True,
|
active=True,
|
||||||
permission_type__in=allowed_deploy,
|
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),
|
Q(user=self.user) | Q(team_id__in=team_ids),
|
||||||
active=True,
|
active=True,
|
||||||
permission_type__in=allowed_check,
|
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,
|
job_type=PERM_INVENTORY_DEPLOY,
|
||||||
inventory__permissions__in=deploy_permissions_ids,
|
inventory__permissions__in=deploy_permissions_ids,
|
||||||
project__permissions__in=deploy_permissions_ids,
|
project__permissions__in=deploy_permissions_ids,
|
||||||
inventory__permissions__pk=F('project__permissions__pk'),
|
inventory__permissions__pk=F('project__permissions__pk'),
|
||||||
inventory_id__in=inventory_ids,
|
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,
|
job_type=PERM_INVENTORY_CHECK,
|
||||||
inventory__permissions__in=check_permissions_ids,
|
inventory__permissions__in=check_permissions_ids,
|
||||||
project__permissions__in=check_permissions_ids,
|
project__permissions__in=check_permissions_ids,
|
||||||
inventory__permissions__pk=F('project__permissions__pk'),
|
inventory__permissions__pk=F('project__permissions__pk'),
|
||||||
inventory_id__in=inventory_ids,
|
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(
|
||||||
return base_qs.filter(id__in=base_ids)
|
Q(id__in=org_admin_ids) |
|
||||||
|
Q(id__in=perm_deploy_ids) |
|
||||||
|
Q(id__in=perm_check_ids)
|
||||||
|
)
|
||||||
|
|
||||||
def can_read(self, obj):
|
def can_read(self, obj):
|
||||||
# you can only see the job templates that you have permission to launch.
|
# 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')
|
qs = qs.prefetch_related('unified_job_template')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
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(
|
base_qs = qs.filter(
|
||||||
credential_id__in=credential_ids,
|
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__organizations__admins__in=[self.user]) |
|
||||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__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_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
||||||
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
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
|
# 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),
|
Q(user=self.user) | Q(team__in=team_ids),
|
||||||
active=True,
|
active=True,
|
||||||
permission_type__in=allowed_deploy,
|
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),
|
Q(user=self.user) | Q(team__in=team_ids),
|
||||||
active=True,
|
active=True,
|
||||||
permission_type__in=allowed_check,
|
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,
|
job_type=PERM_INVENTORY_DEPLOY,
|
||||||
inventory__permissions__in=deploy_permissions_ids,
|
inventory__permissions__in=deploy_permissions_ids,
|
||||||
project__permissions__in=deploy_permissions_ids,
|
project__permissions__in=deploy_permissions_ids,
|
||||||
inventory__permissions__pk=F('project__permissions__pk'),
|
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,
|
job_type=PERM_INVENTORY_CHECK,
|
||||||
inventory__permissions__in=check_permissions_ids,
|
inventory__permissions__in=check_permissions_ids,
|
||||||
project__permissions__in=check_permissions_ids,
|
project__permissions__in=check_permissions_ids,
|
||||||
inventory__permissions__pk=F('project__permissions__pk'),
|
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(
|
||||||
return base_qs.filter(id__in=base_ids)
|
Q(id__in=org_admin_ids) |
|
||||||
|
Q(id__in=perm_deploy_ids) |
|
||||||
|
Q(id__in=perm_check_ids)
|
||||||
|
)
|
||||||
|
|
||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
if not data or '_method' in data: # So the browseable API will work?
|
if not data or '_method' in data: # So the browseable API will work?
|
||||||
@@ -1599,7 +1606,7 @@ class CustomInventoryScriptAccess(BaseAccess):
|
|||||||
model = CustomInventoryScript
|
model = CustomInventoryScript
|
||||||
|
|
||||||
def get_queryset(self):
|
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:
|
if not self.user.is_superuser:
|
||||||
qs = qs.filter(Q(organization__admins__in=[self.user]) | Q(organization__users__in=[self.user]))
|
qs = qs.filter(Q(organization__admins__in=[self.user]) | Q(organization__users__in=[self.user]))
|
||||||
return qs
|
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):
|
def __init__(self):
|
||||||
super(BaseCommandInstance, self).__init__()
|
super(BaseCommandInstance, self).__init__()
|
||||||
|
self.enforce_primary_role = False
|
||||||
self.enforce_roles = False
|
self.enforce_roles = False
|
||||||
self.enforce_hostname_set = False
|
self.enforce_hostname_set = False
|
||||||
self.enforce_unique_find = False
|
self.enforce_unique_find = False
|
||||||
@@ -70,8 +71,13 @@ class BaseCommandInstance(BaseCommand):
|
|||||||
default='',
|
default='',
|
||||||
help='Find instance by specified uuid.')
|
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):
|
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
|
self.enforce_roles = True
|
||||||
|
|
||||||
def include_option_hostname_set(self):
|
def include_option_hostname_set(self):
|
||||||
@@ -107,6 +113,8 @@ class BaseCommandInstance(BaseCommand):
|
|||||||
return CommandError('--hostname and one of --primary or --secondary is required.')
|
return CommandError('--hostname and one of --primary or --secondary is required.')
|
||||||
elif self.enforce_hostname_set:
|
elif self.enforce_hostname_set:
|
||||||
return CommandError('--hostname is required.')
|
return CommandError('--hostname is required.')
|
||||||
|
elif self.enforce_primary_role:
|
||||||
|
return CommandError('--primary is required.')
|
||||||
elif self.enforce_roles:
|
elif self.enforce_roles:
|
||||||
return CommandError('One of --primary or --secondary is required.')
|
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()):
|
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
|
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 self.enforce_hostname_set:
|
||||||
if options['hostname']:
|
if options['hostname']:
|
||||||
|
|||||||
@@ -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)
|
self.logger.log(99, "Removed %d items", n_deleted_items)
|
||||||
else:
|
else:
|
||||||
self.logger.log(99, "Would have removed %d items", n_deleted_items)
|
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_obj = self.inventory
|
||||||
all_name = 'inventory'
|
all_name = 'inventory'
|
||||||
db_variables = all_obj.variables_dict
|
db_variables = all_obj.variables_dict
|
||||||
if self.overwrite_vars or self.overwrite:
|
if self.overwrite_vars:
|
||||||
db_variables = self.all_group.variables
|
db_variables = self.all_group.variables
|
||||||
else:
|
else:
|
||||||
db_variables.update(self.all_group.variables)
|
db_variables.update(self.all_group.variables)
|
||||||
if db_variables != all_obj.variables_dict:
|
if db_variables != all_obj.variables_dict:
|
||||||
all_obj.variables = json.dumps(db_variables)
|
all_obj.variables = json.dumps(db_variables)
|
||||||
all_obj.save(update_fields=['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())
|
self.logger.info('%s variables replaced from "all" group', all_name.capitalize())
|
||||||
else:
|
else:
|
||||||
self.logger.info('%s variables updated from "all" group', all_name.capitalize())
|
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):
|
for group in self.inventory.groups.filter(name__in=group_names):
|
||||||
mem_group = self.all_group.all_groups[group.name]
|
mem_group = self.all_group.all_groups[group.name]
|
||||||
db_variables = group.variables_dict
|
db_variables = group.variables_dict
|
||||||
if self.overwrite_vars or self.overwrite:
|
if self.overwrite_vars:
|
||||||
db_variables = mem_group.variables
|
db_variables = mem_group.variables
|
||||||
else:
|
else:
|
||||||
db_variables.update(mem_group.variables)
|
db_variables.update(mem_group.variables)
|
||||||
if db_variables != group.variables_dict:
|
if db_variables != group.variables_dict:
|
||||||
group.variables = json.dumps(db_variables)
|
group.variables = json.dumps(db_variables)
|
||||||
group.save(update_fields=['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)
|
self.logger.info('Group "%s" variables replaced', group.name)
|
||||||
else:
|
else:
|
||||||
self.logger.info('Group "%s" variables updated', group.name)
|
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):
|
def _update_db_host_from_mem_host(self, db_host, mem_host):
|
||||||
# Update host variables.
|
# Update host variables.
|
||||||
db_variables = db_host.variables_dict
|
db_variables = db_host.variables_dict
|
||||||
if self.overwrite_vars or self.overwrite:
|
if self.overwrite_vars:
|
||||||
db_variables = mem_host.variables
|
db_variables = mem_host.variables
|
||||||
else:
|
else:
|
||||||
db_variables.update(mem_host.variables)
|
db_variables.update(mem_host.variables)
|
||||||
@@ -994,7 +994,7 @@ class Command(NoArgsCommand):
|
|||||||
else:
|
else:
|
||||||
self.logger.info('Host "%s" instance_id added', mem_host.name)
|
self.logger.info('Host "%s" instance_id added', mem_host.name)
|
||||||
if 'variables' in update_fields:
|
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)
|
self.logger.info('Host "%s" variables replaced', mem_host.name)
|
||||||
else:
|
else:
|
||||||
self.logger.info('Host "%s" variables updated', mem_host.name)
|
self.logger.info('Host "%s" variables updated', mem_host.name)
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class CallbackReceiver(object):
|
|||||||
'playbook_on_task_start',
|
'playbook_on_task_start',
|
||||||
'playbook_on_no_hosts_matched',
|
'playbook_on_no_hosts_matched',
|
||||||
'playbook_on_no_hosts_remaining',
|
'playbook_on_no_hosts_remaining',
|
||||||
|
'playbook_on_include',
|
||||||
'playbook_on_import_for_host',
|
'playbook_on_import_for_host',
|
||||||
'playbook_on_not_import_for_host'):
|
'playbook_on_not_import_for_host'):
|
||||||
parent = job_parent_events.get('playbook_on_play_start', None)
|
parent = job_parent_events.get('playbook_on_play_start', None)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
import urllib
|
||||||
|
import weakref
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
@@ -24,51 +25,157 @@ from socketio.namespace import BaseNamespace
|
|||||||
|
|
||||||
logger = logging.getLogger('awx.main.commands.run_socketio_service')
|
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):
|
class TowerBaseNamespace(BaseNamespace):
|
||||||
|
|
||||||
def get_allowed_methods(self):
|
def get_allowed_methods(self):
|
||||||
return ['recv_disconnect']
|
return ['recv_disconnect']
|
||||||
|
|
||||||
def get_initial_acl(self):
|
def get_initial_acl(self):
|
||||||
global valid_sockets
|
request_token = self._get_request_token()
|
||||||
v_user = self.valid_user()
|
if request_token:
|
||||||
self.is_valid_connection = False
|
# (1) This is the first time the socket has been seen (first
|
||||||
if v_user:
|
# namespace joined).
|
||||||
if self.socket.sessid not in valid_sockets:
|
# (2) This socket has already been seen (already joined and maybe
|
||||||
valid_sockets.append(self.socket.sessid)
|
# left a namespace)
|
||||||
self.is_valid_connection = True
|
#
|
||||||
if len(valid_sockets) > 1000:
|
# Note: Assume that the user token is valid if the session is found
|
||||||
valid_sockets = valid_sockets[1:]
|
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())
|
return set(['recv_connect'] + self.get_allowed_methods())
|
||||||
else:
|
else:
|
||||||
logger.warn("Authentication Failure validating user")
|
logger.warn("Authentication Failure validating user")
|
||||||
self.emit("connect_failed", "Authentication failed")
|
self.emit("connect_failed", "Authentication failed")
|
||||||
return set(['recv_connect'])
|
return set(['recv_connect'])
|
||||||
|
|
||||||
def valid_user(self):
|
def _get_request_token(self):
|
||||||
if 'QUERY_STRING' not in self.environ:
|
if 'QUERY_STRING' not in self.environ:
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
k, v = self.environ['QUERY_STRING'].split("=")
|
k, v = self.environ['QUERY_STRING'].split("=")
|
||||||
if k == "Token":
|
if k == "Token":
|
||||||
token_actual = urllib.unquote_plus(v).decode().replace("\"","")
|
token_actual = urllib.unquote_plus(v).decode().replace("\"","")
|
||||||
auth_token = AuthToken.objects.filter(key=token_actual)
|
return token_actual
|
||||||
if not auth_token.exists():
|
except Exception, e:
|
||||||
return False
|
logger.error("Exception validating user: " + str(e))
|
||||||
auth_token = auth_token[0]
|
return False
|
||||||
if not auth_token.expired:
|
return False
|
||||||
return auth_token.user
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except Exception, e:
|
|
||||||
logger.error("Exception validating user: " + str(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def recv_connect(self):
|
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)
|
self.disconnect(silent=False)
|
||||||
|
|
||||||
class TestNamespace(TowerBaseNamespace):
|
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']))
|
logger.info("Received client connect for schedule namespace from %s" % str(self.environ['REMOTE_ADDR']))
|
||||||
super(ScheduleNamespace, self).recv_connect()
|
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):
|
class TowerSocket(object):
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
@@ -115,7 +230,8 @@ class TowerSocket(object):
|
|||||||
'/socket.io/jobs': JobNamespace,
|
'/socket.io/jobs': JobNamespace,
|
||||||
'/socket.io/job_events': JobEventNamespace,
|
'/socket.io/job_events': JobEventNamespace,
|
||||||
'/socket.io/ad_hoc_command_events': AdHocCommandEventNamespace,
|
'/socket.io/ad_hoc_command_events': AdHocCommandEventNamespace,
|
||||||
'/socket.io/schedules': ScheduleNamespace})
|
'/socket.io/schedules': ScheduleNamespace,
|
||||||
|
'/socket.io/control': ControlNamespace})
|
||||||
else:
|
else:
|
||||||
logger.warn("Invalid connect path received: " + path)
|
logger.warn("Invalid connect path received: " + path)
|
||||||
start_response('404 Not Found', [])
|
start_response('404 Not Found', [])
|
||||||
@@ -130,13 +246,12 @@ def notification_handler(server):
|
|||||||
'name': message['event'],
|
'name': message['event'],
|
||||||
'type': 'event',
|
'type': 'event',
|
||||||
}
|
}
|
||||||
for session_id, socket in list(server.sockets.iteritems()):
|
|
||||||
if session_id in valid_sockets:
|
if 'token_key' in message:
|
||||||
try:
|
# Best practice not to send the token over the socket
|
||||||
socket.send_packet(packet)
|
socketController.send_packet(packet, message.pop('token_key'))
|
||||||
except Exception, e:
|
else:
|
||||||
logger.error("Error sending client packet to %s: %s" % (str(session_id), str(packet)))
|
socketController.broadcast_packet(packet)
|
||||||
logger.error("Error was: " + str(e))
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
'''
|
'''
|
||||||
@@ -164,6 +279,7 @@ class Command(NoArgsCommand):
|
|||||||
logger.info('Listening on port http://0.0.0.0:' + str(socketio_listen_port))
|
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')
|
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 = Thread(target=notification_handler, args=(server,))
|
||||||
handler_thread.daemon = True
|
handler_thread.daemon = True
|
||||||
handler_thread.start()
|
handler_thread.start()
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Command(BaseCommandInstance):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Command, self).__init__()
|
super(Command, self).__init__()
|
||||||
|
|
||||||
self.include_options_roles()
|
self.include_option_primary_role()
|
||||||
self.include_option_hostname_uuid_find()
|
self.include_option_hostname_uuid_find()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import mongoengine
|
|||||||
# awx
|
# awx
|
||||||
from awx.fact.models.fact import * # noqa
|
from awx.fact.models.fact import * # noqa
|
||||||
from awx.main.models import * # noqa
|
from awx.main.models import * # noqa
|
||||||
|
from awx.main.utils import timedelta_total_seconds
|
||||||
|
|
||||||
TEST_FACT_ANSIBLE = {
|
TEST_FACT_ANSIBLE = {
|
||||||
"ansible_swapfree_mb" : 4092,
|
"ansible_swapfree_mb" : 4092,
|
||||||
@@ -181,7 +182,7 @@ FACT_FIXTURES = {
|
|||||||
'ansible': TEST_FACT_ANSIBLE,
|
'ansible': TEST_FACT_ANSIBLE,
|
||||||
'packages': TEST_FACT_PACKAGES,
|
'packages': TEST_FACT_PACKAGES,
|
||||||
'services': TEST_FACT_SERVICES,
|
'services': TEST_FACT_SERVICES,
|
||||||
'files': TEST_FACT_ANSIBLE,
|
'files': TEST_FACT_FILES,
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPERIMENT_DEFAULT = {
|
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):
|
class Experiment(object):
|
||||||
def __init__(self, exp, fact_fixtures, raw_db, mongoengine_db):
|
def __init__(self, exp, fact_fixtures, raw_db, mongoengine_db):
|
||||||
self.db = raw_db
|
self.db = raw_db
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import uuid
|
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.models.signals import post_save
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from awx import __version__ as version
|
from awx import __version__ as version
|
||||||
from awx.main.models import ActivityStream, Instance
|
from awx.main.models import ActivityStream, Instance
|
||||||
|
from awx.api.authentication import TokenAuthentication
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('awx.main.middleware')
|
logger = logging.getLogger('awx.main.middleware')
|
||||||
@@ -60,8 +62,11 @@ class ActivityStreamMiddleware(threading.local):
|
|||||||
def set_actor(self, user, sender, instance, **kwargs):
|
def set_actor(self, user, sender, instance, **kwargs):
|
||||||
if sender == ActivityStream:
|
if sender == ActivityStream:
|
||||||
if isinstance(user, User) and instance.actor is None:
|
if isinstance(user, User) and instance.actor is None:
|
||||||
instance.actor = user
|
user = User.objects.filter(id=user.id)
|
||||||
instance.save(update_fields=['actor'])
|
if user.exists():
|
||||||
|
user = user[0]
|
||||||
|
instance.actor = user
|
||||||
|
instance.save(update_fields=['actor'])
|
||||||
else:
|
else:
|
||||||
if instance.id not in self.instance_ids:
|
if instance.id not in self.instance_ids:
|
||||||
self.instance_ids.append(instance.id)
|
self.instance_ids.append(instance.id)
|
||||||
@@ -100,3 +105,18 @@ class HAMiddleware(object):
|
|||||||
|
|
||||||
# Redirect to the base page of the primary instance.
|
# Redirect to the base page of the primary instance.
|
||||||
return HttpResponseRedirect('http://%s%s' % (primary.hostname, request.path))
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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']
|
||||||
@@ -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']
|
||||||
@@ -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')),
|
#('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')
|
'become_password', 'vault_password')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -101,6 +101,13 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
|||||||
help_text=_('Password for this credential (or "ASK" to prompt the '
|
help_text=_('Password for this credential (or "ASK" to prompt the '
|
||||||
'user for machine credentials).'),
|
'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(
|
project = models.CharField(
|
||||||
blank=True,
|
blank=True,
|
||||||
default='',
|
default='',
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class Inventory(CommonModel):
|
|||||||
total_hosts = models.PositiveIntegerField(
|
total_hosts = models.PositiveIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
editable=False,
|
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(
|
hosts_with_active_failures = models.PositiveIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
@@ -1281,7 +1281,9 @@ class CustomInventoryScript(CommonModelNameNotUnique):
|
|||||||
'Organization',
|
'Organization',
|
||||||
related_name='custom_inventory_scripts',
|
related_name='custom_inventory_scripts',
|
||||||
help_text=_('Organization owning this inventory script'),
|
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):
|
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)" %
|
errors.append("'%s' value %s is too small (must be at least %s)" %
|
||||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
(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']:
|
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']))
|
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||||
elif survey_element['type'] == 'integer':
|
elif survey_element['type'] == 'integer':
|
||||||
if survey_element['variable'] in data:
|
if survey_element['variable'] in data:
|
||||||
@@ -250,7 +250,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
|||||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
(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 \
|
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']:
|
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']))
|
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||||
if type(data[survey_element['variable']]) != int:
|
if type(data[survey_element['variable']]) != int:
|
||||||
errors.append("Value %s for %s expected to be an integer" % (data[survey_element['variable']],
|
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)" %
|
errors.append("'%s' value %s is too small (must be at least %s)" %
|
||||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
(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']:
|
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']))
|
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||||
if type(data[survey_element['variable']]) not in (float, int):
|
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']],
|
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']))
|
survey_element['choices']))
|
||||||
return errors
|
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
|
@property
|
||||||
def cache_timeout_blocked(self):
|
def cache_timeout_blocked(self):
|
||||||
if Job.objects.filter(job_template=self, status__in=['pending', 'waiting', 'running']).count() > getattr(settings, 'SCHEDULE_MAX_JOBS', 10):
|
if Job.objects.filter(job_template=self, status__in=['pending', 'waiting', 'running']).count() > getattr(settings, 'SCHEDULE_MAX_JOBS', 10):
|
||||||
@@ -1052,4 +1085,3 @@ class SystemJob(UnifiedJob, SystemJobOptions):
|
|||||||
@property
|
@property
|
||||||
def task_impact(self):
|
def task_impact(self):
|
||||||
return 150
|
return 150
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ from django.conf import settings
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib.auth.models import User
|
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
|
# AWX
|
||||||
from awx.main.fields import AutoOneToOneField
|
from awx.main.fields import AutoOneToOneField
|
||||||
@@ -52,6 +53,12 @@ class Organization(CommonModel):
|
|||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
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):
|
class Team(CommonModelNameNotUnique):
|
||||||
'''
|
'''
|
||||||
@@ -128,7 +135,8 @@ class Permission(CommonModelNameNotUnique):
|
|||||||
# the project parameter is not used when dealing with READ, WRITE, or ADMIN permissions.
|
# 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)
|
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):
|
def __unicode__(self):
|
||||||
return unicode("Permission(name=%s,ON(user=%s,team=%s),FOR(project=%s,inventory=%s,type=%s%s))" % (
|
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='',
|
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):
|
class AuthToken(BaseModel):
|
||||||
'''
|
'''
|
||||||
Custom authentication tokens per user with expiration and request-specific
|
Custom authentication tokens per user with expiration and request-specific
|
||||||
data.
|
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:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -179,8 +203,21 @@ class AuthToken(BaseModel):
|
|||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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='')
|
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
|
@classmethod
|
||||||
def get_request_hash(cls, request):
|
def get_request_hash(cls, request):
|
||||||
@@ -201,25 +238,65 @@ class AuthToken(BaseModel):
|
|||||||
self.key = self.generate_key()
|
self.key = self.generate_key()
|
||||||
return super(AuthToken, self).save(*args, **kwargs)
|
return super(AuthToken, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def refresh(self, save=True):
|
def refresh(self, now=None, save=True):
|
||||||
if not self.pk or not self.expired:
|
if not now:
|
||||||
self.expires = now() + datetime.timedelta(seconds=settings.AUTH_TOKEN_EXPIRATION)
|
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:
|
if save:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def invalidate(self, save=True):
|
def invalidate(self, reason='timeout_reached', save=True):
|
||||||
if not self.expired:
|
if not AuthToken.reason_long(reason):
|
||||||
self.expires = now() - datetime.timedelta(seconds=1)
|
raise ValueError('Invalid reason specified')
|
||||||
if save:
|
self.reason = reason
|
||||||
self.save()
|
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):
|
def generate_key(self):
|
||||||
unique = uuid.uuid4()
|
unique = uuid.uuid4()
|
||||||
return hmac.new(unique.bytes, digestmod=hashlib.sha1).hexdigest()
|
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
|
@property
|
||||||
def expired(self):
|
def invalidated(self):
|
||||||
return bool(self.expires < now())
|
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):
|
def __unicode__(self):
|
||||||
return self.key
|
return self.key
|
||||||
@@ -231,7 +308,7 @@ def user_mark_inactive(user, save=True):
|
|||||||
if user.is_active:
|
if user.is_active:
|
||||||
# Set timestamp to datetime.isoformat() but without the time zone
|
# Set timestamp to datetime.isoformat() but without the time zone
|
||||||
# offset to stay withint the 30 character username limit.
|
# 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')
|
deleted_ts = dtnow.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||||
user.username = '_d_%s' % deleted_ts
|
user.username = '_d_%s' % deleted_ts
|
||||||
user.is_active = False
|
user.is_active = False
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
|||||||
|
|
||||||
LAUNCH_TYPE_CHOICES = [
|
LAUNCH_TYPE_CHOICES = [
|
||||||
('manual', _('Manual')), # Job was started manually by a user.
|
('manual', _('Manual')), # Job was started manually by a user.
|
||||||
|
('relaunch', _('Relaunch')), # Job was started via relaunch.
|
||||||
('callback', _('Callback')), # Job was started via host callback.
|
('callback', _('Callback')), # Job was started via host callback.
|
||||||
('scheduled', _('Scheduled')), # Job was started from a schedule.
|
('scheduled', _('Scheduled')), # Job was started from a schedule.
|
||||||
('dependency', _('Dependency')), # Job was started as a dependency of another job.
|
('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' %
|
print('Executing error task id %s, subtasks: %s' %
|
||||||
(str(self.request.id), str(subtasks)))
|
(str(self.request.id), str(subtasks)))
|
||||||
first_task = None
|
first_task = None
|
||||||
|
first_task_id = None
|
||||||
first_task_type = ''
|
first_task_type = ''
|
||||||
first_task_name = ''
|
first_task_name = ''
|
||||||
if subtasks is not None:
|
if subtasks is not None:
|
||||||
@@ -169,10 +170,10 @@ def handle_work_error(self, task_id, subtasks=None):
|
|||||||
instance_name = ''
|
instance_name = ''
|
||||||
if each_task['type'] == 'project_update':
|
if each_task['type'] == 'project_update':
|
||||||
instance = ProjectUpdate.objects.get(id=each_task['id'])
|
instance = ProjectUpdate.objects.get(id=each_task['id'])
|
||||||
instance_name = instance.project.name
|
instance_name = instance.name
|
||||||
elif each_task['type'] == 'inventory_update':
|
elif each_task['type'] == 'inventory_update':
|
||||||
instance = InventoryUpdate.objects.get(id=each_task['id'])
|
instance = InventoryUpdate.objects.get(id=each_task['id'])
|
||||||
instance_name = instance.inventory_source.inventory.name
|
instance_name = instance.name
|
||||||
elif each_task['type'] == 'job':
|
elif each_task['type'] == 'job':
|
||||||
instance = Job.objects.get(id=each_task['id'])
|
instance = Job.objects.get(id=each_task['id'])
|
||||||
instance_name = instance.job_template.name
|
instance_name = instance.job_template.name
|
||||||
@@ -184,13 +185,14 @@ def handle_work_error(self, task_id, subtasks=None):
|
|||||||
break
|
break
|
||||||
if first_task is None:
|
if first_task is None:
|
||||||
first_task = instance
|
first_task = instance
|
||||||
|
first_task_id = instance.id
|
||||||
first_task_type = each_task['type']
|
first_task_type = each_task['type']
|
||||||
first_task_name = instance_name
|
first_task_name = instance_name
|
||||||
if instance.celery_task_id != task_id:
|
if instance.celery_task_id != task_id:
|
||||||
instance.status = 'failed'
|
instance.status = 'failed'
|
||||||
instance.failed = True
|
instance.failed = True
|
||||||
instance.job_explanation = "Previous Task Failed: %s for %s with celery task id: %s" % \
|
instance.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \
|
||||||
(first_task_type, first_task_name, task_id)
|
(first_task_type, first_task_name, first_task_id)
|
||||||
instance.save()
|
instance.save()
|
||||||
instance.socketio_emit_status("failed")
|
instance.socketio_emit_status("failed")
|
||||||
|
|
||||||
@@ -345,6 +347,8 @@ class BaseTask(Task):
|
|||||||
if local_site_packages not in python_paths:
|
if local_site_packages not in python_paths:
|
||||||
python_paths.insert(0, local_site_packages)
|
python_paths.insert(0, local_site_packages)
|
||||||
env['PYTHONPATH'] = os.pathsep.join(python_paths)
|
env['PYTHONPATH'] = os.pathsep.join(python_paths)
|
||||||
|
if self.should_use_proot:
|
||||||
|
env['PROOT_TMP_DIR'] = settings.AWX_PROOT_BASE_PATH
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def build_safe_env(self, instance, **kwargs):
|
def build_safe_env(self, instance, **kwargs):
|
||||||
@@ -423,6 +427,15 @@ class BaseTask(Task):
|
|||||||
'''
|
'''
|
||||||
logfile = stdout_handle
|
logfile = stdout_handle
|
||||||
logfile_pos = logfile.tell()
|
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 = pexpect.spawnu(args[0], args[1:], cwd=cwd, env=env)
|
||||||
child.logfile_read = logfile
|
child.logfile_read = logfile
|
||||||
canceled = False
|
canceled = False
|
||||||
@@ -601,6 +614,21 @@ class RunJob(BaseTask):
|
|||||||
if credential.ssh_key_data not in (None, ''):
|
if credential.ssh_key_data not in (None, ''):
|
||||||
private_data[cred_name] = decrypt_field(credential, 'ssh_key_data') or ''
|
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
|
return private_data
|
||||||
|
|
||||||
def build_passwords(self, job, **kwargs):
|
def build_passwords(self, job, **kwargs):
|
||||||
@@ -625,12 +653,17 @@ class RunJob(BaseTask):
|
|||||||
Build environment dictionary for ansible-playbook.
|
Build environment dictionary for ansible-playbook.
|
||||||
'''
|
'''
|
||||||
plugin_dir = self.get_path_to('..', 'plugins', 'callback')
|
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)
|
env = super(RunJob, self).build_env(job, **kwargs)
|
||||||
# Set environment variables needed for inventory and job event
|
# Set environment variables needed for inventory and job event
|
||||||
# callbacks to work.
|
# callbacks to work.
|
||||||
env['JOB_ID'] = str(job.pk)
|
env['JOB_ID'] = str(job.pk)
|
||||||
env['INVENTORY_ID'] = str(job.inventory.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_URL'] = settings.INTERNAL_API_URL
|
||||||
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
||||||
env['CALLBACK_CONSUMER_PORT'] = str(settings.CALLBACK_CONSUMER_PORT)
|
env['CALLBACK_CONSUMER_PORT'] = str(settings.CALLBACK_CONSUMER_PORT)
|
||||||
@@ -654,6 +687,8 @@ class RunJob(BaseTask):
|
|||||||
if cloud_cred and cloud_cred.kind == 'aws':
|
if cloud_cred and cloud_cred.kind == 'aws':
|
||||||
env['AWS_ACCESS_KEY'] = cloud_cred.username
|
env['AWS_ACCESS_KEY'] = cloud_cred.username
|
||||||
env['AWS_SECRET_KEY'] = decrypt_field(cloud_cred, 'password')
|
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!
|
# FIXME: Add EC2_URL, maybe EC2_REGION!
|
||||||
elif cloud_cred and cloud_cred.kind == 'rax':
|
elif cloud_cred and cloud_cred.kind == 'rax':
|
||||||
env['RAX_USERNAME'] = cloud_cred.username
|
env['RAX_USERNAME'] = cloud_cred.username
|
||||||
@@ -669,6 +704,8 @@ class RunJob(BaseTask):
|
|||||||
env['VMWARE_USER'] = cloud_cred.username
|
env['VMWARE_USER'] = cloud_cred.username
|
||||||
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
|
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
|
||||||
env['VMWARE_HOST'] = cloud_cred.host
|
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
|
# Set environment variables related to scan jobs
|
||||||
if job.job_type == PERM_INVENTORY_SCAN:
|
if job.job_type == PERM_INVENTORY_SCAN:
|
||||||
@@ -1116,7 +1153,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
if credential:
|
if credential:
|
||||||
for subkey in ('username', 'host', 'project'):
|
for subkey in ('username', 'host', 'project'):
|
||||||
passwords['source_%s' % subkey] = getattr(credential, subkey)
|
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
|
k = 'source_%s' % passkey
|
||||||
passwords[k] = decrypt_field(credential, passkey)
|
passwords[k] = decrypt_field(credential, passkey)
|
||||||
return passwords
|
return passwords
|
||||||
@@ -1149,6 +1186,8 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
if passwords.get('source_username', '') and passwords.get('source_password', ''):
|
if passwords.get('source_username', '') and passwords.get('source_password', ''):
|
||||||
env['AWS_ACCESS_KEY_ID'] = passwords['source_username']
|
env['AWS_ACCESS_KEY_ID'] = passwords['source_username']
|
||||||
env['AWS_SECRET_ACCESS_KEY'] = passwords['source_password']
|
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
|
env['EC2_INI_PATH'] = cloud_credential
|
||||||
elif inventory_update.source == 'rax':
|
elif inventory_update.source == 'rax':
|
||||||
env['RAX_CREDS_FILE'] = cloud_credential
|
env['RAX_CREDS_FILE'] = cloud_credential
|
||||||
@@ -1169,7 +1208,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
env['GCE_PROJECT'] = passwords.get('source_project', '')
|
env['GCE_PROJECT'] = passwords.get('source_project', '')
|
||||||
env['GCE_PEM_FILE_PATH'] = cloud_credential
|
env['GCE_PEM_FILE_PATH'] = cloud_credential
|
||||||
elif inventory_update.source == 'openstack':
|
elif inventory_update.source == 'openstack':
|
||||||
env['OPENSTACK_CONFIG_FILE'] = cloud_credential
|
env['OS_CLIENT_CONFIG_FILE'] = cloud_credential
|
||||||
elif inventory_update.source == 'file':
|
elif inventory_update.source == 'file':
|
||||||
# FIXME: Parse source_env to dict, update env.
|
# FIXME: Parse source_env to dict, update env.
|
||||||
pass
|
pass
|
||||||
@@ -1188,7 +1227,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
inventory = inventory_source.group.inventory
|
inventory = inventory_source.group.inventory
|
||||||
|
|
||||||
# Piece together the initial command to run via. the shell.
|
# 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)])
|
args.extend(['--inventory-id', str(inventory.pk)])
|
||||||
|
|
||||||
# Add appropriate arguments for overwrite if the inventory_update
|
# Add appropriate arguments for overwrite if the inventory_update
|
||||||
@@ -1450,7 +1489,7 @@ class RunSystemJob(BaseTask):
|
|||||||
model = SystemJob
|
model = SystemJob
|
||||||
|
|
||||||
def build_args(self, system_job, **kwargs):
|
def build_args(self, system_job, **kwargs):
|
||||||
args = ['awx-manage', system_job.job_type]
|
args = ['tower-manage', system_job.job_type]
|
||||||
try:
|
try:
|
||||||
json_vars = json.loads(system_job.extra_vars)
|
json_vars = json.loads(system_job.extra_vars)
|
||||||
if 'days' in json_vars and system_job.job_type != 'cleanup_facts':
|
if 'days' in json_vars and system_job.job_type != 'cleanup_facts':
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# 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.users import * # noqa
|
||||||
from awx.main.tests.inventory import * # noqa
|
from awx.main.tests.inventory import * # noqa
|
||||||
from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest # noqa
|
from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest # noqa
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ from django.test.utils import override_settings
|
|||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.main.models import * # noqa
|
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_callback_receiver import CallbackReceiver
|
||||||
from awx.main.management.commands.run_task_system import run_taskmanager
|
from awx.main.management.commands.run_task_system import run_taskmanager
|
||||||
from awx.main.utils import get_ansible_version
|
from awx.main.utils import get_ansible_version
|
||||||
from awx.main.task_engine import TaskEngager as LicenseWriter
|
from awx.main.task_engine import TaskEngager as LicenseWriter
|
||||||
|
from awx.sso.backends import LDAPSettings
|
||||||
|
|
||||||
TEST_PLAYBOOK = '''- hosts: mygroup
|
TEST_PLAYBOOK = '''- hosts: mygroup
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -56,8 +56,12 @@ class QueueTestMixin(object):
|
|||||||
|
|
||||||
def start_redis(self):
|
def start_redis(self):
|
||||||
if not getattr(self, 'redis_process', None):
|
if not getattr(self, 'redis_process', None):
|
||||||
self.redis_process = Popen('redis-server --port 16379 > /dev/null',
|
# Centos 6.5 redis is runnable by non-root user but is not in a normal users path by default
|
||||||
shell=True, executable='/bin/bash')
|
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):
|
def stop_redis(self):
|
||||||
if getattr(self, 'redis_process', None):
|
if getattr(self, 'redis_process', None):
|
||||||
@@ -188,6 +192,20 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
|||||||
self._temp_paths.append(license_path)
|
self._temp_paths.append(license_path)
|
||||||
os.environ['AWX_LICENSE_FILE'] = 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):
|
def create_expired_license_file(self, instance_count=1000, grace_period=False):
|
||||||
license_date = time.time() - 1
|
license_date = time.time() - 1
|
||||||
if not grace_period:
|
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)
|
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':
|
if method_name == 'head':
|
||||||
self.assertFalse(response.content)
|
self.assertFalse(response.content)
|
||||||
#if return_response_object:
|
if return_response_object:
|
||||||
# return response
|
return response
|
||||||
if response.status_code not in [204, 405] and method_name != 'head' and response.content:
|
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)
|
# no JSON responses in these at least for now, 409 should probably return some (FIXME)
|
||||||
if response['Content-Type'].startswith('application/json'):
|
if response['Content-Type'].startswith('application/json'):
|
||||||
@@ -700,7 +718,8 @@ class BaseLiveServerTest(BaseTestMixin, django.test.LiveServerTestCase):
|
|||||||
|
|
||||||
@override_settings(CELERY_ALWAYS_EAGER=True,
|
@override_settings(CELERY_ALWAYS_EAGER=True,
|
||||||
CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
|
CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
|
||||||
ANSIBLE_TRANSPORT='local')
|
ANSIBLE_TRANSPORT='local',
|
||||||
|
DEBUG=True)
|
||||||
class BaseJobExecutionTest(QueueStartStopTestMixin, BaseLiveServerTest):
|
class BaseJobExecutionTest(QueueStartStopTestMixin, BaseLiveServerTest):
|
||||||
'''
|
'''
|
||||||
Base class for celery task tests.
|
Base class for celery task tests.
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ from .run_fact_cache_receiver import * # noqa
|
|||||||
from .commands_monolithic import * # noqa
|
from .commands_monolithic import * # noqa
|
||||||
from .cleanup_facts import * # noqa
|
from .cleanup_facts import * # noqa
|
||||||
from .age_deleted 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 tempfile
|
||||||
import time
|
import time
|
||||||
import urlparse
|
import urlparse
|
||||||
import unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
import django
|
import django
|
||||||
@@ -26,9 +26,6 @@ from django.test.utils import override_settings
|
|||||||
from awx.main.models import * # noqa
|
from awx.main.models import * # noqa
|
||||||
from awx.main.tests.base import BaseTest, BaseLiveServerTest
|
from awx.main.tests.base import BaseTest, BaseLiveServerTest
|
||||||
|
|
||||||
if not hasattr(unittest, 'skipIf'):
|
|
||||||
import unittest2 as unittest
|
|
||||||
|
|
||||||
__all__ = ['CreateDefaultOrgTest', 'DumpDataTest', 'CleanupDeletedTest',
|
__all__ = ['CreateDefaultOrgTest', 'DumpDataTest', 'CleanupDeletedTest',
|
||||||
'CleanupJobsTest', 'CleanupActivityStreamTest',
|
'CleanupJobsTest', 'CleanupActivityStreamTest',
|
||||||
'InventoryImportTest']
|
'InventoryImportTest']
|
||||||
@@ -831,7 +828,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest):
|
|||||||
host_names = set(new_inv.hosts.filter(active=True).values_list('name', flat=True))
|
host_names = set(new_inv.hosts.filter(active=True).values_list('name', flat=True))
|
||||||
self.assertEqual(expected_host_names, host_names)
|
self.assertEqual(expected_host_names, host_names)
|
||||||
expected_inv_vars = {'vara': 'A', 'varc': 'C'}
|
expected_inv_vars = {'vara': 'A', 'varc': 'C'}
|
||||||
if overwrite or overwrite_vars:
|
if overwrite_vars:
|
||||||
expected_inv_vars.pop('varc')
|
expected_inv_vars.pop('varc')
|
||||||
self.assertEqual(new_inv.variables_dict, expected_inv_vars)
|
self.assertEqual(new_inv.variables_dict, expected_inv_vars)
|
||||||
for host in new_inv.hosts.filter(active=True):
|
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):
|
for group in new_inv.groups.filter(active=True):
|
||||||
if group.name == 'servers':
|
if group.name == 'servers':
|
||||||
expected_vars = {'varb': 'B', 'vard': 'D'}
|
expected_vars = {'varb': 'B', 'vard': 'D'}
|
||||||
if overwrite or overwrite_vars:
|
if overwrite_vars:
|
||||||
expected_vars.pop('vard')
|
expected_vars.pop('vard')
|
||||||
self.assertEqual(group.variables_dict, expected_vars)
|
self.assertEqual(group.variables_dict, expected_vars)
|
||||||
children = set(group.children.filter(active=True).values_list('name', flat=True))
|
children = set(group.children.filter(active=True).values_list('name', flat=True))
|
||||||
|
|||||||
@@ -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
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import mock
|
import mock
|
||||||
import unittest
|
import unittest2 as unittest
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from mock import MagicMock
|
from mock import MagicMock
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
# All Rights Reserved
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
import unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|||||||
@@ -1665,6 +1665,54 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
|||||||
inventory_source.save()
|
inventory_source.save()
|
||||||
self.check_inventory_source(inventory_source, initial=False)
|
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):
|
def test_update_from_ec2_without_credential(self):
|
||||||
self.create_test_license_file()
|
self.create_test_license_file()
|
||||||
group = self.group
|
group = self.group
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
from .jobs_monolithic import * # noqa
|
from .jobs_monolithic import * # noqa
|
||||||
from .job_launch import * # noqa
|
from .job_launch import * # noqa
|
||||||
|
from .job_relaunch import * # noqa
|
||||||
from .survey_password import * # noqa
|
from .survey_password import * # noqa
|
||||||
from .start_cancel import * # noqa
|
from .start_cancel import * # noqa
|
||||||
from .base import * # noqa
|
from .base import * # noqa
|
||||||
|
|||||||
@@ -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.
|
# 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):
|
def test_job_cancel(self):
|
||||||
#job = self.job_ops_east_run
|
#job = self.job_ops_east_run
|
||||||
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new')
|
job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new')
|
||||||
|
|||||||
@@ -1,10 +1,56 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
# Python
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
# Django
|
||||||
from django.core.urlresolvers import reverse
|
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.models import * # noqa
|
||||||
from awx.main.tests.base import BaseTest
|
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):
|
class OrganizationsTest(BaseTest):
|
||||||
|
|
||||||
def collection(self):
|
def collection(self):
|
||||||
@@ -222,6 +268,11 @@ class OrganizationsTest(BaseTest):
|
|||||||
last_org = Organization.objects.order_by('-pk')[0]
|
last_org = Organization.objects.order_by('-pk')[0]
|
||||||
self.assertTrue(data1['url'].endswith("/%d/" % last_org.pk))
|
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):
|
def test_post_item_subobjects_projects(self):
|
||||||
|
|
||||||
# first get all the orgs
|
# first get all the orgs
|
||||||
@@ -391,6 +442,10 @@ class OrganizationsTest(BaseTest):
|
|||||||
# also check that DELETE on the collection doesn't work
|
# also check that DELETE on the collection doesn't work
|
||||||
self.delete(self.collection(), expect=405, auth=self.get_super_credentials())
|
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):
|
def test_invalid_post_data(self):
|
||||||
url = reverse('api:organization_list')
|
url = reverse('api:organization_list')
|
||||||
# API should gracefully handle data of an invalid type.
|
# API should gracefully handle data of an invalid type.
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ class ProjectsTest(BaseTransactionTest):
|
|||||||
self.assertEquals(results['count'], 10)
|
self.assertEquals(results['count'], 10)
|
||||||
# org admin
|
# org admin
|
||||||
results = self.get(projects, expect=200, auth=self.get_normal_credentials())
|
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
|
# user on a team
|
||||||
results = self.get(projects, expect=200, auth=self.get_other_credentials())
|
results = self.get(projects, expect=200, auth=self.get_other_credentials())
|
||||||
self.assertEquals(results['count'], 5)
|
self.assertEquals(results['count'], 5)
|
||||||
@@ -300,6 +300,17 @@ class ProjectsTest(BaseTransactionTest):
|
|||||||
got = self.get(proj_orgs, expect=200, auth=self.get_super_credentials())
|
got = self.get(proj_orgs, expect=200, auth=self.get_super_credentials())
|
||||||
self.assertEquals(got['count'], 2)
|
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
|
# TEAMS
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|||||||
@@ -4,18 +4,76 @@
|
|||||||
# Python
|
# Python
|
||||||
import datetime
|
import datetime
|
||||||
import urllib
|
import urllib
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.main.models import * # noqa
|
from awx.main.models import * # noqa
|
||||||
from awx.main.tests.base import BaseTest
|
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
|
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'
|
remote_addr = '127.0.0.2'
|
||||||
response = self.get(user_me_url, expect=401, auth=auth_token,
|
response = self.get(user_me_url, expect=401, auth=auth_token,
|
||||||
remote_addr=remote_addr)
|
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
|
# The WWW-Authenticate header should specify Token auth, since that
|
||||||
# auth method was used in the request.
|
# 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
|
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
|
from awx.main.socket import Socket
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with Socket('websocket', 'w', nowait=True, logger=logger) as websocket:
|
with Socket('websocket', 'w', nowait=True, logger=logger) as websocket:
|
||||||
|
if token_key:
|
||||||
|
payload['token_key'] = token_key
|
||||||
payload['event'] = event
|
payload['event'] = event
|
||||||
payload['endpoint'] = endpoint
|
payload['endpoint'] = endpoint
|
||||||
websocket.publish(payload)
|
websocket.publish(payload)
|
||||||
@@ -512,3 +514,11 @@ def timestamp_apiformat(timestamp):
|
|||||||
if timestamp.endswith('+00:00'):
|
if timestamp.endswith('+00:00'):
|
||||||
timestamp = timestamp[:-6] + 'Z'
|
timestamp = timestamp[:-6] + 'Z'
|
||||||
return timestamp
|
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):
|
def v2_playbook_on_stats(self, stats):
|
||||||
self.playbook_on_stats(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):
|
class AdHocCommandCallbackModule(BaseCallbackModule):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from ansible import constants as C
|
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:
|
try:
|
||||||
import zmq
|
import zmq
|
||||||
@@ -46,7 +49,6 @@ except ImportError:
|
|||||||
class CacheModule(BaseCacheModule):
|
class CacheModule(BaseCacheModule):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
# Basic in-memory caching for typical runs
|
# Basic in-memory caching for typical runs
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
self._cache_prev = {}
|
self._cache_prev = {}
|
||||||
@@ -108,16 +110,15 @@ class CacheModule(BaseCacheModule):
|
|||||||
module = self.identify_new_module(key, value)
|
module = self.identify_new_module(key, value)
|
||||||
# Assume ansible fact triggered the set if no new module found
|
# 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]})
|
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[key] = value
|
||||||
|
self._cache_prev = deepcopy(self._cache)
|
||||||
packet = {
|
packet = {
|
||||||
'host': key,
|
'host': key,
|
||||||
'inventory_id': os.environ['INVENTORY_ID'],
|
'inventory_id': os.environ['INVENTORY_ID'],
|
||||||
'facts': facts,
|
'facts': facts,
|
||||||
'date_key': self.date_key,
|
'date_key': self.date_key,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Emit fact data to tower for processing
|
# Emit fact data to tower for processing
|
||||||
self.socket.send_json(packet)
|
self.socket.send_json(packet)
|
||||||
self.socket.recv()
|
self.socket.recv()
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ you need to define:
|
|||||||
|
|
||||||
export EC2_URL=http://hostname_of_your_cc:port/services/Eucalyptus
|
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
|
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:
|
When run against a specific host, this script returns the following variables:
|
||||||
@@ -121,6 +127,7 @@ from time import time
|
|||||||
import boto
|
import boto
|
||||||
from boto import ec2
|
from boto import ec2
|
||||||
from boto import rds
|
from boto import rds
|
||||||
|
from boto import elasticache
|
||||||
from boto import route53
|
from boto import route53
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@@ -147,9 +154,18 @@ class Ec2Inventory(object):
|
|||||||
# Index of hostname (address) to instance ID
|
# Index of hostname (address) to instance ID
|
||||||
self.index = {}
|
self.index = {}
|
||||||
|
|
||||||
|
# Boto profile to use (if any)
|
||||||
|
self.boto_profile = None
|
||||||
|
|
||||||
# Read settings and parse CLI arguments
|
# Read settings and parse CLI arguments
|
||||||
self.read_settings()
|
|
||||||
self.parse_cli_args()
|
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
|
# Cache
|
||||||
if self.args.refresh_cache:
|
if self.args.refresh_cache:
|
||||||
@@ -186,12 +202,12 @@ class Ec2Inventory(object):
|
|||||||
|
|
||||||
def read_settings(self):
|
def read_settings(self):
|
||||||
''' Reads the settings from the ec2.ini file '''
|
''' Reads the settings from the ec2.ini file '''
|
||||||
if six.PY2:
|
if six.PY3:
|
||||||
config = configparser.SafeConfigParser()
|
|
||||||
else:
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
|
else:
|
||||||
|
config = configparser.SafeConfigParser()
|
||||||
ec2_default_ini_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ec2.ini')
|
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)
|
config.read(ec2_ini_path)
|
||||||
|
|
||||||
# is eucalyptus?
|
# is eucalyptus?
|
||||||
@@ -232,18 +248,72 @@ class Ec2Inventory(object):
|
|||||||
if config.has_option('ec2', 'rds'):
|
if config.has_option('ec2', 'rds'):
|
||||||
self.rds_enabled = config.getboolean('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'):
|
if config.has_option('ec2', 'all_instances'):
|
||||||
self.all_instances = config.getboolean('ec2', 'all_instances')
|
self.all_instances = config.getboolean('ec2', 'all_instances')
|
||||||
else:
|
else:
|
||||||
self.all_instances = False
|
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:
|
if config.has_option('ec2', 'all_rds_instances') and self.rds_enabled:
|
||||||
self.all_rds_instances = config.getboolean('ec2', 'all_rds_instances')
|
self.all_rds_instances = config.getboolean('ec2', 'all_rds_instances')
|
||||||
else:
|
else:
|
||||||
self.all_rds_instances = False
|
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 related
|
||||||
cache_dir = os.path.expanduser(config.get('ec2', 'cache_path'))
|
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):
|
if not os.path.exists(cache_dir):
|
||||||
os.makedirs(cache_dir)
|
os.makedirs(cache_dir)
|
||||||
|
|
||||||
@@ -257,6 +327,12 @@ class Ec2Inventory(object):
|
|||||||
else:
|
else:
|
||||||
self.nested_groups = False
|
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.
|
# Configure which groups should be created.
|
||||||
group_by_options = [
|
group_by_options = [
|
||||||
'group_by_instance_id',
|
'group_by_instance_id',
|
||||||
@@ -272,6 +348,10 @@ class Ec2Inventory(object):
|
|||||||
'group_by_route53_names',
|
'group_by_route53_names',
|
||||||
'group_by_rds_engine',
|
'group_by_rds_engine',
|
||||||
'group_by_rds_parameter_group',
|
'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:
|
for option in group_by_options:
|
||||||
if config.has_option('ec2', option):
|
if config.has_option('ec2', option):
|
||||||
@@ -286,7 +366,7 @@ class Ec2Inventory(object):
|
|||||||
self.pattern_include = re.compile(pattern_include)
|
self.pattern_include = re.compile(pattern_include)
|
||||||
else:
|
else:
|
||||||
self.pattern_include = None
|
self.pattern_include = None
|
||||||
except configparser.NoOptionError as e:
|
except configparser.NoOptionError:
|
||||||
self.pattern_include = None
|
self.pattern_include = None
|
||||||
|
|
||||||
# Do we need to exclude hosts that match a pattern?
|
# Do we need to exclude hosts that match a pattern?
|
||||||
@@ -296,7 +376,7 @@ class Ec2Inventory(object):
|
|||||||
self.pattern_exclude = re.compile(pattern_exclude)
|
self.pattern_exclude = re.compile(pattern_exclude)
|
||||||
else:
|
else:
|
||||||
self.pattern_exclude = None
|
self.pattern_exclude = None
|
||||||
except configparser.NoOptionError as e:
|
except configparser.NoOptionError:
|
||||||
self.pattern_exclude = None
|
self.pattern_exclude = None
|
||||||
|
|
||||||
# Instance filters (see boto and EC2 API docs). Ignore invalid filters.
|
# 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')
|
help='Get all the variables about a specific instance')
|
||||||
parser.add_argument('--refresh-cache', action='store_true', default=False,
|
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)')
|
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()
|
self.args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
@@ -334,6 +416,9 @@ class Ec2Inventory(object):
|
|||||||
self.get_instances_by_region(region)
|
self.get_instances_by_region(region)
|
||||||
if self.rds_enabled:
|
if self.rds_enabled:
|
||||||
self.get_rds_instances_by_region(region)
|
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.inventory, self.cache_path_cache)
|
||||||
self.write_to_cache(self.index, self.cache_path_index)
|
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 = boto.connect_euca(host=self.eucalyptus_host)
|
||||||
conn.APIVersion = '2010-08-31'
|
conn.APIVersion = '2010-08-31'
|
||||||
else:
|
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
|
# connect_to_region will fail "silently" by returning None if the region name is wrong or not supported
|
||||||
if conn is None:
|
if conn is None:
|
||||||
self.fail_with_error("region name: %s likely not supported, or AWS is down. connection to region failed." % region)
|
self.fail_with_error("region name: %s likely not supported, or AWS is down. connection to region failed." % region)
|
||||||
@@ -373,14 +476,14 @@ class Ec2Inventory(object):
|
|||||||
else:
|
else:
|
||||||
backend = 'Eucalyptus' if self.eucalyptus else 'AWS'
|
backend = 'Eucalyptus' if self.eucalyptus else 'AWS'
|
||||||
error = "Error connecting to %s backend.\n%s" % (backend, e.message)
|
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):
|
def get_rds_instances_by_region(self, region):
|
||||||
''' Makes an AWS API call to the list of RDS instances in a particular
|
''' Makes an AWS API call to the list of RDS instances in a particular
|
||||||
region '''
|
region '''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = rds.connect_to_region(region)
|
conn = self.connect_to_aws(rds, region)
|
||||||
if conn:
|
if conn:
|
||||||
instances = conn.get_all_dbinstances()
|
instances = conn.get_all_dbinstances()
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
@@ -392,7 +495,77 @@ class Ec2Inventory(object):
|
|||||||
error = self.get_auth_error_message()
|
error = self.get_auth_error_message()
|
||||||
if not e.reason == "Forbidden":
|
if not e.reason == "Forbidden":
|
||||||
error = "Looks like AWS RDS is down:\n%s" % e.message
|
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):
|
def get_auth_error_message(self):
|
||||||
''' create an informative error message if there is an issue authenticating'''
|
''' create an informative error message if there is an issue authenticating'''
|
||||||
@@ -411,8 +584,11 @@ class Ec2Inventory(object):
|
|||||||
|
|
||||||
return '\n'.join(errors)
|
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'''
|
'''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.stderr.write(err_msg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -428,8 +604,8 @@ class Ec2Inventory(object):
|
|||||||
''' Adds an instance to the inventory and index, as long as it is
|
''' Adds an instance to the inventory and index, as long as it is
|
||||||
addressable '''
|
addressable '''
|
||||||
|
|
||||||
# Only want running instances unless all_instances is True
|
# Only return instances with desired instance states
|
||||||
if not self.all_instances and instance.state != 'running':
|
if instance.state not in self.ec2_instance_states:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Select the best destination address
|
# 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)
|
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):
|
def get_route53_records(self):
|
||||||
''' Get and store the map of resource records to domain names that
|
''' Get and store the map of resource records to domain names that
|
||||||
@@ -681,7 +1094,6 @@ class Ec2Inventory(object):
|
|||||||
|
|
||||||
return list(name_list)
|
return list(name_list)
|
||||||
|
|
||||||
|
|
||||||
def get_host_info_dict_from_instance(self, instance):
|
def get_host_info_dict_from_instance(self, instance):
|
||||||
instance_vars = {}
|
instance_vars = {}
|
||||||
for key in vars(instance):
|
for key in vars(instance):
|
||||||
@@ -727,6 +1139,91 @@ class Ec2Inventory(object):
|
|||||||
|
|
||||||
return instance_vars
|
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):
|
def get_host_info(self):
|
||||||
''' Get variables about a specific host '''
|
''' Get variables about a specific host '''
|
||||||
|
|
||||||
@@ -790,13 +1287,16 @@ class Ec2Inventory(object):
|
|||||||
cache.write(json_data)
|
cache.write(json_data)
|
||||||
cache.close()
|
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):
|
def to_safe(self, word):
|
||||||
''' Converts 'bad' characters in a string to underscores so they can be
|
''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups '''
|
||||||
used as Ansible groups '''
|
regex = "[^A-Za-z0-9\_"
|
||||||
|
if self.replace_dash_in_groups:
|
||||||
return re.sub("[^A-Za-z0-9\_]", "_", word)
|
regex += "\-"
|
||||||
|
return re.sub(regex + "]", "_", word)
|
||||||
|
|
||||||
def json_format_dict(self, data, pretty=False):
|
def json_format_dict(self, data, pretty=False):
|
||||||
''' Converts a dict to a JSON object and dumps it as a formatted
|
''' Converts a dict to a JSON object and dumps it as a formatted
|
||||||
@@ -810,3 +1310,4 @@ class Ec2Inventory(object):
|
|||||||
|
|
||||||
# Run the script
|
# Run the script
|
||||||
Ec2Inventory()
|
Ec2Inventory()
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Examples:
|
|||||||
$ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"
|
$ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"
|
||||||
|
|
||||||
Use the GCE inventory script to print out instance specific information
|
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>
|
Author: Eric Johnson <erjohnso@google.com>
|
||||||
Version: 0.0.1
|
Version: 0.0.1
|
||||||
@@ -112,9 +112,9 @@ class GceInventory(object):
|
|||||||
|
|
||||||
# Just display data for specific host
|
# Just display data for specific host
|
||||||
if self.args.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)),
|
self.get_instance(self.args.host)),
|
||||||
pretty=self.args.pretty)
|
pretty=self.args.pretty))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Otherwise, assume user wants all instances grouped
|
# Otherwise, assume user wants all instances grouped
|
||||||
@@ -237,7 +237,7 @@ class GceInventory(object):
|
|||||||
'''Gets details about a specific instance '''
|
'''Gets details about a specific instance '''
|
||||||
try:
|
try:
|
||||||
return self.driver.ex_get_node(instance_name)
|
return self.driver.ex_get_node(instance_name)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def group_instances(self):
|
def group_instances(self):
|
||||||
@@ -257,7 +257,10 @@ class GceInventory(object):
|
|||||||
|
|
||||||
tags = node.extra['tags']
|
tags = node.extra['tags']
|
||||||
for t in 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)
|
if groups.has_key(tag): groups[tag].append(name)
|
||||||
else: groups[tag] = [name]
|
else: groups[tag] = [name]
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ class OpenStackInventory(object):
|
|||||||
|
|
||||||
def __init__(self, private=False, refresh=False):
|
def __init__(self, private=False, refresh=False):
|
||||||
config_files = os_client_config.config.CONFIG_FILES
|
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')
|
config_files.append('/etc/ansible/openstack.yml')
|
||||||
self.openstack_config = os_client_config.config.OpenStackConfig(
|
self.openstack_config = os_client_config.config.OpenStackConfig(
|
||||||
config_files)
|
config_files)
|
||||||
@@ -156,7 +154,8 @@ def main():
|
|||||||
elif args.host:
|
elif args.host:
|
||||||
inventory.get_host(args.host)
|
inventory.get_host(args.host)
|
||||||
except shade.OpenStackCloudException as e:
|
except shade.OpenStackCloudException as e:
|
||||||
sys.exit(e.message)
|
sys.stderr.write('%s\n' % e.message)
|
||||||
|
sys.exit(1)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ import warnings
|
|||||||
import collections
|
import collections
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
from ansible.constants import get_config, mk_boolean
|
from ansible.constants import get_config, mk_boolean
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -167,6 +169,9 @@ except ImportError:
|
|||||||
print('pyrax is required for this module')
|
print('pyrax is required for this module')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
|
||||||
NON_CALLABLES = (basestring, bool, dict, int, list, type(None))
|
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))
|
print(json.dumps(hostvars, sort_keys=True, indent=4))
|
||||||
|
|
||||||
|
|
||||||
def _list(regions):
|
def _list_into_cache(regions):
|
||||||
groups = collections.defaultdict(list)
|
groups = collections.defaultdict(list)
|
||||||
hostvars = collections.defaultdict(dict)
|
hostvars = collections.defaultdict(dict)
|
||||||
images = {}
|
images = {}
|
||||||
@@ -242,7 +247,7 @@ def _list(regions):
|
|||||||
if cs is None:
|
if cs is None:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Connecting to Rackspace region "%s" has caused Pyrax to '
|
'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)
|
RuntimeWarning)
|
||||||
continue
|
continue
|
||||||
for server in cs.servers.list():
|
for server in cs.servers.list():
|
||||||
@@ -264,7 +269,7 @@ def _list(regions):
|
|||||||
|
|
||||||
hostvars[server.name]['rax_region'] = region
|
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['%s_%s_%s' % (prefix, key, value)].append(server.name)
|
||||||
|
|
||||||
groups['instance-%s' % server.id].append(server.name)
|
groups['instance-%s' % server.id].append(server.name)
|
||||||
@@ -334,7 +339,31 @@ def _list(regions):
|
|||||||
|
|
||||||
if hostvars:
|
if hostvars:
|
||||||
groups['_meta'] = {'hostvars': 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():
|
def parse_args():
|
||||||
@@ -344,6 +373,9 @@ def parse_args():
|
|||||||
group.add_argument('--list', action='store_true',
|
group.add_argument('--list', action='store_true',
|
||||||
help='List active servers')
|
help='List active servers')
|
||||||
group.add_argument('--host', help='List details about the specific host')
|
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()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
@@ -382,7 +414,7 @@ def setup():
|
|||||||
pyrax.keyring_auth(keyring_username, region=region)
|
pyrax.keyring_auth(keyring_username, region=region)
|
||||||
else:
|
else:
|
||||||
pyrax.set_credential_file(creds_file, region=region)
|
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.stderr.write("%s: %s\n" % (e, e.message))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -410,7 +442,7 @@ def main():
|
|||||||
args = parse_args()
|
args = parse_args()
|
||||||
regions = setup()
|
regions = setup()
|
||||||
if args.list:
|
if args.list:
|
||||||
_list(regions)
|
_list(regions, refresh_cache=args.refresh_cache)
|
||||||
elif args.host:
|
elif args.host:
|
||||||
host(regions, args.host)
|
host(regions, args.host)
|
||||||
sys.exit(0)
|
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.
|
required if these options are specified using environment variables.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@@ -37,6 +39,8 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
# Disable logging message trigged by pSphere/suds.
|
# Disable logging message trigged by pSphere/suds.
|
||||||
try:
|
try:
|
||||||
from logging import NullHandler
|
from logging import NullHandler
|
||||||
@@ -95,7 +99,7 @@ class VMwareInventory(object):
|
|||||||
Saves the value to cache with the name given.
|
Saves the value to cache with the name given.
|
||||||
'''
|
'''
|
||||||
if self.config.has_option('defaults', 'cache_dir'):
|
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):
|
if not os.path.exists(cache_dir):
|
||||||
os.makedirs(cache_dir)
|
os.makedirs(cache_dir)
|
||||||
cache_file = os.path.join(cache_dir, name)
|
cache_file = os.path.join(cache_dir, name)
|
||||||
@@ -115,7 +119,7 @@ class VMwareInventory(object):
|
|||||||
else:
|
else:
|
||||||
cache_max_age = 0
|
cache_max_age = 0
|
||||||
cache_stat = os.stat(cache_file)
|
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:
|
with open(cache_file) as cache:
|
||||||
return json.load(cache)
|
return json.load(cache)
|
||||||
return default
|
return default
|
||||||
@@ -147,7 +151,7 @@ class VMwareInventory(object):
|
|||||||
seen = seen or set()
|
seen = seen or set()
|
||||||
if isinstance(obj, ManagedObject):
|
if isinstance(obj, ManagedObject):
|
||||||
try:
|
try:
|
||||||
obj_unicode = unicode(getattr(obj, 'name'))
|
obj_unicode = text_type(getattr(obj, 'name'))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
obj_unicode = ()
|
obj_unicode = ()
|
||||||
if obj in seen:
|
if obj in seen:
|
||||||
@@ -164,7 +168,7 @@ class VMwareInventory(object):
|
|||||||
obj_info = self._get_obj_info(val, depth - 1, seen)
|
obj_info = self._get_obj_info(val, depth - 1, seen)
|
||||||
if obj_info != ():
|
if obj_info != ():
|
||||||
d[attr] = obj_info
|
d[attr] = obj_info
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
return d
|
return d
|
||||||
elif isinstance(obj, SudsObject):
|
elif isinstance(obj, SudsObject):
|
||||||
@@ -207,8 +211,8 @@ class VMwareInventory(object):
|
|||||||
host_info[k] = v
|
host_info[k] = v
|
||||||
try:
|
try:
|
||||||
host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress
|
host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
print >> sys.stderr, e
|
print(e, file=sys.stderr)
|
||||||
host_info = self._flatten_dict(host_info, prefix)
|
host_info = self._flatten_dict(host_info, prefix)
|
||||||
if ('%s_ipAddress' % prefix) in host_info:
|
if ('%s_ipAddress' % prefix) in host_info:
|
||||||
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix]
|
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix]
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ try:
|
|||||||
from azure import WindowsAzureError
|
from azure import WindowsAzureError
|
||||||
from azure.servicemanagement import ServiceManagementService
|
from azure.servicemanagement import ServiceManagementService
|
||||||
except ImportError as e:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ class AzureInventory(object):
|
|||||||
# Cache setting defaults.
|
# Cache setting defaults.
|
||||||
# These can be overridden in settings (see `read_settings`).
|
# These can be overridden in settings (see `read_settings`).
|
||||||
cache_dir = os.path.expanduser('~')
|
cache_dir = os.path.expanduser('~')
|
||||||
self.cache_path_cache = '%s/.ansible-azure.cache' % cache_dir
|
self.cache_path_cache = os.path.join(cache_dir, '.ansible-azure.cache')
|
||||||
self.cache_path_index = '%s/.ansible-azure.index' % cache_dir
|
self.cache_path_index = os.path.join(cache_dir, '.ansible-azure.index')
|
||||||
self.cache_max_age = 0
|
self.cache_max_age = 0
|
||||||
|
|
||||||
# Read settings and parse CLI arguments
|
# Read settings and parse CLI arguments
|
||||||
@@ -103,15 +103,13 @@ class AzureInventory(object):
|
|||||||
# Add the `['_meta']['hostvars']` information.
|
# Add the `['_meta']['hostvars']` information.
|
||||||
hostvars = {}
|
hostvars = {}
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
for host in set(reduce(lambda x, y: x + y,
|
for host in set([h for hosts in data.values() for h in hosts if h]):
|
||||||
[i for i in data.values()])):
|
hostvars[host] = self.get_host(host, jsonify=False)
|
||||||
if host is not None:
|
|
||||||
hostvars[host] = self.get_host(host, jsonify=False)
|
|
||||||
data['_meta'] = {'hostvars': hostvars}
|
data['_meta'] = {'hostvars': hostvars}
|
||||||
|
|
||||||
# JSONify the data.
|
# JSONify the data.
|
||||||
data_to_print = self.json_format_dict(data, pretty=True)
|
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):
|
def get_host(self, hostname, jsonify=True):
|
||||||
"""Return information about the given hostname, based on what
|
"""Return information about the given hostname, based on what
|
||||||
@@ -153,9 +151,9 @@ class AzureInventory(object):
|
|||||||
|
|
||||||
# Cache related
|
# Cache related
|
||||||
if config.has_option('azure', 'cache_path'):
|
if config.has_option('azure', 'cache_path'):
|
||||||
cache_path = os.path.expanduser(config.get('azure', 'cache_path'))
|
cache_path = os.path.expandvars(os.path.expanduser(config.get('azure', 'cache_path')))
|
||||||
self.cache_path_cache = cache_path + '/ansible-azure.cache'
|
self.cache_path_cache = os.path.join(cache_path, 'ansible-azure.cache')
|
||||||
self.cache_path_index = cache_path + '/ansible-azure.index'
|
self.cache_path_index = os.path.join(cache_path, 'ansible-azure.index')
|
||||||
if config.has_option('azure', 'cache_max_age'):
|
if config.has_option('azure', 'cache_max_age'):
|
||||||
self.cache_max_age = config.getint('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():
|
for cloud_service in self.sms.list_hosted_services():
|
||||||
self.add_deployments(cloud_service)
|
self.add_deployments(cloud_service)
|
||||||
except WindowsAzureError as e:
|
except WindowsAzureError as e:
|
||||||
print "Looks like Azure's API is down:"
|
print("Looks like Azure's API is down:")
|
||||||
print
|
print("")
|
||||||
print e
|
print(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def add_deployments(self, cloud_service):
|
def add_deployments(self, cloud_service):
|
||||||
@@ -209,12 +207,10 @@ class AzureInventory(object):
|
|||||||
try:
|
try:
|
||||||
for deployment in self.sms.get_hosted_service_properties(cloud_service.service_name,embed_detail=True).deployments.deployments:
|
for deployment in self.sms.get_hosted_service_properties(cloud_service.service_name,embed_detail=True).deployments.deployments:
|
||||||
self.add_deployment(cloud_service, deployment)
|
self.add_deployment(cloud_service, deployment)
|
||||||
#if deployment.deployment_slot == "Production":
|
|
||||||
# self.add_deployment(cloud_service, deployment)
|
|
||||||
except WindowsAzureError as e:
|
except WindowsAzureError as e:
|
||||||
print "Looks like Azure's API is down:"
|
print("Looks like Azure's API is down:")
|
||||||
print
|
print("")
|
||||||
print e
|
print(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def add_deployment(self, cloud_service, deployment):
|
def add_deployment(self, cloud_service, deployment):
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re # noqa
|
||||||
import sys
|
import sys
|
||||||
import djcelery
|
import djcelery
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@@ -63,7 +64,7 @@ USE_L10N = True
|
|||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
os.path.join(BASE_DIR, 'ui', 'dist'),
|
os.path.join(BASE_DIR, 'ui', 'static'),
|
||||||
os.path.join(BASE_DIR, 'static'),
|
os.path.join(BASE_DIR, 'static'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,16 +119,32 @@ REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
|||||||
|
|
||||||
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
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',
|
'django.core.context_processors.request',
|
||||||
'awx.ui.context_processors.settings',
|
'awx.ui.context_processors.settings',
|
||||||
'awx.ui.context_processors.version',
|
'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.HAMiddleware',
|
||||||
'awx.main.middleware.ActivityStreamMiddleware',
|
'awx.main.middleware.ActivityStreamMiddleware',
|
||||||
|
'awx.sso.middleware.SocialAuthMiddleware',
|
||||||
'crum.CurrentRequestUserMiddleware',
|
'crum.CurrentRequestUserMiddleware',
|
||||||
|
'awx.main.middleware.AuthTokenTimeoutMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATE_DIRS = (
|
TEMPLATE_DIRS = (
|
||||||
@@ -159,10 +176,12 @@ INSTALLED_APPS = (
|
|||||||
'kombu.transport.django',
|
'kombu.transport.django',
|
||||||
'polymorphic',
|
'polymorphic',
|
||||||
'taggit',
|
'taggit',
|
||||||
|
'social.apps.django_app.default',
|
||||||
'awx.main',
|
'awx.main',
|
||||||
'awx.api',
|
'awx.api',
|
||||||
'awx.ui',
|
'awx.ui',
|
||||||
'awx.fact',
|
'awx.fact',
|
||||||
|
'awx.sso',
|
||||||
)
|
)
|
||||||
|
|
||||||
INTERNAL_IPS = ('127.0.0.1',)
|
INTERNAL_IPS = ('127.0.0.1',)
|
||||||
@@ -199,16 +218,35 @@ REST_FRAMEWORK = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
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',
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
)
|
)
|
||||||
|
|
||||||
# LDAP server (default to None to skip using LDAP authentication).
|
# LDAP server (default to None to skip using LDAP authentication).
|
||||||
AUTH_LDAP_SERVER_URI = None
|
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.
|
# Seconds before auth tokens expire.
|
||||||
AUTH_TOKEN_EXPIRATION = 1800
|
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.
|
# If set, serve only minified JS for UI.
|
||||||
USE_MINIFIED_JS = False
|
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
|
# Any ANSIBLE_* settings will be passed to the subprocess environment by the
|
||||||
# celery task.
|
# celery task.
|
||||||
|
|
||||||
@@ -355,6 +457,12 @@ AWX_JOB_TEMPLATE_HISTORY = 10
|
|||||||
# The directory in which proot will create new temporary directories for its root
|
# The directory in which proot will create new temporary directories for its root
|
||||||
AWX_PROOT_BASE_PATH = "/tmp"
|
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.
|
# Default list of modules allowed for ad hoc commands.
|
||||||
AD_HOC_COMMANDS = [
|
AD_HOC_COMMANDS = [
|
||||||
'command',
|
'command',
|
||||||
@@ -473,13 +581,19 @@ VMWARE_EXCLUDE_EMPTY_GROUPS = True
|
|||||||
# provide a list here.
|
# provide a list here.
|
||||||
# Source: https://developers.google.com/compute/docs/zones
|
# Source: https://developers.google.com/compute/docs/zones
|
||||||
GCE_REGION_CHOICES = [
|
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-a', 'US Central (A)'),
|
||||||
('us-central1-b', 'US Central (B)'),
|
('us-central1-b', 'US Central (B)'),
|
||||||
|
('us-central1-c', 'US Central (C)'),
|
||||||
('us-central1-f', 'US Central (F)'),
|
('us-central1-f', 'US Central (F)'),
|
||||||
('europe-west1-a', 'Europe West (A)'),
|
|
||||||
('europe-west1-b', 'Europe West (B)'),
|
('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-a', 'Asia East (A)'),
|
||||||
('asia-east1-b', 'Asia East (B)'),
|
('asia-east1-b', 'Asia East (B)'),
|
||||||
|
('asia-east1-c', 'Asia East (C)'),
|
||||||
]
|
]
|
||||||
GCE_REGIONS_BLACKLIST = []
|
GCE_REGIONS_BLACKLIST = []
|
||||||
|
|
||||||
@@ -616,7 +730,7 @@ LOGGING = {
|
|||||||
'level': 'WARNING',
|
'level': 'WARNING',
|
||||||
'class':'logging.handlers.RotatingFileHandler',
|
'class':'logging.handlers.RotatingFileHandler',
|
||||||
'filters': ['require_debug_false'],
|
'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
|
'maxBytes': 1024 * 1024 * 5, # 5 MB
|
||||||
'backupCount': 5,
|
'backupCount': 5,
|
||||||
'formatter':'simple',
|
'formatter':'simple',
|
||||||
@@ -708,7 +822,12 @@ LOGGING = {
|
|||||||
'propagate': False,
|
'propagate': False,
|
||||||
},
|
},
|
||||||
'django_auth_ldap': {
|
'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
|
# Enable PROOT for tower-qa integration tests
|
||||||
AWX_PROOT_ENABLED = True
|
AWX_PROOT_ENABLED = True
|
||||||
|
|
||||||
|
PENDO_TRACKING_STATE = "off"
|
||||||
|
|
||||||
# Use Django-Jenkins if installed. Only run tests for awx.main app.
|
# Use Django-Jenkins if installed. Only run tests for awx.main app.
|
||||||
try:
|
try:
|
||||||
import django_jenkins
|
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
|
# default settings for development. If not present, we can still run using
|
||||||
# only the defaults.
|
# only the defaults.
|
||||||
try:
|
try:
|
||||||
include(
|
include(optional('local_*.py'), scope=locals())
|
||||||
optional('local_*.py'),
|
include('postprocess.py', scope=locals())
|
||||||
scope=locals(),
|
|
||||||
)
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -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},
|
'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
|
# INVENTORY IMPORT TEST SETTINGS
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -481,6 +603,9 @@ TEST_AUTH_LDAP_TEAM_MAP_2_RESULT = {
|
|||||||
TEST_AWS_ACCESS_KEY_ID = ''
|
TEST_AWS_ACCESS_KEY_ID = ''
|
||||||
TEST_AWS_SECRET_ACCESS_KEY = ''
|
TEST_AWS_SECRET_ACCESS_KEY = ''
|
||||||
TEST_AWS_REGIONS = 'all'
|
TEST_AWS_REGIONS = 'all'
|
||||||
|
# Check IAM STS credentials
|
||||||
|
TEST_AWS_SECURITY_TOKEN = ''
|
||||||
|
|
||||||
|
|
||||||
# Rackspace credentials
|
# Rackspace credentials
|
||||||
TEST_RACKSPACE_USERNAME = ''
|
TEST_RACKSPACE_USERNAME = ''
|
||||||
|
|||||||
@@ -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
|
# Attempt to load settings from /etc/tower/settings.py first, followed by
|
||||||
# /etc/tower/conf.d/*.py.
|
# /etc/tower/conf.d/*.py.
|
||||||
try:
|
try:
|
||||||
include(
|
include(settings_file, optional(settings_files), scope=locals())
|
||||||
settings_file,
|
include('postprocess.py', scope=locals())
|
||||||
optional(settings_files),
|
|
||||||
scope=locals(),
|
|
||||||
)
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
@@ -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()
|
||||||
@@ -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')
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
@@ -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)
|
||||||
@@ -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'),
|
||||||
|
)
|
||||||
@@ -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 style %}
|
||||||
{{ block.super }}
|
{{ 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">
|
<style type="text/css">
|
||||||
html body {
|
html body {
|
||||||
background: #ddd;
|
background: #ddd;
|
||||||
@@ -183,7 +183,7 @@ html body .dropdown-submenu:hover>a {
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block branding %}
|
{% 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 %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block userlinks %}
|
{% block userlinks %}
|
||||||
@@ -196,7 +196,7 @@ html body .dropdown-submenu:hover>a {
|
|||||||
|
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
<div id="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.
|
Copyright © 2015 <a href="http://www.ansible.com" target="_blank">Ansible, Inc.</a> All rights reserved.
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% 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 |