Merge branch 'master' into expunge-zeromq-unstable

Conflicts:
	setup/roles/awx_install/handlers/main.yml
This commit is contained in:
Luke Sneeringer
2014-11-10 08:47:08 -06:00
17 changed files with 202 additions and 169 deletions

View File

@@ -41,7 +41,7 @@ from awx.main.utils import update_scm_url, get_type_for_model, get_model_for_typ
logger = logging.getLogger('awx.api.serializers') logger = logging.getLogger('awx.api.serializers')
# Fields that should be summarized regardless of object type. # Fields that should be summarized regardless of object type.
DEFAULT_SUMMARY_FIELDS = ('name', 'description')#, 'type') DEFAULT_SUMMARY_FIELDS = ('name', 'description')#, 'created_by', 'modified_by')#, 'type')
# Keys are fields (foreign keys) where, if found on an instance, summary info # Keys are fields (foreign keys) where, if found on an instance, summary info
# should be added to the serialized data. Values are a tuple of field names on # should be added to the serialized data. Values are a tuple of field names on
@@ -49,7 +49,7 @@ DEFAULT_SUMMARY_FIELDS = ('name', 'description')#, 'type')
# the related object). # the related object).
SUMMARIZABLE_FK_FIELDS = { SUMMARIZABLE_FK_FIELDS = {
'organization': DEFAULT_SUMMARY_FIELDS, 'organization': DEFAULT_SUMMARY_FIELDS,
'user': ('username', 'first_name', 'last_name'), 'user': ('id', 'username', 'first_name', 'last_name'),
'team': DEFAULT_SUMMARY_FIELDS, 'team': DEFAULT_SUMMARY_FIELDS,
'inventory': DEFAULT_SUMMARY_FIELDS + ('has_active_failures', 'inventory': DEFAULT_SUMMARY_FIELDS + ('has_active_failures',
'total_hosts', 'total_hosts',
@@ -289,6 +289,15 @@ class BaseSerializer(serializers.ModelSerializer):
# Can be raised by the reverse accessor for a OneToOneField. # Can be raised by the reverse accessor for a OneToOneField.
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
if getattr(obj, 'created_by', None) and obj.created_by.is_active:
summary_fields['created_by'] = SortedDict()
for field in SUMMARIZABLE_FK_FIELDS['user']:
summary_fields['created_by'][field] = getattr(obj.created_by, field)
if getattr(obj, 'modified_by', None) and obj.modified_by.is_active:
summary_fields['modified_by'] = SortedDict()
for field in SUMMARIZABLE_FK_FIELDS['user']:
summary_fields['modified_by'][field] = getattr(obj.modified_by, field)
print summary_fields
return summary_fields return summary_fields
def get_created(self, obj): def get_created(self, obj):

View File

@@ -5,6 +5,7 @@
# Python # Python
import datetime import datetime
import dateutil import dateutil
import functools
import time import time
import re import re
import socket import socket
@@ -431,30 +432,32 @@ class DashboardInventoryGraphView(APIView):
return Response(dashboard_data) return Response(dashboard_data)
class UserCreateAPIMixin(object): def disallow_superuser_escalation(cls):
"""A mixin subclass that ensures that only a superuser is able to create """Decorator that ensures that the post, put, and patch methods on the
another superuser. class, if they exist, perform a sanity check and disallow superuser
escalation by non-superusers.
""" """
def post(self, request, pk=None): # Create a method decorator that ensures superuser escalation by
self._superuser_sanity_check(request) # non-superusers is disallowed.
return super(UserCreateAPIMixin, self).post(request, pk=pk) def superuser_lockdown(method):
@functools.wraps(method)
def fx(self, request, *a, **kw):
if not request.user.is_superuser:
if request.DATA.get('is_superuser', False):
raise PermissionDenied('Only superusers may create '
'other superusers.')
return method(self, request, *a, **kw)
return fx
# def put(self, request, pk=None): # Ensure that if post, put, or patch methods exist, that they are decorated
# self._superuser_sanity_check(request) # with the sanity check decorator.
# return super(UserCreateAPIMixin, self).put(request, pk=pk) for vuln_method in ('post', 'put', 'patch'):
original_method = getattr(cls, vuln_method, None)
if original_method is not None:
setattr(cls, vuln_method, superuser_lockdown(original_method))
# def patch(self, request, pk=None): # Return the class object.
# self._superuser_sanity_check(request) return cls
# return super(UserCreateAPIMixin, self).patch(request, pk=pk)
def _superuser_sanity_check(self, request):
"""Ensure that if a non-superuser tries to create a superuser,
that the request is rejected.
"""
if not request.user.is_superuser:
if request.DATA.get('is_superuser', False):
raise PermissionDenied('Only superusers may create '
'other superusers.')
class ScheduleList(ListAPIView): class ScheduleList(ListAPIView):
@@ -518,14 +521,16 @@ class OrganizationInventoriesList(SubListAPIView):
parent_model = Organization parent_model = Organization
relationship = 'inventories' relationship = 'inventories'
class OrganizationUsersList(UserCreateAPIMixin, SubListCreateAPIView): @disallow_superuser_escalation
class OrganizationUsersList(SubListCreateAPIView):
model = User model = User
serializer_class = UserSerializer serializer_class = UserSerializer
parent_model = Organization parent_model = Organization
relationship = 'users' relationship = 'users'
class OrganizationAdminsList(UserCreateAPIMixin, SubListCreateAPIView): @disallow_superuser_escalation
class OrganizationAdminsList(SubListCreateAPIView):
model = User model = User
serializer_class = UserSerializer serializer_class = UserSerializer
@@ -565,7 +570,8 @@ class TeamDetail(RetrieveUpdateDestroyAPIView):
model = Team model = Team
serializer_class = TeamSerializer serializer_class = TeamSerializer
class TeamUsersList(UserCreateAPIMixin, SubListCreateAPIView): @disallow_superuser_escalation
class TeamUsersList(SubListCreateAPIView):
model = User model = User
serializer_class = UserSerializer serializer_class = UserSerializer
@@ -762,7 +768,9 @@ class ProjectUpdateCancel(GenericAPIView):
else: else:
return self.http_method_not_allowed(request, *args, **kwargs) return self.http_method_not_allowed(request, *args, **kwargs)
class UserList(UserCreateAPIMixin, ListCreateAPIView):
@disallow_superuser_escalation
class UserList(ListCreateAPIView):
model = User model = User
serializer_class = UserSerializer serializer_class = UserSerializer
@@ -841,6 +849,7 @@ class UserActivityStreamList(SubListAPIView):
return qs.filter(Q(actor=parent) | Q(user__in=[parent])) return qs.filter(Q(actor=parent) | Q(user__in=[parent]))
class UserDetail(RetrieveUpdateDestroyAPIView): class UserDetail(RetrieveUpdateDestroyAPIView):
model = User model = User
@@ -1467,6 +1476,7 @@ class JobTemplateLaunch(GenericAPIView):
data['passwords_needed_to_start'] = obj.passwords_needed_to_start data['passwords_needed_to_start'] = obj.passwords_needed_to_start
data['ask_variables_on_launch'] = obj.ask_variables_on_launch data['ask_variables_on_launch'] = obj.ask_variables_on_launch
data['variables_needed_to_start'] = obj.variables_needed_to_start data['variables_needed_to_start'] = obj.variables_needed_to_start
data['credential_needed_to_start'] = obj.credential is None
data['survey_enabled'] = obj.survey_enabled data['survey_enabled'] = obj.survey_enabled
return Response(data) return Response(data)

View File

@@ -826,41 +826,56 @@ class JobTemplateAccess(BaseAccess):
org_admin_qs = base_qs.filter( org_admin_qs = base_qs.filter(
project__organizations__admins__in=[self.user] project__organizations__admins__in=[self.user]
) )
allowed = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY] allowed = [PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY]
perm_qs = base_qs.filter( allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
# perm_qs = base_qs.filter(
# Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
# Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
# inventory__permissions__permission_type__in=allowed,
# project__permissions__permission_type__in=allowed,
# inventory__permissions__active=True,
# project__permissions__active=True,
# inventory__permissions__pk=F('project__permissions__pk'),
# )
perm_deploy_qs = base_qs.filter(
Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]), Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]), Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
inventory__permissions__permission_type__in=allowed, job_type=PERM_INVENTORY_DEPLOY,
project__permissions__permission_type__in=allowed, inventory__permissions__permission_type__in=allowed_deploy,
project__permissions__permission_type__in=allowed_deploy,
inventory__permissions__active=True, inventory__permissions__active=True,
project__permissions__active=True, project__permissions__active=True,
inventory__permissions__pk=F('project__permissions__pk'), inventory__permissions__pk=F('project__permissions__pk'),
) )
perm_check_qs = base_qs.filter(
Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
job_type=PERM_INVENTORY_CHECK,
inventory__permissions__permission_type__in=allowed_check,
project__permissions__permission_type__in=allowed_check,
inventory__permissions__active=True,
project__permissions__active=True,
inventory__permissions__pk=F('project__permissions__pk'),
)
# FIXME: I *think* this should work... needs more testing. # FIXME: I *think* this should work... needs more testing.
return org_admin_qs | perm_qs return org_admin_qs | perm_deploy_qs | perm_check_qs
def can_read(self, obj): def can_read(self, obj):
# you can only see the job templates that you have permission to launch. # you can only see the job templates that you have permission to launch.
data = { return self.can_start(obj)
'job_type': obj.job_type,
}
if obj.inventory and obj.inventory.pk:
data['inventory'] = obj.inventory.pk
if obj.project and obj.project.pk:
data['project'] = obj.project.pk
if obj.credential:
data['credential'] = obj.credential.pk
if obj.cloud_credential:
data['cloud_credential'] = obj.cloud_credential.pk
return self.can_add(data)
def can_add(self, data): def can_add(self, data):
''' '''
a user can create a job template if they are a superuser, an org admin a user can create a job template if they are a superuser, an org admin
of any org that the project is a member, or if they have user or team of any org that the project is a member, or if they have user or team
based permissions tying the project to the inventory source for the based permissions tying the project to the inventory source for the
given action. users who are able to create deploy jobs can also make given action as well as the 'create' deploy permission.
check (dry run) jobs. Users who are able to create deploy jobs can also run normal and check (dry run) jobs.
''' '''
if not data or '_method' in data: # So the browseable API will work? if not data or '_method' in data: # So the browseable API will work?
return True return True
@@ -950,29 +965,32 @@ class JobTemplateAccess(BaseAccess):
if obj.inventory is None or obj.project is None: if obj.inventory is None or obj.project is None:
return False return False
# If the user has admin access to the project they can start a job # If the user has admin access to the project they can start a job
if self.user.can_access(Project, 'admin', obj.project): if self.user.can_access(Project, 'admin', obj.project, None):
return True return True
# Otherwise check for explicitly granted permissions # Otherwise check for explicitly granted permissions
permission_qs = Permission.objects.filter( permission_qs = Permission.objects.filter(
Q(user=self.user) | Q(team__users__in=[self.user]), Q(user=self.user) | Q(team__users__in=[self.user]),
inventory=inventory, inventory=obj.inventory,
project=project, project=obj.project,
permission_type__in=[PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY], permission_type__in=[PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY],
) )
has_perm = False has_perm = False
for perm in permission_qs: for perm in permission_qs:
# If you have job template create permission that implies both CHECK and DEPLOY # If you have job template create permission that implies both CHECK and DEPLOY
# If you have DEPLOY permissions you can run both CHECK and DEPLOY # If you have DEPLOY permissions you can run both CHECK and DEPLOY
if perm.permission_type in [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]: if perm.permission_type in [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY] and \
obj.job_type == PERM_INVENTORY_DEPLOY:
has_perm = True has_perm = True
# If you only have CHECK permission then you can only run CHECK # If you only have CHECK permission then you can only run CHECK
if perm.permission_type == PERM_INVENTORY_CHECK and perm.permission_type == PERM_INVENTORY_CHECK: if perm.permission_type in [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK] and \
obj.job_type == PERM_INVENTORY_CHECK:
has_perm = True has_perm = True
dep_access = self.user.can_access(Inventory, 'read', obj.inventory) and \ dep_access = self.user.can_access(Inventory, 'read', obj.inventory) and \
self.user.can_access(Project, 'read', obj.project) self.user.can_access(Project, 'read', obj.project)
return self.can_read(obj) and dep_access and has_perm return dep_access and has_perm
def can_change(self, obj, data): def can_change(self, obj, data):
return self.can_read(obj) and self.can_add(data) return self.can_read(obj) and self.can_add(data)
@@ -998,17 +1016,44 @@ class JobAccess(BaseAccess):
project__organizations__admins__in=[self.user] project__organizations__admins__in=[self.user]
) )
allowed = [PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY] allowed = [PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY]
perm_qs = base_qs.filter(
allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
# perm_qs = base_qs.filter(
# Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
# Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
# inventory__permissions__permission_type__in=allowed,
# project__permissions__permission_type__in=allowed,
# inventory__permissions__active=True,
# project__permissions__active=True,
# inventory__permissions__pk=F('project__permissions__pk'),
# )
perm_deploy_qs = base_qs.filter(
Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]), Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]), Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
inventory__permissions__permission_type__in=allowed, job_type=PERM_INVENTORY_DEPLOY,
project__permissions__permission_type__in=allowed, inventory__permissions__permission_type__in=allowed_deploy,
project__permissions__permission_type__in=allowed_deploy,
inventory__permissions__active=True, inventory__permissions__active=True,
project__permissions__active=True, project__permissions__active=True,
inventory__permissions__pk=F('project__permissions__pk'), inventory__permissions__pk=F('project__permissions__pk'),
) )
perm_check_qs = base_qs.filter(
Q(inventory__permissions__user=self.user) | Q(inventory__permissions__team__users__in=[self.user]),
Q(project__permissions__user=self.user) | Q(project__permissions__team__users__in=[self.user]),
job_type=PERM_INVENTORY_CHECK,
inventory__permissions__permission_type__in=allowed_check,
project__permissions__permission_type__in=allowed_check,
inventory__permissions__active=True,
project__permissions__active=True,
inventory__permissions__pk=F('project__permissions__pk'),
)
# FIXME: I *think* this should work... needs more testing. # FIXME: I *think* this should work... needs more testing.
return org_admin_qs | perm_qs return org_admin_qs | perm_deploy_qs | perm_check_qs
def can_add(self, data): def can_add(self, data):
if not data or '_method' in data: # So the browseable API will work? if not data or '_method' in data: # So the browseable API will work?

View File

@@ -339,12 +339,13 @@ class BaseTask(Task):
- /etc/tower (to prevent obtaining db info or secret key) - /etc/tower (to prevent obtaining db info or secret key)
- /var/lib/awx (except for current project) - /var/lib/awx (except for current project)
- /var/log/tower - /var/log/tower
- /var/log/supervisor
- /tmp (except for own tmp files) - /tmp (except for own tmp files)
''' '''
new_args = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '-r', '/'] new_args = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '-r', '/']
hide_paths = ['/etc/tower', '/var/lib/awx', '/var/log/tower', hide_paths = ['/etc/tower', '/var/lib/awx', '/var/log/tower',
tempfile.gettempdir(), settings.PROJECTS_ROOT, '/var/log/supervisor', tempfile.gettempdir(),
settings.JOBOUTPUT_ROOT] settings.PROJECTS_ROOT, settings.JOBOUTPUT_ROOT]
hide_paths.extend(getattr(settings, 'AWX_PROOT_HIDE_PATHS', None) or []) hide_paths.extend(getattr(settings, 'AWX_PROOT_HIDE_PATHS', None) or [])
for path in sorted(set(hide_paths)): for path in sorted(set(hide_paths)):
if not os.path.exists(path): if not os.path.exists(path):
@@ -1052,7 +1053,7 @@ class RunInventoryUpdate(BaseTask):
pass pass
elif inventory_update.source == 'custom': elif inventory_update.source == 'custom':
for env_k in inventory_update.source_vars_dict: for env_k in inventory_update.source_vars_dict:
if str(env_k) not in os.environ: if str(env_k) not in os.environ and str(env_k) not in settings.INV_ENV_VARIABLE_BLACKLIST:
env[str(env_k)] = unicode(inventory_update.source_vars_dict[env_k]) env[str(env_k)] = unicode(inventory_update.source_vars_dict[env_k])
return env return env

View File

@@ -3,6 +3,7 @@
# Python # Python
from distutils.version import StrictVersion as Version from distutils.version import StrictVersion as Version
import glob
import json import json
import os import os
import shutil import shutil
@@ -176,6 +177,15 @@ TEST_PROOT_PLAYBOOK = '''
assert: assert:
that: that:
- "not temp_stat.stat.exists" - "not temp_stat.stat.exists"
- name: check for supervisor log path
stat: path={{ supervisor_log_path }}
register: supervisor_log_stat
when: supervisor_log_path is defined
- name: check that supervisor log path was not found
assert:
that:
- "not supervisor_log_stat.stat.exists"
when: supervisor_log_path is defined
- name: try to run a tower-manage command - name: try to run a tower-manage command
command: tower-manage validate command: tower-manage validate
ignore_errors: true ignore_errors: true
@@ -1353,6 +1363,11 @@ class RunJobTest(BaseCeleryTest):
# Create a temp directory that should not be visible to the playbook. # Create a temp directory that should not be visible to the playbook.
temp_path = tempfile.mkdtemp() temp_path = tempfile.mkdtemp()
self._temp_paths.append(temp_path) self._temp_paths.append(temp_path)
# Find a file in supervisor logs that should not be visible.
try:
supervisor_log_path = glob.glob('/var/log/supervisor/*')[0]
except IndexError:
supervisor_log_path = None
# Create our test project and job template. # Create our test project and job template.
self.create_test_project(TEST_PROOT_PLAYBOOK) self.create_test_project(TEST_PROOT_PLAYBOOK)
project_path = self.project.local_path project_path = self.project.local_path
@@ -1364,6 +1379,8 @@ class RunJobTest(BaseCeleryTest):
'other_project_path': other_project_path, 'other_project_path': other_project_path,
'temp_path': temp_path, 'temp_path': temp_path,
} }
if supervisor_log_path:
extra_vars['supervisor_log_path'] = supervisor_log_path
job = self.create_test_job(job_template=job_template, verbosity=3, job = self.create_test_job(job_template=job_template, verbosity=3,
extra_vars=json.dumps(extra_vars)) extra_vars=json.dumps(extra_vars))
self.assertEqual(job.status, 'new') self.assertEqual(job.status, 'new')

View File

@@ -193,10 +193,13 @@ class VMwareInventory(object):
host_info = { host_info = {
'name': host.name, 'name': host.name,
'tag': host.tag, 'tag': host.tag,
'datastores': self._get_obj_info(host.datastore, depth=0),
'networks': self._get_obj_info(host.network, depth=0),
'vms': self._get_obj_info(host.vm, depth=0),
} }
for attr in ('datastore', 'network', 'vm'):
try:
value = getattr(host, attr)
host_info['%ss' % attr] = self._get_obj_info(value, depth=0)
except AttributeError:
host_info['%ss' % attr] = []
for k, v in self._get_obj_info(host.summary, depth=0).items(): for k, v in self._get_obj_info(host.summary, depth=0).items():
if isinstance(v, collections.MutableMapping): if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items(): for k2, v2 in v.items():
@@ -219,11 +222,21 @@ class VMwareInventory(object):
vm_info = { vm_info = {
'name': vm.name, 'name': vm.name,
'tag': vm.tag, 'tag': vm.tag,
'datastores': self._get_obj_info(vm.datastore, depth=0),
'networks': self._get_obj_info(vm.network, depth=0),
'resourcePool': self._get_obj_info(vm.resourcePool, depth=0),
'guestState': vm.guest.guestState,
} }
for attr in ('datastore', 'network'):
try:
value = getattr(vm, attr)
vm_info['%ss' % attr] = self._get_obj_info(value, depth=0)
except AttributeError:
vm_info['%ss' % attr] = []
try:
vm_info['resourcePool'] = self._get_obj_info(vm.resourcePool, depth=0)
except AttributeError:
vm_info['resourcePool'] = ''
try:
vm_info['guestState'] = vm.guest.guestState
except AttributeError:
vm_info['guestState'] = ''
for k, v in self._get_obj_info(vm.summary, depth=0).items(): for k, v in self._get_obj_info(vm.summary, depth=0).items():
if isinstance(v, collections.MutableMapping): if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items(): for k2, v2 in v.items():

View File

@@ -351,6 +351,8 @@ RAX_GROUP_FILTER = r'^(?!instance-.+).+$'
RAX_HOST_FILTER = r'^.+$' RAX_HOST_FILTER = r'^.+$'
RAX_EXCLUDE_EMPTY_GROUPS = True RAX_EXCLUDE_EMPTY_GROUPS = True
INV_ENV_VARIABLE_BLACKLIST = ("HOME", "_")
# ---------------- # ----------------
# -- Amazon EC2 -- # -- Amazon EC2 --
# ---------------- # ----------------

View File

@@ -649,6 +649,10 @@ angular.module('Tower', [
$AnsibleConfig = Store('AnsibleConfig'); $AnsibleConfig = Store('AnsibleConfig');
} }
//the authorization controller redirects to the home page automatcially if there is no last path defined. in order to override
// this, set the last path to /portal for instances where portal is visited for the first time.
$rootScope.lastPath = ($location.path() === "/portal") ? 'portal' : undefined;
LoadConfig(); LoadConfig();
} }
]); ]);

View File

@@ -19,7 +19,7 @@
tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
debug_mode: true, // Enable console logging messages debug_mode: false, // Enable console logging messages
password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong. password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong.
// This value controls progress bar colors: // This value controls progress bar colors:

View File

@@ -77,10 +77,9 @@ function JobsListController ($rootScope, $log, $scope, $compile, $routeParams, C
case 'pending': case 'pending':
case 'waiting': case 'waiting':
queued_scope.search('queued_job'); queued_scope.search('queued_job');
break;
case 'successful':
completed_scope.search('completed_job'); completed_scope.search('completed_job');
break; break;
case 'successful':
case 'failed': case 'failed':
case 'error': case 'error':
case 'canceled': case 'canceled':

View File

@@ -79,7 +79,6 @@ function PortalController($scope, $compile, $routeParams, $rootScope, $location,
searchSize: 'col-lg-6 col-md-6' searchSize: 'col-lg-6 col-md-6'
}); });
$rootScope.flashMessage = null;
SearchInit({ SearchInit({
scope: $scope, scope: $scope,
@@ -120,49 +119,11 @@ function PortalController($scope, $compile, $routeParams, $rootScope, $location,
$('.list-well:eq(1)').css('margin-top' , '0px'); $('.list-well:eq(1)').css('margin-top' , '0px');
}); });
// function processEvent(event) {
// switch(event.status) {
// case 'running':
// jobs_scope.search('running_job');
// jobs_scope.search('queued_job');
// break;
// case 'new':
// case 'pending':
// case 'waiting':
// jobs_scope.search('queued_job');
// break;
// case 'successful':
// jobs_scope.search('completed_job');
// case 'failed':
// case 'error':
// case 'canceled':
// jobs_scope.search('completed_job');
// jobs_scope.search('running_job');
// jobs_scope.search('queued_job');
// }
// }
if ($rootScope.removeJobStatusChange) { if ($rootScope.removeJobStatusChange) {
$rootScope.removeJobStatusChange(); $rootScope.removeJobStatusChange();
} }
$rootScope.removeJobStatusChange = $rootScope.$on('JobStatusChange', function() { $rootScope.removeJobStatusChange = $rootScope.$on('JobStatusChange', function() {
jobs_scope.refreshJobs(); jobs_scope.search('portal_job'); //processEvent(event);
// if(data.status==='pending'){
// // $scope.refresh();
// $('#portal-jobs').empty();
// // $rootScope.flashMessage = null;
// PortalJobsWidget({
// scope: $scope,
// target: 'portal-jobs',
// searchSize: 'col-lg-6 col-md-6'
// });
// }
//x`processEvent(data);
}); });
$scope.submitJob = function (id) { $scope.submitJob = function (id) {

View File

@@ -53,7 +53,6 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
$('#password-modal').dialog('close'); $('#password-modal').dialog('close');
} }
scope.$emit(callback, data); scope.$emit(callback, data);
scope.$destroy();
}) })
.error(function(data, status) { .error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!', ProcessErrors(scope, data, status, null, { hdr: 'Error!',
@@ -241,7 +240,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
html += "<label for=\"" + fld + "\">" + field.label + "</label>\n"; html += "<label for=\"" + fld + "\">" + field.label + "</label>\n";
html += "<input type=\"password\" "; html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" '; html += "ng-model=\"" + fld + '" ';
html += "ng-keydown=\"keydown($event)\" "; // html += "ng-keydown=\"keydown($event)\" ";
html += 'name="' + fld + '" '; html += 'name="' + fld + '" ';
html += "class=\"password-field form-control input-sm\" "; html += "class=\"password-field form-control input-sm\" ";
html += (field.associated) ? "ng-change=\"clearPWConfirm('" + field.associated + "')\" " : ""; html += (field.associated) ? "ng-change=\"clearPWConfirm('" + field.associated + "')\" " : "";
@@ -262,7 +261,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
html += "<label for=\"" + fld + "\"> " + field.label + "</label>\n"; html += "<label for=\"" + fld + "\"> " + field.label + "</label>\n";
html += "<input type=\"password\" "; html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" '; html += "ng-model=\"" + fld + '" ';
html += "ng-keydown=\"keydown($event)\" "; // html += "ng-keydown=\"keydown($event)\" ";
html += 'name="' + fld + '" '; html += 'name="' + fld + '" ';
html += "class=\"form-control input-sm\" "; html += "class=\"form-control input-sm\" ";
html += "ng-change=\"checkStatus()\" "; html += "ng-change=\"checkStatus()\" ";
@@ -323,11 +322,11 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
// $('#password-modal').dialog('open'); // $('#password-modal').dialog('open');
// $('#password-accept-button').attr({ "disabled": "disabled" }); // $('#password-accept-button').attr({ "disabled": "disabled" });
// }); // });
scope.keydown = function(e){ // scope.keydown = function(e){
if(e.keyCode===13){ // if(e.keyCode===13){
scope.passwordAccept(); // scope.passwordAccept();
} // }
}; // };
// scope.passwordAccept = function() { // scope.passwordAccept = function() {
// if (!scope.password_form.$invalid) { // if (!scope.password_form.$invalid) {
@@ -764,7 +763,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
} }
scope.removePlaybookLaunchFinished = scope.$on('PlaybookLaunchFinished', function(e, data) { scope.removePlaybookLaunchFinished = scope.$on('PlaybookLaunchFinished', function(e, data) {
//var base = $location.path().replace(/^\//, '').split('/')[0]; //var base = $location.path().replace(/^\//, '').split('/')[0];
if(scope.portalMode===false || scope.$parent.portalMode===false){ if(scope.portalMode===false || scope.$parent.portalMode===false ){
$location.path('/jobs/' + data.job); $location.path('/jobs/' + data.job);
} }

View File

@@ -87,6 +87,9 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
else if (scope.jobs) { else if (scope.jobs) {
list = scope.jobs; list = scope.jobs;
} }
else if(scope.portal_jobs){
list=scope.portal_jobs;
}
job = Find({ list: list, key: 'id', val: id }); job = Find({ list: list, key: 'id', val: id });
if (job.type === 'job') { if (job.type === 'job') {
if(scope.$parent.portalMode===true){ if(scope.$parent.portalMode===true){
@@ -405,6 +408,9 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
JobsControllerInit({ scope: scope, parent_scope: parent_scope }); JobsControllerInit({ scope: scope, parent_scope: parent_scope });
JobsListUpdate({ scope: scope, parent_scope: parent_scope, list: list }); JobsListUpdate({ scope: scope, parent_scope: parent_scope, list: list });
parent_scope.$emit('listLoaded'); parent_scope.$emit('listLoaded');
// setTimeout(function(){
// scope.$apply();
// }, 300);
}); });
if (base === 'jobs' && list.name === 'completed_jobs') { if (base === 'jobs' && list.name === 'completed_jobs') {

View File

@@ -14,8 +14,8 @@
angular.module('PortalJobsListDefinition', []) angular.module('PortalJobsListDefinition', [])
.value( 'PortalJobsList', { .value( 'PortalJobsList', {
name: 'jobs', name: 'portal_jobs',
iterator: 'job', iterator: 'portal_job',
editTitle: 'Jobs', editTitle: 'Jobs',
'class': 'table-condensed', 'class': 'table-condensed',
index: false, index: false,
@@ -39,8 +39,8 @@ angular.module('PortalJobsListDefinition', [])
columnClass: 'col-lg-1 col-md-2 col-sm-2 col-xs-2', columnClass: 'col-lg-1 col-md-2 col-sm-2 col-xs-2',
// awToolTip: "{{ job.status_tip }}", // awToolTip: "{{ job.status_tip }}",
// awTipPlacement: "top", // awTipPlacement: "top",
dataTitle: "{{ job.status_popover_title }}", dataTitle: "{{ portal_job.status_popover_title }}",
icon: 'icon-job-{{ job.status }}', icon: 'icon-job-{{ portal_job.status }}',
iconOnly: true, iconOnly: true,
// ngClick:"viewJobLog(job.id)", // ngClick:"viewJobLog(job.id)",
searchable: true, searchable: true,
@@ -73,7 +73,7 @@ angular.module('PortalJobsListDefinition', [])
fieldActions: { fieldActions: {
job_details: { job_details: {
mode: 'all', mode: 'all',
ngClick: "viewJobLog(job.id)", ngClick: "viewJobLog(portal_job.id)",
awToolTip: 'View job details', awToolTip: 'View job details',
dataPlacement: 'top' dataPlacement: 'top'
} }

View File

@@ -19,23 +19,12 @@ angular.module('PortalJobsWidget', ['RestServices', 'Utilities'])
choicesCount = 0, choicesCount = 0,
listCount = 0, listCount = 0,
jobs_scope = scope.$new(true), jobs_scope = scope.$new(true),
// completed_scope = scope.$new(true),
// running_scope = scope.$new(true),
// queued_scope = scope.$new(true),
// scheduled_scope = scope.$new(true),
max_rows, max_rows,
user,
html, e; html, e;
html = ''; html = '';
html += "<div class=\"portal-job-template-container\">\n"; html += "<div class=\"portal-job-template-container\">\n";
// html+= "<span id='portal-job-template-header'>Job Templates </span>";
// html += "<ul id=\"job_status_tabs\" class=\"nav nav-tabs\">\n";
// html += "<li class=\"active\"><a id=\"active_jobs_link\" ng-click=\"toggleTab($event, 'active_jobs_link', 'job_status_tabs')\"\n";
// html += " href=\"#active-jobs-tab\" data-toggle=\"tab\">Jobs</a></li>\n";
// html += "<li><a id=\"scheduled_jobs_link\" ng-click=\"toggleTab($event, 'scheduled_jobs_link', 'job_status_tabs')\"\n";
// html += "href=\"#scheduled-jobs-tab\" data-toggle=\"tab\">Schedule</a></li>\n";
// html += "</ul>\n";
// html += "<div id=\"portal-job-template-tab-content\" class=\"tab-content \">\n";
html += "<div class=\"tab-pane active\" id=\"active-jobs-tab\">\n"; html += "<div class=\"tab-pane active\" id=\"active-jobs-tab\">\n";
html += "<div class=\"row search-row\" id='portal-job-template-search'>\n"; html += "<div class=\"row search-row\" id='portal-job-template-search'>\n";
html += "<div class=\"col-lg-6 col-md-6\" id=\"active-jobs-search-container\"></div>\n"; html += "<div class=\"col-lg-6 col-md-6\" id=\"active-jobs-search-container\"></div>\n";
@@ -44,8 +33,6 @@ angular.module('PortalJobsWidget', ['RestServices', 'Utilities'])
html += "<div id=\"active-jobs\" class=\"job-list-target\"></div>\n"; html += "<div id=\"active-jobs\" class=\"job-list-target\"></div>\n";
html += "</div>\n"; //list html += "</div>\n"; //list
html += "</div>\n"; //active-jobs-tab html += "</div>\n"; //active-jobs-tab
// html += "<div class=\"tab-pane\" id=\"scheduled-jobs-tab\"></div>\n";
// html += "</div>\n"; // jobs-list-container
html += "</div>\n"; html += "</div>\n";
e = angular.element(document.getElementById(target)); e = angular.element(document.getElementById(target));
@@ -71,44 +58,17 @@ angular.module('PortalJobsWidget', ['RestServices', 'Utilities'])
if (PortalJobsList.fields.type) { if (PortalJobsList.fields.type) {
PortalJobsList.fields.type.searchOptions = scope.type_choices; PortalJobsList.fields.type.searchOptions = scope.type_choices;
} }
user = scope.$parent.current_user.id;
LoadJobsScope({ LoadJobsScope({
parent_scope: scope, parent_scope: scope,
scope: jobs_scope, scope: jobs_scope,
list: PortalJobsList, list: PortalJobsList,
id: 'active-jobs', id: 'active-jobs',
url: GetBasePath('unified_jobs') + '?status__in=running,completed,failed,successful,error,canceled', url: GetBasePath('jobs')+'?created_by='+user,
pageSize: max_rows, pageSize: max_rows,
spinner: true spinner: true
}); });
// completed_scope.showJobType = true;
// LoadJobsScope({
// parent_scope: scope,
// scope: completed_scope,
// list: PortalJobsList,
// id: 'active-jobs',
// url: GetBasePath('unified_jobs') + '?or__status=successful&or__status=failed&or__status=error&or__status=canceled',
// // searchParams: search_params,
// pageSize: max_rows
// });
// LoadJobsScope({
// parent_scope: scope,
// scope: running_scope,
// list: PortalJobsList,
// id: 'active-jobs',
// url: GetBasePath('unified_jobs') + '?status=running',
// pageSize: max_rows
// });
// LoadJobsScope({
// parent_scope: scope,
// scope: queued_scope,
// list: PortalJobsList,
// id: 'active-jobs',
// url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting&or__status=new',
// pageSize: max_rows
// });
$(window).resize(_.debounce(function() { $(window).resize(_.debounce(function() {
resizePortalJobsWidget(); resizePortalJobsWidget();

View File

@@ -64,7 +64,16 @@ angular.module('AuthService', ['ngCookies', 'Utilities'])
//$rootScope.$destroy(); //$rootScope.$destroy();
$cookieStore.remove('token_expires'); $cookieStore.remove('token_expires');
$cookieStore.remove('current_user'); $cookieStore.remove('current_user');
$cookieStore.remove('lastPath');
if($cookieStore.get('lastPath')==='/portal'){
$cookieStore.put( 'lastPath', '/portal');
$rootScope.lastPath = '/portal';
}
else {
$cookieStore.remove('lastPath');
$rootScope.lastPath = '/home';
}
$cookieStore.remove('token'); $cookieStore.remove('token');
$cookieStore.put('userLoggedIn', false); $cookieStore.put('userLoggedIn', false);
$cookieStore.put('sessionExpired', false); $cookieStore.put('sessionExpired', false);
@@ -75,7 +84,6 @@ angular.module('AuthService', ['ngCookies', 'Utilities'])
$rootScope.sessionExpired = false; $rootScope.sessionExpired = false;
$rootScope.token = null; $rootScope.token = null;
$rootScope.token_expires = null; $rootScope.token_expires = null;
$rootScope.lastPath = '/home';
$rootScope.login_username = null; $rootScope.login_username = null;
$rootScope.login_password = null; $rootScope.login_password = null;
}, },

View File

@@ -28,7 +28,6 @@ angular.module('SocketIO', ['AuthService', 'Utilities'])
endpoint = params.endpoint, endpoint = params.endpoint,
protocol = $location.protocol(), protocol = $location.protocol(),
config, socketPort, config, socketPort,
// handshakeData,
url; url;
// Since some pages are opened in a new tab, we might get here before AnsibleConfig is available. // Since some pages are opened in a new tab, we might get here before AnsibleConfig is available.