Merge branch 'release_3.1.0' into fixJobResults

This commit is contained in:
jlmitch5 2016-12-16 14:12:52 -05:00 committed by GitHub
commit 55e4e9efb6
69 changed files with 1874 additions and 1342 deletions

View File

@ -77,7 +77,7 @@ class FieldLookupBackend(BaseFilterBackend):
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
'startswith', 'istartswith', 'endswith', 'iendswith',
'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'in',
'isnull')
'isnull', 'search')
def get_field_from_lookup(self, model, lookup):
field = None
@ -148,6 +148,15 @@ class FieldLookupBackend(BaseFilterBackend):
re.compile(value)
except re.error as e:
raise ValueError(e.args[0])
elif new_lookup.endswith('__search'):
related_model = getattr(field, 'related_model', None)
if not related_model:
raise ValueError('%s is not searchable' % new_lookup[:-8])
new_lookups = []
for rm_field in related_model._meta.fields:
if rm_field.name in ('username', 'first_name', 'last_name', 'email', 'name', 'description'):
new_lookups.append('{}__{}__icontains'.format(new_lookup[:-8], rm_field.name))
return value, new_lookups
else:
value = self.value_to_python_for_field(field, value)
return value, new_lookup
@ -160,6 +169,7 @@ class FieldLookupBackend(BaseFilterBackend):
or_filters = []
chain_filters = []
role_filters = []
search_filters = []
for key, values in request.query_params.lists():
if key in self.RESERVED_NAMES:
continue
@ -181,6 +191,16 @@ class FieldLookupBackend(BaseFilterBackend):
role_filters.append(values[0])
continue
# Search across related objects.
if key.endswith('__search'):
for value in values:
for search_term in force_text(value).replace(',', ' ').split():
search_value, new_keys = self.value_to_python(queryset.model, key, search_term)
assert isinstance(new_keys, list)
for new_key in new_keys:
search_filters.append((new_key, search_value))
continue
# Custom chain__ and or__ filters, mutually exclusive (both can
# precede not__).
q_chain = False
@ -211,7 +231,7 @@ class FieldLookupBackend(BaseFilterBackend):
and_filters.append((q_not, new_key, value))
# Now build Q objects for database query filter.
if and_filters or or_filters or chain_filters or role_filters:
if and_filters or or_filters or chain_filters or role_filters or search_filters:
args = []
for n, k, v in and_filters:
if n:
@ -234,6 +254,11 @@ class FieldLookupBackend(BaseFilterBackend):
else:
q |= Q(**{k:v})
args.append(q)
if search_filters:
q = Q()
for k,v in search_filters:
q |= Q(**{k:v})
args.append(q)
for n,k,v in chain_filters:
if n:
q = ~Q(**{k:v})

View File

@ -267,10 +267,25 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
fields = []
for field in self.model._meta.fields:
if field.name in ('username', 'first_name', 'last_name', 'email',
'name', 'description', 'email'):
'name', 'description'):
fields.append(field.name)
return fields
@property
def related_search_fields(self):
fields = []
for field in self.model._meta.fields:
if field.name.endswith('_role'):
continue
if getattr(field, 'related_model', None):
fields.append('{}__search'.format(field.name))
for rel in self.model._meta.related_objects:
name = rel.get_accessor_name()
if name.endswith('_set'):
continue
fields.append('{}__search'.format(name))
return fields
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
# Base class for a list view that allows creating new objects.

View File

@ -182,6 +182,10 @@ class Metadata(metadata.SimpleMetadata):
if getattr(view, 'search_fields', None):
metadata['search_fields'] = view.search_fields
# Add related search fields if available from the view.
if getattr(view, 'related_search_fields', None):
metadata['related_search_fields'] = view.related_search_fields
return metadata

View File

@ -56,6 +56,10 @@ within all designated text fields of a model.
_Added in AWX 1.4_
(_Added in Ansible Tower 3.1.0_) Search across related fields:
?related__search=findme
## Filtering
Any additional query string parameters may be used to filter the list of

View File

@ -65,6 +65,9 @@ from awx.api.generics import * # noqa
from awx.conf.license import get_license, feature_enabled, feature_exists, LicenseForbids
from awx.main.models import * # noqa
from awx.main.utils import * # noqa
from awx.main.utils import (
callback_filter_out_ansible_extra_vars
)
from awx.api.permissions import * # noqa
from awx.api.renderers import * # noqa
from awx.api.serializers import * # noqa
@ -2663,7 +2666,7 @@ class JobTemplateCallback(GenericAPIView):
# Send a signal to celery that the job should be started.
kv = {"inventory_sources_already_updated": inventory_sources_already_updated}
if extra_vars is not None:
kv['extra_vars'] = extra_vars
kv['extra_vars'] = callback_filter_out_ansible_extra_vars(extra_vars)
result = job.signal_start(**kv)
if not result:
data = dict(msg=_('Error starting job!'))

View File

@ -1,6 +1,7 @@
# Python
import contextlib
import logging
import sys
import threading
import time
@ -86,6 +87,7 @@ class SettingsWrapper(UserSettingsHolder):
self.__dict__['_awx_conf_settings'] = self
self.__dict__['_awx_conf_preload_expires'] = None
self.__dict__['_awx_conf_preload_lock'] = threading.RLock()
self.__dict__['_awx_conf_init_readonly'] = False
def _get_supported_settings(self):
return settings_registry.get_registered_settings()
@ -110,6 +112,20 @@ class SettingsWrapper(UserSettingsHolder):
return
# Otherwise update local preload timeout.
self.__dict__['_awx_conf_preload_expires'] = time.time() + SETTING_CACHE_TIMEOUT
# Check for any settings that have been defined in Python files and
# make those read-only to avoid overriding in the database.
if not self._awx_conf_init_readonly and 'migrate_to_database_settings' not in sys.argv:
defaults_snapshot = self._get_default('DEFAULTS_SNAPSHOT')
for key in self._get_writeable_settings():
init_default = defaults_snapshot.get(key, None)
try:
file_default = self._get_default(key)
except AttributeError:
file_default = None
if file_default != init_default and file_default is not None:
logger.warning('Setting %s has been marked read-only!', key)
settings_registry._registry[key]['read_only'] = True
self.__dict__['_awx_conf_init_readonly'] = True
# If local preload timer has expired, check to see if another process
# has already preloaded the cache and skip preloading if so.
if cache.get('_awx_conf_preload_expires', empty) is not empty:

File diff suppressed because it is too large Load Diff

View File

@ -1253,6 +1253,11 @@ class Command(NoArgsCommand):
except re.error:
raise CommandError('invalid regular expression for --host-filter')
'''
TODO: Remove this deprecation when we remove support for rax.py
'''
self.logger.info("Rackspace inventory sync is Deprecated in Tower 3.1.0 and support for Rackspace will be removed in a future release.")
begin = time.time()
self.load_inventory_from_database()

View File

@ -47,6 +47,11 @@ class Migration(migrations.Migration):
name='uuid',
field=models.CharField(max_length=40),
),
migrations.AlterField(
model_name='credential',
name='become_method',
field=models.CharField(default=b'', help_text='Privilege escalation method.', max_length=32, blank=True, choices=[(b'', 'None'), (b'sudo', 'Sudo'), (b'su', 'Su'), (b'pbrun', 'Pbrun'), (b'pfexec', 'Pfexec'), (b'dzdo', 'DZDO'), (b'pmrun', 'Pmrun')]),
),
# Add Workflows
migrations.AlterField(
model_name='unifiedjob',

View File

@ -50,6 +50,8 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
('su', _('Su')),
('pbrun', _('Pbrun')),
('pfexec', _('Pfexec')),
('dzdo', _('DZDO')),
('pmrun', _('Pmrun')),
#('runas', _('Runas')),
]

View File

@ -654,6 +654,16 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin):
def get_notification_friendly_name(self):
return "Job"
'''
Canceling a job also cancels the implicit project update with launch_type
run.
'''
def cancel(self):
res = super(Job, self).cancel()
if self.project_update:
self.project_update.cancel()
return res
class JobHostSummary(CreatedModifiedModel):
'''

View File

@ -210,7 +210,7 @@ class AuthToken(BaseModel):
REASON_CHOICES = [
('', _('Token not invalidated')),
('timeout_reached', _('Token is expired')),
('limit_reached', _('Maximum per-user sessions reached')),
('limit_reached', _('The maximum number of allowed sessions for this user has been exceeded.')),
# 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')),

View File

@ -366,7 +366,9 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl
@classmethod
def _get_unified_jt_copy_names(cls):
return (super(WorkflowJobTemplate, cls)._get_unified_jt_copy_names() +
base_list = super(WorkflowJobTemplate, cls)._get_unified_jt_copy_names()
base_list.remove('labels')
return (base_list +
['survey_spec', 'survey_enabled', 'organization'])
def get_absolute_url(self):

View File

@ -346,6 +346,7 @@ class TaskManager():
'Celery, so it has been marked as failed.',
))
task_obj.save()
_send_notification_templates(task_obj, 'failed')
connection.on_commit(lambda: task_obj.websocket_emit_status('failed'))
logger.error("Task %s appears orphaned... marking as failed" % task)

View File

@ -109,8 +109,12 @@ def send_notifications(notification_list, job_id=None):
raise TypeError("notification_list should be of type list")
if job_id is not None:
job_actual = UnifiedJob.objects.get(id=job_id)
for notification_id in notification_list:
notification = Notification.objects.get(id=notification_id)
notifications = Notification.objects.filter(id__in=notification_list)
if job_id is not None:
job_actual.notifications.add(*notifications)
for notification in notifications:
try:
sent = notification.notification_template.send(notification.subject, notification.body)
notification.status = "successful"
@ -121,8 +125,6 @@ def send_notifications(notification_list, job_id=None):
notification.error = smart_str(e)
finally:
notification.save()
if job_id is not None:
job_actual.notifications.add(notification)
@task(bind=True, queue='default')
@ -618,7 +620,7 @@ class BaseTask(Task):
for child_proc in child_procs:
os.kill(child_proc.pid, signal.SIGKILL)
os.kill(main_proc.pid, signal.SIGKILL)
except TypeError:
except (TypeError, psutil.Error):
os.kill(job.pid, signal.SIGKILL)
else:
os.kill(job.pid, signal.SIGTERM)
@ -708,6 +710,11 @@ class BaseTask(Task):
stdout_handle.close()
except Exception:
pass
instance = self.update_model(pk)
if instance.cancel_flag:
status = 'canceled'
instance = self.update_model(pk, status=status, result_traceback=tb,
output_replacements=output_replacements,
**extra_update_fields)
@ -809,7 +816,7 @@ class RunJob(BaseTask):
env['REST_API_URL'] = settings.INTERNAL_API_URL
env['REST_API_TOKEN'] = job.task_auth_token or ''
env['TOWER_HOST'] = settings.TOWER_URL_BASE
env['MAX_EVENT_RES'] = settings.MAX_EVENT_RES_DATA
env['MAX_EVENT_RES'] = str(settings.MAX_EVENT_RES_DATA)
env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE
env['CALLBACK_CONNECTION'] = settings.BROKER_URL
if getattr(settings, 'JOB_CALLBACK_DEBUG', False):
@ -1045,17 +1052,18 @@ class RunJob(BaseTask):
local_project_sync = job.project.create_project_update(launch_type="sync")
local_project_sync.job_type = 'run'
local_project_sync.save()
# save the associated project update before calling run() so that a
# cancel() call on the job can cancel the project update
job = self.update_model(job.pk, project_update=local_project_sync)
project_update_task = local_project_sync._get_task_class()
try:
project_update_task().run(local_project_sync.id)
job.scm_revision = job.project.scm_revision
job.project_update = local_project_sync
job.save()
job = self.update_model(job.pk, scm_revision=job.project.scm_revision)
except Exception:
job.status = 'failed'
job.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \
('project_update', local_project_sync.name, local_project_sync.id)
job.save()
job = self.update_model(job.pk, status='failed',
job_explanation=('Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' %
('project_update', local_project_sync.name, local_project_sync.id)))
raise
def post_run_hook(self, job, status, **kwargs):
@ -1737,6 +1745,10 @@ class RunAdHocCommand(BaseTask):
d[re.compile(r'^pfexec password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^RUNAS password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^runas password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^DZDO password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^dzdo password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^PMRUN password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^pmrun password.*:\s*?$', re.M)] = 'become_password'
d[re.compile(r'^SSH password:\s*?$', re.M)] = 'ssh_password'
d[re.compile(r'^Password:\s*?$', re.M)] = 'ssh_password'
return d

View File

@ -77,7 +77,7 @@ def test_net_cred_ssh_agent(mocker, get_ssh_version):
mocker.patch.object(run_job, 'post_run_hook', return_value=None)
run_job.run(mock_job.id)
assert run_job.update_model.call_count == 3
assert run_job.update_model.call_count == 4
job_args = run_job.update_model.call_args_list[1][1].get('job_args')
assert 'ssh-add' in job_args

View File

@ -38,17 +38,17 @@ def test_send_notifications_list(mocker):
mock_job = mocker.MagicMock(spec=UnifiedJob)
patches.append(mocker.patch('awx.main.models.UnifiedJob.objects.get', return_value=mock_job))
mock_notification = mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'})
patches.append(mocker.patch('awx.main.models.Notification.objects.get', return_value=mock_notification))
mock_notifications = [mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'})]
patches.append(mocker.patch('awx.main.models.Notification.objects.filter', return_value=mock_notifications))
with apply_patches(patches):
send_notifications([1,2], job_id=1)
assert Notification.objects.get.call_count == 2
assert mock_notification.status == "successful"
assert mock_notification.save.called
assert Notification.objects.filter.call_count == 1
assert mock_notifications[0].status == "successful"
assert mock_notifications[0].save.called
assert mock_job.notifications.add.called
assert mock_job.notifications.add.called_with(mock_notification)
assert mock_job.notifications.add.called_with(*mock_notifications)
@pytest.mark.parametrize("current_instances,call_count", [(91, 2), (89,1)])

View File

@ -43,7 +43,8 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
'copy_m2m_relationships' ,'cache_list_capabilities', 'to_python_boolean',
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
'_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided',
'get_current_apps', 'set_current_apps', 'OutputEventFilter']
'get_current_apps', 'set_current_apps', 'OutputEventFilter',
'callback_filter_out_ansible_extra_vars',]
def get_object_or_400(klass, *args, **kwargs):
@ -824,3 +825,12 @@ class OutputEventFilter(object):
self._current_event_data = next_event_data
else:
self._current_event_data = None
def callback_filter_out_ansible_extra_vars(extra_vars):
extra_vars_redacted = {}
for key, value in extra_vars.iteritems():
if not key.startswith('ansible_'):
extra_vars_redacted[key] = value
return extra_vars_redacted

View File

@ -22,19 +22,19 @@ EXAMPLES = '''
# {
# "source": "apt",
# "version": "1.0.6-5",
# "architecture": "amd64",
# "arch": "amd64",
# "name": "libbz2-1.0"
# },
# {
# "source": "apt",
# "version": "2.7.1-4ubuntu1",
# "architecture": "amd64",
# "arch": "amd64",
# "name": "patch"
# },
# {
# "source": "apt",
# "version": "4.8.2-19ubuntu1",
# "architecture": "amd64",
# "arch": "amd64",
# "name": "gcc-4.8-base"
# }, ... ] } }
'''
@ -64,7 +64,7 @@ def deb_package_list():
ac_pkg = apt_cache[package].installed
package_details = dict(name=package,
version=ac_pkg.version,
architecture=ac_pkg.architecture,
arch=ac_pkg.architecture,
source='apt')
installed_packages.append(package_details)
return installed_packages

View File

@ -82,14 +82,13 @@ PASSWORD_HASHERS = (
# Configure a default UUID for development only.
SYSTEM_UUID = '00000000-0000-0000-0000-000000000000'
# Store a snapshot of default settings at this point (only for migrating from
# file to database settings).
if 'migrate_to_database_settings' in sys.argv:
DEFAULTS_SNAPSHOT = {}
this_module = sys.modules[__name__]
for setting in dir(this_module):
if setting == setting.upper():
DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting))
# Store a snapshot of default settings at this point before loading any
# customizable config files.
DEFAULTS_SNAPSHOT = {}
this_module = sys.modules[__name__]
for setting in dir(this_module):
if setting == setting.upper():
DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting))
# If there is an `/etc/tower/settings.py`, include it.
# If there is a `/etc/tower/conf.d/*.py`, include them.

View File

@ -57,14 +57,13 @@ LOGGING['handlers']['fact_receiver']['filename'] = '/var/log/tower/fact_receiver
LOGGING['handlers']['system_tracking_migrations']['filename'] = '/var/log/tower/tower_system_tracking_migrations.log'
LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log'
# Store a snapshot of default settings at this point (only for migrating from
# file to database settings).
if 'migrate_to_database_settings' in sys.argv:
DEFAULTS_SNAPSHOT = {}
this_module = sys.modules[__name__]
for setting in dir(this_module):
if setting == setting.upper():
DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting))
# Store a snapshot of default settings at this point before loading any
# customizable config files.
DEFAULTS_SNAPSHOT = {}
this_module = sys.modules[__name__]
for setting in dir(this_module):
if setting == setting.upper():
DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting))
# Load settings from any .py files in the global conf.d directory specified in
# the environment, defaulting to /etc/tower/conf.d/.

View File

@ -20,15 +20,13 @@ export default ['templateUrl', function(templateUrl) {
{label: 'Hosts', value: 'host'},
{label: 'Inventories', value: 'inventory'},
{label: 'Inventory Scripts', value: 'inventory_script'},
{label: 'Job Templates', value: 'job_template'},
{label: 'Jobs', value: 'job'},
{label: 'Organizations', value: 'organization'},
{label: 'Projects', value: 'project'},
{label: 'Schedules', value: 'schedule'},
{label: 'Teams', value: 'team'},
{label: 'Templates', value: 'template'},
{label: 'Users', value: 'user'},
{label: 'Workflow Job Templates', value: 'workflow_job_template'}
{label: 'Users', value: 'user'}
];
CreateSelect2({

View File

@ -36,6 +36,35 @@ export function CredentialsList($scope, $rootScope, $location, $log,
$scope.selected = [];
}
$scope.$on(`${list.iterator}_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
$scope.$watchCollection(`${$scope.list.name}`, function() {
optionsRequestDataProcessing();
}
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
// Set the item type label
if (list.fields.kind && $scope.options &&
$scope.options.hasOwnProperty('kind')) {
$scope.options.kind.choices.every(function(choice) {
if (choice[0] === item.kind) {
itm.kind_label = choice[1];
return false;
}
return true;
});
}
});
}
$scope.addCredential = function() {
$state.go('credentials.add');
};

View File

@ -13,7 +13,7 @@
export function JobsListController($state, $rootScope, $log, $scope, $compile, $stateParams,
ClearScope, Find, DeleteJob, RelaunchJob, AllJobsList, ScheduledJobsList, GetBasePath, Dataset, GetChoices) {
ClearScope, Find, DeleteJob, RelaunchJob, AllJobsList, ScheduledJobsList, GetBasePath, Dataset) {
ClearScope();
@ -28,40 +28,44 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$scope.showJobType = true;
}
$scope.$on(`${list.iterator}_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
_.forEach($scope[list.name], buildTooltips);
if ($scope.removeChoicesReady) {
$scope.removeChoicesReady();
$scope.$watchCollection(`${$scope.list.name}`, function() {
optionsRequestDataProcessing();
}
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
if(item.summary_fields && item.summary_fields.source_workflow_job &&
item.summary_fields.source_workflow_job.id){
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
}
// Set the item type label
if (list.fields.type) {
$scope.type_choices.every(function(choice) {
if (choice.value === item.type) {
itm.type_label = choice.label;
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
if(item.summary_fields && item.summary_fields.source_workflow_job &&
item.summary_fields.source_workflow_job.id){
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
}
// Set the item type label
if (list.fields.type && $scope.options &&
$scope.options.hasOwnProperty('type')) {
$scope.options.type.choices.every(function(choice) {
if (choice[0] === item.type) {
itm.type_label = choice[1];
return false;
}
return true;
});
}
});
});
GetChoices({
scope: $scope,
url: GetBasePath('unified_jobs'),
field: 'type',
variable: 'type_choices',
callback: 'choicesReady'
buildTooltips(itm);
});
}
function buildTooltips(job) {
job.status_tip = 'Job ' + job.status + ". Click for details.";
}
@ -131,5 +135,5 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $
}
JobsListController.$inject = ['$state', '$rootScope', '$log', '$scope', '$compile', '$stateParams',
'ClearScope', 'Find', 'DeleteJob', 'RelaunchJob', 'AllJobsList', 'ScheduledJobsList', 'GetBasePath', 'Dataset', 'GetChoices'
'ClearScope', 'Find', 'DeleteJob', 'RelaunchJob', 'AllJobsList', 'ScheduledJobsList', 'GetBasePath', 'Dataset'
];

View File

@ -38,10 +38,39 @@ export function ProjectsList($scope, $rootScope, $location, $log, $stateParams,
$rootScope.flashMessage = null;
}
$scope.$watch(`${list.name}`, function() {
_.forEach($scope[list.name], buildTooltips);
$scope.$on(`${list.iterator}_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
$scope.$watchCollection(`${$scope.list.name}`, function() {
optionsRequestDataProcessing();
}
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
// Set the item type label
if (list.fields.scm_type && $scope.options &&
$scope.options.hasOwnProperty('scm_type')) {
$scope.options.scm_type.choices.every(function(choice) {
if (choice[0] === item.scm_type) {
itm.type_label = choice[1];
return false;
}
return true;
});
}
buildTooltips(itm);
});
}
function buildTooltips(project) {
project.statusIcon = GetProjectIcon(project.status);
project.statusTip = GetProjectToolTip(project.status);

View File

@ -217,6 +217,7 @@ export function TeamsEdit($scope, $rootScope, $stateParams,
$rootScope.flashMessage = null;
if ($scope[form.name + '_form'].$valid) {
var data = processNewData(form.fields);
Rest.setUrl(defaultUrl);
Rest.put(data).success(function() {
$state.go($state.current, null, { reload: true });
})

View File

@ -311,7 +311,7 @@ export function UsersEdit($scope, $rootScope, $location,
$scope.formSave = function() {
$rootScope.flashMessage = null;
if ($scope[form.name + '_form'].$valid) {
Rest.setUrl(defaultUrl + id + '/');
Rest.setUrl(defaultUrl + '/');
var data = processNewData(form.fields);
Rest.put(data).success(function() {
$state.go($state.current, null, { reload: true });

View File

@ -47,7 +47,7 @@ export default
};
scope.editJobTemplate = function (jobTemplateId) {
$state.go('templates.editJobTemplate', {id: jobTemplateId});
$state.go('templates.editJobTemplate', {job_template_id: jobTemplateId});
};
}
}];

View File

@ -263,7 +263,7 @@ export default
"ssh_key_unlock": {
label: i18n._('Private Key Passphrase'),
type: 'sensitive',
ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
ngShow: "kind.value === 'ssh' || kind.value === 'scm' || kind.value === 'net'",
ngDisabled: "keyEntered === false || ssh_key_unlock_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
subCheckbox: {
variable: 'ssh_key_unlock_ask',

View File

@ -307,6 +307,17 @@ export default
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
}, {
name: 'allow_simultaneous',
label: i18n._('Enable Concurrent Jobs'),
type: 'checkbox',
column: 2,
awPopOver: "<p>" + i18n._("If enabled, simultaneous runs of this job template will be allowed.") + "</p>",
dataPlacement: 'right',
dataTitle: i18n._('Enable Concurrent Jobs'),
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
}]
},
callback_url: {

View File

@ -185,7 +185,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
type: 'number',
integer: true,
min: 0,
ngShow: "scm_update_on_launch && projectSelected && scm_type.value !== 'manual'",
ngShow: "scm_update_on_launch && scm_type.value !== 'manual'",
spinner: true,
"default": '0',
awPopOver: '<p>' + i18n._('Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +

View File

@ -25,9 +25,6 @@ export default
case 'inventory':
rtnTitle = 'INVENTORIES';
break;
case 'job_template':
rtnTitle = 'JOB TEMPLATES';
break;
case 'credential':
rtnTitle = 'CREDENTIALS';
break;
@ -55,9 +52,6 @@ export default
case 'template':
rtnTitle = 'TEMPLATES';
break;
case 'workflow_job_template':
rtnTitle = 'WORKFLOW JOB TEMPLATES';
break;
}
return rtnTitle;

View File

@ -5,10 +5,10 @@
*************************************************/
export default
['$scope', '$state', '$stateParams', 'generateList', 'GroupManageService', 'GetBasePath', 'CopyMoveGroupList', 'group',
function($scope, $state, $stateParams, GenerateList, GroupManageService, GetBasePath, CopyMoveGroupList, group){
var list = CopyMoveGroupList,
view = GenerateList;
['$scope', '$state', '$stateParams', 'GroupManageService', 'GetBasePath', 'CopyMoveGroupList', 'group', 'Dataset',
function($scope, $state, $stateParams, GroupManageService, GetBasePath, CopyMoveGroupList, group, Dataset){
var list = CopyMoveGroupList;
$scope.item = group;
$scope.submitMode = $stateParams.groups === undefined ? 'move' : 'copy';
$scope['toggle_'+ list.iterator] = function(id){
@ -58,33 +58,18 @@
$(el).prop('disabled', (idx, value) => !value);
});
};
var init = function(){
var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/';
url += $stateParams.group ? '?not__id__in=' + group.id + ',' + _.last($stateParams.group) : '?not__id=' + group.id;
list.basePath = url;
$scope.atRootLevel = $stateParams.group ? false : true;
view.inject(list, {
mode: 'lookup',
id: 'copyMove-list',
scope: $scope,
input_type: 'radio'
});
// @issue: OLD SEARCH
// SearchInit({
// scope: $scope,
// set: list.name,
// list: list,
// url: url
// });
// PaginateInit({
// scope: $scope,
// list: list,
// url : url,
// mode: 'lookup'
// });
// $scope.search(list.iterator, null, true, false);
// remove the current group from list
};
function init(){
var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/';
url += $stateParams.group ? '?not__id__in=' + group.id + ',' + _.last($stateParams.group) : '?not__id=' + group.id;
list.basePath = url;
$scope.atRootLevel = $stateParams.group ? false : true;
// search init
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
}
init();
}];

View File

@ -5,10 +5,10 @@
*************************************************/
export default
['$scope', '$state', '$stateParams', 'generateList', 'HostManageService', 'GetBasePath', 'CopyMoveGroupList', 'host',
function($scope, $state, $stateParams, GenerateList, HostManageService, GetBasePath, CopyMoveGroupList, host){
var list = CopyMoveGroupList,
view = GenerateList;
['$scope', '$state', '$stateParams', 'generateList', 'HostManageService', 'GetBasePath', 'CopyMoveGroupList', 'host', 'Dataset',
function($scope, $state, $stateParams, GenerateList, HostManageService, GetBasePath, CopyMoveGroupList, host, Dataset){
var list = CopyMoveGroupList;
$scope.item = host;
$scope.submitMode = 'copy';
$scope['toggle_'+ list.iterator] = function(id){
@ -40,29 +40,11 @@
}
};
var init = function(){
var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/';
list.basePath = url;
view.inject(list, {
mode: 'lookup',
id: 'copyMove-list',
scope: $scope,
input_type: 'radio'
});
// search init
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
// @issue: OLD SEARCH
// SearchInit({
// scope: $scope,
// set: list.name,
// list: list,
// url: url
// });
// PaginateInit({
// scope: $scope,
// list: list,
// url : url,
// mode: 'lookup'
// });
// $scope.search(list.iterator, null, true, false);
};
init();
}];

View File

@ -10,7 +10,7 @@
<input type="radio" ng-model="submitMode" value="move" class="ng-pristine ng-untouched ng-valid"> Move
</label>
</div>
<div id="copyMove-list"></div>
<div id="copyMove-list" ui-view="copyMoveList"></div>
<div class="copyMove-root form-group pull-left" ng-hide="atRootLevel">
<span><input type="checkbox" ng-model="targetRootGroup" ng-change="toggleTargetRootGroup()"> Use the inventory root</span>
</div>

View File

@ -10,14 +10,31 @@ import CopyMoveHostsController from './copy-move-hosts.controller';
var copyMoveGroupRoute = {
name: 'inventoryManage.copyMoveGroup',
url: '/copy-move-group/{group_id}',
url: '/copy-move-group/{group_id:int}',
searchPrefix: 'copy',
data: {
group_id: 'group_id',
},
params: {
copy_search: {
value: {
not__id__in: null
},
dynamic: true,
squash: ''
}
},
ncyBreadcrumb: {
label: "COPY OR MOVE {{item.name}}"
},
resolve: {
Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', 'group',
function(list, qs, $stateParams, GetBasePath, group) {
$stateParams.copy_search.not__id__in = ($stateParams.group.length > 0 ? group.id + ',' + _.last($stateParams.group) : group.id);
let path = GetBasePath(list.name);
return qs.search(path, $stateParams.copy_search);
}
],
group: ['GroupManageService', '$stateParams', function(GroupManageService, $stateParams){
return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]);
}]
@ -26,16 +43,33 @@ var copyMoveGroupRoute = {
'form@inventoryManage' : {
controller: CopyMoveGroupsController,
templateUrl: templateUrl('inventories/manage/copy-move/copy-move'),
},
'copyMoveList@inventoryManage.copyMoveGroup': {
templateProvider: function(CopyMoveGroupList, generateList) {
let html = generateList.build({
list: CopyMoveGroupList,
mode: 'lookup',
input_type: 'radio'
});
return html;
}
}
}
};
var copyMoveHostRoute = {
name: 'inventoryManage.copyMoveHost',
url: '/copy-move-host/{host_id}',
searchPrefix: 'copy',
ncyBreadcrumb: {
label: "COPY OR MOVE {{item.name}}"
},
resolve: {
Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) {
let path = GetBasePath(list.name);
return qs.search(path, $stateParams.copy_search);
}
],
host: ['HostManageService', '$stateParams', function(HostManageService, $stateParams){
return HostManageService.get({id: $stateParams.host_id}).then(res => res.data.results[0]);
}]
@ -44,6 +78,16 @@ var copyMoveHostRoute = {
'form@inventoryManage': {
templateUrl: templateUrl('inventories/manage/copy-move/copy-move'),
controller: CopyMoveHostsController,
},
'copyMoveList@inventoryManage.copyMoveHost': {
templateProvider: function(CopyMoveGroupList, generateList) {
let html = generateList.build({
list: CopyMoveGroupList,
mode: 'lookup',
input_type: 'radio'
});
return html;
}
}
}
};

View File

@ -176,3 +176,27 @@ job-results-standard-out {
flex: 1;
display: flex
}
.JobResults-extraVarsHelp {
margin-left: 10px;
color: @default-icon;
}
.JobResults .CodeMirror.cm-s-default,
.JobResults .CodeMirror-line {
background-color: #f6f6f6;
}
.JobResults .CodeMirror-gutter.CodeMirror-lint-markers,
.JobResults .CodeMirror-gutter.CodeMirror-linenumbers {
background-color: #ebebeb;
color: @b7grey;
}
.JobResults .CodeMirror-lines {
cursor: default;
}
.JobResults .CodeMirror-cursors {
display: none;
}

View File

@ -353,6 +353,10 @@
<label class="JobResults-resultRowLabel
JobResults-resultRowLabel--fullWidth">
Extra Variables
<i class="JobResults-extraVarsHelp fa fa-question-circle"
aw-tool-tip="Read only view of extra variables added to the job template."
data-placement="top">
</i>
</label>
<textarea
rows="6"
@ -414,6 +418,7 @@
<div class="StandardOut-panelHeaderText">
<i class="JobResults-statusResultIcon
fa icon-job-{{ job_status }}"
ng-show="stdoutFullScreen"
aw-tool-tip="Job {{status_label}}"
data-tip-watch="status_tooltip"
aw-tip-placement="top"

View File

@ -86,7 +86,7 @@ export default
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
"view": {
mode: "all",
ngClick: "viewJob(job.id)",
ngClick: "viewJobDetails(job)",
awToolTip: "View the job",
dataPlacement: "top"
},

View File

@ -37,6 +37,7 @@ export default
},
kind: {
label: i18n._('Type'),
ngBind: 'credential.kind_label',
excludeModal: true,
nosort: true,
columnClass: 'col-md-2 hidden-sm hidden-xs'

View File

@ -12,7 +12,7 @@ export default
.value('CopyMoveGroupList', {
name: 'groups',
iterator: 'group',
iterator: 'copy',
selectTitle: 'Copy Groups',
index: false,
well: false,

View File

@ -30,7 +30,8 @@ export default
dataPlacement: 'right',
icon: "icon-job-{{ project.statusIcon }}",
columnClass: "List-staticColumn--smallStatus",
nosort: true
nosort: true,
excludeModal: true
},
name: {
key: true,
@ -46,6 +47,7 @@ export default
},
scm_type: {
label: i18n._('Type'),
ngBind: 'project.type_label',
excludeModal: true,
columnClass: 'col-lg-3 col-md-2 col-sm-3 hidden-xs'
},

View File

@ -31,8 +31,7 @@ export default
name: {
label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-5 col-sm-5 col-xs-7 List-staticColumnAdjacent',
sourceModel: 'unified_job_template',
sourceField: 'name',
ngBind: 'schedule.summary_fields.unified_job_template.name',
ngClick: "editSchedule(schedule)",
awToolTip: "{{ schedule.nameTip | sanitize}}",
dataTipWatch: 'schedule.nameTip',

View File

@ -5,55 +5,33 @@
*************************************************/
export default
[ '$rootScope','Wait', 'generateList', 'NotificationsList',
'GetBasePath' , 'Rest' ,
'ProcessErrors', 'Prompt', '$state', 'GetChoices', 'Empty', 'Find',
'ngToast', '$compile', '$filter','ToggleNotification',
'NotificationsListInit', '$stateParams', 'management_job',
[ 'NotificationsList', 'GetBasePath', 'ToggleNotification', 'NotificationsListInit',
'$stateParams', 'Dataset', '$scope',
function(
$rootScope,Wait, GenerateList, NotificationsList,
GetBasePath, Rest,
ProcessErrors, Prompt, $state, GetChoices, Empty, Find, ngToast,
$compile, $filter, ToggleNotification, NotificationsListInit,
$stateParams, management_job) {
var scope = $rootScope.$new(),
url = GetBasePath('notification_templates'),
defaultUrl = GetBasePath('system_job_templates'),
list,
view = GenerateList,
NotificationsList, GetBasePath, ToggleNotification, NotificationsListInit,
$stateParams, Dataset, $scope) {
var defaultUrl = GetBasePath('system_job_templates'),
list = NotificationsList,
id = $stateParams.management_id;
list = _.cloneDeep(NotificationsList);
delete list.actions.add;
list.listTitle = `${management_job.name} <div class="List-titleLockup"></div> Notifications`;
list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12";
list.searchRowActions = {
add: {
label: 'Add Notification',
mode: 'all', // One of: edit, select, all
ngClick: 'addNotificationTemplate()',
awToolTip: 'Create a new notification template',
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD NOTIFICATION TEMPLATE'
}
};
view.inject( list, {
mode: 'edit',
cancelButton: true,
scope: scope
});
function init() {
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
NotificationsListInit({
scope: scope,
url: defaultUrl,
id: id
});
NotificationsListInit({
scope: $scope,
url: defaultUrl,
id: id
});
scope.formCancel = function() {
$state.go('managementJobsList');
};
$scope.$watch(`${list.iterator}_dataset`, function() {
// The list data has changed and we need to update which notifications are on/off
$scope.$emit('relatednotifications');
});
}
scope.toggleNotification = function(event, notifier_id, column) {
$scope.toggleNotification = function(event, notifier_id, column) {
var notifier = this.notification;
try {
$(event.target).tooltip('hide');
@ -62,60 +40,15 @@ export default
// ignore
}
ToggleNotification({
scope: scope,
url: defaultUrl,
id: id,
scope: $scope,
url: defaultUrl + id,
notifier: notifier,
column: column,
callback: 'NotificationRefresh'
});
};
if (scope.removePostRefresh) {
scope.removePostRefresh();
}
scope.removePostRefresh = scope.$on('PostRefresh', function () {
scope.$emit('relatednotifications');
});
if (scope.removeChoicesHere) {
scope.removeChoicesHere();
}
scope.removeChoicesHere = scope.$on('choicesReadyNotifierList', function () {
list.fields.notification_type.searchOptions = scope.notification_type_options;
// @issue: OLD SEARCH
// SearchInit({
// scope: scope,
// set: 'notifications',
// list: list,
// url: url
// });
if ($rootScope.addedItem) {
scope.addedItem = $rootScope.addedItem;
delete $rootScope.addedItem;
}
// @issue: OLD SEARCH
// PaginateInit({
// scope: scope,
// list: list,
// url: url
// });
//
// scope.search(list.iterator);
});
GetChoices({
scope: scope,
url: url,
field: 'notification_type',
variable: 'notification_type_options',
callback: 'choicesReadyNotifierList'
});
init();
}
];

View File

@ -4,43 +4,40 @@
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'managementJobsList.notifications',
route: '/:management_id/notifications',
templateUrl: templateUrl('management-jobs/notifications/notifications'),
controller: 'managementJobsNotificationsController',
params: {card: null},
resolve: {
management_job:
[ '$stateParams',
'$q',
'Rest',
'GetBasePath',
'ProcessErrors',
function($stateParams, $q, rest, getBasePath, ProcessErrors) {
if ($stateParams.card) {
return $q.when($stateParams.card);
}
var managementJobId = $stateParams.management_id;
var url = getBasePath('system_job_templates') + managementJobId + '/';
rest.setUrl(url);
return rest.get()
.then(function(data) {
return data.data;
}).catch(function (response) {
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get management job info. GET returned status: ' +
response.status
});
});
params: {
notification_search: {}
},
searchPrefix: 'notification',
views: {
'@managementJobsList': {
controller: 'managementJobsNotificationsController',
templateProvider: function(NotificationsList, generateList, ParentObject) {
// include name of parent resource in listTitle
NotificationsList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Notifications`;
let html = generateList.build({
list: NotificationsList,
mode: 'edit'
});
html = generateList.wrapPanel(html);
return generateList.insertFormView() + html;
}
]
}
},
resolve: {
Dataset: ['NotificationsList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) {
let path = `${GetBasePath('notification_templates')}`;
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}
],
ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath) {
let path = `${GetBasePath('system_job_templates')}${$stateParams.management_id}`;
Rest.setUrl(path);
return Rest.get(path).then((res) => res.data);
}]
},
ncyBreadcrumb: {
parent: 'managementJobsList',

View File

@ -29,34 +29,38 @@
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
GetChoices({
scope: $scope,
url: defaultUrl,
field: 'notification_type',
variable: 'notification_type_options',
callback: 'choicesReadyNotifierList'
});
}
$scope.removeChoicesHere = $scope.$on('choicesReadyNotifierList', function() {
list.fields.notification_type.searchOptions = $scope.notification_type_options;
if ($rootScope.addedItem) {
$scope.addedItem = $rootScope.addedItem;
delete $rootScope.addedItem;
}
$scope.notification_templates.forEach(function(notification_template, i) {
setStatus(notification_template);
$scope.notification_type_options.forEach(function(type) {
if (type.value === notification_template.notification_type) {
$scope.notification_templates[i].notification_type = type.label;
var recent_notifications = notification_template.summary_fields.recent_notifications;
$scope.notification_templates[i].status = recent_notifications && recent_notifications.length > 0 ? recent_notifications[0].status : "none";
}
});
$scope.$on(`notification_template_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
});
$scope.$watchCollection("notification_templates", function() {
optionsRequestDataProcessing();
}
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched.
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
// Set the item type label
if (list.fields.notification_type && $scope.options &&
$scope.options.hasOwnProperty('notification_type')) {
$scope.options.notification_type.choices.every(function(choice) {
if (choice[0] === item.notification_type) {
itm.type_label = choice[1];
var recent_notifications = itm.summary_fields.recent_notifications;
itm.status = recent_notifications && recent_notifications.length > 0 ? recent_notifications[0].status : "none";
return false;
}
return true;
});
}
setStatus(itm);
});
}
function setStatus(notification_template) {
var html, recent_notifications = notification_template.summary_fields.recent_notifications;

View File

@ -36,6 +36,7 @@ export default ['i18n', function(i18n){
},
notification_type: {
label: i18n._('Type'),
ngBind: "notification_template.type_label",
searchType: 'select',
searchOptions: [],
excludeModal: true,
@ -47,7 +48,7 @@ export default ['i18n', function(i18n){
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addNotification()',
awToolTip: i18n._('Create a new custom inventory'),
awToolTip: i18n._('Create a new notification template'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ' + i18n._('ADD'),
ngShow: 'canAdd'

View File

@ -12,9 +12,9 @@
*/
export default ['$scope', '$rootScope', 'ProcessErrors', 'GetBasePath',
'SelectionInit', 'templateUrl', '$state', 'Rest', '$q', 'Wait',
'SelectionInit', 'templateUrl', '$state', 'Rest', '$q', 'Wait', '$window',
function($scope, $rootScope, ProcessErrors, GetBasePath,
SelectionInit, templateUrl, $state, Rest, $q, Wait) {
SelectionInit, templateUrl, $state, Rest, $q, Wait, $window) {
$scope.$on("linkLists", function() {
if ($state.current.name.split(".")[1] === "users") {
@ -32,8 +32,15 @@ function($scope, $rootScope, ProcessErrors, GetBasePath,
$scope.add_users = $scope.$parent.add_user_dataset.results;
$scope.selectedItems = [];
$scope.$on('selectedOrDeselected', ()=>{
throw {name: 'NotYetImplemented'};
$scope.$on('selectedOrDeselected', function(e, value) {
let item = value.value;
if (item.isSelected) {
$scope.selectedItems.push(item.id);
}
else {
$scope.selectedItems = _.remove($scope.selectedItems, { id: item.id });
}
});
}
@ -42,7 +49,7 @@ function($scope, $rootScope, ProcessErrors, GetBasePath,
var url, listToClose,
payloads = $scope.selectedItems.map(function(val) {
return {id: val.id};
return {id: val};
});
url = $scope.$parent.orgRelatedUrls[$scope.addUsersType];
@ -69,5 +76,11 @@ function($scope, $rootScope, ProcessErrors, GetBasePath,
});
});
};
$scope.linkoutUser = function(userId) {
// Open the edit user form in a new tab so as not to navigate the user
// away from the modal
$window.open('/#/users/' + userId,'_blank');
};
});
}];

View File

@ -37,7 +37,7 @@ export default ['$stateParams', '$scope', 'UserList', 'Rest', '$state',
}
$scope.addUsers = function() {
$compile("<add-users add-users-type='admin' class='AddUsers'></add-users>")($scope);
$compile("<add-users add-users-type='admins' class='AddUsers'></add-users>")($scope);
};
$scope.editUser = function(id) {

View File

@ -37,7 +37,7 @@ export default ['$stateParams', '$scope', 'OrgUserList', 'AddUserList','Rest', '
}
$scope.addUsers = function() {
$compile("<add-users add-users-type='user' class='AddUsers'></add-users>")($scope);
$compile("<add-users add-users-type='users' class='AddUsers'></add-users>")($scope);
};
$scope.editUser = function(id) {

View File

@ -99,6 +99,7 @@ export default [{
list.iterator = 'add_user';
list.name = 'add_users';
list.multiSelect = true;
list.fields.username.ngClick = 'linkoutUser(add_user.id)';
delete list.actions;
delete list.fieldActions;
return list;
@ -386,6 +387,7 @@ export default [{
}
};
list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12";
list.listTitle = 'Admins';
return list;
}],
AddAdminList: ['UserList', function(UserList) {
@ -394,6 +396,7 @@ export default [{
list.iterator = 'add_user';
list.name = 'add_users';
list.multiSelect = true;
list.fields.username.ngClick = 'linkoutUser(add_user.id)';
delete list.actions;
delete list.fieldActions;
return list;

View File

@ -45,7 +45,19 @@ export default
let path = `${GetBasePath('job_templates')}${$stateParams.id}`;
Rest.setUrl(path);
return Rest.get(path).then((res) => res.data);
}]
}],
UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
function(Rest, GetBasePath, $stateParams, $q) {
Rest.setUrl(GetBasePath('unified_jobs'));
var val = $q.defer();
Rest.options()
.then(function(data) {
val.resolve(data.data);
}, function(data) {
val.reject(data);
});
return val.promise;
}]
},
views: {
'@': {
@ -119,7 +131,19 @@ export default
let path = `${GetBasePath('workflow_job_templates')}${$stateParams.id}`;
Rest.setUrl(path);
return Rest.get(path).then((res) => res.data);
}]
}],
UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
function(Rest, GetBasePath, $stateParams, $q) {
Rest.setUrl(GetBasePath('unified_jobs'));
var val = $q.defer();
Rest.options()
.then(function(data) {
val.resolve(data.data);
}, function(data) {
val.reject(data);
});
return val.promise;
}]
},
views: {
'@': {
@ -190,7 +214,19 @@ export default
let path = `${GetBasePath('projects')}${$stateParams.id}`;
Rest.setUrl(path);
return Rest.get(path).then((res) => res.data);
}]
}],
UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
function(Rest, GetBasePath, $stateParams, $q) {
Rest.setUrl(GetBasePath('unified_jobs'));
var val = $q.defer();
Rest.options()
.then(function(data) {
val.resolve(data.data);
}, function(data) {
val.reject(data);
});
return val.promise;
}]
},
views: {
'@': {
@ -268,6 +304,18 @@ export default
}
],
ParentObject: [() =>{return {endpoint:'/api/v1/schedules'}; }],
UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
function(Rest, GetBasePath, $stateParams, $q) {
Rest.setUrl(GetBasePath('unified_jobs'));
var val = $q.defer();
Rest.options()
.then(function(data) {
val.resolve(data.data);
}, function(data) {
val.reject(data);
});
return val.promise;
}]
},
views: {
'list@jobs': {

View File

@ -14,12 +14,12 @@
export default [
'$scope', '$compile', '$location', '$stateParams', 'SchedulesList', 'Rest',
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Wait', 'rbacUiControlService',
'Find', 'ToggleSchedule', 'DeleteSchedule', 'GetChoices', '$q', '$state', 'Dataset', 'ParentObject',
'Find', 'ToggleSchedule', 'DeleteSchedule', 'GetChoices', '$q', '$state', 'Dataset', 'ParentObject', 'UnifiedJobsOptions',
function($scope, $compile, $location, $stateParams,
SchedulesList, Rest, ProcessErrors, ReturnToCaller, ClearScope,
GetBasePath, Wait, rbacUiControlService, Find,
ToggleSchedule, DeleteSchedule, GetChoices,
$q, $state, Dataset, ParentObject) {
$q, $state, Dataset, ParentObject, UnifiedJobsOptions) {
ClearScope();
@ -43,11 +43,45 @@ export default [
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$scope.unified_job_options = UnifiedJobsOptions.actions.GET;
_.forEach($scope[list.name], buildTooltips);
// _.forEach($scope[list.name], buildTooltips);
}
$scope.$on(`${list.iterator}_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
$scope.$watchCollection(`${$scope.list.name}`, function() {
optionsRequestDataProcessing();
}
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
// Set the item type label
if (list.fields.type && $scope.unified_job_options &&
$scope.unified_job_options.hasOwnProperty('type')) {
$scope.unified_job_options.type.choices.every(function(choice) {
if (choice[0] === itm.summary_fields.unified_job_template.unified_job_type) {
itm.type_label = choice[1];
return false;
}
return true;
});
}
buildTooltips(itm);
});
}
function buildTooltips(schedule) {
var job = schedule.summary_fields.unified_job_template;
if (schedule.enabled) {
schedule.play_tip = 'Schedule is active. Click to stop.';
schedule.status = 'active';
@ -57,6 +91,18 @@ export default [
schedule.status = 'stopped';
schedule.status_tip = 'Schedule is stopped. Click to activate.';
}
schedule.nameTip = schedule.name;
// include the word schedule if the schedule name does not include the word schedule
if (schedule.name.indexOf("schedule") === -1 && schedule.name.indexOf("Schedule") === -1) {
schedule.nameTip += " schedule";
}
schedule.nameTip += " for ";
if (job.name.indexOf("job") === -1 && job.name.indexOf("Job") === -1) {
schedule.nameTip += "job ";
}
schedule.nameTip += job.name;
schedule.nameTip += ". Click to edit schedule.";
}
$scope.refreshSchedules = function() {
@ -99,7 +145,7 @@ export default [
name: 'projectSchedules.edit',
params: {
id: schedule.unified_job_template,
schedule_id: schedule.id
schedule_id: schedule.id
}
});
break;
@ -109,7 +155,7 @@ export default [
name: 'managementJobSchedules.edit',
params: {
id: schedule.unified_job_template,
schedule_id: schedule.id
schedule_id: schedule.id
}
});
break;
@ -136,7 +182,7 @@ export default [
throw err;
}
});
});
});
}
};
@ -160,7 +206,7 @@ export default [
};
base = $location.path().replace(/^\//, '').split('/')[0];
if (base === 'management_jobs') {
$scope.base = base = 'system_job_templates';
}
@ -175,17 +221,5 @@ export default [
$scope.formCancel = function() {
$state.go('^', null, { reload: true });
};
// @issue - believe this is no longer necessary now that parent object is resolved prior to controller initilizing
// Wait('start');
// GetChoices({
// scope: $scope,
// url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json'
// field: 'type',
// variable: 'type_choices',
// callback: 'choicesReady'
// });
}
];

View File

@ -1133,4 +1133,22 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
});
}
};
}])
.directive('awPasswordToggle', [function() {
return {
restrict: 'A',
link: function(scope, element) {
$(element).click(function() {
var buttonInnerHTML = $(element).html();
if (buttonInnerHTML.indexOf("Show") > -1) {
$(element).html("Hide");
$(element).closest('.input-group').find('input').first().attr("type", "text");
} else {
$(element).html("Show");
$(element).closest('.input-group').find('input').first().attr("type", "password");
}
});
}
};
}]);

View File

@ -846,28 +846,15 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "\t" + label();
if (field.hasShowInputButton) {
var tooltip = i18n._("Toggle the display of plaintext.");
field.toggleInput = function(id) {
var buttonId = id + "_show_input_button",
inputId = id + "_input",
buttonInnerHTML = $(buttonId).html();
if (buttonInnerHTML.indexOf("Show") > -1) {
$(buttonId).html("Hide");
$(inputId).attr("type", "text");
} else {
$(buttonId).html("Show");
$(inputId).attr("type", "password");
}
};
html += "\<div class='input-group";
html += (horizontal) ? " " + getFieldWidth() : "";
html += "'>\n";
// TODO: make it so that the button won't show up if the mode is edit, hasShowInputButton !== true, and there are no contents in the field.
html += "<span class='input-group-btn'>\n";
html += "<button type='button' class='btn btn-default show_input_button Form-passwordButton' ";
html += "<button aw-password-toggle type='button' class='btn btn-default show_input_button Form-passwordButton' ";
html += buildId(field, fld + "_show_input_button", this.form);
html += `aw-tool-tip='${tooltip}'} aw-tip-placement='top'`;
html += "tabindex='-1' ";
html += "ng-click='" + fld + "_field.toggleInput(\"#" + this.form.name + "_" + fld + "\")'";
html += (field.ngDisabled) ? "ng-disabled='" + field.ngDisabled + "'" : "";
html += ">\n" + field.showInputInnerHTML;
html += "\n</button>\n";

View File

@ -18,13 +18,23 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
// grab a single model from the cache, if present
if (cache.get(path)) {
defer.resolve({[name] : new DjangoSearchModel(name, path, cache.get(path), relations)});
defer.resolve({
models: {
[name] : new DjangoSearchModel(name, path, cache.get(path), relations)
},
options: cache.get(path)
});
} else {
this.url = path;
resolve = this.options(path)
.then((res) => {
base = res.data.actions.GET;
defer.resolve({[name]: new DjangoSearchModel(name, path, base, relations)});
defer.resolve({
models: {
[name]: new DjangoSearchModel(name, path, base, relations)
},
options: res
});
});
}
return defer.promise;
@ -76,9 +86,9 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
if (Array.isArray(value)){
return _.map(value, (item) => {
return `${key.split('__').join(':')}:${item}`;
});
});
}
else {
else {
return `${key.split('__').join(':')}:${value}`;
}
},

View File

@ -15,8 +15,9 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
path = GetBasePath($scope.basePath) || $scope.basePath;
relations = getRelationshipFields($scope.dataset.results);
$scope.searchTags = stripDefaultParams($state.params[`${$scope.iterator}_search`]);
qs.initFieldset(path, $scope.djangoModel, relations).then((models) => {
$scope.models = models;
qs.initFieldset(path, $scope.djangoModel, relations).then((data) => {
$scope.models = data.models;
$scope.$emit(`${$scope.list.iterator}_options`, data.options);
});
}
@ -102,12 +103,12 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
params.page = '1';
queryset = _.merge(queryset, params, (objectValue, sourceValue, key, object) => {
if (object[key] && object[key] !== sourceValue){
return [object[key], sourceValue];
return [object[key], sourceValue];
}
else {
// // https://lodash.com/docs/3.10.1#merge
// If customizer fn returns undefined merging is handled by default _.merge algorithm
return undefined;
return undefined;
}
});
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic

View File

@ -31,7 +31,7 @@ export default function($stateProvider) {
order_by: "name"
},
dynamic: true,
squash: true
squash: ''
}
}
};

View File

@ -4,10 +4,11 @@
* All Rights Reserved
*************************************************/
export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Alert',
'TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath',
'InitiatePlaybookRun', 'Wait', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'TemplatesService',
'QuerySet', 'GetChoices', 'TemplateCopyService',
export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest',
'Alert','TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors',
'GetBasePath', 'InitiatePlaybookRun', 'Wait', '$state', '$filter',
'Dataset', 'rbacUiControlService', 'TemplatesService','QuerySet',
'GetChoices', 'TemplateCopyService',
function(
$scope, $rootScope, $location, $stateParams, Rest, Alert,
TemplateList, Prompt, ClearScope, ProcessErrors, GetBasePath,
@ -36,38 +37,40 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Al
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$scope.options = {};
$rootScope.flashMessage = null;
}
if ($scope.removeChoicesReady) {
$scope.removeChoicesReady();
$scope.$on(`${list.iterator}_options`, function(event, data){
$scope.options = data.data.actions.GET;
optionsRequestDataProcessing();
});
$scope.$watchCollection('templates', function() {
optionsRequestDataProcessing();
}
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
);
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
$scope[list.name].forEach(function(item, item_idx) {
var itm = $scope[list.name][item_idx];
// Set the item type label
if (list.fields.type) {
$scope.type_choices.every(function(choice) {
if (choice.value === item.type) {
itm.type_label = choice.label;
return false;
}
return true;
});
}
});
});
GetChoices({
scope: $scope,
url: GetBasePath('unified_job_templates'),
field: 'type',
variable: 'type_choices',
callback: 'choicesReady'
// Set the item type label
if (list.fields.type && $scope.options.hasOwnProperty('type')) {
$scope.options.type.choices.every(function(choice) {
if (choice[0] === item.type) {
itm.type_label = choice[1];
return false;
}
return true;
});
}
});
}
$scope.$on(`ws-jobs`, function () {
// @issue - this is no longer quite as ham-fisted but I'd like for someone else to take a peek
// calling $state.reload(); here was problematic when launching a job because job launch also

View File

@ -115,7 +115,7 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA
},
'jobTemplateList@templates.editWorkflowJobTemplate.workflowMaker': {
templateProvider: function(WorkflowMakerJobTemplateList, generateList) {
//debugger;
let html = generateList.build({
list: WorkflowMakerJobTemplateList,
input_type: 'radio',

View File

@ -183,6 +183,7 @@
<div class="StandardOut-panelHeaderText">
<i class="WorkflowResults-statusResultIcon
fa icon-job-{{ workflow.status }}"
ng-show="stdoutFullScreen"
aw-tool-tip="Job {{status_label}}"
aw-tip-placement="top"
data-original-title>

View File

@ -12,7 +12,6 @@ module.exports = {
]
},
options: {
open: 'external',
proxy: {
target: `https://${django_host}:${django_port}`,
ws: true

View File

@ -34,15 +34,15 @@ msgstr ""
#: client/src/forms/Inventories.js:153
#: client/src/forms/JobTemplates.js:414
#: client/src/forms/Organizations.js:75
#: client/src/forms/Projects.js:238
#: client/src/forms/Projects.js:237
#: client/src/forms/Teams.js:86
#: client/src/forms/Workflows.js:126
#: client/src/forms/Workflows.js:127
#: client/src/inventory-scripts/inventory-scripts.list.js:45
#: client/src/lists/Credentials.js:59
#: client/src/lists/Inventories.js:68
#: client/src/lists/Projects.js:67
#: client/src/lists/Teams.js:50
#: client/src/lists/Templates.js:61
#: client/src/lists/Templates.js:62
#: client/src/lists/Users.js:58
#: client/src/notifications/notificationTemplates.list.js:52
msgid "ADD"
@ -81,7 +81,7 @@ msgid "Account Token"
msgstr ""
#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:20
#: client/src/shared/list-generator/list-generator.factory.js:502
#: client/src/shared/list-generator/list-generator.factory.js:538
msgid "Actions"
msgstr ""
@ -94,7 +94,7 @@ msgstr ""
#: client/src/forms/Inventories.js:150
#: client/src/forms/Organizations.js:72
#: client/src/forms/Teams.js:83
#: client/src/forms/Workflows.js:123
#: client/src/forms/Workflows.js:124
msgid "Add"
msgstr ""
@ -119,8 +119,8 @@ msgid "Add Project"
msgstr ""
#: client/src/forms/JobTemplates.js:459
#: client/src/forms/Workflows.js:171
#: client/src/shared/form-generator.js:1703
#: client/src/forms/Workflows.js:172
#: client/src/shared/form-generator.js:1707
msgid "Add Survey"
msgstr ""
@ -136,7 +136,7 @@ msgstr ""
#: client/src/forms/Inventories.js:151
#: client/src/forms/JobTemplates.js:412
#: client/src/forms/Organizations.js:73
#: client/src/forms/Projects.js:236
#: client/src/forms/Projects.js:235
msgid "Add a permission"
msgstr ""
@ -219,6 +219,10 @@ msgstr ""
msgid "Authorize Password"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:101
msgid "Azure AD"
msgstr ""
#: client/src/forms/Projects.js:80
msgid "Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. Together the base path and selected playbook directory provide the full path used to locate playbooks."
msgstr ""
@ -235,11 +239,11 @@ msgstr ""
msgid "CREDENTIALS"
msgstr ""
#: client/src/forms/Projects.js:195
#: client/src/forms/Projects.js:194
msgid "Cache Timeout"
msgstr ""
#: client/src/forms/Projects.js:184
#: client/src/forms/Projects.js:183
msgid "Cache Timeout%s (seconds)%s"
msgstr ""
@ -265,7 +269,8 @@ msgstr ""
msgid "Call to get project failed. GET status:"
msgstr ""
#: client/src/shared/form-generator.js:1691
#: client/src/configuration/configuration.controller.js:414
#: client/src/shared/form-generator.js:1695
msgid "Cancel"
msgstr ""
@ -281,6 +286,10 @@ msgstr ""
msgid "Canceled. Click for details"
msgstr ""
#: client/src/forms/Projects.js:82
msgid "Change %s under \"Configure Tower\" to change this location."
msgstr ""
#: client/src/shared/form-generator.js:1084
msgid "Choose a %s"
msgstr ""
@ -289,7 +298,7 @@ msgstr ""
msgid "Choose your license file, agree to the End User License Agreement, and click submit."
msgstr ""
#: client/src/forms/Projects.js:152
#: client/src/forms/Projects.js:151
msgid "Clean"
msgstr ""
@ -317,7 +326,7 @@ msgstr ""
msgid "Client Secret"
msgstr ""
#: client/src/shared/form-generator.js:1695
#: client/src/shared/form-generator.js:1699
msgid "Close"
msgstr ""
@ -346,6 +355,14 @@ msgstr ""
msgid "Confirm Password"
msgstr ""
#: client/src/configuration/configuration.controller.js:421
msgid "Confirm Reset"
msgstr ""
#: client/src/configuration/configuration.controller.js:430
msgid "Confirm factory reset"
msgstr ""
#: client/src/forms/JobTemplates.js:255
#: client/src/forms/JobTemplates.js:273
#: client/src/forms/WorkflowMaker.js:141
@ -357,11 +374,11 @@ msgstr ""
msgid "Control the level of output ansible will produce as the playbook executes."
msgstr ""
#: client/src/lists/Templates.js:99
#: client/src/lists/Templates.js:100
msgid "Copy"
msgstr ""
#: client/src/lists/Templates.js:102
#: client/src/lists/Templates.js:103
msgid "Copy template"
msgstr ""
@ -402,7 +419,7 @@ msgstr ""
msgid "Create a new team"
msgstr ""
#: client/src/lists/Templates.js:59
#: client/src/lists/Templates.js:60
msgid "Create a new template"
msgstr ""
@ -435,7 +452,7 @@ msgstr ""
msgid "Custom Script"
msgstr ""
#: client/src/app.js:405
#: client/src/app.js:409
msgid "DASHBOARD"
msgstr ""
@ -451,7 +468,7 @@ msgstr ""
#: client/src/lists/Credentials.js:90
#: client/src/lists/Inventories.js:92
#: client/src/lists/Teams.js:77
#: client/src/lists/Templates.js:123
#: client/src/lists/Templates.js:125
#: client/src/lists/Users.js:87
#: client/src/notifications/notificationTemplates.list.js:89
msgid "Delete"
@ -473,7 +490,7 @@ msgstr ""
msgid "Delete notification"
msgstr ""
#: client/src/forms/Projects.js:162
#: client/src/forms/Projects.js:161
msgid "Delete on Update"
msgstr ""
@ -481,7 +498,7 @@ msgstr ""
msgid "Delete team"
msgstr ""
#: client/src/lists/Templates.js:126
#: client/src/lists/Templates.js:128
msgid "Delete template"
msgstr ""
@ -489,7 +506,7 @@ msgstr ""
msgid "Delete the job"
msgstr ""
#: client/src/forms/Projects.js:164
#: client/src/forms/Projects.js:163
msgid "Delete the local repository in its entirety prior to performing an update."
msgstr ""
@ -505,7 +522,7 @@ msgstr ""
msgid "Delete user"
msgstr ""
#: client/src/forms/Projects.js:164
#: client/src/forms/Projects.js:163
msgid "Depending on the size of the repository this may significantly increase the amount of time required to complete an update."
msgstr ""
@ -517,7 +534,7 @@ msgstr ""
#: client/src/forms/Teams.js:34
#: client/src/forms/Users.js:142
#: client/src/forms/Users.js:167
#: client/src/forms/Workflows.js:40
#: client/src/forms/Workflows.js:41
#: client/src/inventory-scripts/inventory-scripts.form.js:32
#: client/src/inventory-scripts/inventory-scripts.list.js:25
#: client/src/lists/Credentials.js:34
@ -550,6 +567,12 @@ msgstr ""
msgid "Details"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:70
#: client/src/configuration/configuration.controller.js:159
#: client/src/configuration/configuration.controller.js:212
msgid "Discard changes"
msgstr ""
#: client/src/forms/Teams.js:148
msgid "Dissasociate permission from team"
msgstr ""
@ -567,7 +590,7 @@ msgstr ""
msgid "Drag and drop your custom inventory script file here or create one in the field to import your custom inventory."
msgstr ""
#: client/src/forms/Projects.js:175
#: client/src/forms/Projects.js:174
msgid "Each time a job runs using this project, perform an update to the local repository prior to starting the job."
msgstr ""
@ -576,7 +599,7 @@ msgstr ""
#: client/src/lists/Credentials.js:71
#: client/src/lists/Inventories.js:78
#: client/src/lists/Teams.js:60
#: client/src/lists/Templates.js:107
#: client/src/lists/Templates.js:108
#: client/src/lists/Users.js:68
#: client/src/notifications/notificationTemplates.list.js:63
#: client/src/notifications/notificationTemplates.list.js:72
@ -584,8 +607,8 @@ msgid "Edit"
msgstr ""
#: client/src/forms/JobTemplates.js:466
#: client/src/forms/Workflows.js:178
#: client/src/shared/form-generator.js:1707
#: client/src/forms/Workflows.js:179
#: client/src/shared/form-generator.js:1711
msgid "Edit Survey"
msgstr ""
@ -613,7 +636,7 @@ msgstr ""
msgid "Edit team"
msgstr ""
#: client/src/lists/Templates.js:109
#: client/src/lists/Templates.js:110
msgid "Edit template"
msgstr ""
@ -646,7 +669,7 @@ msgstr ""
msgid "Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update using this job template."
msgstr ""
#: client/src/helpers/Credentials.js:308
#: client/src/helpers/Credentials.js:306
msgid "Encrypted credentials are not supported."
msgstr ""
@ -670,6 +693,10 @@ msgstr ""
msgid "Enter the hostname or IP address which corresponds to your VMware vCenter."
msgstr ""
#: client/src/configuration/configuration.controller.js:272
#: client/src/configuration/configuration.controller.js:350
#: client/src/configuration/configuration.controller.js:384
#: client/src/configuration/configuration.controller.js:403
#: client/src/controllers/Projects.js:133
#: client/src/controllers/Projects.js:155
#: client/src/controllers/Projects.js:180
@ -684,8 +711,8 @@ msgstr ""
#: client/src/controllers/Users.js:267
#: client/src/controllers/Users.js:321
#: client/src/controllers/Users.js:94
#: client/src/helpers/Credentials.js:312
#: client/src/helpers/Credentials.js:328
#: client/src/helpers/Credentials.js:310
#: client/src/helpers/Credentials.js:326
#: client/src/login/loginModal/thirdPartySignOn/thirdPartySignOn.service.js:119
msgid "Error!"
msgstr ""
@ -711,8 +738,8 @@ msgstr ""
#: client/src/forms/JobTemplates.js:352
#: client/src/forms/JobTemplates.js:364
#: client/src/forms/Workflows.js:71
#: client/src/forms/Workflows.js:83
#: client/src/forms/Workflows.js:72
#: client/src/forms/Workflows.js:84
msgid "Extra Variables"
msgstr ""
@ -732,7 +759,7 @@ msgstr ""
msgid "Failed to add new user. POST returned status:"
msgstr ""
#: client/src/helpers/Credentials.js:313
#: client/src/helpers/Credentials.js:311
msgid "Failed to create new Credential. POST status:"
msgstr ""
@ -753,7 +780,15 @@ msgstr ""
msgid "Failed to retrieve user: %s. GET status:"
msgstr ""
#: client/src/helpers/Credentials.js:329
#: client/src/configuration/configuration.controller.js:351
msgid "Failed to save settings. Returned status:"
msgstr ""
#: client/src/configuration/configuration.controller.js:385
msgid "Failed to save toggle settings. Returned status:"
msgstr ""
#: client/src/helpers/Credentials.js:327
msgid "Failed to update Credential. PUT status:"
msgstr ""
@ -802,6 +837,22 @@ msgstr ""
msgid "Forks"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:102
msgid "Github"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:103
msgid "Github Org"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:104
msgid "Github Team"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:105
msgid "Google OAuth2"
msgstr ""
#: client/src/forms/Teams.js:118
msgid "Granted Permissions"
msgstr ""
@ -949,7 +1000,7 @@ msgstr ""
msgid "JOB TEMPLATE"
msgstr ""
#: client/src/app.js:425
#: client/src/app.js:429
#: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:113
#: client/src/main-menu/main-menu.partial.html:122
#: client/src/main-menu/main-menu.partial.html:43
@ -963,7 +1014,7 @@ msgstr ""
msgid "Job Tags"
msgstr ""
#: client/src/lists/Templates.js:64
#: client/src/lists/Templates.js:65
msgid "Job Template"
msgstr ""
@ -986,6 +1037,10 @@ msgstr ""
msgid "Jobs"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:106
msgid "LDAP"
msgstr ""
#: client/src/main-menu/main-menu.partial.html:83
msgid "LOG OUT"
msgstr ""
@ -996,8 +1051,8 @@ msgstr ""
#: client/src/forms/JobTemplates.js:340
#: client/src/forms/JobTemplates.js:345
#: client/src/forms/Workflows.js:59
#: client/src/forms/Workflows.js:64
#: client/src/forms/Workflows.js:60
#: client/src/forms/Workflows.js:65
#: client/src/lists/Templates.js:47
msgid "Labels"
msgstr ""
@ -1012,8 +1067,8 @@ msgid "Last Updated"
msgstr ""
#: client/src/lists/PortalJobTemplates.js:39
#: client/src/lists/Templates.js:83
#: client/src/shared/form-generator.js:1699
#: client/src/lists/Templates.js:84
#: client/src/shared/form-generator.js:1703
msgid "Launch"
msgstr ""
@ -1049,19 +1104,19 @@ msgstr ""
msgid "Limit"
msgstr ""
#: client/src/shared/socket/socket.service.js:176
#: client/src/shared/socket/socket.service.js:170
msgid "Live events: attempting to connect to the Tower server."
msgstr ""
#: client/src/shared/socket/socket.service.js:180
#: client/src/shared/socket/socket.service.js:174
msgid "Live events: connected. Pages containing job status information will automatically update in real-time."
msgstr ""
#: client/src/shared/socket/socket.service.js:184
#: client/src/shared/socket/socket.service.js:178
msgid "Live events: error connecting to the Tower server."
msgstr ""
#: client/src/shared/form-generator.js:1962
#: client/src/shared/form-generator.js:1977
msgid "Loading..."
msgstr ""
@ -1131,7 +1186,7 @@ msgstr ""
#: client/src/forms/Users.js:139
#: client/src/forms/Users.js:164
#: client/src/forms/Users.js:190
#: client/src/forms/Workflows.js:33
#: client/src/forms/Workflows.js:34
#: client/src/inventory-scripts/inventory-scripts.form.js:25
#: client/src/inventory-scripts/inventory-scripts.list.js:20
#: client/src/lists/CompletedJobs.js:43
@ -1195,7 +1250,7 @@ msgid "New User"
msgstr ""
#: client/src/forms/Workflows.js:19
msgid "New Workflow"
msgid "New Workflow Job Template"
msgstr ""
#: client/src/controllers/Users.js:174
@ -1293,7 +1348,7 @@ msgid "OpenStack domains define administrative boundaries. It is only needed for
msgstr ""
#: client/src/forms/JobTemplates.js:347
#: client/src/forms/Workflows.js:66
#: client/src/forms/Workflows.js:67
msgid "Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display."
msgstr ""
@ -1309,8 +1364,8 @@ msgstr ""
#: client/src/forms/Projects.js:49
#: client/src/forms/Teams.js:39
#: client/src/forms/Users.js:59
#: client/src/forms/Workflows.js:46
#: client/src/forms/Workflows.js:52
#: client/src/forms/Workflows.js:47
#: client/src/forms/Workflows.js:53
#: client/src/inventory-scripts/inventory-scripts.form.js:37
#: client/src/inventory-scripts/inventory-scripts.list.js:30
#: client/src/lists/Inventories.js:52
@ -1337,7 +1392,7 @@ msgid "PASSWORD"
msgstr ""
#: client/src/organizations/list/organizations-list.partial.html:44
#: client/src/shared/form-generator.js:1865
#: client/src/shared/form-generator.js:1880
#: client/src/shared/list-generator/list-generator.factory.js:245
msgid "PLEASE ADD ITEMS TO THIS LIST"
msgstr ""
@ -1356,7 +1411,7 @@ msgid "Pagerduty subdomain"
msgstr ""
#: client/src/forms/JobTemplates.js:358
#: client/src/forms/Workflows.js:77
#: client/src/forms/Workflows.js:78
msgid "Pass extra command line variables to the playbook. This is the %s or %s command line parameter for %s. Provide key/value pairs using either YAML or JSON."
msgstr ""
@ -1419,8 +1474,8 @@ msgstr ""
#: client/src/forms/Inventories.js:142
#: client/src/forms/JobTemplates.js:403
#: client/src/forms/Organizations.js:64
#: client/src/forms/Projects.js:228
#: client/src/forms/Workflows.js:115
#: client/src/forms/Projects.js:227
#: client/src/forms/Workflows.js:116
msgid "Permissions"
msgstr ""
@ -1493,9 +1548,9 @@ msgstr ""
#: client/src/forms/Inventories.js:91
#: client/src/forms/JobTemplates.js:396
#: client/src/forms/Organizations.js:57
#: client/src/forms/Projects.js:220
#: client/src/forms/Projects.js:219
#: client/src/forms/Teams.js:110
#: client/src/forms/Workflows.js:108
#: client/src/forms/Workflows.js:109
msgid "Please save before assigning permissions"
msgstr ""
@ -1508,7 +1563,7 @@ msgstr ""
msgid "Please save before assigning to teams"
msgstr ""
#: client/src/forms/Workflows.js:184
#: client/src/forms/Workflows.js:185
msgid "Please save before defining the workflow graph"
msgstr ""
@ -1593,7 +1648,7 @@ msgstr ""
msgid "Project Name"
msgstr ""
#: client/src/forms/Projects.js:101
#: client/src/forms/Projects.js:100
msgid "Project Path"
msgstr ""
@ -1638,6 +1693,10 @@ msgstr ""
msgid "Provisioning Callback URL"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:107
msgid "RADIUS"
msgstr ""
#: client/src/dashboard/lists/jobs/jobs-list.partial.html:4
msgid "RECENT JOB RUNS"
msgstr ""
@ -1684,7 +1743,7 @@ msgstr ""
msgid "Remove"
msgstr ""
#: client/src/forms/Projects.js:154
#: client/src/forms/Projects.js:153
msgid "Remove any local modifications prior to performing an update."
msgstr ""
@ -1692,6 +1751,20 @@ msgstr ""
msgid "Request License"
msgstr ""
#: client/src/configuration/auth-form/sub-forms/auth-azure.form.js:41
#: client/src/configuration/auth-form/sub-forms/auth-github-org.form.js:31
#: client/src/configuration/auth-form/sub-forms/auth-github-team.form.js:31
#: client/src/configuration/auth-form/sub-forms/auth-github.form.js:27
#: client/src/configuration/auth-form/sub-forms/auth-google-oauth2.form.js:39
#: client/src/configuration/auth-form/sub-forms/auth-ldap.form.js:87
#: client/src/configuration/auth-form/sub-forms/auth-radius.form.js:32
#: client/src/configuration/auth-form/sub-forms/auth-saml.form.js:59
#: client/src/configuration/jobs-form/configuration-jobs.form.js:55
#: client/src/configuration/system-form/configuration-system.form.js:41
#: client/src/configuration/ui-form/configuration-ui.form.js:35
msgid "Reset All"
msgstr ""
#: client/src/lists/Projects.js:42
msgid "Revision"
msgstr ""
@ -1704,26 +1777,30 @@ msgstr ""
#: client/src/forms/Inventories.js:120
#: client/src/forms/Inventories.js:166
#: client/src/forms/Organizations.js:88
#: client/src/forms/Projects.js:250
#: client/src/forms/Projects.js:249
#: client/src/forms/Teams.js:137
#: client/src/forms/Teams.js:99
#: client/src/forms/Users.js:201
msgid "Role"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:108
msgid "SAML"
msgstr ""
#: client/src/controllers/Projects.js:657
msgid "SCM Branch"
msgstr ""
#: client/src/forms/Projects.js:155
#: client/src/forms/Projects.js:154
msgid "SCM Clean"
msgstr ""
#: client/src/forms/Projects.js:131
#: client/src/forms/Projects.js:130
msgid "SCM Credential"
msgstr ""
#: client/src/forms/Projects.js:166
#: client/src/forms/Projects.js:165
msgid "SCM Delete"
msgstr ""
@ -1736,7 +1813,7 @@ msgid "SCM Type"
msgstr ""
#: client/src/dashboard/graphs/dashboard-graphs.partial.html:49
#: client/src/forms/Projects.js:176
#: client/src/forms/Projects.js:175
msgid "SCM Update"
msgstr ""
@ -1744,7 +1821,7 @@ msgstr ""
msgid "SCM Update Cancel"
msgstr ""
#: client/src/forms/Projects.js:146
#: client/src/forms/Projects.js:145
msgid "SCM Update Options"
msgstr ""
@ -1765,7 +1842,7 @@ msgstr ""
msgid "SIGN IN WITH"
msgstr ""
#: client/src/app.js:509
#: client/src/app.js:513
msgid "SOCKETS"
msgstr ""
@ -1798,11 +1875,17 @@ msgstr ""
msgid "Save"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:81
#: client/src/configuration/configuration.controller.js:170
#: client/src/configuration/configuration.controller.js:220
msgid "Save changes"
msgstr ""
#: client/src/license/license.partial.html:122
msgid "Save successful!"
msgstr ""
#: client/src/lists/Templates.js:91
#: client/src/lists/Templates.js:92
msgid "Schedule"
msgstr ""
@ -1814,7 +1897,7 @@ msgstr ""
msgid "Schedule future SCM updates"
msgstr ""
#: client/src/lists/Templates.js:94
#: client/src/lists/Templates.js:95
msgid "Schedule future job template runs"
msgstr ""
@ -1838,8 +1921,21 @@ msgstr ""
msgid "Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users."
msgstr ""
#: client/src/shared/form-generator.js:1691
msgid "Select"
msgstr ""
#: client/src/configuration/jobs-form/configuration-jobs.controller.js:87
#: client/src/configuration/ui-form/configuration-ui.controller.js:82
msgid "Select commands"
msgstr ""
#: client/src/forms/Projects.js:98
msgid "Select from the list of directories found in the base path.Together the base path and the playbook directory provide the full path used to locate playbooks."
msgid "Select from the list of directories found in the Project Base Path. Together the base path and the playbook directory provide the full path used to locate playbooks."
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:226
msgid "Select group types"
msgstr ""
#: client/src/forms/JobTemplates.js:152
@ -1945,7 +2041,7 @@ msgid "Split up your organization to associate content and control permissions f
msgstr ""
#: client/src/lists/PortalJobTemplates.js:42
#: client/src/lists/Templates.js:86
#: client/src/lists/Templates.js:87
msgid "Start a job using this template"
msgstr ""
@ -2017,7 +2113,7 @@ msgstr ""
#: client/src/forms/Inventories.js:126
#: client/src/forms/Inventories.js:173
#: client/src/forms/Organizations.js:95
#: client/src/forms/Projects.js:256
#: client/src/forms/Projects.js:255
msgid "Team Roles"
msgstr ""
@ -2093,6 +2189,14 @@ msgstr ""
msgid "There is no SCM update information available for this project. An update has not yet been completed. If you have not already done so, start an update for this project."
msgstr ""
#: client/src/configuration/configuration.controller.js:273
msgid "There was an error resetting value. Returned status:"
msgstr ""
#: client/src/configuration/configuration.controller.js:404
msgid "There was an error resetting values. Returned status:"
msgstr ""
#: client/src/helpers/Credentials.js:138
msgid "This is the tenant name. This value is usually the same as the username."
msgstr ""
@ -2114,6 +2218,10 @@ msgstr ""
msgid "This value does not match the password you entered previously. Please confirm that password."
msgstr ""
#: client/src/configuration/configuration.controller.js:429
msgid "This will reset all configuration values to their factory defaults. Are you sure you want to proceed?"
msgstr ""
#: client/src/dashboard/lists/jobs/jobs-list.partial.html:14
msgid "Time"
msgstr ""
@ -2122,7 +2230,7 @@ msgstr ""
msgid "Time Remaining"
msgstr ""
#: client/src/forms/Projects.js:192
#: client/src/forms/Projects.js:191
msgid "Time in seconds to consider a project to be current. During job runs and callbacks the task system will evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, and a new project update will be performed."
msgstr ""
@ -2192,7 +2300,7 @@ msgstr ""
msgid "Update in Progress"
msgstr ""
#: client/src/forms/Projects.js:173
#: client/src/forms/Projects.js:172
msgid "Update on Launch"
msgstr ""
@ -2200,11 +2308,6 @@ msgstr ""
msgid "Upgrade"
msgstr ""
#: client/src/forms/Projects.js:100
#: client/src/forms/Projects.js:82
msgid "Use %s in your environment settings file to determine the base path value."
msgstr ""
#: client/src/notifications/notificationTemplates.form.js:404
msgid "Use SSL"
msgstr ""
@ -2221,7 +2324,7 @@ msgstr ""
#: client/src/forms/Inventories.js:115
#: client/src/forms/Inventories.js:161
#: client/src/forms/Organizations.js:83
#: client/src/forms/Projects.js:245
#: client/src/forms/Projects.js:244
#: client/src/forms/Teams.js:94
msgid "User"
msgstr ""
@ -2291,7 +2394,7 @@ msgstr ""
#: client/src/lists/Credentials.js:80
#: client/src/lists/Inventories.js:85
#: client/src/lists/Teams.js:69
#: client/src/lists/Templates.js:115
#: client/src/lists/Templates.js:117
#: client/src/lists/Users.js:78
#: client/src/notifications/notificationTemplates.list.js:80
msgid "View"
@ -2306,8 +2409,8 @@ msgid "View JSON examples at %s"
msgstr ""
#: client/src/forms/JobTemplates.js:450
#: client/src/forms/Workflows.js:162
#: client/src/shared/form-generator.js:1711
#: client/src/forms/Workflows.js:163
#: client/src/shared/form-generator.js:1715
msgid "View Survey"
msgstr ""
@ -2347,7 +2450,7 @@ msgstr ""
msgid "View team"
msgstr ""
#: client/src/lists/Templates.js:117
#: client/src/lists/Templates.js:119
msgid "View template"
msgstr ""
@ -2363,6 +2466,16 @@ msgstr ""
msgid "View user"
msgstr ""
#: client/src/forms/Workflows.js:22
msgid "WORKFLOW"
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:68
#: client/src/configuration/configuration.controller.js:157
#: client/src/configuration/configuration.controller.js:210
msgid "Warning: Unsaved Changes"
msgstr ""
#: client/src/login/loginModal/loginModal.partial.html:17
msgid "Welcome to Ansible Tower! &nbsp;Please sign in."
msgstr ""
@ -2376,12 +2489,12 @@ msgstr ""
msgid "When this template is submitted as a job, setting the type to %s will execute the playbook, running tasks on the selected hosts."
msgstr ""
#: client/src/forms/Workflows.js:186
#: client/src/shared/form-generator.js:1715
#: client/src/forms/Workflows.js:187
#: client/src/shared/form-generator.js:1719
msgid "Workflow Editor"
msgstr ""
#: client/src/lists/Templates.js:69
#: client/src/lists/Templates.js:70
msgid "Workflow Job Template"
msgstr ""
@ -2397,6 +2510,12 @@ msgstr ""
msgid "You do not have permission to add a user."
msgstr ""
#: client/src/configuration/auth-form/configuration-auth.controller.js:67
#: client/src/configuration/configuration.controller.js:156
#: client/src/configuration/configuration.controller.js:209
msgid "You have unsaved changes. Would you like to proceed <strong>without</strong> saving?"
msgstr ""
#: client/src/shared/form-generator.js:960
msgid "Your password must be %d characters long."
msgstr ""

View File

@ -100,7 +100,7 @@ var dev = {
'select2': path.resolve() + '/node_modules/select2/dist/js/select2.full.js'
}
},
devtool: 'sourcemap',
devtool: 'inline-source-map',
watch: true,
};

View File

@ -1,4 +1,4 @@
The requirements.txt and requirements_ansible.txt files are generated from requirements.in and requirements_ansible.in, respectively, using `pip-tools` `pip-compile`.
The requirements.txt and requirements_ansible.txt files are generated from requirements.in and requirements_ansible.in, respectively, using `pip-tools` `pip-compile`. The following commands should do this if ran inside the tower_tools container.
```
virtualenv /buildit

View File

@ -40,6 +40,7 @@ python-saml==2.2.0
python-social-auth==0.2.21
redbaron==0.6.2
requests-futures==0.9.7
service-identity==16.0.0
shade==1.13.1
slackclient==1.0.2
twilio==5.6.0

View File

@ -2,26 +2,24 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt requirements.in
# pip-compile --output-file requirements/requirements.txt requirements/requirements.in
#
# NOTE: I thought we had to pull these guys out but I can't remember why
# and without them we get segfaults
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@master#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
apache-libcloud==1.3.0
appdirs==1.4.0 # via os-client-config, python-ironicclient, rply
asgi-amqp==0.3.1
asgiref==1.0.0 # via asgi-amqp, channels, daphne
autobahn==0.16.1 # via daphne
attrs==16.3.0 # via service-identity
autobahn==0.17.0 # via daphne
azure-batch==1.0.0 # via azure
azure-common==1.1.4 # via azure-batch, azure-mgmt-batch, azure-mgmt-compute, azure-mgmt-keyvault, azure-mgmt-logic, azure-mgmt-network, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-storage, azure-servicebus, azure-servicemanagement-legacy, azure-storage
azure-common[autorest]==1.1.4 # via azure-batch, azure-mgmt-batch, azure-mgmt-compute, azure-mgmt-keyvault, azure-mgmt-logic, azure-mgmt-network, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-storage, azure-servicebus, azure-servicemanagement-legacy, azure-storage
azure-mgmt-batch==1.0.0 # via azure-mgmt
azure-mgmt-compute==0.30.0rc6 # via azure-mgmt
azure-mgmt-keyvault==0.30.0rc6 # via azure-mgmt
@ -52,9 +50,9 @@ chardet==2.3.0 # via msrest
cliff==2.3.0 # via osc-lib, python-designateclient, python-heatclient, python-mistralclient, python-neutronclient, python-openstackclient
cmd2==0.6.9 # via cliff
constantly==15.1.0 # via twisted
cryptography==1.6 # via azure-storage, pyopenssl, python-magnumclient, secretstorage
cryptography==1.7.1 # via adal, azure-storage, pyopenssl, python-magnumclient, secretstorage
daphne==0.15.0 # via channels
debtcollector==1.9.0 # via oslo.config, oslo.utils, python-designateclient, python-keystoneclient, python-neutronclient
debtcollector==1.10.0 # via oslo.config, oslo.utils, python-designateclient, python-keystoneclient, python-neutronclient
decorator==4.0.10 # via python-magnumclient, shade
defusedxml==0.4.1 # via python-saml
django-auth-ldap==1.2.8
@ -78,7 +76,7 @@ functools32==3.2.3.post2 # via jsonschema
futures==3.0.5 # via azure-storage, python-swiftclient, requests-futures
gevent-websocket==0.9.5
gevent==1.1.2 # via gevent-websocket
greenlet==0.4.10 # via gevent
greenlet==0.4.11 # via gevent
httplib2==0.9.2 # via twilio
idna==2.1 # via cryptography
incremental==16.10.1 # via twisted
@ -100,33 +98,33 @@ jsonpatch==1.14 # via shade, warlock
jsonpickle==0.9.3 # via asgi-amqp
jsonpointer==1.10 # via jsonpatch
jsonschema==2.5.1 # via python-designateclient, python-ironicclient, warlock
keyring==10.0.2 # via msrest
keystoneauth1==2.15.0 # via openstacksdk, os-client-config, osc-lib, python-cinderclient, python-designateclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient, shade
keyring==10.1 # via msrest, msrestazure
keystoneauth1==2.16.0 # via openstacksdk, os-client-config, osc-lib, python-cinderclient, python-designateclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient, shade
kombu==3.0.35 # via asgi-amqp, celery
#lxml==3.6.4
lxml==3.7.0
M2Crypto==0.25.1
Markdown==2.6.7
mock==2.0.0
monotonic==1.2 # via oslo.utils
more-itertools==2.3 # via irc, jaraco.functools, jaraco.itertools
more-itertools==2.4.1 # via irc, jaraco.functools, jaraco.itertools
msgpack-python==0.4.7 # via asgi-amqp, oslo.serialization
msrest==0.4.4 # via azure-common, msrestazure
msrestazure==0.4.4 # via azure-common
msrestazure==0.4.6 # via azure-common
munch==2.0.4 # via shade
netaddr==0.7.18 # via oslo.config, oslo.utils, python-neutronclient
netifaces==0.10.5 # via oslo.utils, shade
oauthlib==2.0.1 # via python-social-auth, requests-oauthlib
openstacksdk==0.9.10 # via python-openstackclient
openstacksdk==0.9.11 # via python-openstackclient
ordereddict==1.1
os-client-config==1.24.0 # via openstacksdk, osc-lib, python-magnumclient, python-neutronclient, shade
os-diskconfig-python-novaclient-ext==0.1.3 # via rackspace-novaclient
os-networksv2-python-novaclient-ext==0.26 # via rackspace-novaclient
os-virtual-interfacesv2-python-novaclient-ext==0.20 # via rackspace-novaclient
osc-lib==1.2.0 # via python-designateclient, python-heatclient, python-ironicclient, python-mistralclient, python-neutronclient, python-openstackclient
oslo.config==3.19.0 # via python-keystoneclient
oslo.i18n==3.10.0 # via osc-lib, oslo.config, oslo.utils, python-cinderclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient
oslo.serialization==2.14.0 # via python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient
oslo.utils==3.18.0 # via osc-lib, oslo.serialization, python-cinderclient, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient
oslo.config==3.21.0 # via python-keystoneclient
oslo.i18n==3.11.0 # via osc-lib, oslo.config, oslo.utils, python-cinderclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient
oslo.serialization==2.15.0 # via python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-neutronclient, python-novaclient
oslo.utils==3.20.0 # via osc-lib, oslo.serialization, python-cinderclient, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient
pbr==1.10.0 # via cliff, debtcollector, keystoneauth1, mock, openstacksdk, osc-lib, oslo.i18n, oslo.serialization, oslo.utils, positional, python-cinderclient, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-troveclient, requestsexceptions, shade, stevedore
pexpect==3.1
positional==1.1.1 # via keystoneauth1, python-keystoneclient
@ -134,35 +132,36 @@ PrettyTable==0.7.2 # via cliff, python-cinderclient, python-glanceclient,
psphere==0.5.2
psutil==5.0.0
psycopg2==2.6.2
pyasn1==0.1.9 # via cryptography
pyasn1-modules==0.0.8 # via service-identity
pyasn1==0.1.9 # via cryptography, pyasn1-modules, service-identity
pycparser==2.17 # via cffi
pygerduty==0.35.1
PyJWT==1.4.2 # via python-social-auth
pyOpenSSL==16.2.0
PyJWT==1.4.2 # via adal, python-social-auth
pyOpenSSL==16.2.0 # via service-identity
pyparsing==2.1.10 # via cliff, cmd2, oslo.utils
pyrad==2.0 # via django-radius
python-cinderclient==1.9.0 # via python-openstackclient, shade
python-dateutil==2.6.0 # via azure-storage
python-designateclient==2.3.0 # via shade
python-dateutil==2.6.0 # via adal, azure-storage
python-designateclient==2.4.0 # via shade
python-glanceclient==2.5.0 # via python-openstackclient, shade
python-heatclient==1.6.1 # via shade
python-heatclient==1.7.0 # via shade
python-ironicclient==1.8.0 # via shade
python-keystoneclient==3.7.0 # via python-glanceclient, python-mistralclient, python-openstackclient, shade
python-keystoneclient==3.8.0 # via python-glanceclient, python-mistralclient, python-openstackclient, shade
python-ldap==2.4.28 # via django-auth-ldap
python-logstash==0.4.6
python-magnumclient==2.3.1 # via shade
python-memcached==1.58
python-mistralclient==2.1.1 # via python-troveclient
python-mistralclient==2.1.2 # via python-troveclient
python-neutronclient==6.0.0 # via shade
python-novaclient==6.0.0 # via ip-associations-python-novaclient-ext, os-diskconfig-python-novaclient-ext, os-networksv2-python-novaclient-ext, os-virtual-interfacesv2-python-novaclient-ext, python-openstackclient, rackspace-auth-openstack, rackspace-novaclient, rax-default-network-flags-python-novaclient-ext, rax-scheduled-images-python-novaclient-ext, shade
python-openid==2.2.5 # via python-social-auth
python-openstackclient==3.4.1 # via python-ironicclient
python-openstackclient==3.5.0 # via python-ironicclient
python-radius==1.0
python-saml==2.2.0
python-social-auth==0.2.21
python-swiftclient==3.2.0 # via python-heatclient, python-troveclient, shade
python-troveclient==2.6.0 # via shade
pytz==2016.7 # via babel, celery, irc, oslo.serialization, oslo.utils, tempora, twilio
python-troveclient==2.7.0 # via shade
pytz==2016.10 # via babel, celery, irc, oslo.serialization, oslo.utils, tempora, twilio
PyYAML==3.12 # via cliff, djangorestframework-yaml, os-client-config, psphere, python-heatclient, python-ironicclient, python-mistralclient
rackspace-auth-openstack==1.3 # via rackspace-novaclient
rackspace-novaclient==2.1
@ -171,16 +170,17 @@ rax-scheduled-images-python-novaclient-ext==0.3.1 # via rackspace-novaclient
redbaron==0.6.2
requests-futures==0.9.7
requests-oauthlib==0.7.0 # via msrest, python-social-auth
requests==2.12.1 # via azure-servicebus, azure-servicemanagement-legacy, azure-storage, keystoneauth1, msrest, python-cinderclient, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-mistralclient, python-neutronclient, python-novaclient, python-social-auth, python-swiftclient, python-troveclient, requests-futures, requests-oauthlib, slackclient
requests==2.11.1 # via adal, azure-servicebus, azure-servicemanagement-legacy, azure-storage, keystoneauth1, msrest, python-cinderclient, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-mistralclient, python-neutronclient, python-novaclient, python-social-auth, python-swiftclient, python-troveclient, requests-futures, requests-oauthlib, slackclient
requestsexceptions==1.1.3 # via os-client-config, shade
rfc3986==0.4.1 # via oslo.config
rply==0.7.4 # via baron
secretstorage==2.3.1 # via keyring
service-identity==16.0.0
shade==1.13.1
simplejson==3.10.0 # via osc-lib, python-cinderclient, python-neutronclient, python-novaclient, python-troveclient
six==1.10.0 # via asgi-amqp, asgiref, autobahn, cliff, cryptography, debtcollector, django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, keystoneauth1, mock, more-itertools, openstacksdk, osc-lib, oslo.config, oslo.i18n, oslo.serialization, oslo.utils, pygerduty, pyopenssl, pyrad, python-cinderclient, python-dateutil, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-memcached, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-social-auth, python-swiftclient, python-troveclient, shade, slackclient, stevedore, tempora, twilio, txaio, warlock, websocket-client
slackclient==1.0.2
stevedore==1.18.0 # via cliff, keystoneauth1, openstacksdk, osc-lib, oslo.config, python-designateclient, python-keystoneclient, python-magnumclient
stevedore==1.19.1 # via cliff, keystoneauth1, openstacksdk, osc-lib, oslo.config, python-designateclient, python-keystoneclient, python-magnumclient
suds==0.4 # via psphere
tempora==1.6.1 # via irc, jaraco.logging
twilio==5.6.0
@ -190,10 +190,10 @@ typing==3.5.2.2 # via m2crypto
unicodecsv==0.14.1 # via cliff
uWSGI==2.0.14
warlock==1.2.0 # via python-glanceclient
websocket-client==0.37.0 # via slackclient
websocket-client==0.40.0 # via slackclient
wrapt==1.10.8 # via debtcollector, positional
xmltodict==0.10.2
zope.interface==4.3.2 # via twisted
zope.interface==4.3.3 # via twisted
# The following packages are considered to be unsafe in a requirements file:
# setuptools # via cryptography, django-polymorphic, python-ldap, zope.interface

View File

@ -10,7 +10,7 @@ ADD requirements/requirements.txt requirements/requirements_ansible.txt requirem
RUN yum -y update && yum -y install curl epel-release
RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -
RUN yum -y localinstall http://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-6-x86_64/pgdg-centos94-9.4-3.noarch.rpm
RUN yum -y update && yum -y install openssh-server ansible mg vim tmux git mercurial subversion python-devel python-psycopg2 make postgresql postgresql-devel nginx nodejs python-psutil libxml2-devel libxslt-devel libstdc++.so.6 gcc cyrus-sasl-devel cyrus-sasl openldap-devel libffi-devel zeromq-devel python-pip xmlsec1-devel swig krb5-devel xmlsec1-openssl xmlsec1 xmlsec1-openssl-devel libtool-ltdl-devel rabbitmq-server bubblewrap
RUN yum -y update && yum -y install openssh-server ansible mg vim tmux git mercurial subversion python-devel python-psycopg2 make postgresql postgresql-devel nginx nodejs python-psutil libxml2-devel libxslt-devel libstdc++.so.6 gcc cyrus-sasl-devel cyrus-sasl openldap-devel libffi-devel zeromq-devel python-pip xmlsec1-devel swig krb5-devel xmlsec1-openssl xmlsec1 xmlsec1-openssl-devel libtool-ltdl-devel rabbitmq-server bubblewrap zanata-python-client gettext
RUN pip install virtualenv
RUN /usr/bin/ssh-keygen -q -t rsa -N "" -f /root/.ssh/id_rsa
RUN mkdir -p /etc/tower

5
tools/scripts/manage_translations.py Normal file → Executable file
View File

@ -194,9 +194,8 @@ if __name__ == "__main__":
except OSError as e:
if e.errno == os.errno.ENOENT:
print('''
You need zanata-python-client, install it.
1. Install zanata-python-client, use
$ dnf install zanata-python-client
You need zanata python client, install it.
1. Install zanta python client
2. Create ~/.config/zanata.ini file:
$ vim ~/.config/zanata.ini
[servers]