mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 03:10:42 -03:30
Merge remote-tracking branch 'origin/master' into reintroduce-zeromq-unstable
* origin/master: Add tests for inventory_filters and group_by, fix to convert None to empty string. Job submission Fix up unified job creation parameter passing Roll this back, it doesn't do what I expect Make sure we propogate source_script into the related fields it needs to be in Update job template launch docs with credential_needed_to_start documentation Custom Inventory Scripts forms/Sources.js Add instance filters and group by options for EC2 inventory sources. Implements https://trello.com/c/QOVhP0mH Make sure when we call create_unified_job we can pass important fields as _id or without Make sure we pass extra parameters down to the unified job create method so we can pick up credential if given Add system job template launch documentation Track source_script in summary and related fields for relevant inventory sources Host events Simplify include_vars based on mpd feedback Update the apt-cache when installing python-pip Only the primary should syncdb and migrate Update package changelogs Update release history and process docs Update ansible-tower service config in playbook
This commit is contained in:
commit
8c4aee35d1
@ -4,7 +4,7 @@ Ansible Tower
|
||||
Tower provides a web-based user interface, REST API and task engine built on top of
|
||||
Ansible.
|
||||
|
||||
The current version under development is 2.0.0.
|
||||
The current version under development is 2.1.0.
|
||||
|
||||
Development releases always use the 'master' branch.
|
||||
|
||||
@ -21,6 +21,10 @@ Release schedule
|
||||
* 1.4.10, April 28, 2014.
|
||||
* 1.4.11, May 30, 2014.
|
||||
* 2.0.0, August 19, 2014
|
||||
* 2.0.1, September 4, 2014
|
||||
* 2.0.2, October 6, 2014
|
||||
* 2.0.3, November 14, 2014
|
||||
* 2.0.4, November 21, 2014
|
||||
|
||||
Any fixes should be applied on the appropriate release branch and be cherry-picked to
|
||||
master.
|
||||
|
||||
@ -81,6 +81,7 @@ SUMMARIZABLE_FK_FIELDS = {
|
||||
'current_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
|
||||
'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
|
||||
'inventory_source': ('source', 'last_updated', 'status'),
|
||||
'source_script': ('name', 'description'),
|
||||
}
|
||||
|
||||
class ChoiceField(fields.ChoiceField):
|
||||
@ -959,13 +960,15 @@ class InventorySourceOptionsSerializer(BaseSerializer):
|
||||
|
||||
class Meta:
|
||||
fields = ('*', 'source', 'source_path', 'source_script', 'source_vars', 'credential',
|
||||
'source_regions', 'overwrite', 'overwrite_vars')
|
||||
'source_regions', 'instance_filters', 'group_by', 'overwrite', 'overwrite_vars')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(InventorySourceOptionsSerializer, self).get_related(obj)
|
||||
if obj.credential and obj.credential.active:
|
||||
res['credential'] = reverse('api:credential_detail',
|
||||
args=(obj.credential.pk,))
|
||||
if obj.source_script and obj.source_script.active:
|
||||
res['source_script'] = reverse('api:inventory_script_detail', args=(obj.source_script.pk,))
|
||||
return res
|
||||
|
||||
def validate_source(self, attrs, source):
|
||||
@ -999,6 +1002,10 @@ class InventorySourceOptionsSerializer(BaseSerializer):
|
||||
for cp in ('azure', 'ec2', 'gce', 'rax'):
|
||||
get_regions = getattr(self.opts.model, 'get_%s_region_choices' % cp)
|
||||
field_opts['%s_region_choices' % cp] = get_regions()
|
||||
field_opts = metadata.get('group_by', {})
|
||||
for cp in ('ec2',):
|
||||
get_group_by_choices = getattr(self.opts.model, 'get_%s_group_by_choices' % cp)
|
||||
field_opts['%s_group_by_choices' % cp] = get_group_by_choices()
|
||||
return metadata
|
||||
|
||||
def to_native(self, obj):
|
||||
|
||||
@ -14,6 +14,9 @@ The response will include the following fields:
|
||||
job_template (array, read-only)
|
||||
* `survey_enabled`: Flag indicating if whether the job_template has an enabled
|
||||
survey (boolean, read-only)
|
||||
* `credential_needed_to_start`: Flag indicating the presence of a credential
|
||||
associated with the job template. If not then one should be supplied when
|
||||
launching the job (boolean, read-only)
|
||||
|
||||
Make a POST request to this resource to launch the job_template. If any
|
||||
passwords or variables are required, they must be passed via POST data.
|
||||
|
||||
10
awx/api/templates/api/system_job_template_launch.md
Normal file
10
awx/api/templates/api/system_job_template_launch.md
Normal file
@ -0,0 +1,10 @@
|
||||
Launch a Job Template:
|
||||
|
||||
Make a POST request to this resource to launch the system job template.
|
||||
|
||||
An extra parameter `extra_vars` is suggested in order to pass extra parameters
|
||||
to the system job task. For example: `{"days": 30}` to perform the action on
|
||||
items older than 30 days.
|
||||
|
||||
If successful, the response status code will be 202. If the job cannot be
|
||||
launched, a 405 status code will be returned.
|
||||
@ -1499,7 +1499,7 @@ class JobTemplateLaunch(GenericAPIView):
|
||||
if validation_errors:
|
||||
return Response(dict(errors=validation_errors),
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
new_job = obj.create_unified_job()
|
||||
new_job = obj.create_unified_job(**request.DATA)
|
||||
result = new_job.signal_start(**request.DATA)
|
||||
if not result:
|
||||
data = dict(passwords_needed_to_start=obj.passwords_needed_to_start)
|
||||
|
||||
512
awx/main/migrations/0061_v210_changes.py
Normal file
512
awx/main/migrations/0061_v210_changes.py
Normal file
@ -0,0 +1,512 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'InventorySource.instance_filters'
|
||||
db.add_column(u'main_inventorysource', 'instance_filters',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'InventorySource.group_by'
|
||||
db.add_column(u'main_inventorysource', 'group_by',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'InventoryUpdate.instance_filters'
|
||||
db.add_column(u'main_inventoryupdate', 'instance_filters',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'InventoryUpdate.group_by'
|
||||
db.add_column(u'main_inventoryupdate', 'group_by',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'InventorySource.instance_filters'
|
||||
db.delete_column(u'main_inventorysource', 'instance_filters')
|
||||
|
||||
# Deleting field 'InventorySource.group_by'
|
||||
db.delete_column(u'main_inventorysource', 'group_by')
|
||||
|
||||
# Deleting field 'InventoryUpdate.instance_filters'
|
||||
db.delete_column(u'main_inventoryupdate', 'instance_filters')
|
||||
|
||||
# Deleting field 'InventoryUpdate.group_by'
|
||||
db.delete_column(u'main_inventoryupdate', 'group_by')
|
||||
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'main.activitystream': {
|
||||
'Meta': {'object_name': 'ActivityStream'},
|
||||
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'object1': ('django.db.models.fields.TextField', [], {}),
|
||||
'object2': ('django.db.models.fields.TextField', [], {}),
|
||||
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
|
||||
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
|
||||
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'main.authtoken': {
|
||||
'Meta': {'object_name': 'AuthToken'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.credential': {
|
||||
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'su_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'su_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'sudo_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
|
||||
},
|
||||
'main.custominventoryscript': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'CustomInventoryScript'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.host': {
|
||||
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
|
||||
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.instance': {
|
||||
'Meta': {'object_name': 'Instance'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
|
||||
},
|
||||
'main.inventory': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
|
||||
},
|
||||
'main.inventorysource': {
|
||||
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'main.inventoryupdate': {
|
||||
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
|
||||
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.job': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.jobevent': {
|
||||
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
|
||||
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
|
||||
},
|
||||
'main.jobhostsummary': {
|
||||
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
|
||||
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
|
||||
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'main.joborigin': {
|
||||
'Meta': {'object_name': 'JobOrigin'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
|
||||
},
|
||||
'main.jobtemplate': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
|
||||
},
|
||||
'main.organization': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.profile': {
|
||||
'Meta': {'object_name': 'Profile'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.project': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
|
||||
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.schedule': {
|
||||
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.systemjob': {
|
||||
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
|
||||
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
|
||||
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.systemjobtemplate': {
|
||||
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
|
||||
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
|
||||
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'main.team': {
|
||||
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
|
||||
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'main.unifiedjob': {
|
||||
'Meta': {'object_name': 'UnifiedJob'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
|
||||
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
|
||||
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
|
||||
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
|
||||
},
|
||||
'main.unifiedjobtemplate': {
|
||||
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
|
||||
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
|
||||
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
|
||||
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['main']
|
||||
@ -784,6 +784,18 @@ class InventorySourceOptions(BaseModel):
|
||||
blank=True,
|
||||
default='',
|
||||
)
|
||||
instance_filters = models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Comma-separated list of filter expressions (EC2 only). Hosts are imported when ANY of the filters match.'),
|
||||
)
|
||||
group_by = models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Limit groups automatically created from inventory source (EC2 only).'),
|
||||
)
|
||||
overwrite = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_('Overwrite local groups and hosts from remote inventory source.'),
|
||||
@ -815,6 +827,20 @@ class InventorySourceOptions(BaseModel):
|
||||
regions.append((region.name, label))
|
||||
return regions
|
||||
|
||||
@classmethod
|
||||
def get_ec2_group_by_choices(cls):
|
||||
return [
|
||||
('availability_zone', 'Availability Zone'),
|
||||
('ami_id', 'Image ID'),
|
||||
('instance_id', 'Instance ID'),
|
||||
('instance_type', 'Instance Type'),
|
||||
('key_pair', 'Key Name'),
|
||||
('region', 'Region'),
|
||||
('security_group', 'Security Group'),
|
||||
('tag_keys', 'Tags'),
|
||||
('vpc_id', 'VPC ID'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_rax_region_choices(cls):
|
||||
# Not possible to get rax regions without first authenticating, so use
|
||||
@ -900,6 +926,43 @@ class InventorySourceOptions(BaseModel):
|
||||
|
||||
source_vars_dict = VarsDictProperty('source_vars')
|
||||
|
||||
def clean_instance_filters(self):
|
||||
instance_filters = unicode(self.instance_filters or '')
|
||||
if self.source != 'ec2':
|
||||
return ''
|
||||
invalid_filters = []
|
||||
instance_filter_re = re.compile(r'^(?:tag:.+)|(?:[a-z][a-z\.-]*[a-z])=.*$')
|
||||
for instance_filter in instance_filters.split(','):
|
||||
instance_filter = instance_filter.strip()
|
||||
if not instance_filter:
|
||||
continue
|
||||
if not instance_filter_re.match(instance_filter):
|
||||
invalid_filters.append(instance_filter)
|
||||
if invalid_filters:
|
||||
raise ValidationError('Invalid filter expression%s: %s' %
|
||||
('' if len(invalid_filters) == 1 else 's',
|
||||
', '.join(invalid_filters)))
|
||||
return instance_filters
|
||||
|
||||
def clean_group_by(self):
|
||||
group_by = unicode(self.group_by or '')
|
||||
if self.source != 'ec2':
|
||||
return ''
|
||||
get_choices = getattr(self, 'get_%s_group_by_choices' % self.source)
|
||||
valid_choices = [x[0] for x in get_choices()]
|
||||
choice_transform = lambda x: x.strip().lower()
|
||||
valid_choices = [choice_transform(x) for x in valid_choices]
|
||||
choices = [choice_transform(x) for x in group_by.split(',') if x.strip()]
|
||||
invalid_choices = []
|
||||
for c in choices:
|
||||
if c not in valid_choices and c not in invalid_choices:
|
||||
invalid_choices.append(c)
|
||||
if invalid_choices:
|
||||
raise ValidationError('Invalid group by choice%s: %s' %
|
||||
('' if len(invalid_choices) == 1 else 's',
|
||||
', '.join(invalid_choices)))
|
||||
return ','.join(choices)
|
||||
|
||||
|
||||
class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
||||
|
||||
@ -936,7 +999,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
||||
@classmethod
|
||||
def _get_unified_job_field_names(cls):
|
||||
return ['name', 'description', 'source', 'source_path', 'source_script', 'source_vars',
|
||||
'credential', 'source_regions', 'overwrite', 'overwrite_vars']
|
||||
'credential', 'source_regions', 'instance_filters', 'group_by', 'overwrite', 'overwrite_vars']
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
new_instance = bool(self.pk)
|
||||
|
||||
@ -293,16 +293,19 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique):
|
||||
unified_job_class = self._get_unified_job_class()
|
||||
parent_field_name = unified_job_class._get_parent_field_name()
|
||||
kwargs.pop('%s_id' % parent_field_name, None)
|
||||
kwargs[parent_field_name] = self
|
||||
create_kwargs = {}
|
||||
create_kwargs[parent_field_name] = self
|
||||
for field_name in self._get_unified_job_field_names():
|
||||
if field_name in kwargs:
|
||||
create_kwargs[field_name] = kwargs[field_name]
|
||||
continue
|
||||
# Foreign keys can be specified as field_name or field_name_id.
|
||||
if hasattr(self, '%s_id' % field_name) and ('%s_id' % field_name) in kwargs:
|
||||
create_kwargs['%s_id' % field_name] = kwargs['%s_id' % field_name] = kwargs[field_name]
|
||||
continue
|
||||
kwargs[field_name] = getattr(self, field_name)
|
||||
kwargs = self._update_unified_job_kwargs(**kwargs)
|
||||
unified_job = unified_job_class(**kwargs)
|
||||
create_kwargs[field_name] = getattr(self, field_name)
|
||||
kwargs = self._update_unified_job_kwargs(**create_kwargs)
|
||||
unified_job = unified_job_class(**create_kwargs)
|
||||
if save_unified_job:
|
||||
unified_job.save()
|
||||
return unified_job
|
||||
|
||||
@ -956,6 +956,12 @@ class RunInventoryUpdate(BaseTask):
|
||||
ec2_opts.setdefault('all_rds_instances', 'False')
|
||||
ec2_opts.setdefault('rds', 'False')
|
||||
ec2_opts.setdefault('nested_groups', 'True')
|
||||
if inventory_update.instance_filters:
|
||||
ec2_opts.setdefault('instance_filters', inventory_update.instance_filters)
|
||||
group_by = [x.strip().lower() for x in inventory_update.group_by.split(',') if x.strip()]
|
||||
for choice in inventory_update.get_ec2_group_by_choices():
|
||||
value = bool((group_by and choice[0] in group_by) or (not group_by and choice[0] != 'instance_id'))
|
||||
ec2_opts.setdefault('group_by_%s' % choice[0], str(value))
|
||||
if 'cache_path' not in ec2_opts:
|
||||
cache_path = tempfile.mkdtemp(prefix='ec2_cache', dir=kwargs.get('private_data_dir', None))
|
||||
ec2_opts['cache_path'] = cache_path
|
||||
|
||||
@ -1135,7 +1135,7 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
pass # If should_fail is None, we don't care.
|
||||
return inventory_update
|
||||
|
||||
def check_inventory_source(self, inventory_source, initial=True, enabled_host_pks=None):
|
||||
def check_inventory_source(self, inventory_source, initial=True, enabled_host_pks=None, instance_id_group_ok=False):
|
||||
enabled_host_pks = enabled_host_pks or set()
|
||||
inventory_source = InventorySource.objects.get(pk=inventory_source.pk)
|
||||
inventory = inventory_source.group.inventory
|
||||
@ -1158,7 +1158,7 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
url = reverse('api:inventory_source_hosts_list', args=(inventory_source.pk,))
|
||||
response = self.get(url, expect=200)
|
||||
self.assertNotEqual(response['count'], 0)
|
||||
for host in inventory.hosts.all():
|
||||
for host in inventory.hosts.filter(active=True):
|
||||
source_pks = host.inventory_sources.values_list('pk', flat=True)
|
||||
self.assertTrue(inventory_source.pk in source_pks)
|
||||
self.assertTrue(host.has_inventory_sources)
|
||||
@ -1172,20 +1172,21 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
url = reverse('api:host_inventory_sources_list', args=(host.pk,))
|
||||
response = self.get(url, expect=200)
|
||||
self.assertNotEqual(response['count'], 0)
|
||||
for group in inventory.groups.all():
|
||||
for group in inventory.groups.filter(active=True):
|
||||
source_pks = group.inventory_sources.values_list('pk', flat=True)
|
||||
self.assertTrue(inventory_source.pk in source_pks)
|
||||
self.assertTrue(group.has_inventory_sources)
|
||||
self.assertTrue(group.children.filter(active=True).exists() or
|
||||
group.hosts.filter(active=True).exists())
|
||||
# Make sure EC2 instance ID groups and RDS groups are excluded.
|
||||
if inventory_source.source == 'ec2':
|
||||
if inventory_source.source == 'ec2' and not instance_id_group_ok:
|
||||
self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I),
|
||||
group.name)
|
||||
if inventory_source.source == 'ec2':
|
||||
self.assertFalse(re.match(r'^rds|rds_.+|type_db_.+$', group.name, re.I),
|
||||
group.name)
|
||||
# Make sure Rackspace instance ID groups are excluded.
|
||||
if inventory_source.source == 'rax':
|
||||
if inventory_source.source == 'rax' and not instance_id_group_ok:
|
||||
self.assertFalse(re.match(r'^instance-.+$', group.name, re.I),
|
||||
group.name)
|
||||
with self.current_user(self.super_django_user):
|
||||
@ -1194,7 +1195,7 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
self.assertNotEqual(response['count'], 0)
|
||||
# Try to set a source on a child group that was imported. Should not
|
||||
# be allowed.
|
||||
for group in inventory_source.group.children.all():
|
||||
for group in inventory_source.group.children.filter(active=True):
|
||||
inv_src_2 = group.inventory_source
|
||||
inv_src_url2 = reverse('api:inventory_source_detail', args=(inv_src_2.pk,))
|
||||
with self.current_user(self.super_django_user):
|
||||
@ -1255,10 +1256,36 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
'source': 'ec2',
|
||||
'credential': aws_cred_id,
|
||||
'source_regions': '',
|
||||
'instance_filters': '',
|
||||
'group_by': '',
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=200)
|
||||
self.assertEqual(response['source_regions'], '')
|
||||
# Null for instance filters and group_by should be converted to empty
|
||||
# string.
|
||||
inv_src_data['instance_filters'] = None
|
||||
inv_src_data['group_by'] = None
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=200)
|
||||
self.assertEqual(response['instance_filters'], '')
|
||||
self.assertEqual(response['group_by'], '')
|
||||
# Invalid string for instance filters.
|
||||
inv_src_data['instance_filters'] = 'tag-key_123=Name,'
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=400)
|
||||
# Valid string for instance filters.
|
||||
inv_src_data['instance_filters'] = 'tag-key=Name'
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=200)
|
||||
# Invalid string for group_by.
|
||||
inv_src_data['group_by'] = 'ec2_region,'
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=400)
|
||||
# Valid string for group_by.
|
||||
inv_src_data['group_by'] = 'region,key_pair,instance_type'
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url1, inv_src_data, expect=200)
|
||||
# All region.
|
||||
inv_src_data['source_regions'] = 'ALL'
|
||||
with self.current_user(self.super_django_user):
|
||||
@ -1292,6 +1319,8 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
'source': 'rax',
|
||||
'credential': rax_cred_id,
|
||||
'source_regions': '',
|
||||
'instance_filters': None,
|
||||
'group_by': None,
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.put(inv_src_url2, inv_src_data, expect=200)
|
||||
@ -1450,10 +1479,15 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
# Also change the host name, and verify it is not deleted, but instead
|
||||
# updated because the instance ID matches.
|
||||
enabled_host_pks = set(self.inventory.hosts.filter(enabled=True).values_list('pk', flat=True))
|
||||
instance_types = {}
|
||||
for host in self.inventory.hosts.all():
|
||||
host.enabled = False
|
||||
host.name = 'changed-%s' % host.name
|
||||
host.save()
|
||||
# Get instance types for later use with instance_filters.
|
||||
instance_type = host.variables_dict.get('ec2_instance_type', '')
|
||||
if instance_type:
|
||||
instance_types.setdefault(instance_type, []).append(host.pk)
|
||||
old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
|
||||
self.check_inventory_source(inventory_source, initial=False, enabled_host_pks=enabled_host_pks)
|
||||
new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True))
|
||||
@ -1461,6 +1495,18 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
# Verify that main group is in top level groups (hasn't been added as
|
||||
# its own child).
|
||||
self.assertTrue(self.group in self.inventory.root_groups)
|
||||
# Now add instance filters and verify that only the matching hosts are
|
||||
# synced, specify new cache path to force refresh.
|
||||
cache_path2 = tempfile.mkdtemp(prefix='awx_ec2_')
|
||||
self._temp_paths.append(cache_path2)
|
||||
instance_type = max(instance_types.items(), key=lambda x: len(x[1]))[0]
|
||||
inventory_source.instance_filters = 'instance-type=%s' % instance_type
|
||||
inventory_source.source_vars = '---\n\nnested_groups: false\ncache_path: %s\n' % cache_path2
|
||||
inventory_source.overwrite = True
|
||||
inventory_source.save()
|
||||
self.check_inventory_source(inventory_source, initial=False)
|
||||
new_host_pks = set(self.inventory.hosts.filter(active=True).values_list('pk', flat=True))
|
||||
self.assertEqual(new_host_pks, set(instance_types[instance_type]))
|
||||
|
||||
def test_update_from_ec2_with_nested_groups(self):
|
||||
source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '')
|
||||
@ -1494,16 +1540,67 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
self.assertFalse(name.startswith('key_'))
|
||||
self.assertFalse(name.startswith('security_group_'))
|
||||
self.assertFalse(name.startswith('tag_'))
|
||||
self.assertFalse(name.startswith('ami-'))
|
||||
self.assertFalse(name.startswith('vpc-'))
|
||||
self.assertTrue('ec2' in child_names)
|
||||
self.assertTrue('regions' in child_names)
|
||||
self.assertTrue('types' in child_names)
|
||||
self.assertTrue('keys' in child_names)
|
||||
self.assertTrue('security_groups' in child_names)
|
||||
self.assertTrue('tags' in child_names)
|
||||
self.assertTrue('images' in child_names)
|
||||
self.assertFalse('instances' in child_names)
|
||||
# Make sure we clean up the cache path when finished (when one is not
|
||||
# provided explicitly via source_vars).
|
||||
new_cache_paths = set(glob.glob(cache_path_pattern))
|
||||
self.assertEqual(old_cache_paths, new_cache_paths)
|
||||
# Sync again with group_by set to a non-empty value.
|
||||
cache_path = tempfile.mkdtemp(prefix='awx_ec2_')
|
||||
self._temp_paths.append(cache_path)
|
||||
inventory_source.group_by = 'region,instance_type'
|
||||
inventory_source.source_vars = '---\n\ncache_path: %s\n' % cache_path
|
||||
inventory_source.overwrite = True
|
||||
inventory_source.save()
|
||||
self.check_inventory_source(inventory_source, initial=False)
|
||||
# Verify that only the desired groups are returned.
|
||||
child_names = self.group.children.filter(active=True).values_list('name', flat=True)
|
||||
self.assertTrue('ec2' in child_names)
|
||||
self.assertTrue('regions' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='regions').children.filter(active=True).count())
|
||||
self.assertTrue('types' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='types').children.filter(active=True).count())
|
||||
self.assertFalse('keys' in child_names)
|
||||
self.assertFalse('security_groups' in child_names)
|
||||
self.assertFalse('tags' in child_names)
|
||||
self.assertFalse('images' in child_names)
|
||||
self.assertFalse('vpcs' in child_names)
|
||||
self.assertFalse('instances' in child_names)
|
||||
# Sync again with group_by set to include all possible groups.
|
||||
cache_path2 = tempfile.mkdtemp(prefix='awx_ec2_')
|
||||
self._temp_paths.append(cache_path2)
|
||||
inventory_source.group_by = 'instance_id, region, availability_zone, ami_id, instance_type, key_pair, vpc_id, security_group, tag_keys'
|
||||
inventory_source.source_vars = '---\n\ncache_path: %s\n' % cache_path2
|
||||
inventory_source.overwrite = True
|
||||
inventory_source.save()
|
||||
self.check_inventory_source(inventory_source, initial=False, instance_id_group_ok=True)
|
||||
# Verify that only the desired groups are returned.
|
||||
# Skip vpcs as selected inventory may or may not have any.
|
||||
child_names = self.group.children.filter(active=True).values_list('name', flat=True)
|
||||
self.assertTrue('ec2' in child_names)
|
||||
self.assertTrue('regions' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='regions').children.filter(active=True).count())
|
||||
self.assertTrue('types' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='types').children.filter(active=True).count())
|
||||
self.assertTrue('keys' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='keys').children.filter(active=True).count())
|
||||
self.assertTrue('security_groups' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='security_groups').children.filter(active=True).count())
|
||||
self.assertTrue('tags' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='tags').children.filter(active=True).count())
|
||||
self.assertTrue('images' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='images').children.filter(active=True).count())
|
||||
self.assertTrue('instances' in child_names)
|
||||
self.assertTrue(self.group.children.get(name='instances').children.filter(active=True).count())
|
||||
return
|
||||
# Print out group/host tree for debugging.
|
||||
print
|
||||
@ -1515,7 +1612,7 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
||||
draw_tree(c, d+1)
|
||||
for g in self.inventory.root_groups.order_by('name'):
|
||||
draw_tree(g)
|
||||
|
||||
|
||||
def test_update_from_rax(self):
|
||||
source_username = getattr(settings, 'TEST_RACKSPACE_USERNAME', '')
|
||||
source_password = getattr(settings, 'TEST_RACKSPACE_API_KEY', '')
|
||||
|
||||
@ -68,6 +68,21 @@ cache_max_age = 300
|
||||
# Organize groups into a nested/hierarchy instead of a flat namespace.
|
||||
nested_groups = False
|
||||
|
||||
# The EC2 inventory output can become very large. To manage its size,
|
||||
# configure which groups should be created.
|
||||
group_by_instance_id = True
|
||||
group_by_region = True
|
||||
group_by_availability_zone = True
|
||||
group_by_ami_id = True
|
||||
group_by_instance_type = True
|
||||
group_by_key_pair = True
|
||||
group_by_vpc_id = True
|
||||
group_by_security_group = True
|
||||
group_by_tag_keys = True
|
||||
group_by_route53_names = True
|
||||
group_by_rds_engine = True
|
||||
group_by_rds_parameter_group = True
|
||||
|
||||
# If you only want to include hosts that match a certain regular expression
|
||||
# pattern_include = stage-*
|
||||
|
||||
|
||||
@ -253,6 +253,27 @@ class Ec2Inventory(object):
|
||||
else:
|
||||
self.nested_groups = False
|
||||
|
||||
# Configure which groups should be created.
|
||||
group_by_options = [
|
||||
'group_by_instance_id',
|
||||
'group_by_region',
|
||||
'group_by_availability_zone',
|
||||
'group_by_ami_id',
|
||||
'group_by_instance_type',
|
||||
'group_by_key_pair',
|
||||
'group_by_vpc_id',
|
||||
'group_by_security_group',
|
||||
'group_by_tag_keys',
|
||||
'group_by_route53_names',
|
||||
'group_by_rds_engine',
|
||||
'group_by_rds_parameter_group',
|
||||
]
|
||||
for option in group_by_options:
|
||||
if config.has_option('ec2', option):
|
||||
setattr(self, option, config.getboolean('ec2', option))
|
||||
else:
|
||||
setattr(self, option, True)
|
||||
|
||||
# Do we need to just include hosts that match a pattern?
|
||||
try:
|
||||
pattern_include = config.get('ec2', 'pattern_include')
|
||||
@ -405,56 +426,77 @@ class Ec2Inventory(object):
|
||||
self.index[dest] = [region, instance.id]
|
||||
|
||||
# Inventory: Group by instance ID (always a group of 1)
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
else:
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
self.push(self.inventory, instance.placement, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, region, instance.placement)
|
||||
if self.group_by_availability_zone:
|
||||
self.push(self.inventory, instance.placement, dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, instance.placement)
|
||||
self.push_group(self.inventory, 'zones', instance.placement)
|
||||
|
||||
# Inventory: Group by Amazon Machine Image (AMI) ID
|
||||
if self.group_by_ami_id:
|
||||
ami_id = self.to_safe(instance.image_id)
|
||||
self.push(self.inventory, ami_id, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'images', ami_id)
|
||||
|
||||
# Inventory: Group by instance type
|
||||
type_name = self.to_safe('type_' + instance.instance_type)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
if self.group_by_instance_type:
|
||||
type_name = self.to_safe('type_' + instance.instance_type)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by key pair
|
||||
if instance.key_name:
|
||||
if self.group_by_key_pair and instance.key_name:
|
||||
key_name = self.to_safe('key_' + instance.key_name)
|
||||
self.push(self.inventory, key_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'keys', key_name)
|
||||
|
||||
|
||||
# Inventory: Group by VPC
|
||||
if self.group_by_vpc_id and instance.vpc_id:
|
||||
vpc_id_name = self.to_safe(instance.vpc_id)
|
||||
self.push(self.inventory, vpc_id_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'vpcs', vpc_id_name)
|
||||
|
||||
# Inventory: Group by security group
|
||||
try:
|
||||
for group in instance.groups:
|
||||
key = self.to_safe("security_group_" + group.name)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
except AttributeError:
|
||||
print 'Package boto seems a bit older.'
|
||||
print 'Please upgrade boto >= 2.3.0.'
|
||||
sys.exit(1)
|
||||
if self.group_by_security_group:
|
||||
try:
|
||||
for group in instance.groups:
|
||||
key = self.to_safe("security_group_" + group.name)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
except AttributeError:
|
||||
print 'Package boto seems a bit older.'
|
||||
print 'Please upgrade boto >= 2.3.0.'
|
||||
sys.exit(1)
|
||||
|
||||
# Inventory: Group by tag keys
|
||||
for k, v in instance.tags.iteritems():
|
||||
key = self.to_safe("tag_" + k + "=" + v)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k))
|
||||
self.push_group(self.inventory, self.to_safe("tag_" + k), key)
|
||||
if self.group_by_tag_keys:
|
||||
for k, v in instance.tags.iteritems():
|
||||
key = self.to_safe("tag_" + k + "=" + v)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k))
|
||||
self.push_group(self.inventory, self.to_safe("tag_" + k), key)
|
||||
|
||||
# Inventory: Group by Route53 domain names if enabled
|
||||
if self.route53_enabled:
|
||||
if self.route53_enabled and self.group_by_route53_names:
|
||||
route53_names = self.get_instance_route53_names(instance)
|
||||
for name in route53_names:
|
||||
self.push(self.inventory, name, dest)
|
||||
@ -476,10 +518,6 @@ class Ec2Inventory(object):
|
||||
return
|
||||
|
||||
# Select the best destination address
|
||||
#if instance.subnet_id:
|
||||
#dest = getattr(instance, self.vpc_destination_variable)
|
||||
#else:
|
||||
#dest = getattr(instance, self.destination_variable)
|
||||
dest = instance.endpoint[0]
|
||||
|
||||
if not dest:
|
||||
@ -490,49 +528,64 @@ class Ec2Inventory(object):
|
||||
self.index[dest] = [region, instance.id]
|
||||
|
||||
# Inventory: Group by instance ID (always a group of 1)
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
else:
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
self.push(self.inventory, instance.availability_zone, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, region, instance.availability_zone)
|
||||
|
||||
# Inventory: Group by instance type
|
||||
type_name = self.to_safe('type_' + instance.instance_class)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by security group
|
||||
try:
|
||||
if instance.security_group:
|
||||
key = self.to_safe("security_group_" + instance.security_group.name)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
if self.group_by_availability_zone:
|
||||
self.push(self.inventory, instance.availability_zone, dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, instance.availability_zone)
|
||||
self.push_group(self.inventory, 'zones', instance.availability_zone)
|
||||
|
||||
except AttributeError:
|
||||
print 'Package boto seems a bit older.'
|
||||
print 'Please upgrade boto >= 2.3.0.'
|
||||
sys.exit(1)
|
||||
# Inventory: Group by instance type
|
||||
if self.group_by_instance_type:
|
||||
type_name = self.to_safe('type_' + instance.instance_class)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by VPC
|
||||
if self.group_by_vpc_id and instance.vpc_id:
|
||||
vpc_id_name = self.to_safe(instance.vpc_id)
|
||||
self.push(self.inventory, vpc_id_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'vpcs', vpc_id_name)
|
||||
|
||||
# Inventory: Group by security group
|
||||
if self.group_by_security_group:
|
||||
try:
|
||||
if instance.security_group:
|
||||
key = self.to_safe("security_group_" + instance.security_group.name)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
|
||||
except AttributeError:
|
||||
print 'Package boto seems a bit older.'
|
||||
print 'Please upgrade boto >= 2.3.0.'
|
||||
sys.exit(1)
|
||||
|
||||
# Inventory: Group by engine
|
||||
self.push(self.inventory, self.to_safe("rds_" + instance.engine), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_engines', self.to_safe("rds_" + instance.engine))
|
||||
if self.group_by_rds_engine:
|
||||
self.push(self.inventory, self.to_safe("rds_" + instance.engine), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_engines', self.to_safe("rds_" + instance.engine))
|
||||
|
||||
# Inventory: Group by parameter group
|
||||
self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe("rds_parameter_group_" + instance.parameter_group.name))
|
||||
if self.group_by_rds_parameter_group:
|
||||
self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe("rds_parameter_group_" + instance.parameter_group.name))
|
||||
|
||||
# Global Tag: all RDS instances
|
||||
self.push(self.inventory, 'rds', dest)
|
||||
|
||||
@ -387,15 +387,13 @@ EC2_ENABLED_VALUE = 'running'
|
||||
EC2_INSTANCE_ID_VAR = 'ec2_id'
|
||||
|
||||
# Filter for allowed group/host names when importing inventory from EC2.
|
||||
# By default, filter group of one created for each instance, filter all RDS
|
||||
# hosts, and exclude all groups without children, hosts and variables.
|
||||
EC2_GROUP_FILTER = r'^(?!i-[a-f0-9]{8,}).+$'
|
||||
EC2_GROUP_FILTER = r'^.+$'
|
||||
EC2_HOST_FILTER = r'^.+$'
|
||||
EC2_EXCLUDE_EMPTY_GROUPS = True
|
||||
|
||||
|
||||
# ------------
|
||||
# -- VMWare --
|
||||
# -- VMware --
|
||||
# ------------
|
||||
VMWARE_REGIONS_BLACKLIST = []
|
||||
|
||||
@ -408,7 +406,7 @@ VMWARE_ENABLED_VALUE = 'poweredOn'
|
||||
VMWARE_INSTANCE_ID_VAR = 'vmware_uuid'
|
||||
|
||||
# Filter for allowed group and host names when importing inventory
|
||||
# from EC2.
|
||||
# from VMware.
|
||||
VMWARE_GROUP_FILTER = r'^.+$'
|
||||
VMWARE_HOST_FILTER = r'^.+$'
|
||||
VMWARE_EXCLUDE_EMPTY_GROUPS = True
|
||||
|
||||
@ -71,7 +71,16 @@ angular.module('SourceFormDefinition', [])
|
||||
editRequired: false,
|
||||
dataTitle: 'Instance Filters',
|
||||
dataPlacement: 'right',
|
||||
awPopOver: "<p>Open the <a href=http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html target='_blank'>documentation</a> for a complete list of filter options.</p>",
|
||||
awPopOver: "<p>Provide a comma-separated list of filter expressions. " +
|
||||
"Hosts are imported to Tower when <em>ANY</em> of the filters match.</p>" +
|
||||
"Limit to hosts having a tag:<br />\n" +
|
||||
"<blockquote>tag-key=TowerManaged</blockquote>\n" +
|
||||
"Limit to hosts using either key pair:<br />\n" +
|
||||
"<blockquote>key-name=staging, key-name=production</blockquote>\n" +
|
||||
"Limit to hosts where the Name tag begins with <em>test</em>:<br />\n" +
|
||||
"<blockquote>tag:Name=test*</blockquote>\n" +
|
||||
"<p>View the <a href=\"http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html\" target=\"_blank\">Describe Instances documentation</a> " +
|
||||
"for a complete list of supported filters.</p>",
|
||||
dataContainer: 'body'
|
||||
},
|
||||
group_by: {
|
||||
@ -81,22 +90,22 @@ angular.module('SourceFormDefinition', [])
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awMultiselect: 'group_by_choices',
|
||||
dataTitle: 'Group By',
|
||||
dataTitle: 'Only Group By',
|
||||
dataPlacement: 'right',
|
||||
awPopOver: "<p>FIXME: Create these automatic groups by default. give examples</p>",
|
||||
awPopOver: "<p>Select which groups to create automatically. " +
|
||||
"Tower will create group names similar to the following examples based on the options selected:</p><ul>" +
|
||||
"<li>Availability Zone: <strong>zones » us-east-1b</strong></li>" +
|
||||
"<li>Image ID: <strong>images » ami-b007ab1e</strong></li>" +
|
||||
"<li>Instance ID: <strong>instances » i-ca11ab1e</strong></li>" +
|
||||
"<li>Instance Type: <strong>types » type_m1_medium</strong></li>" +
|
||||
"<li>Key Name: <strong>keys » key_testing</strong></li>" +
|
||||
"<li>Region: <strong>regions » us-east-1</strong></li>" +
|
||||
"<li>Security Group: <strong>security_groups » security_group_default</strong></li>" +
|
||||
"<li>Tags: <strong>tags » tag_Name » tag_Name_host1</strong></li>" +
|
||||
"<li>VPC ID: <strong>vpcs » vpc-5ca1ab1e</strong></li>" +
|
||||
"</ul><p>If blank, all groups above are created except <em>Instance ID</em>.</p>",
|
||||
dataContainer: 'body'
|
||||
},
|
||||
// group_tag_filters: {
|
||||
// label: 'Tag Filters',
|
||||
// type: 'text',
|
||||
// ngShow: "source && source.value == 'ec2' && group_by.value.indexOf('tag_keys') >= 0", // FIXME: Not sure what's needed to make the last expression work.
|
||||
// addRequired: false,
|
||||
// editRequired: false,
|
||||
// dataTitle: 'Tag Filters',
|
||||
// dataPlacement: 'right',
|
||||
// awPopOver: "<p>FIXME: When grouping by tags, specify which tag keys become groups.</p>",
|
||||
// dataContainer: 'body'
|
||||
// },
|
||||
source_script: {
|
||||
label : "Custom Inventory Scripts",
|
||||
type: 'lookup',
|
||||
|
||||
@ -273,7 +273,8 @@ angular.module('EventViewerHelper', ['ModalDialog', 'Utilities', 'EventsViewerFo
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('GetEvent', ['Wait', 'Rest', 'ProcessErrors', function(Wait, Rest, ProcessErrors) {
|
||||
.factory('GetEvent', ['Wait', 'Rest', 'ProcessErrors',
|
||||
function(Wait, Rest, ProcessErrors) {
|
||||
return function(params) {
|
||||
var url = params.url,
|
||||
scope = params.scope,
|
||||
@ -295,70 +296,79 @@ angular.module('EventViewerHelper', ['ModalDialog', 'Utilities', 'EventsViewerFo
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
scope.next_event_set = data.next;
|
||||
scope.prev_event_set = data.previous;
|
||||
data.results.forEach(function(event) {
|
||||
var msg, key, event_data = {};
|
||||
if (event.event_data.res) {
|
||||
if (typeof event.event_data.res !== 'object') {
|
||||
// turn event_data.res into an object
|
||||
msg = event.event_data.res;
|
||||
event.event_data.res = {};
|
||||
event.event_data.res.msg = msg;
|
||||
}
|
||||
for (key in event.event_data) {
|
||||
if (key !== "res") {
|
||||
event.event_data.res[key] = event.event_data[key];
|
||||
}
|
||||
}
|
||||
if (event.event_data.res.ansible_facts) {
|
||||
// don't show fact gathering results
|
||||
event.event_data.res.task = "Gathering Facts";
|
||||
delete event.event_data.res.ansible_facts;
|
||||
}
|
||||
event.event_data.res.status = getStatus(event);
|
||||
event_data = event.event_data.res;
|
||||
}
|
||||
else {
|
||||
event.event_data.status = getStatus(event);
|
||||
event_data = event.event_data;
|
||||
}
|
||||
// convert results to stdout
|
||||
if (event_data.results && typeof event_data.results === "object" && Array.isArray(event_data.results)) {
|
||||
event_data.stdout = "";
|
||||
event_data.results.forEach(function(row) {
|
||||
event_data.stdout += row + "\n";
|
||||
});
|
||||
delete event_data.results;
|
||||
}
|
||||
if (event_data.invocation) {
|
||||
for (key in event_data.invocation) {
|
||||
event_data[key] = event_data.invocation[key];
|
||||
}
|
||||
delete event_data.invocation;
|
||||
}
|
||||
event_data.play = event.play;
|
||||
if (event.task) {
|
||||
event_data.task = event.task;
|
||||
}
|
||||
event_data.created = event.created;
|
||||
event_data.role = event.role;
|
||||
event_data.host_id = event.host;
|
||||
event_data.host_name = event.host_name;
|
||||
if (event_data.host) {
|
||||
delete event_data.host;
|
||||
}
|
||||
event_data.id = event.id;
|
||||
event_data.parent = event.parent;
|
||||
event_data.event = (event.event_display) ? event.event_display : event.event;
|
||||
results.push(event_data);
|
||||
});
|
||||
if (show_event) {
|
||||
scope.$emit('ShowNextEvent', results, show_event);
|
||||
|
||||
if(jQuery.isEmptyObject(data)) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get event ' + url + '. ' });
|
||||
|
||||
}
|
||||
else {
|
||||
scope.$emit('EventReady', results);
|
||||
}
|
||||
scope.next_event_set = data.next;
|
||||
scope.prev_event_set = data.previous;
|
||||
data.results.forEach(function(event) {
|
||||
var msg, key, event_data = {};
|
||||
if (event.event_data.res) {
|
||||
if (typeof event.event_data.res !== 'object') {
|
||||
// turn event_data.res into an object
|
||||
msg = event.event_data.res;
|
||||
event.event_data.res = {};
|
||||
event.event_data.res.msg = msg;
|
||||
}
|
||||
for (key in event.event_data) {
|
||||
if (key !== "res") {
|
||||
event.event_data.res[key] = event.event_data[key];
|
||||
}
|
||||
}
|
||||
if (event.event_data.res.ansible_facts) {
|
||||
// don't show fact gathering results
|
||||
event.event_data.res.task = "Gathering Facts";
|
||||
delete event.event_data.res.ansible_facts;
|
||||
}
|
||||
event.event_data.res.status = getStatus(event);
|
||||
event_data = event.event_data.res;
|
||||
}
|
||||
else {
|
||||
event.event_data.status = getStatus(event);
|
||||
event_data = event.event_data;
|
||||
}
|
||||
// convert results to stdout
|
||||
if (event_data.results && typeof event_data.results === "object" && Array.isArray(event_data.results)) {
|
||||
event_data.stdout = "";
|
||||
event_data.results.forEach(function(row) {
|
||||
event_data.stdout += row + "\n";
|
||||
});
|
||||
delete event_data.results;
|
||||
}
|
||||
if (event_data.invocation) {
|
||||
for (key in event_data.invocation) {
|
||||
event_data[key] = event_data.invocation[key];
|
||||
}
|
||||
delete event_data.invocation;
|
||||
}
|
||||
event_data.play = event.play;
|
||||
if (event.task) {
|
||||
event_data.task = event.task;
|
||||
}
|
||||
event_data.created = event.created;
|
||||
event_data.role = event.role;
|
||||
event_data.host_id = event.host;
|
||||
event_data.host_name = event.host_name;
|
||||
if (event_data.host) {
|
||||
delete event_data.host;
|
||||
}
|
||||
event_data.id = event.id;
|
||||
event_data.parent = event.parent;
|
||||
event_data.event = (event.event_display) ? event.event_display : event.event;
|
||||
results.push(event_data);
|
||||
});
|
||||
if (show_event) {
|
||||
scope.$emit('ShowNextEvent', results, show_event);
|
||||
}
|
||||
else {
|
||||
scope.$emit('EventReady', results);
|
||||
}
|
||||
} //else statement
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
|
||||
@ -238,22 +238,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
id: 'all',
|
||||
text: 'All'
|
||||
}]);
|
||||
// FIXME: Should come from API.
|
||||
scope.group_by_choices = [
|
||||
{label: 'All', name: 'All', value: 'all'},
|
||||
{label: 'Instance ID', name: 'Instance ID', value: 'instance_id'},
|
||||
{label: 'Region', name: 'Region', value: 'region'},
|
||||
{label: 'Availability Zone', name: 'Availability Zone', value: 'availability_zone'},
|
||||
{label: 'AMI ID', name: 'AMI ID', value: 'ami_id'},
|
||||
{label: 'Instance Type', name: 'Instance Type', value: 'instance_type'},
|
||||
{label: 'Key Pair', name: 'Key Pair', value: 'key_pair'},
|
||||
{label: 'Security Group', name: 'Security Group', value: 'security_group'},
|
||||
{label: 'Tag Keys', name: 'Tag Keys', value: 'tag_keys'},
|
||||
];
|
||||
$('#s2id_source_group_by').select2('data', [{
|
||||
id: 'all',
|
||||
text: 'All'
|
||||
}]);
|
||||
scope.group_by_choices = scope.ec2_group_by;
|
||||
$('#s2id_group_by').select2('data', []);
|
||||
$('#source_form').addClass('squeeze');
|
||||
} else if (scope.source.value === 'gce') {
|
||||
scope.source_region_choices = scope.gce_regions;
|
||||
@ -860,10 +846,13 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
|
||||
setTimeout(function(){ textareaResize('group_variables'); }, 300);
|
||||
}
|
||||
else if ($(e.target).text() === 'Source') {
|
||||
if (sources_scope.source && (sources_scope.source.value === 'ec2' || sources_scope.source.value === 'custom')) {
|
||||
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
||||
Wait('start');
|
||||
ParseTypeChange({ scope: sources_scope, variable: 'source_vars', parse_variable: SourceForm.fields.source_vars.parseTypeName,
|
||||
field_id: 'source_source_vars', onReady: waitStop });
|
||||
}
|
||||
else if (sources_scope.source && (sources_scope.source.value === 'custom')) {
|
||||
Wait('start');
|
||||
ParseTypeChange({ scope: sources_scope, variable: 'extra_vars', parse_variable: SourceForm.fields.extra_vars.parseTypeName,
|
||||
field_id: 'source_extra_vars', onReady: waitStop });
|
||||
}
|
||||
@ -1009,6 +998,23 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
|
||||
}];
|
||||
$('#s2id_source_source_regions').select2('data', master.source_regions);
|
||||
}
|
||||
if (data.group_by && data.source === 'ec2') {
|
||||
set = sources_scope.ec2_group_by;
|
||||
opts = [];
|
||||
list = data.group_by.split(',');
|
||||
for (i = 0; i < list.length; i++) {
|
||||
for (j = 0; j < set.length; j++) {
|
||||
if (list[i] === set[j].value) {
|
||||
opts.push({
|
||||
id: set[j].value,
|
||||
text: set[j].label
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
master.group_by = opts;
|
||||
$('#s2id_source_group_by').select2('data', opts);
|
||||
}
|
||||
sources_scope.group_update_url = data.related.update;
|
||||
modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded
|
||||
//Wait('stop');
|
||||
@ -1119,6 +1125,16 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
// Load options for group_by
|
||||
GetChoices({
|
||||
scope: sources_scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'group_by',
|
||||
variable: 'ec2_group_by',
|
||||
choice_name: 'ec2_group_by_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
if (parent_scope.removeAddTreeRefreshed) {
|
||||
@ -1179,6 +1195,17 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
|
||||
data.source_regions = r.join();
|
||||
|
||||
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
||||
data.instance_filters = sources_scope.instance_filters;
|
||||
// Create a string out of selected list of regions
|
||||
var group_by = $('#s2id_source_group_by').select2("data");
|
||||
r = [];
|
||||
for (i = 0; i < group_by.length; i++) {
|
||||
r.push(group_by[i].id);
|
||||
}
|
||||
data.group_by = r.join();
|
||||
}
|
||||
|
||||
if (sources_scope.source && (sources_scope.source.value === 'ec2' || sources_scope.source.value === 'custom')) {
|
||||
// for ec2, validate variable data
|
||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.source_vars, true);
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
|
||||
}
|
||||
delete(job_launch_data.extra_vars);
|
||||
if(!Empty(scope.credential)){
|
||||
job_launch_data.credential = scope.credential;
|
||||
job_launch_data.credential_id = scope.credential;
|
||||
}
|
||||
Rest.setUrl(url);
|
||||
Rest.post(job_launch_data)
|
||||
@ -517,11 +517,9 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
|
||||
if(question.type === 'integer'){
|
||||
min = (!Empty(question.min)) ? Number(question.min) : "";
|
||||
max = (!Empty(question.max)) ? Number(question.max) : "" ;
|
||||
html+='<input type="number" id="'+question.variable+'" ng-model="'+question.variable+'" class="form-control" name="'+question.variable+'" ng-min="'+min+'" ng-max="'+max+'" integer>'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+ question.variable + '.$dirty && ' +
|
||||
'job_launch_form.'+question.variable+'.$error.required\">A value is required!</div>'+
|
||||
'<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$error.number || job_launch_form.'+question.variable+'.$error.integer">This is not valid integer!</div>'+
|
||||
html+='<input type="number" id="'+question.variable+'" ng-model="'+question.variable+'" class="form-control" name="'+question.variable+'" ng-required="'+question.required+'" integer />'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+ question.variable + '.$dirty && job_launch_form.'+question.variable+'.$error.required">A value is required!</div>'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$dirty && (job_launch_form.'+question.variable+'.$error.number || job_launch_form.'+question.variable+'.$error.integer)" >This is not valid integer!</div>'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$error.ngMin || job_launch_form.'+question.variable+'.$error.ngMax"> The value must be in range {{'+min+'}} to {{'+max+'}}!</div>';
|
||||
|
||||
}
|
||||
@ -530,10 +528,9 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
|
||||
min = (!Empty(question.min)) ? question.min : "";
|
||||
max = (!Empty(question.max)) ? question.max : "" ;
|
||||
defaultValue = (!Empty(question.default)) ? question.default : (!Empty(question.default_float)) ? question.default_float : "" ;
|
||||
html+='<input type="number" id="'+question.variable+'" ng-model="'+question.variable+'" class=" form-control" name="'+question.variable+'" ng-min="'+min+'" ng-max="'+max+'" smart-float>'+
|
||||
html+='<input type="number" id="'+question.variable+'" ng-model="'+question.variable+'" class=" form-control" name="'+question.variable+'" ng-required="'+question.variable+'" smart-float />'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$error.number || job_launch_form.'+question.variable+'.$error.float">This is not valid float!</div>'+
|
||||
'<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$error.ngMin || job_launch_form.'+question.variable+'.$error.ngMax"> The value must be in range {{'+min+'}} to {{'+max+'}}!</div>';
|
||||
// '<div class="error survey_error" ng-show="job_launch_form.'+question.variable+'.$dirty || job_launch_form.'+question.variable+'.$error.required"> A value is required!</div>';
|
||||
}
|
||||
html+='</div>';
|
||||
if(question.index === scope.survey_questions.length-1){
|
||||
|
||||
@ -148,6 +148,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
// scope: true,
|
||||
link: function (scope, elem, attr, ctrl) {
|
||||
scope.$watch(attr.ngMin, function () {
|
||||
ctrl.$setViewValue(ctrl.$viewValue);
|
||||
@ -162,7 +163,6 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minValidator);
|
||||
ctrl.$formatters.push(minValidator);
|
||||
}
|
||||
@ -173,6 +173,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
// scope: true,
|
||||
link: function (scope, elem, attr, ctrl) {
|
||||
scope.$watch(attr.ngMax, function () {
|
||||
ctrl.$setViewValue(ctrl.$viewValue);
|
||||
@ -187,7 +188,6 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxValidator);
|
||||
ctrl.$formatters.push(maxValidator);
|
||||
}
|
||||
@ -196,14 +196,19 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
|
||||
|
||||
.directive('smartFloat', function() {
|
||||
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
|
||||
var FLOAT_REGEXP_1 = /^\$?\d+(.\d{3})*(\,\d*)?$/, //Numbers like: 1.123,56
|
||||
FLOAT_REGEXP_2 = /^\$?\d+(,\d{3})*(\.\d*)?$/; //Numbers like: 1,123.56
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (FLOAT_REGEXP.test(viewValue)) {
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (FLOAT_REGEXP_1.test(Number(viewValue))) {
|
||||
ctrl.$setValidity('float', true);
|
||||
return parseFloat(viewValue.replace(',', '.'));
|
||||
return parseFloat(viewValue.replace('.', '').replace(',', '.'));
|
||||
} else if (FLOAT_REGEXP_2.test(Number(viewValue))) {
|
||||
ctrl.$setValidity('float', true);
|
||||
return parseFloat(viewValue.replace(',', ''));
|
||||
} else {
|
||||
ctrl.$setValidity('float', false);
|
||||
return undefined;
|
||||
@ -211,6 +216,21 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
});
|
||||
}
|
||||
};
|
||||
// var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
|
||||
// return {
|
||||
// require: 'ngModel',
|
||||
// link: function(scope, elm, attrs, ctrl) {
|
||||
// ctrl.$parsers.unshift(function(viewValue) {
|
||||
// if (FLOAT_REGEXP.test(viewValue)) {
|
||||
// ctrl.$setValidity('float', true);
|
||||
// return parseFloat(viewValue.replace(',', '.'));
|
||||
// } else {
|
||||
// ctrl.$setValidity('float', false);
|
||||
// return undefined;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
})
|
||||
|
||||
// integer Validate that input is of type integer. Taken from Angular developer
|
||||
@ -221,6 +241,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
// override/interfere with this directive.
|
||||
.directive('integer', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
|
||||
@ -1,21 +1,38 @@
|
||||
Release Process
|
||||
===============
|
||||
|
||||
This document describes the process of created and publishing an Ansible Tower release.
|
||||
This document describes the process of creating and publishing an Ansible Tower release.
|
||||
|
||||
Time for a release
|
||||
------------------
|
||||
|
||||
When the time comes for a release, the first step is to tag the release in git.
|
||||
When the time comes for a release, the following steps will ensure a smooth and
|
||||
successful release.
|
||||
|
||||
1. Verify that the `__version__` variable has been updated in `awx/__init__.py`.
|
||||
|
||||
__version__ = 'X.Y.Z'
|
||||
|
||||
2. Update the rpm package changelog by adding a new entry to the file `packaging/rpm/ansible-tower.spec`.
|
||||
|
||||
3. Update the debian package changelog by adding a new entry to the file `packaging/debian/changelog`.
|
||||
|
||||
4. Tag and push the release to git.
|
||||
|
||||
# git tag <X.Y.Z>
|
||||
# git push --tags
|
||||
|
||||
5. Create and push a release branch to git.
|
||||
|
||||
# git branch release_<X.Y.Z>
|
||||
# git checkout release_<X.Y.Z>
|
||||
# git push origin release_<X.Y.Z>
|
||||
|
||||
Monitor Jenkins
|
||||
---------------
|
||||
Once tagged, [Jenkins](http://50.116.42.103/view/Tower/) will take care of the
|
||||
following steps. The jenkins job
|
||||
[Release_Tower](http://50.116.42.103/view/Tower/job/Release_Tower/) will detect
|
||||
the recent tag, and initiate the `OFFICIAL=yes` build process.
|
||||
Once tagged, one must launch the [Release_Tower](http://50.116.42.103/view/Tower/job/Release_Tower/) with the following parameters:
|
||||
* `GIT_BRANCH=origin/tags/<X.Y.Z>`
|
||||
* `OFFICIAL=yes`
|
||||
|
||||
The following jobs will be triggered:
|
||||
* [Build_Tower_TAR](http://50.116.42.103/view/Tower/)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user