mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 01:17:37 -02:30
Merge branch 'devel' into 4853-survey-workflow-buttons
This commit is contained in:
2
Makefile
2
Makefile
@@ -937,7 +937,7 @@ install:
|
|||||||
$(PYTHON) setup.py install $(SETUP_INSTALL_ARGS)
|
$(PYTHON) setup.py install $(SETUP_INSTALL_ARGS)
|
||||||
|
|
||||||
docker-auth:
|
docker-auth:
|
||||||
docker login -e 1234@5678.com -u oauth2accesstoken -p "$(GCLOUD_AUTH)" https://gcr.io
|
docker login -u oauth2accesstoken -p "$(GCLOUD_AUTH)" https://gcr.io
|
||||||
|
|
||||||
# Docker isolated rampart
|
# Docker isolated rampart
|
||||||
docker-isolated:
|
docker-isolated:
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ import ansiconv
|
|||||||
from social.backends.utils import load_backends
|
from social.backends.utils import load_backends
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.main.tasks import send_notifications, update_host_smart_inventory_memberships, delete_inventory
|
from awx.main.tasks import send_notifications, update_host_smart_inventory_memberships
|
||||||
from awx.main.access import get_user_queryset
|
from awx.main.access import get_user_queryset
|
||||||
from awx.main.ha import is_ha_environment
|
from awx.main.ha import is_ha_environment
|
||||||
from awx.api.authentication import TaskAuthentication, TokenGetAuthentication
|
from awx.api.authentication import TaskAuthentication, TokenGetAuthentication
|
||||||
@@ -1839,15 +1839,13 @@ class InventoryDetail(ControlledByScmMixin, RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.pending_deletion is True:
|
|
||||||
return Response(dict(error=_("Inventory is already being deleted.")), status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
if not request.user.can_access(self.model, 'delete', obj):
|
if not request.user.can_access(self.model, 'delete', obj):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
obj.websocket_emit_status('pending_deletion')
|
try:
|
||||||
delete_inventory.delay(obj.id)
|
obj.schedule_deletion()
|
||||||
obj.pending_deletion = True
|
return Response(status=status.HTTP_202_ACCEPTED)
|
||||||
obj.save(update_fields=['pending_deletion'])
|
except RuntimeError, e:
|
||||||
return Response(status=status.HTTP_202_ACCEPTED)
|
return Response(dict(error=_("{0}".format(e))), status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):
|
class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class IsolatedManager(object):
|
|||||||
'CALLBACK_QUEUE': '',
|
'CALLBACK_QUEUE': '',
|
||||||
'CALLBACK_CONNECTION': '',
|
'CALLBACK_CONNECTION': '',
|
||||||
'ANSIBLE_RETRY_FILES_ENABLED': 'False',
|
'ANSIBLE_RETRY_FILES_ENABLED': 'False',
|
||||||
'ANSIBLE_HOST_KEY_CHECKING': 'False'
|
'ANSIBLE_HOST_KEY_CHECKING': 'False',
|
||||||
|
'ANSIBLE_LIBRARY': os.path.join(os.path.dirname(awx.__file__), 'plugins', 'isolated')
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -204,7 +205,7 @@ class IsolatedManager(object):
|
|||||||
os.symlink(self.cwd, self.path_to('project'))
|
os.symlink(self.cwd, self.path_to('project'))
|
||||||
|
|
||||||
# create directories for build artifacts to live in
|
# create directories for build artifacts to live in
|
||||||
os.makedirs(self.path_to('artifacts', 'job_events'), mode=stat.S_IRUSR | stat.S_IWUSR)
|
os.makedirs(self.path_to('artifacts', 'job_events'), mode=stat.S_IXUSR + stat.S_IWUSR + stat.S_IRUSR)
|
||||||
|
|
||||||
def _missing_artifacts(self, path_list, buff):
|
def _missing_artifacts(self, path_list, buff):
|
||||||
missing_artifacts = filter(lambda path: not os.path.exists(path), path_list)
|
missing_artifacts = filter(lambda path: not os.path.exists(path), path_list)
|
||||||
@@ -347,7 +348,6 @@ class IsolatedManager(object):
|
|||||||
args = ['ansible-playbook', '-u', settings.AWX_ISOLATED_USERNAME, '-i',
|
args = ['ansible-playbook', '-u', settings.AWX_ISOLATED_USERNAME, '-i',
|
||||||
hostname_string, 'heartbeat_isolated.yml']
|
hostname_string, 'heartbeat_isolated.yml']
|
||||||
env = cls._base_management_env()
|
env = cls._base_management_env()
|
||||||
env['ANSIBLE_LIBRARY'] = os.path.join(os.path.dirname(awx.__file__), 'lib', 'management_modules')
|
|
||||||
env['ANSIBLE_STDOUT_CALLBACK'] = 'json'
|
env['ANSIBLE_STDOUT_CALLBACK'] = 'json'
|
||||||
|
|
||||||
buff = cStringIO.StringIO()
|
buff = cStringIO.StringIO()
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ class Migration(migrations.Migration):
|
|||||||
name='pending_deletion',
|
name='pending_deletion',
|
||||||
field=models.BooleanField(default=False, help_text='Flag indicating the inventory is being deleted.', editable=False),
|
field=models.BooleanField(default=False, help_text='Flag indicating the inventory is being deleted.', editable=False),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='inventory',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(related_name='inventories', on_delete=models.deletion.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True),
|
||||||
|
),
|
||||||
|
|
||||||
# Facts
|
# Facts
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin):
|
|||||||
'Organization',
|
'Organization',
|
||||||
related_name='inventories',
|
related_name='inventories',
|
||||||
help_text=_('Organization containing this inventory.'),
|
help_text=_('Organization containing this inventory.'),
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
)
|
)
|
||||||
variables = models.TextField(
|
variables = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -373,6 +374,16 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin):
|
|||||||
raise ValidationError(_("Credential kind must be 'insights'."))
|
raise ValidationError(_("Credential kind must be 'insights'."))
|
||||||
return self.insights_credential
|
return self.insights_credential
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def schedule_deletion(self):
|
||||||
|
from awx.main.tasks import delete_inventory
|
||||||
|
if self.pending_deletion is True:
|
||||||
|
raise RuntimeError("Inventory is already pending deletion.")
|
||||||
|
self.websocket_emit_status('pending_deletion')
|
||||||
|
delete_inventory.delay(self.pk)
|
||||||
|
self.pending_deletion = True
|
||||||
|
self.save(update_fields=['pending_deletion'])
|
||||||
|
|
||||||
|
|
||||||
class SmartInventoryMembership(BaseModel):
|
class SmartInventoryMembership(BaseModel):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -510,3 +510,13 @@ def get_current_user_from_drf_request(sender, **kwargs):
|
|||||||
request = get_current_request()
|
request = get_current_request()
|
||||||
drf_request = getattr(request, 'drf_request', None)
|
drf_request = getattr(request, 'drf_request', None)
|
||||||
return (getattr(drf_request, 'user', False), 0)
|
return (getattr(drf_request, 'user', False), 0)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_delete, sender=Organization)
|
||||||
|
def delete_inventory_for_org(sender, instance, **kwargs):
|
||||||
|
inventories = Inventory.objects.filter(organization__pk=instance.pk)
|
||||||
|
for inventory in inventories:
|
||||||
|
try:
|
||||||
|
inventory.schedule_deletion()
|
||||||
|
except RuntimeError, e:
|
||||||
|
logger.debug(e)
|
||||||
|
|||||||
@@ -360,18 +360,17 @@ def update_host_smart_inventory_memberships():
|
|||||||
def delete_inventory(inventory_id):
|
def delete_inventory(inventory_id):
|
||||||
with ignore_inventory_computed_fields(), \
|
with ignore_inventory_computed_fields(), \
|
||||||
ignore_inventory_group_removal():
|
ignore_inventory_group_removal():
|
||||||
with transaction.atomic():
|
try:
|
||||||
try:
|
i = Inventory.objects.get(id=inventory_id)
|
||||||
i = Inventory.objects.get(id=inventory_id)
|
|
||||||
except Inventory.DoesNotExist:
|
|
||||||
logger.error("Delete Inventory failed due to missing inventory: " + str(inventory_id))
|
|
||||||
return
|
|
||||||
i.delete()
|
i.delete()
|
||||||
emit_channel_notification(
|
emit_channel_notification(
|
||||||
'inventories-status_changed',
|
'inventories-status_changed',
|
||||||
{'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'}
|
{'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'}
|
||||||
)
|
)
|
||||||
logger.debug('Deleted inventory: %s' % inventory_id)
|
logger.debug('Deleted inventory: %s' % inventory_id)
|
||||||
|
except Inventory.DoesNotExist:
|
||||||
|
logger.error("Delete Inventory failed due to missing inventory: " + str(inventory_id))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class BaseTask(Task):
|
class BaseTask(Task):
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def test_async_inventory_duplicate_deletion_prevention(delete, get, inventory, a
|
|||||||
|
|
||||||
resp = delete(reverse('api:inventory_detail', kwargs={'pk': inventory.id}), alice)
|
resp = delete(reverse('api:inventory_detail', kwargs={'pk': inventory.id}), alice)
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == 400
|
||||||
assert resp.data['error'] == 'Inventory is already being deleted.'
|
assert resp.data['error'] == 'Inventory is already pending deletion.'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('order_by', ('script', '-script', 'script,pk', '-script,pk'))
|
@pytest.mark.parametrize('order_by', ('script', '-script', 'script,pk', '-script,pk'))
|
||||||
@@ -314,12 +314,12 @@ class TestControlledBySCM:
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
class TestInsightsCredential:
|
class TestInsightsCredential:
|
||||||
def test_insights_credential(self, patch, insights_inventory, admin_user, insights_credential):
|
def test_insights_credential(self, patch, insights_inventory, admin_user, insights_credential):
|
||||||
patch(insights_inventory.get_absolute_url(),
|
patch(insights_inventory.get_absolute_url(),
|
||||||
{'insights_credential': insights_credential.id}, admin_user,
|
{'insights_credential': insights_credential.id}, admin_user,
|
||||||
expect=200)
|
expect=200)
|
||||||
|
|
||||||
def test_non_insights_credential(self, patch, insights_inventory, admin_user, scm_credential):
|
def test_non_insights_credential(self, patch, insights_inventory, admin_user, scm_credential):
|
||||||
patch(insights_inventory.get_absolute_url(),
|
patch(insights_inventory.get_absolute_url(),
|
||||||
{'insights_credential': scm_credential.id}, admin_user,
|
{'insights_credential': scm_credential.id}, admin_user,
|
||||||
expect=400)
|
expect=400)
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ def test_build_isolated_job_data(private_data_dir, rsa_key):
|
|||||||
|
|
||||||
path = os.path.join(private_data_dir, 'artifacts')
|
path = os.path.join(private_data_dir, 'artifacts')
|
||||||
assert os.path.isdir(path)
|
assert os.path.isdir(path)
|
||||||
assert stat.S_IMODE(os.stat(path).st_mode) == stat.S_IRUSR + stat.S_IWUSR # user rw
|
assert stat.S_IMODE(os.stat(path).st_mode) == stat.S_IXUSR + stat.S_IWUSR + stat.S_IRUSR # user rwx
|
||||||
|
|
||||||
path = os.path.join(private_data_dir, 'args')
|
path = os.path.join(private_data_dir, 'args')
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r') as f:
|
||||||
|
|||||||
@@ -106,14 +106,14 @@ function(i18n) {
|
|||||||
related: {
|
related: {
|
||||||
ansible_facts: {
|
ansible_facts: {
|
||||||
name: 'ansible_facts',
|
name: 'ansible_facts',
|
||||||
awToolTip: i18n._('Please save before viewing facts'),
|
awToolTip: i18n._('Please save before viewing facts.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Facts'),
|
title: i18n._('Facts'),
|
||||||
skipGenerator: true
|
skipGenerator: true
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
name: 'groups',
|
name: 'groups',
|
||||||
awToolTip: i18n._('Please save before defining groups'),
|
awToolTip: i18n._('Please save before defining groups.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
ngClick: "$state.go('hosts.edit.groups')",
|
ngClick: "$state.go('hosts.edit.groups')",
|
||||||
title: i18n._('Groups'),
|
title: i18n._('Groups'),
|
||||||
@@ -122,7 +122,7 @@ function(i18n) {
|
|||||||
},
|
},
|
||||||
insights: {
|
insights: {
|
||||||
name: 'insights',
|
name: 'insights',
|
||||||
awToolTip: i18n._('Please save before viewing Insights'),
|
awToolTip: i18n._('Please save before viewing Insights.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Insights'),
|
title: i18n._('Insights'),
|
||||||
skipGenerator: true,
|
skipGenerator: true,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
export default ['i18n', function(i18n) {
|
export default ['i18n', function(i18n) {
|
||||||
return {
|
return {
|
||||||
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||||
awToolTip: i18n._('Please save and run a job to view'),
|
awToolTip: i18n._('Please save and run a job to view.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
name: 'completed_jobs',
|
name: 'completed_jobs',
|
||||||
basePath: 'unified_jobs',
|
basePath: 'unified_jobs',
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function(i18n){
|
|||||||
related: {
|
related: {
|
||||||
nested_groups: {
|
nested_groups: {
|
||||||
name: 'nested_groups',
|
name: 'nested_groups',
|
||||||
awToolTip: i18n._('Please save before defining groups'),
|
awToolTip: i18n._('Please save before defining groups.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
ngClick: "$state.go('inventories.edit.groups.edit.nested_groups')",
|
ngClick: "$state.go('inventories.edit.groups.edit.nested_groups')",
|
||||||
title: i18n._('Groups'),
|
title: i18n._('Groups'),
|
||||||
@@ -91,7 +91,7 @@ function(i18n){
|
|||||||
},
|
},
|
||||||
nested_hosts: {
|
nested_hosts: {
|
||||||
name: 'nested_hosts',
|
name: 'nested_hosts',
|
||||||
awToolTip: i18n._('Please save before defining hosts'),
|
awToolTip: i18n._('Please save before defining hosts.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts')",
|
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts')",
|
||||||
include: "NestedHostsListDefinition",
|
include: "NestedHostsListDefinition",
|
||||||
|
|||||||
@@ -107,14 +107,14 @@ function(i18n) {
|
|||||||
related: {
|
related: {
|
||||||
ansible_facts: {
|
ansible_facts: {
|
||||||
name: 'ansible_facts',
|
name: 'ansible_facts',
|
||||||
awToolTip: i18n._('Please save before viewing facts'),
|
awToolTip: i18n._('Please save before viewing facts.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Facts'),
|
title: i18n._('Facts'),
|
||||||
skipGenerator: true
|
skipGenerator: true
|
||||||
},
|
},
|
||||||
nested_groups: {
|
nested_groups: {
|
||||||
name: 'nested_groups',
|
name: 'nested_groups',
|
||||||
awToolTip: i18n._('Please save before defining groups'),
|
awToolTip: i18n._('Please save before defining groups.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts.edit.nested_groups')",
|
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts.edit.nested_groups')",
|
||||||
title: i18n._('Groups'),
|
title: i18n._('Groups'),
|
||||||
|
|||||||
@@ -107,14 +107,14 @@ function(i18n) {
|
|||||||
related: {
|
related: {
|
||||||
ansible_facts: {
|
ansible_facts: {
|
||||||
name: 'ansible_facts',
|
name: 'ansible_facts',
|
||||||
awToolTip: i18n._('Please save before viewing facts'),
|
awToolTip: i18n._('Please save before viewing facts.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Facts'),
|
title: i18n._('Facts'),
|
||||||
skipGenerator: true
|
skipGenerator: true
|
||||||
},
|
},
|
||||||
nested_groups: {
|
nested_groups: {
|
||||||
name: 'nested_groups',
|
name: 'nested_groups',
|
||||||
awToolTip: i18n._('Please save before defining groups'),
|
awToolTip: i18n._('Please save before defining groups.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
ngClick: "$state.go('inventories.edit.hosts.edit.nested_groups')",
|
ngClick: "$state.go('inventories.edit.hosts.edit.nested_groups')",
|
||||||
title: i18n._('Groups'),
|
title: i18n._('Groups'),
|
||||||
@@ -122,7 +122,7 @@ function(i18n) {
|
|||||||
},
|
},
|
||||||
insights: {
|
insights: {
|
||||||
name: 'insights',
|
name: 'insights',
|
||||||
awToolTip: i18n._('Please save before viewing Insights'),
|
awToolTip: i18n._('Please save before viewing Insights.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Insights'),
|
title: i18n._('Insights'),
|
||||||
skipGenerator: true,
|
skipGenerator: true,
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCo
|
|||||||
related: {
|
related: {
|
||||||
permissions: {
|
permissions: {
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v2/inventories/{{$stateParams.smartinventory_id}}/access_list/',
|
basePath: 'api/v2/inventories/{{$stateParams.smartinventory_id}}/access_list/',
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ function(i18n, InventoryCompletedJobsList) {
|
|||||||
related: {
|
related: {
|
||||||
permissions: {
|
permissions: {
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/access_list/',
|
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/access_list/',
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
@@ -172,7 +172,7 @@ function(i18n, InventoryCompletedJobsList) {
|
|||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
name: 'groups',
|
name: 'groups',
|
||||||
awToolTip: i18n._('Please save before creating groups'),
|
awToolTip: i18n._('Please save before creating groups.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
include: "GroupList",
|
include: "GroupList",
|
||||||
title: i18n._('Groups'),
|
title: i18n._('Groups'),
|
||||||
@@ -181,7 +181,7 @@ function(i18n, InventoryCompletedJobsList) {
|
|||||||
},
|
},
|
||||||
hosts: {
|
hosts: {
|
||||||
name: 'hosts',
|
name: 'hosts',
|
||||||
awToolTip: i18n._('Please save before creating hosts'),
|
awToolTip: i18n._('Please save before creating hosts.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
include: "RelatedHostsListDefinition",
|
include: "RelatedHostsListDefinition",
|
||||||
title: i18n._('Hosts'),
|
title: i18n._('Hosts'),
|
||||||
@@ -190,7 +190,7 @@ function(i18n, InventoryCompletedJobsList) {
|
|||||||
},
|
},
|
||||||
inventory_sources: {
|
inventory_sources: {
|
||||||
name: 'inventory_sources',
|
name: 'inventory_sources',
|
||||||
awToolTip: i18n._('Please save before defining inventory sources'),
|
awToolTip: i18n._('Please save before defining inventory sources.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
title: i18n._('Sources'),
|
title: i18n._('Sources'),
|
||||||
iterator: 'inventory_source',
|
iterator: 'inventory_source',
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
data-placement="top"
|
data-placement="top"
|
||||||
ng-click="deleteJob()"
|
ng-click="deleteJob()"
|
||||||
ng-hide="job_status == 'running' ||
|
ng-hide="job_status == 'running' ||
|
||||||
job_status == 'pending' "
|
job_status == 'pending' || !job.summary_fields.user_capabilities.delete"
|
||||||
aw-tool-tip="Delete"
|
aw-tool-tip="Delete"
|
||||||
data-original-title=""
|
data-original-title=""
|
||||||
title="">
|
title="">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default ['i18n', function(i18n){
|
|||||||
return {
|
return {
|
||||||
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
awToolTip: i18n._('Please save before adding notifications'),
|
awToolTip: i18n._('Please save before adding notifications.'),
|
||||||
name: 'notifications' ,
|
name: 'notifications' ,
|
||||||
title: i18n._('Notifications'),
|
title: i18n._('Notifications'),
|
||||||
iterator: 'notification',
|
iterator: 'notification',
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default ['NotificationsList', 'i18n',
|
|||||||
users: {
|
users: {
|
||||||
name: 'users',
|
name: 'users',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
awToolTip: i18n._('Please save before adding users'),
|
awToolTip: i18n._('Please save before adding users.'),
|
||||||
basePath: 'api/v2/organizations/{{$stateParams.organization_id}}/access_list/',
|
basePath: 'api/v2/organizations/{{$stateParams.organization_id}}/access_list/',
|
||||||
search: {
|
search: {
|
||||||
order_by: 'username'
|
order_by: 'username'
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) {
|
|||||||
related: {
|
related: {
|
||||||
permissions: {
|
permissions: {
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
djangoModel: 'access_list',
|
djangoModel: 'access_list',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v2/projects/{{$stateParams.project_id}}/access_list/',
|
basePath: 'api/v2/projects/{{$stateParams.project_id}}/access_list/',
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default ['i18n', function(i18n) {
|
|||||||
users: {
|
users: {
|
||||||
name: 'users',
|
name: 'users',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
awToolTip: i18n._('Please save before adding users'),
|
awToolTip: i18n._('Please save before adding users.'),
|
||||||
basePath: 'api/v2/teams/{{$stateParams.team_id}}/access_list/',
|
basePath: 'api/v2/teams/{{$stateParams.team_id}}/access_list/',
|
||||||
search: {
|
search: {
|
||||||
order_by: 'username'
|
order_by: 'username'
|
||||||
@@ -110,7 +110,7 @@ export default ['i18n', function(i18n) {
|
|||||||
// @todo ask about name field / serializer on this endpoint
|
// @todo ask about name field / serializer on this endpoint
|
||||||
order_by: 'id'
|
order_by: 'id'
|
||||||
},
|
},
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
hideSearchAndActions: true,
|
hideSearchAndActions: true,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
export default ['i18n', function(i18n) {
|
export default ['i18n', function(i18n) {
|
||||||
return {
|
return {
|
||||||
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||||
awToolTip: i18n._('Please save and run a job to view'),
|
awToolTip: i18n._('Please save and run a job to view.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
name: 'completed_jobs',
|
name: 'completed_jobs',
|
||||||
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs',
|
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
Empty, ToJSON, CallbackHelpInit, GetChoices,
|
Empty, ToJSON, CallbackHelpInit, GetChoices,
|
||||||
$state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService
|
$state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Inject dynamic view
|
// Inject dynamic view
|
||||||
let defaultUrl = GetBasePath('job_templates'),
|
let defaultUrl = GetBasePath('job_templates'),
|
||||||
form = JobTemplateForm(),
|
form = JobTemplateForm(),
|
||||||
@@ -45,6 +45,8 @@
|
|||||||
default_val: false
|
default_val: false
|
||||||
});
|
});
|
||||||
CallbackHelpInit({ scope: $scope });
|
CallbackHelpInit({ scope: $scope });
|
||||||
|
|
||||||
|
$scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.');
|
||||||
}
|
}
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default
|
|||||||
[ '$filter', '$scope', '$rootScope',
|
[ '$filter', '$scope', '$rootScope',
|
||||||
'$location', '$stateParams', 'JobTemplateForm', 'GenerateForm',
|
'$location', '$stateParams', 'JobTemplateForm', 'GenerateForm',
|
||||||
'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup',
|
'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup',
|
||||||
'ParseTypeChange', 'Wait', 'selectedLabels',
|
'ParseTypeChange', 'Wait', 'selectedLabels', 'i18n',
|
||||||
'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit',
|
'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit',
|
||||||
'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
|
'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
|
||||||
'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels',
|
'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels',
|
||||||
@@ -22,7 +22,7 @@ export default
|
|||||||
$filter, $scope, $rootScope,
|
$filter, $scope, $rootScope,
|
||||||
$location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
$location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
||||||
ProcessErrors, GetBasePath, md5Setup,
|
ProcessErrors, GetBasePath, md5Setup,
|
||||||
ParseTypeChange, Wait, selectedLabels,
|
ParseTypeChange, Wait, selectedLabels, i18n,
|
||||||
Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state,
|
Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state,
|
||||||
CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels
|
CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels
|
||||||
) {
|
) {
|
||||||
@@ -54,6 +54,7 @@ export default
|
|||||||
$scope.showJobType = false;
|
$scope.showJobType = false;
|
||||||
$scope.instance_groups = InstanceGroupsData;
|
$scope.instance_groups = InstanceGroupsData;
|
||||||
$scope.credentialNotPresent = false;
|
$scope.credentialNotPresent = false;
|
||||||
|
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
|
||||||
|
|
||||||
SurveyControllerInit({
|
SurveyControllerInit({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
|||||||
},
|
},
|
||||||
permissions: {
|
permissions: {
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/access_list/',
|
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/access_list/',
|
||||||
search: {
|
search: {
|
||||||
@@ -447,7 +447,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
|||||||
ngClick: 'addSurvey()',
|
ngClick: 'addSurvey()',
|
||||||
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
|
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
|
||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
awToolTip: 'Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.',
|
awToolTip: '{{surveyTooltip}}',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
label: i18n._('Add Survey'),
|
label: i18n._('Add Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
@@ -457,7 +457,9 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
|||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
|
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
|
||||||
label: i18n._('Edit Survey'),
|
label: i18n._('Edit Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton',
|
||||||
|
awToolTip: '{{surveyTooltip}}',
|
||||||
|
dataPlacement: 'top'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
|
|||||||
related: {
|
related: {
|
||||||
permissions: {
|
permissions: {
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v2/workflow_job_templates/{{$stateParams.workflow_job_template_id}}/access_list/',
|
basePath: 'api/v2/workflow_job_templates/{{$stateParams.workflow_job_template_id}}/access_list/',
|
||||||
search: {
|
search: {
|
||||||
@@ -167,7 +167,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
|
|||||||
ngClick: 'addSurvey()',
|
ngClick: 'addSurvey()',
|
||||||
ngShow: '!survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
|
ngShow: '!survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
|
||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
awToolTip: 'Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.',
|
awToolTip: '{{surveyTooltip}}',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
label: i18n._('Add Survey'),
|
label: i18n._('Add Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
@@ -177,12 +177,14 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
|
|||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
ngShow: 'survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
|
ngShow: 'survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
|
||||||
label: i18n._('Edit Survey'),
|
label: i18n._('Edit Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton',
|
||||||
|
awToolTip: '{{surveyTooltip}}',
|
||||||
|
dataPlacement: 'top'
|
||||||
},
|
},
|
||||||
workflow_editor: {
|
workflow_editor: {
|
||||||
ngClick: 'openWorkflowMaker()',
|
ngClick: 'openWorkflowMaker()',
|
||||||
ngShow: '$state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\')',
|
ngShow: '$state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\')',
|
||||||
awToolTip: i18n._('Please save before defining the workflow graph'),
|
awToolTip: '{{workflowEditorTooltip}}',
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
label: i18n._('Workflow Editor'),
|
label: i18n._('Workflow Editor'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
export default [
|
export default [
|
||||||
'$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
|
'$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
|
||||||
'Wait', '$state', 'CreateSelect2', 'TemplatesService',
|
'Wait', '$state', 'CreateSelect2', 'TemplatesService',
|
||||||
'ToJSON', 'ParseTypeChange', '$q', 'Rest', 'GetBasePath', 'availableLabels',
|
'ToJSON', 'ParseTypeChange', '$q', 'Rest', 'GetBasePath', 'availableLabels', 'i18n',
|
||||||
function($scope, WorkflowForm, GenerateForm, Alert, ProcessErrors,
|
function($scope, WorkflowForm, GenerateForm, Alert, ProcessErrors,
|
||||||
Wait, $state, CreateSelect2, TemplatesService, ToJSON,
|
Wait, $state, CreateSelect2, TemplatesService, ToJSON,
|
||||||
ParseTypeChange, $q, Rest, GetBasePath, availableLabels) {
|
ParseTypeChange, $q, Rest, GetBasePath, availableLabels, i18n) {
|
||||||
|
|
||||||
// Inject dynamic view
|
// Inject dynamic view
|
||||||
let form = WorkflowForm(),
|
let form = WorkflowForm(),
|
||||||
@@ -41,6 +41,9 @@ export default [
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
addNew: true
|
addNew: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.workflowEditorTooltip = i18n._("Please save before defining the workflow graph.");
|
||||||
|
$scope.surveyTooltip = i18n._('Please save before adding a survey to this workflow.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.formSave = function () {
|
$scope.formSave = function () {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ export default [
|
|||||||
'ProcessErrors', 'GetBasePath', '$q', 'ParseTypeChange',
|
'ProcessErrors', 'GetBasePath', '$q', 'ParseTypeChange',
|
||||||
'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2',
|
'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2',
|
||||||
'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification',
|
'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification',
|
||||||
'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData',
|
'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'i18n',
|
||||||
function($scope, $stateParams, WorkflowForm, GenerateForm, Alert,
|
function($scope, $stateParams, WorkflowForm, GenerateForm, Alert,
|
||||||
ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
|
ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
|
||||||
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
|
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
|
||||||
TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData) {
|
TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n) {
|
||||||
|
|
||||||
$scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) {
|
$scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||||
if (val === false) {
|
if (val === false) {
|
||||||
$scope.canAddWorkflowJobTemplate = false;
|
$scope.canAddWorkflowJobTemplate = false;
|
||||||
@@ -60,6 +60,9 @@ export default [
|
|||||||
opts: opts
|
opts: opts
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
|
||||||
|
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
|
||||||
|
|
||||||
$scope.workflow_job_template_obj = workflowJobTemplateData;
|
$scope.workflow_job_template_obj = workflowJobTemplateData;
|
||||||
$scope.name = workflowJobTemplateData.name;
|
$scope.name = workflowJobTemplateData.name;
|
||||||
$scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
|
$scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export default ['i18n', function(i18n) {
|
|||||||
related: {
|
related: {
|
||||||
organizations: {
|
organizations: {
|
||||||
name: 'organizations',
|
name: 'organizations',
|
||||||
awToolTip: i18n._('Please save before assigning to organizations'),
|
awToolTip: i18n._('Please save before assigning to organizations.'),
|
||||||
basePath: 'api/v2/users/{{$stateParams.user_id}}/organizations',
|
basePath: 'api/v2/users/{{$stateParams.user_id}}/organizations',
|
||||||
emptyListText: i18n._('Please add user to an Organization.'),
|
emptyListText: i18n._('Please add user to an Organization.'),
|
||||||
search: {
|
search: {
|
||||||
@@ -146,7 +146,7 @@ export default ['i18n', function(i18n) {
|
|||||||
},
|
},
|
||||||
teams: {
|
teams: {
|
||||||
name: 'teams',
|
name: 'teams',
|
||||||
awToolTip: i18n._('Please save before assigning to teams'),
|
awToolTip: i18n._('Please save before assigning to teams.'),
|
||||||
basePath: 'api/v2/users/{{$stateParams.user_id}}/teams',
|
basePath: 'api/v2/users/{{$stateParams.user_id}}/teams',
|
||||||
search: {
|
search: {
|
||||||
page_size: '10'
|
page_size: '10'
|
||||||
@@ -177,7 +177,7 @@ export default ['i18n', function(i18n) {
|
|||||||
page_size: '10',
|
page_size: '10',
|
||||||
order_by: 'id'
|
order_by: 'id'
|
||||||
},
|
},
|
||||||
awToolTip: i18n._('Please save before assigning to organizations'),
|
awToolTip: i18n._('Please save before assigning to organizations.'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
hideSearchAndActions: true,
|
hideSearchAndActions: true,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
|||||||
Reference in New Issue
Block a user