mirror of
https://github.com/ansible/awx.git
synced 2026-03-14 07:27:28 -02:30
Merge branch 'release_3.1.2' into devel
* release_3.1.2: (33 commits) updating changelog for 3.1.2 Add back SRC_ONLY_PKGS Fix ubuntu 14 restart service list pt 2 Make sure the insight playbook fetch doesn't quote user and pass Add requirements/vendor to gitignore Conditionally install from local python dependencies in spec file Remove requirements/vendor on make clean. Update brew-srpm target to generate local requirements files Get offline pip installs working Add clean-dist target Navigate back to the jobDetails state when the user clicks outside the host event modal. Don't use jinja quote filter on insights username or password fix legacy standard out Fixed permissions typo add test, restore old behavior in api test create _survey_element_validation and use it for updating extra_vars Do not set the default if the field was not passed in to kwargs_extra_vars Remove log aggregator port required mark. Modify according to review feedback. Host Event json should be read-only ...
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -108,6 +108,7 @@ reports
|
||||
*.results
|
||||
local/
|
||||
*.mo
|
||||
requirements/vendor
|
||||
|
||||
# AWX python libs populated by requirements.txt
|
||||
awx/lib/.deps_built
|
||||
|
||||
51
Makefile
51
Makefile
@@ -225,15 +225,18 @@ clean-tmp:
|
||||
clean-venv:
|
||||
rm -rf venv/
|
||||
|
||||
clean-dist:
|
||||
rm -rf dist
|
||||
|
||||
# Remove temporary build files, compiled Python files.
|
||||
clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle
|
||||
clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle clean-dist
|
||||
rm -rf awx/public
|
||||
rm -rf awx/lib/site-packages
|
||||
rm -rf dist/*
|
||||
rm -rf awx/job_status
|
||||
rm -rf awx/job_output
|
||||
rm -rf reports
|
||||
rm -f awx/awx_test.sqlite3
|
||||
rm -rf requirements/vendor
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
rm -rf build $(NAME)-$(VERSION) *.egg-info
|
||||
@@ -282,7 +285,11 @@ virtualenv_tower:
|
||||
fi
|
||||
|
||||
requirements_ansible: virtualenv_ansible
|
||||
$(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed --no-binary $(SRC_ONLY_PKGS) -r requirements/requirements_ansible.txt
|
||||
if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \
|
||||
cat requirements/requirements_ansible.txt requirements/requirements_ansible_local.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \
|
||||
else \
|
||||
cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --no-binary $(SRC_ONLY_PKGS) --ignore-installed -r /dev/stdin ; \
|
||||
fi
|
||||
$(VENV_BASE)/ansible/bin/pip uninstall --yes -r requirements/requirements_ansible_uninstall.txt
|
||||
|
||||
requirements_ansible_dev:
|
||||
@@ -292,7 +299,11 @@ requirements_ansible_dev:
|
||||
|
||||
# Install third-party requirements needed for Tower's environment.
|
||||
requirements_tower: virtualenv_tower
|
||||
$(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed --no-binary $(SRC_ONLY_PKGS) -r requirements/requirements.txt
|
||||
if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \
|
||||
cat requirements/requirements.txt requirements/requirements_local.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \
|
||||
else \
|
||||
cat requirements/requirements.txt requirements/requirements_git.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --no-binary $(SRC_ONLY_PKGS) --ignore-installed -r /dev/stdin ; \
|
||||
fi
|
||||
$(VENV_BASE)/tower/bin/pip uninstall --yes -r requirements/requirements_tower_uninstall.txt
|
||||
|
||||
requirements_tower_dev:
|
||||
@@ -690,39 +701,44 @@ setup_bundle_tarball: setup-bundle-build setup-bundle-build/$(OFFLINE_TAR_FILE)
|
||||
rpm-build:
|
||||
mkdir -p $@
|
||||
|
||||
rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE)
|
||||
rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE) tar-build/$(SETUP_TAR_FILE)
|
||||
cp packaging/rpm/$(NAME).spec rpm-build/
|
||||
cp packaging/rpm/tower.te rpm-build/
|
||||
cp packaging/rpm/tower.fc rpm-build/
|
||||
cp packaging/rpm/$(NAME).sysconfig rpm-build/
|
||||
cp packaging/remove_tower_source.py rpm-build/
|
||||
cp packaging/bytecompile.sh rpm-build/
|
||||
cp tar-build/$(SETUP_TAR_FILE) rpm-build/
|
||||
if [ "$(OFFICIAL)" != "yes" ] ; then \
|
||||
(cd dist/ && tar zxf $(SDIST_TAR_FILE)) ; \
|
||||
(cd dist/ && mv $(NAME)-$(VERSION)-$(BUILD) $(NAME)-$(VERSION)) ; \
|
||||
(cd dist/ && tar czf ../rpm-build/$(SDIST_TAR_FILE) $(NAME)-$(VERSION)) ; \
|
||||
ln -sf $(SDIST_TAR_FILE) rpm-build/$(NAME)-$(VERSION).tar.gz ; \
|
||||
(cd tar-build/ && tar zxf $(SETUP_TAR_FILE)) ; \
|
||||
(cd tar-build/ && mv $(NAME)-setup-$(VERSION)-$(BUILD) $(NAME)-setup-$(VERSION)) ; \
|
||||
(cd tar-build/ && tar czf ../rpm-build/$(SETUP_TAR_FILE) $(NAME)-setup-$(VERSION)) ; \
|
||||
ln -sf $(SETUP_TAR_FILE) rpm-build/$(NAME)-setup-$(VERSION).tar.gz ; \
|
||||
else \
|
||||
cp -a dist/$(SDIST_TAR_FILE) rpm-build/ ; \
|
||||
fi
|
||||
|
||||
rpmtar: sdist rpm-build/$(SDIST_TAR_FILE)
|
||||
|
||||
brewrpmtar: rpm-build/python-deps.tar.gz rpmtar
|
||||
brewrpmtar: rpm-build/python-deps.tar.gz requirements/requirements_local.txt requirements/requirements_ansible_local.txt rpmtar
|
||||
|
||||
rpm-build/python-deps.tar.gz: requirements/vendor rpm-build
|
||||
tar czf rpm-build/python-deps.tar.gz requirements/vendor
|
||||
|
||||
requirements/vendor:
|
||||
pip download \
|
||||
cat requirements/requirements.txt requirements/requirements_git.txt | pip download \
|
||||
--no-binary=:all: \
|
||||
--requirement=requirements/requirements_ansible.txt \
|
||||
--requirement=/dev/stdin \
|
||||
--dest=$@ \
|
||||
--exists-action=i
|
||||
|
||||
pip download \
|
||||
cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | pip download \
|
||||
--no-binary=:all: \
|
||||
--requirement=requirements/requirements.txt \
|
||||
--requirement=/dev/stdin \
|
||||
--dest=$@ \
|
||||
--exists-action=i
|
||||
|
||||
@@ -732,6 +748,21 @@ requirements/vendor:
|
||||
--dest=$@ \
|
||||
--exists-action=i
|
||||
|
||||
requirements/requirements_local.txt:
|
||||
@echo "This is going to take a while..."
|
||||
pip download \
|
||||
--requirement=requirements/requirements_git.txt \
|
||||
--no-deps \
|
||||
--exists-action=w \
|
||||
--dest=requirements/vendor 2>/dev/null | sed -n 's/^\s*Saved\s*//p' > $@
|
||||
|
||||
requirements/requirements_ansible_local.txt:
|
||||
pip download \
|
||||
--requirement=requirements/requirements_ansible_git.txt \
|
||||
--no-deps \
|
||||
--exists-action=w \
|
||||
--dest=requirements/vendor 2>/dev/null | sed -n 's/^\s*Saved\s*//p' > $@
|
||||
|
||||
rpm-build/$(RPM_NVR).src.rpm: /etc/mock/$(MOCK_CFG).cfg
|
||||
$(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build --buildsrpm --spec rpm-build/$(NAME).spec --sources rpm-build \
|
||||
--define "tower_version $(VERSION)" --define "tower_release $(RELEASE)" $(SCL_DEFINES)
|
||||
|
||||
@@ -60,7 +60,7 @@ def executor(tmpdir_factory, request):
|
||||
|
||||
cli = PlaybookCLI(['', 'playbook.yml'])
|
||||
cli.parse()
|
||||
options = cli.parser.parse_args([])[0]
|
||||
options = cli.parser.parse_args(['-v'])[0]
|
||||
loader = DataLoader()
|
||||
variable_manager = VariableManager()
|
||||
inventory = Inventory(loader=loader, variable_manager=variable_manager,
|
||||
|
||||
@@ -30,6 +30,8 @@ from ansible.plugins.callback.default import CallbackModule as DefaultCallbackMo
|
||||
from .events import event_context
|
||||
from .minimal import CallbackModule as MinimalCallbackModule
|
||||
|
||||
CENSORED = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" # noqa
|
||||
|
||||
|
||||
class BaseCallbackModule(CallbackBase):
|
||||
'''
|
||||
@@ -69,8 +71,12 @@ class BaseCallbackModule(CallbackBase):
|
||||
else:
|
||||
task = None
|
||||
|
||||
if event_data.get('res') and event_data['res'].get('_ansible_no_log', False):
|
||||
event_data['res'] = {'censored': "the output has been hidden due to the fact that 'no_log: true' was specified for this result"} # noqa
|
||||
if event_data.get('res'):
|
||||
if event_data['res'].get('_ansible_no_log', False):
|
||||
event_data['res'] = {'censored': CENSORED}
|
||||
for i, item in enumerate(event_data['res'].get('results', [])):
|
||||
if event_data['res']['results'][i].get('_ansible_no_log', False):
|
||||
event_data['res']['results'][i] = {'censored': CENSORED}
|
||||
|
||||
with event_context.display_lock:
|
||||
try:
|
||||
|
||||
@@ -242,9 +242,11 @@ register(
|
||||
field_class=fields.IntegerField,
|
||||
allow_null=True,
|
||||
label=_('Logging Aggregator Port'),
|
||||
help_text=_('Port on Logging Aggregator to send logs to (if required).'),
|
||||
help_text=_('Port on Logging Aggregator to send logs to (if required and not'
|
||||
' provided in Logging Aggregator).'),
|
||||
category=_('Logging'),
|
||||
category_slug='logging',
|
||||
required=False
|
||||
)
|
||||
register(
|
||||
'LOG_AGGREGATOR_TYPE',
|
||||
|
||||
@@ -130,13 +130,18 @@ class SurveyJobTemplateMixin(models.Model):
|
||||
for survey_element in self.survey_spec.get("spec", []):
|
||||
default = survey_element.get('default')
|
||||
variable_key = survey_element.get('variable')
|
||||
|
||||
if survey_element.get('type') == 'password':
|
||||
if variable_key in kwargs_extra_vars and default:
|
||||
kw_value = kwargs_extra_vars[variable_key]
|
||||
if kw_value.startswith('$encrypted$') and kw_value != default:
|
||||
kwargs_extra_vars[variable_key] = default
|
||||
|
||||
if default is not None:
|
||||
extra_vars[variable_key] = default
|
||||
data = {variable_key: default}
|
||||
errors = self._survey_element_validation(survey_element, data)
|
||||
if not errors:
|
||||
extra_vars[variable_key] = default
|
||||
|
||||
# Overwrite job template extra vars with explicit job extra vars
|
||||
# and add on job extra vars
|
||||
@@ -144,6 +149,65 @@ class SurveyJobTemplateMixin(models.Model):
|
||||
kwargs['extra_vars'] = json.dumps(extra_vars)
|
||||
return kwargs
|
||||
|
||||
def _survey_element_validation(self, survey_element, data):
|
||||
errors = []
|
||||
if survey_element['variable'] not in data and survey_element['required']:
|
||||
errors.append("'%s' value missing" % survey_element['variable'])
|
||||
elif survey_element['type'] in ["textarea", "text", "password"]:
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) not in (str, unicode):
|
||||
errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']],
|
||||
survey_element['variable']))
|
||||
return errors
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (length is %s must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], len(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']]) > int(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'integer':
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) != int:
|
||||
errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']],
|
||||
survey_element['variable']))
|
||||
return errors
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \
|
||||
data[survey_element['variable']] < int(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \
|
||||
data[survey_element['variable']] > int(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'float':
|
||||
if survey_element['variable'] in data:
|
||||
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']],
|
||||
survey_element['variable']))
|
||||
return errors
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'multiselect':
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) != list:
|
||||
errors.append("'%s' value is expected to be a list." % survey_element['variable'])
|
||||
else:
|
||||
for val in data[survey_element['variable']]:
|
||||
if val not in survey_element['choices']:
|
||||
errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'],
|
||||
survey_element['choices']))
|
||||
elif survey_element['type'] == 'multiplechoice':
|
||||
if survey_element['variable'] in data:
|
||||
if data[survey_element['variable']] not in survey_element['choices']:
|
||||
errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']],
|
||||
survey_element['variable'],
|
||||
survey_element['choices']))
|
||||
return errors
|
||||
|
||||
def survey_variable_validation(self, data):
|
||||
errors = []
|
||||
if not self.survey_enabled:
|
||||
@@ -153,62 +217,7 @@ class SurveyJobTemplateMixin(models.Model):
|
||||
if 'description' not in self.survey_spec:
|
||||
errors.append("'description' missing from survey spec.")
|
||||
for survey_element in self.survey_spec.get("spec", []):
|
||||
if survey_element['variable'] not in data and \
|
||||
survey_element['required']:
|
||||
errors.append("'%s' value missing" % survey_element['variable'])
|
||||
elif survey_element['type'] in ["textarea", "text", "password"]:
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) not in (str, unicode):
|
||||
errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']],
|
||||
survey_element['variable']))
|
||||
continue
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (length is %s must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], len(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']]) > int(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'integer':
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) != int:
|
||||
errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']],
|
||||
survey_element['variable']))
|
||||
continue
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \
|
||||
data[survey_element['variable']] < int(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \
|
||||
data[survey_element['variable']] > int(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'float':
|
||||
if survey_element['variable'] in data:
|
||||
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']],
|
||||
survey_element['variable']))
|
||||
continue
|
||||
if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']):
|
||||
errors.append("'%s' value %s is too small (must be at least %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['min']))
|
||||
if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']):
|
||||
errors.append("'%s' value %s is too large (must be no more than %s)." %
|
||||
(survey_element['variable'], data[survey_element['variable']], survey_element['max']))
|
||||
elif survey_element['type'] == 'multiselect':
|
||||
if survey_element['variable'] in data:
|
||||
if type(data[survey_element['variable']]) != list:
|
||||
errors.append("'%s' value is expected to be a list." % survey_element['variable'])
|
||||
else:
|
||||
for val in data[survey_element['variable']]:
|
||||
if val not in survey_element['choices']:
|
||||
errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'],
|
||||
survey_element['choices']))
|
||||
elif survey_element['type'] == 'multiplechoice':
|
||||
if survey_element['variable'] in data:
|
||||
if data[survey_element['variable']] not in survey_element['choices']:
|
||||
errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']],
|
||||
survey_element['variable'],
|
||||
survey_element['choices']))
|
||||
errors += self._survey_element_validation(survey_element, data)
|
||||
return errors
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import json
|
||||
from awx.main.tasks import RunJob
|
||||
from awx.main.models import (
|
||||
Job,
|
||||
JobTemplate,
|
||||
WorkflowJobTemplate
|
||||
)
|
||||
|
||||
@@ -78,6 +79,18 @@ def test_job_args_unredacted_passwords(job):
|
||||
assert extra_vars['secret_key'] == 'my_password'
|
||||
|
||||
|
||||
def test_update_kwargs_survey_invalid_default(survey_spec_factory):
|
||||
spec = survey_spec_factory('var2')
|
||||
spec['spec'][0]['required'] = False
|
||||
spec['spec'][0]['min'] = 3
|
||||
spec['spec'][0]['default'] = 1
|
||||
jt = JobTemplate(name="test-jt", survey_spec=spec, survey_enabled=True, extra_vars="var2: 2")
|
||||
defaulted_extra_vars = jt._update_unified_job_kwargs()
|
||||
assert 'extra_vars' in defaulted_extra_vars
|
||||
# Make sure we did not set the invalid default of 1
|
||||
assert json.loads(defaulted_extra_vars['extra_vars'])['var2'] == 2
|
||||
|
||||
|
||||
class TestWorkflowSurveys:
|
||||
def test_update_kwargs_survey_defaults(self, survey_spec_factory):
|
||||
"Assure that the survey default over-rides a JT variable"
|
||||
|
||||
@@ -147,7 +147,17 @@ def test_https_logging_handler_splunk_auth_info():
|
||||
('http://localhost', None, 'http://localhost'),
|
||||
('http://localhost', 80, 'http://localhost'),
|
||||
('http://localhost', 8080, 'http://localhost:8080'),
|
||||
('https://localhost', 443, 'https://localhost:443')
|
||||
('https://localhost', 443, 'https://localhost:443'),
|
||||
('ftp://localhost', 443, 'ftp://localhost:443'),
|
||||
('https://localhost:550', 443, 'https://localhost:550'),
|
||||
('https://localhost:yoho/foobar', 443, 'https://localhost:443/foobar'),
|
||||
('https://localhost:yoho/foobar', None, 'https://localhost:yoho/foobar'),
|
||||
('http://splunk.server:8088/services/collector/event', 80,
|
||||
'http://splunk.server:8088/services/collector/event'),
|
||||
('http://splunk.server/services/collector/event', 80,
|
||||
'http://splunk.server/services/collector/event'),
|
||||
('http://splunk.server/services/collector/event', 8088,
|
||||
'http://splunk.server:8088/services/collector/event'),
|
||||
])
|
||||
def test_https_logging_handler_http_host_format(host, port, normalized):
|
||||
handler = HTTPSHandler(host=host, port=port)
|
||||
|
||||
@@ -6,6 +6,7 @@ import logging
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
import urlparse
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from copy import copy
|
||||
from requests.exceptions import RequestException
|
||||
@@ -148,10 +149,21 @@ class BaseHTTPSHandler(logging.Handler):
|
||||
|
||||
def get_http_host(self):
|
||||
host = self.host or ''
|
||||
if not host.startswith('http'):
|
||||
host = 'http://%s' % self.host
|
||||
if self.port != 80 and self.port is not None:
|
||||
host = '%s:%s' % (host, str(self.port))
|
||||
# urlparse requires scheme to be provided, default to use http if
|
||||
# missing
|
||||
if not urlparse.urlsplit(host).scheme:
|
||||
host = 'http://%s' % host
|
||||
parsed = urlparse.urlsplit(host)
|
||||
# Insert self.port if its special and port number is either not
|
||||
# given in host or given as non-numerical
|
||||
try:
|
||||
port = parsed.port or self.port
|
||||
except ValueError:
|
||||
port = self.port
|
||||
if port not in (80, None):
|
||||
new_netloc = '%s:%s' % (parsed.hostname, port)
|
||||
return urlparse.urlunsplit((parsed.scheme, new_netloc, parsed.path,
|
||||
parsed.query, parsed.fragment))
|
||||
return host
|
||||
|
||||
def get_post_kwargs(self, payload_input):
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
- name: update project using insights
|
||||
uri:
|
||||
url: "{{insights_url}}/r/insights/v1/maintenance?ansible=true"
|
||||
user: "{{scm_username|quote}}"
|
||||
password: "{{scm_password|quote}}"
|
||||
user: "{{scm_username}}"
|
||||
password: "{{scm_password}}"
|
||||
force_basic_auth: yes
|
||||
when: scm_type == 'insights'
|
||||
register: insights_output
|
||||
@@ -124,8 +124,8 @@
|
||||
get_url:
|
||||
url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook"
|
||||
dest: "{{project_path|quote}}/{{item.name}}-{{item.maintenance_id}}.yml"
|
||||
url_username: "{{scm_username|quote}}"
|
||||
url_password: "{{scm_password|quote}}"
|
||||
url_username: "{{scm_username}}"
|
||||
url_password: "{{scm_password}}"
|
||||
force_basic_auth: yes
|
||||
force: yes
|
||||
when: scm_type == 'insights' and item.name != None
|
||||
@@ -136,8 +136,8 @@
|
||||
get_url:
|
||||
url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook"
|
||||
dest: "{{project_path|quote}}/insights-plan-{{item.maintenance_id}}.yml"
|
||||
url_username: "{{scm_username|quote}}"
|
||||
url_password: "{{scm_password|quote}}"
|
||||
url_username: "{{scm_username}}"
|
||||
url_password: "{{scm_password}}"
|
||||
force_basic_auth: yes
|
||||
force: yes
|
||||
when: scm_type == 'insights' and item.name == None
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</a>
|
||||
<span class="HostEvent-title">{{event.host_name}}</span>
|
||||
<!-- close -->
|
||||
<button ui-sref="jobResult" type="button" class="close">
|
||||
<button ng-click="closeHostEvent()" type="button" class="close">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
<!-- controls -->
|
||||
<div class="HostEvent-controls">
|
||||
<button ui-sref="jobResult" class="btn btn-sm btn-default HostEvent-close">Close</button>
|
||||
<button ng-click="closeHostEvent()" class="btn btn-sm btn-default HostEvent-close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
var container = document.getElementById(el);
|
||||
var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line
|
||||
lineNumbers: true,
|
||||
mode: mode
|
||||
mode: mode,
|
||||
readOnly: true
|
||||
});
|
||||
editor.setSize("100%", 200);
|
||||
editor.getDoc().setValue(data);
|
||||
@@ -29,6 +30,19 @@
|
||||
return $state.current.name === name;
|
||||
};
|
||||
|
||||
$scope.getActiveHostIndex = function(){
|
||||
var result = $scope.hostResults.filter(function( obj ) {
|
||||
return obj.id === $scope.event.id;
|
||||
});
|
||||
return $scope.hostResults.indexOf(result[0]);
|
||||
};
|
||||
|
||||
$scope.closeHostEvent = function() {
|
||||
// Unbind the listener so it doesn't fire when we close the modal via navigation
|
||||
$('#HostEvent').off('hidden.bs.modal');
|
||||
$state.go('jobDetail');
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
hostEvent.event_name = hostEvent.event;
|
||||
$scope.event = _.cloneDeep(hostEvent);
|
||||
@@ -81,6 +95,10 @@
|
||||
}
|
||||
}
|
||||
$('#HostEvent').modal('show');
|
||||
|
||||
$('#HostEvent').on('hidden.bs.modal', function () {
|
||||
$scope.closeHostEvent();
|
||||
});
|
||||
};
|
||||
init();
|
||||
}];
|
||||
|
||||
@@ -38,7 +38,8 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
|
||||
|
||||
// used for tag search
|
||||
$scope.list = {
|
||||
basePath: jobData.related.job_events
|
||||
basePath: jobData.related.job_events,
|
||||
name: 'job_events'
|
||||
};
|
||||
|
||||
// used for tag search
|
||||
@@ -450,13 +451,6 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
|
||||
var getSkeleton = function(url) {
|
||||
jobResultsService.getEvents(url)
|
||||
.then(events => {
|
||||
// old job check: if the job is complete, there is result stdout, and
|
||||
// there are no job events, it's an old job
|
||||
if ($scope.jobFinished) {
|
||||
$scope.showLegacyJobErrorMessage = $scope.job.result_stdout.length &&
|
||||
!events.results.length;
|
||||
}
|
||||
|
||||
events.results.forEach(event => {
|
||||
if (event.start_line === 0 && event.end_line === 0) {
|
||||
$scope.isOld++;
|
||||
|
||||
@@ -1368,4 +1368,33 @@ function(ConfigurationUtils, i18n, $rootScope) {
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('awRequireMultiple', [function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function postLink(scope, element, attrs, ngModel) {
|
||||
// Watch for changes to the required attribute
|
||||
attrs.$observe('required', function(value) {
|
||||
if(value) {
|
||||
ngModel.$validators.required = function (value) {
|
||||
if(angular.isArray(value)) {
|
||||
if(value.length === 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return (!value[0] || value[0] === "") ? false : true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
delete ngModel.$validators.required;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<select class="form-control SurveyMaker-previewSelect" ng-model="selectedValue" multi-select ng-required="isRequired" ng-disabled="isDisabled">
|
||||
<select class="form-control SurveyMaker-previewSelect" ng-model="selectedValue" multi-select ng-required="isRequired" ng-disabled="isDisabled" aw-require-multiple>
|
||||
<option ng-repeat="choice in choices" value="{{choice}}">{{choice}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -207,7 +207,7 @@ export default ['$state','moment', '$timeout', '$window',
|
||||
}
|
||||
|
||||
function update() {
|
||||
let userCanAddEdit = (scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate;
|
||||
let userCanAddEdit = (scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate;
|
||||
if(scope.dimensionsSet) {
|
||||
// Declare the nodes
|
||||
let nodes = tree.nodes(scope.treeData),
|
||||
@@ -813,7 +813,7 @@ export default ['$state','moment', '$timeout', '$window',
|
||||
|
||||
function add_node() {
|
||||
this.on("click", function(d) {
|
||||
if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
scope.addNode({
|
||||
parent: d,
|
||||
betweenTwoNodes: false
|
||||
@@ -824,7 +824,7 @@ export default ['$state','moment', '$timeout', '$window',
|
||||
|
||||
function add_node_between() {
|
||||
this.on("click", function(d) {
|
||||
if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
scope.addNode({
|
||||
parent: d,
|
||||
betweenTwoNodes: true
|
||||
@@ -835,7 +835,7 @@ export default ['$state','moment', '$timeout', '$window',
|
||||
|
||||
function remove_node() {
|
||||
this.on("click", function(d) {
|
||||
if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) {
|
||||
scope.deleteNode({
|
||||
nodeToDelete: d
|
||||
});
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
-e git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv
|
||||
-e git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
|
||||
-e git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic
|
||||
-e git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding
|
||||
-e git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
apache-libcloud==1.3.0
|
||||
appdirs==1.4.2
|
||||
asgi-amqp==0.4.1
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
#
|
||||
# pip-compile --output-file requirements/requirements.txt requirements/requirements.in
|
||||
#
|
||||
git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv
|
||||
git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic
|
||||
git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding
|
||||
git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
|
||||
git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
adal==0.4.3 # via msrestazure
|
||||
amqp==1.4.9 # via kombu
|
||||
anyjson==0.3.3 # via kombu
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
-e git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
apache-libcloud==1.3.0
|
||||
azure==2.0.0rc6
|
||||
backports.ssl-match-hostname==3.5.0.1
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# pip-compile --output-file requirements/requirements_ansible.txt requirements/requirements_ansible.in
|
||||
#
|
||||
git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
adal==0.4.3 # via msrestazure
|
||||
amqp==1.4.9 # via kombu
|
||||
anyjson==0.3.3 # via kombu
|
||||
|
||||
1
requirements/requirements_ansible_git.txt
Normal file
1
requirements/requirements_ansible_git.txt
Normal file
@@ -0,0 +1 @@
|
||||
git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
5
requirements/requirements_git.txt
Normal file
5
requirements/requirements_git.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv
|
||||
git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic
|
||||
git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding
|
||||
git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
|
||||
git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax
|
||||
3
setup.py
3
setup.py
@@ -126,7 +126,8 @@ setup(
|
||||
("%s" % docdir, ["docs/licenses/*",]),
|
||||
("%s" % bindir, ["tools/scripts/ansible-tower-service",
|
||||
"tools/scripts/failure-event-handler",
|
||||
"tools/scripts/tower-python"]),
|
||||
"tools/scripts/tower-python",
|
||||
"tools/scripts/ansible-tower-setup"]),
|
||||
("%s" % sosconfig, ["tools/sosreport/tower.py"])]),
|
||||
options = {
|
||||
'egg_info': {
|
||||
|
||||
@@ -67,3 +67,7 @@ services:
|
||||
image: postgres:9.4.1
|
||||
memcached:
|
||||
image: memcached:alpine
|
||||
logstash:
|
||||
build:
|
||||
context: ./docker-compose
|
||||
dockerfile: Dockerfile-logstash
|
||||
|
||||
@@ -3,7 +3,9 @@ FROM centos:7
|
||||
ADD Makefile /tmp/Makefile
|
||||
RUN mkdir /tmp/requirements
|
||||
ADD requirements/requirements.txt \
|
||||
requirements/requirements_git.txt \
|
||||
requirements/requirements_ansible.txt \
|
||||
requirements/requirements_ansible_git.txt \
|
||||
requirements/requirements_dev.txt \
|
||||
requirements/requirements_ansible_uninstall.txt \
|
||||
requirements/requirements_tower_uninstall.txt \
|
||||
|
||||
3
tools/scripts/ansible-tower-setup
Executable file
3
tools/scripts/ansible-tower-setup
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec /var/lib/awx/setup/setup.sh "$@"
|
||||
Reference in New Issue
Block a user