mirror of
https://github.com/ansible/awx.git
synced 2026-03-20 18:37:39 -02:30
Expand dbconfig support
* Support updating settings values * Support activity stream endpoint * Support clearing value * Improve type conversion system for displaying values
This commit is contained in:
@@ -2111,6 +2111,35 @@ class TowerSettingsSerializer(BaseSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = TowerSettings
|
model = TowerSettings
|
||||||
fields = ('key', 'description', 'category', 'value', 'value_type', 'user')
|
fields = ('key', 'description', 'category', 'value', 'value_type', 'user')
|
||||||
|
read_only_fields = ('description', 'category', 'value_type', 'user')
|
||||||
|
|
||||||
|
def from_native(self, data, files):
|
||||||
|
if data['key'] not in settings.TOWER_SETTINGS_MANIFEST:
|
||||||
|
self._errors = {'key': 'Key {0} is not a valid settings key'.format(data['key'])}
|
||||||
|
return
|
||||||
|
current_val = TowerSettings.objects.filter(key=data['key'])
|
||||||
|
if current_val.exists():
|
||||||
|
current_val.delete()
|
||||||
|
manifest_val = settings.TOWER_SETTINGS_MANIFEST[data['key']]
|
||||||
|
data['description'] = manifest_val['description']
|
||||||
|
data['category'] = manifest_val['category']
|
||||||
|
data['value_type'] = manifest_val['type']
|
||||||
|
return super(TowerSettingsSerializer, self).from_native(data, files)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
manifest = settings.TOWER_SETTINGS_MANIFEST
|
||||||
|
if attrs['key'] not in manifest:
|
||||||
|
raise serializers.ValidationError(dict(key=["Key {0} is not a valid settings key".format(attrs['key'])]))
|
||||||
|
# TODO: Type checking/coercion, contextual validation
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def save_object(self, obj, **kwargs):
|
||||||
|
print("kwargs {0}".format(kwargs))
|
||||||
|
manifest_val = settings.TOWER_SETTINGS_MANIFEST[obj.key]
|
||||||
|
obj.description = manifest_val['description']
|
||||||
|
obj.category = manifest_val['category']
|
||||||
|
obj.value_type = manifest_val['type']
|
||||||
|
return super(TowerSettingsSerializer, self).save_object(obj, **kwargs)
|
||||||
|
|
||||||
class AuthTokenSerializer(serializers.Serializer):
|
class AuthTokenSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,8 @@ activity_stream_urls = patterns('awx.api.views',
|
|||||||
)
|
)
|
||||||
|
|
||||||
settings_urls = patterns('awx.api.views',
|
settings_urls = patterns('awx.api.views',
|
||||||
url(r'^$', 'settings_list'))
|
url(r'^$', 'settings_list'),
|
||||||
|
url(r'^reset/$', 'settings_reset'))
|
||||||
|
|
||||||
v1_urls = patterns('awx.api.views',
|
v1_urls = patterns('awx.api.views',
|
||||||
url(r'^$', 'api_v1_root_view'),
|
url(r'^$', 'api_v1_root_view'),
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ from awx.api.renderers import * # noqa
|
|||||||
from awx.api.serializers import * # noqa
|
from awx.api.serializers import * # noqa
|
||||||
from awx.fact.models import * # noqa
|
from awx.fact.models import * # noqa
|
||||||
from awx.main.utils import emit_websocket_notification
|
from awx.main.utils import emit_websocket_notification
|
||||||
|
from awx.main.conf import TowerConfiguration
|
||||||
|
|
||||||
def api_exception_handler(exc):
|
def api_exception_handler(exc):
|
||||||
'''
|
'''
|
||||||
@@ -2971,7 +2972,12 @@ class SettingsList(ListCreateAPIView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
SettingsTuple = namedtuple('Settings', ['key', 'description', 'category', 'value', 'value_type', 'user'])
|
SettingsTuple = namedtuple('Settings', ['key', 'description', 'category', 'value', 'value_type', 'user'])
|
||||||
# TODO: Filter by what the user can see
|
# TODO: Filter by what the user can see
|
||||||
all_defined_settings = {s.key: SettingsTuple(s.key, s.description, s.category, s.value, s.value_type, s.user) for s in TowerSettings.objects.all()}
|
all_defined_settings = {s.key: SettingsTuple(s.key,
|
||||||
|
s.description,
|
||||||
|
s.category,
|
||||||
|
s.value_converted,
|
||||||
|
s.value_type,
|
||||||
|
s.user) for s in TowerSettings.objects.all()}
|
||||||
manifest_settings = settings.TOWER_SETTINGS_MANIFEST
|
manifest_settings = settings.TOWER_SETTINGS_MANIFEST
|
||||||
settings_actual = []
|
settings_actual = []
|
||||||
for settings_key in manifest_settings:
|
for settings_key in manifest_settings:
|
||||||
@@ -2987,6 +2993,18 @@ class SettingsList(ListCreateAPIView):
|
|||||||
None))
|
None))
|
||||||
return settings_actual
|
return settings_actual
|
||||||
|
|
||||||
|
class SettingsReset(APIView):
|
||||||
|
|
||||||
|
view_name = "Reset a settings value"
|
||||||
|
new_in_300 = True
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
# TODO: RBAC
|
||||||
|
setting_key = request.DATA.get('key', None)
|
||||||
|
if setting_key is not None:
|
||||||
|
TowerSettings.objects.filter(key=settings_key).delete()
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
# Create view functions for all of the class-based views to simplify inclusion
|
# Create view functions for all of the class-based views to simplify inclusion
|
||||||
# in URL patterns and reverse URL lookups, converting CamelCase names to
|
# in URL patterns and reverse URL lookups, converting CamelCase names to
|
||||||
# lowercase_with_underscore (e.g. MyView.as_view() becomes my_view).
|
# lowercase_with_underscore (e.g. MyView.as_view() becomes my_view).
|
||||||
|
|||||||
@@ -5,23 +5,13 @@ import json
|
|||||||
from django.conf import settings as django_settings
|
from django.conf import settings as django_settings
|
||||||
from awx.main.models.configuration import TowerSettings
|
from awx.main.models.configuration import TowerSettings
|
||||||
|
|
||||||
class TowerSettings(object):
|
class TowerConfiguration(object):
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
ts = TowerSettings.objects.filter(key=name)
|
ts = TowerSettings.objects.filter(key=name)
|
||||||
if not ts.exists:
|
if not ts.exists:
|
||||||
return getattr(django_settings, key)
|
return getattr(django_settings, key)
|
||||||
ts = ts[0]
|
return ts[0].value_converted
|
||||||
if ts.value_type == 'json':
|
|
||||||
converted_type = json.loads(ts.value)
|
|
||||||
elif ts.value_type == 'password':
|
|
||||||
converted_type = ts.value
|
|
||||||
elif ts.value_type == 'list':
|
|
||||||
converted_type = [x.strip() for x in a.split(',')]
|
|
||||||
else:
|
|
||||||
t = getattr(__builtin__, ts.value_type)
|
|
||||||
converted_type = t(ts.value)
|
|
||||||
return converted_type
|
|
||||||
|
|
||||||
def create(key, value):
|
def create(key, value):
|
||||||
settings_manifest = django_settings.TOWER_SETTINGS_MANIFEST
|
settings_manifest = django_settings.TOWER_SETTINGS_MANIFEST
|
||||||
|
|||||||
@@ -22,11 +22,23 @@ class Migration(SchemaMigration):
|
|||||||
))
|
))
|
||||||
db.send_create_signal('main', ['TowerSettings'])
|
db.send_create_signal('main', ['TowerSettings'])
|
||||||
|
|
||||||
|
# Adding M2M table for field tower_settings on 'ActivityStream'
|
||||||
|
m2m_table_name = db.shorten_name(u'main_activitystream_tower_settings')
|
||||||
|
db.create_table(m2m_table_name, (
|
||||||
|
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||||
|
('activitystream', models.ForeignKey(orm['main.activitystream'], null=False)),
|
||||||
|
('towersettings', models.ForeignKey(orm['main.towersettings'], null=False))
|
||||||
|
))
|
||||||
|
db.create_unique(m2m_table_name, ['activitystream_id', 'towersettings_id'])
|
||||||
|
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
# Deleting model 'TowerSettings'
|
# Deleting model 'TowerSettings'
|
||||||
db.delete_table(u'main_towersettings')
|
db.delete_table(u'main_towersettings')
|
||||||
|
|
||||||
|
# Removing M2M table for field tower_settings on 'ActivityStream'
|
||||||
|
db.delete_table(db.shorten_name(u'main_activitystream_tower_settings'))
|
||||||
|
|
||||||
|
|
||||||
models = {
|
models = {
|
||||||
u'auth.group': {
|
u'auth.group': {
|
||||||
@@ -91,6 +103,7 @@ class Migration(SchemaMigration):
|
|||||||
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", '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'}),
|
'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'}),
|
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'tower_settings': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.TowerSettings']", 'symmetrical': 'False', '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': ('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']"}),
|
'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'})
|
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class ActivityStream(models.Model):
|
|||||||
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)
|
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)
|
||||||
schedule = models.ManyToManyField("Schedule", blank=True)
|
schedule = models.ManyToManyField("Schedule", blank=True)
|
||||||
custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True)
|
custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True)
|
||||||
|
tower_settings = models.ManyToManyField("TowerSettings", blank=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('api:activity_stream_detail', args=(self.pk,))
|
return reverse('api:activity_stream_detail', args=(self.pk,))
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class TowerSettings(CreatedModifiedModel):
|
|||||||
('int', _('Integer')),
|
('int', _('Integer')),
|
||||||
('float', _('Decimal')),
|
('float', _('Decimal')),
|
||||||
('json', _('JSON')),
|
('json', _('JSON')),
|
||||||
|
('bool', _('Boolean')),
|
||||||
('password', _('Password')),
|
('password', _('Password')),
|
||||||
('list', _('List'))
|
('list', _('List'))
|
||||||
]
|
]
|
||||||
@@ -43,3 +44,15 @@ class TowerSettings(CreatedModifiedModel):
|
|||||||
editable=False,
|
editable=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value_converted(self):
|
||||||
|
if self.value_type == 'json':
|
||||||
|
converted_type = json.loads(self.value)
|
||||||
|
elif self.value_type == 'password':
|
||||||
|
converted_type = self.value
|
||||||
|
elif self.value_type == 'list':
|
||||||
|
converted_type = [x.strip() for x in self.value.split(',')]
|
||||||
|
else:
|
||||||
|
t = __builtins__[self.value_type]
|
||||||
|
converted_type = t(self.value)
|
||||||
|
return converted_type
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ model_serializer_mapping = {
|
|||||||
JobTemplate: JobTemplateSerializer,
|
JobTemplate: JobTemplateSerializer,
|
||||||
Job: JobSerializer,
|
Job: JobSerializer,
|
||||||
AdHocCommand: AdHocCommandSerializer,
|
AdHocCommand: AdHocCommandSerializer,
|
||||||
|
TowerSettings: TowerSettingsSerializer,
|
||||||
}
|
}
|
||||||
|
|
||||||
def activity_stream_create(sender, instance, created, **kwargs):
|
def activity_stream_create(sender, instance, created, **kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user