mirror of
https://github.com/ansible/awx.git
synced 2026-01-17 04:31:21 -03:30
resolve conflict: index.html
This commit is contained in:
commit
4237b2e207
@ -189,8 +189,6 @@ class TaskPermission(ModelAccessPermission):
|
||||
# token.
|
||||
if view.model == Inventory and request.method.lower() in ('head', 'get'):
|
||||
return bool(not obj or obj.pk == unified_job.inventory_id)
|
||||
elif view.model in (JobEvent, AdHocCommandEvent) and request.method.lower() == 'post':
|
||||
return bool(not obj or obj.pk == unified_job.pk)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@ -2244,10 +2244,10 @@ class WorkflowJobListSerializer(WorkflowJobSerializer, UnifiedJobListSerializer)
|
||||
pass
|
||||
|
||||
class WorkflowNodeBaseSerializer(BaseSerializer):
|
||||
job_type = serializers.SerializerMethodField()
|
||||
job_tags = serializers.SerializerMethodField()
|
||||
limit = serializers.SerializerMethodField()
|
||||
skip_tags = serializers.SerializerMethodField()
|
||||
job_type = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
job_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
success_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
failure_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
always_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
@ -2263,17 +2263,12 @@ class WorkflowNodeBaseSerializer(BaseSerializer):
|
||||
res['unified_job_template'] = obj.unified_job_template.get_absolute_url()
|
||||
return res
|
||||
|
||||
def get_job_type(self, obj):
|
||||
return obj.char_prompts.get('job_type', None)
|
||||
|
||||
def get_job_tags(self, obj):
|
||||
return obj.char_prompts.get('job_tags', None)
|
||||
|
||||
def get_skip_tags(self, obj):
|
||||
return obj.char_prompts.get('skip_tags', None)
|
||||
|
||||
def get_limit(self, obj):
|
||||
return obj.char_prompts.get('limit', None)
|
||||
def validate(self, attrs):
|
||||
# char_prompts go through different validation, so remove them here
|
||||
for fd in ['job_type', 'job_tags', 'skip_tags', 'limit']:
|
||||
if fd in attrs:
|
||||
attrs.pop(fd)
|
||||
return super(WorkflowNodeBaseSerializer, self).validate(attrs)
|
||||
|
||||
|
||||
class WorkflowJobTemplateNodeSerializer(WorkflowNodeBaseSerializer):
|
||||
@ -2421,7 +2416,9 @@ class JobEventSerializer(BaseSerializer):
|
||||
model = JobEvent
|
||||
fields = ('*', '-name', '-description', 'job', 'event', 'counter',
|
||||
'event_display', 'event_data', 'event_level', 'failed',
|
||||
'changed', 'host', 'host_name', 'parent', 'play', 'task', 'role')
|
||||
'changed', 'uuid', 'host', 'host_name', 'parent', 'playbook',
|
||||
'play', 'task', 'role', 'stdout', 'start_line', 'end_line',
|
||||
'verbosity')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(JobEventSerializer, self).get_related(obj)
|
||||
@ -2457,16 +2454,8 @@ class AdHocCommandEventSerializer(BaseSerializer):
|
||||
model = AdHocCommandEvent
|
||||
fields = ('*', '-name', '-description', 'ad_hoc_command', 'event',
|
||||
'counter', 'event_display', 'event_data', 'failed',
|
||||
'changed', 'host', 'host_name')
|
||||
|
||||
def to_internal_value(self, data):
|
||||
ret = super(AdHocCommandEventSerializer, self).to_internal_value(data)
|
||||
# AdHocCommandAdHocCommandEventsList should be the only view creating
|
||||
# AdHocCommandEvent instances, so keep the ad_hoc_command it sets, even
|
||||
# though ad_hoc_command is a read-only field.
|
||||
if 'ad_hoc_command' in data:
|
||||
ret['ad_hoc_command'] = data['ad_hoc_command']
|
||||
return ret
|
||||
'changed', 'uuid', 'host', 'host_name', 'stdout',
|
||||
'start_line', 'end_line', 'verbosity')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(AdHocCommandEventSerializer, self).get_related(obj)
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
# Workflow Job Template Workflow Node List
|
||||
|
||||
Workflow nodes reference templates to execute and define the ordering
|
||||
in which to execute them. After a job in this workflow finishes,
|
||||
the subsequent actions are to:
|
||||
|
||||
- run nodes contained in "failure_nodes" or "always_nodes" if job failed
|
||||
- run nodes contained in "success_nodes" or "always_nodes" if job succeeded
|
||||
|
||||
The workflow job is marked as `successful` if all of the jobs running as
|
||||
a part of the workflow job have completed, and the workflow job has not
|
||||
been canceled. Even if a job within the workflow has failed, the workflow
|
||||
job will not be marked as failed.
|
||||
|
||||
{% include "api/sub_list_create_api_view.md" %}
|
||||
@ -2406,6 +2406,7 @@ class JobTemplateLabelList(DeleteLastUnattachLabelMixin, SubListCreateAttachDeta
|
||||
serializer_class = LabelSerializer
|
||||
parent_model = JobTemplate
|
||||
relationship = 'labels'
|
||||
new_in_300 = True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# If a label already exists in the database, attach it instead of erroring out
|
||||
@ -2699,6 +2700,7 @@ class WorkflowJobTemplateList(ListCreateAPIView):
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = WorkflowJobTemplateListSerializer
|
||||
always_allow_superuser = False
|
||||
new_in_310 = True
|
||||
|
||||
# TODO: RBAC
|
||||
'''
|
||||
@ -2716,10 +2718,12 @@ class WorkflowJobTemplateDetail(RetrieveUpdateDestroyAPIView):
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = WorkflowJobTemplateSerializer
|
||||
always_allow_superuser = False
|
||||
new_in_310 = True
|
||||
|
||||
|
||||
class WorkflowJobTemplateLabelList(JobTemplateLabelList):
|
||||
parent_model = WorkflowJobTemplate
|
||||
new_in_310 = True
|
||||
|
||||
|
||||
# TODO:
|
||||
@ -2727,6 +2731,7 @@ class WorkflowJobTemplateLaunch(GenericAPIView):
|
||||
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = EmptySerializer
|
||||
new_in_310 = True
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
data = {}
|
||||
@ -2752,6 +2757,12 @@ class WorkflowJobTemplateWorkflowNodesList(SubListCreateAPIView):
|
||||
parent_model = WorkflowJobTemplate
|
||||
relationship = 'workflow_job_template_nodes'
|
||||
parent_key = 'workflow_job_template'
|
||||
new_in_310 = True
|
||||
|
||||
def update_raw_data(self, data):
|
||||
for fd in ['job_type', 'job_tags', 'skip_tags', 'limit', 'skip_tags']:
|
||||
data[fd] = None
|
||||
return super(WorkflowJobTemplateWorkflowNodesList, self).update_raw_data(data)
|
||||
|
||||
# TODO:
|
||||
class WorkflowJobTemplateJobsList(SubListAPIView):
|
||||
@ -2767,12 +2778,14 @@ class WorkflowJobList(ListCreateAPIView):
|
||||
|
||||
model = WorkflowJob
|
||||
serializer_class = WorkflowJobListSerializer
|
||||
new_in_310 = True
|
||||
|
||||
# TODO:
|
||||
class WorkflowJobDetail(RetrieveDestroyAPIView):
|
||||
|
||||
model = WorkflowJob
|
||||
serializer_class = WorkflowJobSerializer
|
||||
new_in_310 = True
|
||||
|
||||
class WorkflowJobWorkflowNodesList(SubListAPIView):
|
||||
|
||||
@ -2782,6 +2795,7 @@ class WorkflowJobWorkflowNodesList(SubListAPIView):
|
||||
parent_model = WorkflowJob
|
||||
relationship = 'workflow_job_nodes'
|
||||
parent_key = 'workflow_job'
|
||||
new_in_310 = True
|
||||
|
||||
class SystemJobTemplateList(ListAPIView):
|
||||
|
||||
@ -3052,21 +3066,6 @@ class GroupJobEventsList(BaseJobEventsList):
|
||||
class JobJobEventsList(BaseJobEventsList):
|
||||
|
||||
parent_model = Job
|
||||
authentication_classes = [TaskAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
|
||||
permission_classes = (TaskPermission,)
|
||||
|
||||
# Post allowed for job event callback only.
|
||||
def post(self, request, *args, **kwargs):
|
||||
parent_obj = get_object_or_404(self.parent_model, pk=self.kwargs['pk'])
|
||||
data = request.data.copy()
|
||||
data['job'] = parent_obj.pk
|
||||
serializer = self.get_serializer(data=data)
|
||||
if serializer.is_valid():
|
||||
self.instance = serializer.save()
|
||||
headers = {'Location': serializer.data['url']}
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED,
|
||||
headers=headers)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class JobJobPlaysList(BaseJobEventsList):
|
||||
|
||||
@ -3457,25 +3456,8 @@ class HostAdHocCommandEventsList(BaseAdHocCommandEventsList):
|
||||
class AdHocCommandAdHocCommandEventsList(BaseAdHocCommandEventsList):
|
||||
|
||||
parent_model = AdHocCommand
|
||||
authentication_classes = [TaskAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
|
||||
permission_classes = (TaskPermission,)
|
||||
new_in_220 = True
|
||||
|
||||
# Post allowed for ad hoc event callback only.
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.user:
|
||||
raise PermissionDenied()
|
||||
parent_obj = get_object_or_404(self.parent_model, pk=self.kwargs['pk'])
|
||||
data = request.data.copy()
|
||||
data['ad_hoc_command'] = parent_obj
|
||||
serializer = self.get_serializer(data=data)
|
||||
if serializer.is_valid():
|
||||
self.instance = serializer.save()
|
||||
headers = {'Location': serializer.data['url']}
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED,
|
||||
headers=headers)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class AdHocCommandActivityStreamList(SubListAPIView):
|
||||
|
||||
@ -3586,7 +3568,11 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
dark_bg = (content_only and dark) or (not content_only and (dark or not dark_val))
|
||||
content, start, end, absolute_end = unified_job.result_stdout_raw_limited(start_line, end_line)
|
||||
|
||||
# Remove any ANSI escape sequences containing job event data.
|
||||
content = re.sub(r'\x1b\[K(?:[A-Za-z0-9+/=]+\x1b\[\d+D)+\x1b\[K', '', content)
|
||||
|
||||
body = ansiconv.to_html(cgi.escape(content))
|
||||
|
||||
context = {
|
||||
'title': get_view_name(self.__class__),
|
||||
'body': mark_safe(body),
|
||||
|
||||
22
awx/lib/sitecustomize.py
Normal file
22
awx/lib/sitecustomize.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Python
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Based on http://stackoverflow.com/a/6879344/131141 -- Initialize tower display
|
||||
# callback as early as possible to wrap ansible.display.Display methods.
|
||||
|
||||
def argv_ready(argv):
|
||||
if argv and os.path.basename(argv[0]) in {'ansible', 'ansible-playbook'}:
|
||||
import tower_display_callback # noqa
|
||||
|
||||
|
||||
class argv_placeholder(object):
|
||||
|
||||
def __del__(self):
|
||||
argv_ready(sys.argv)
|
||||
|
||||
|
||||
if hasattr(sys, 'argv'):
|
||||
argv_ready(sys.argv)
|
||||
else:
|
||||
sys.argv = argv_placeholder()
|
||||
25
awx/lib/tower_display_callback/__init__.py
Normal file
25
awx/lib/tower_display_callback/__init__.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Tower Display Callback
|
||||
from . import cleanup # noqa (registers control persistent cleanup)
|
||||
from . import display # noqa (wraps ansible.display.Display methods)
|
||||
from .module import TowerDefaultCallbackModule, TowerMinimalCallbackModule
|
||||
|
||||
__all__ = ['TowerDefaultCallbackModule', 'TowerMinimalCallbackModule']
|
||||
72
awx/lib/tower_display_callback/cleanup.py
Normal file
72
awx/lib/tower_display_callback/cleanup.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import atexit
|
||||
import glob
|
||||
import os
|
||||
import pwd
|
||||
|
||||
# PSUtil
|
||||
import psutil
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
@atexit.register
|
||||
def terminate_ssh_control_masters():
|
||||
# Determine if control persist is being used and if any open sockets
|
||||
# exist after running the playbook.
|
||||
cp_path = os.environ.get('ANSIBLE_SSH_CONTROL_PATH', '')
|
||||
if not cp_path:
|
||||
return
|
||||
cp_dir = os.path.dirname(cp_path)
|
||||
if not os.path.exists(cp_dir):
|
||||
return
|
||||
cp_pattern = os.path.join(cp_dir, 'ansible-ssh-*')
|
||||
cp_files = glob.glob(cp_pattern)
|
||||
if not cp_files:
|
||||
return
|
||||
|
||||
# Attempt to find any running control master processes.
|
||||
username = pwd.getpwuid(os.getuid())[0]
|
||||
ssh_cm_procs = []
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
pname = proc.name()
|
||||
pcmdline = proc.cmdline()
|
||||
pusername = proc.username()
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
if pusername != username:
|
||||
continue
|
||||
if pname != 'ssh':
|
||||
continue
|
||||
for cp_file in cp_files:
|
||||
if pcmdline and cp_file in pcmdline[0]:
|
||||
ssh_cm_procs.append(proc)
|
||||
break
|
||||
|
||||
# Terminate then kill control master processes. Workaround older
|
||||
# version of psutil that may not have wait_procs implemented.
|
||||
for proc in ssh_cm_procs:
|
||||
proc.terminate()
|
||||
procs_gone, procs_alive = psutil.wait_procs(ssh_cm_procs, timeout=5)
|
||||
for proc in procs_alive:
|
||||
proc.kill()
|
||||
92
awx/lib/tower_display_callback/display.py
Normal file
92
awx/lib/tower_display_callback/display.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import functools
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
# Ansible
|
||||
from ansible.utils.display import Display
|
||||
|
||||
# Tower Display Callback
|
||||
from tower_display_callback.events import event_context
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def with_context(**context):
|
||||
global event_context
|
||||
|
||||
def wrap(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
with event_context.set_local(**context):
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
return wrap
|
||||
|
||||
|
||||
for attr in dir(Display):
|
||||
if attr.startswith('_') or 'cow' in attr or 'prompt' in attr:
|
||||
continue
|
||||
if attr in ('display', 'v', 'vv', 'vvv', 'vvvv', 'vvvvv', 'vvvvvv', 'verbose'):
|
||||
continue
|
||||
if not callable(getattr(Display, attr)):
|
||||
continue
|
||||
setattr(Display, attr, with_context(**{attr: True})(getattr(Display, attr)))
|
||||
|
||||
|
||||
def with_verbosity(f):
|
||||
global event_context
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
host = args[2] if len(args) >= 3 else kwargs.get('host', None)
|
||||
caplevel = args[3] if len(args) >= 4 else kwargs.get('caplevel', 2)
|
||||
context = dict(verbose=True, verbosity=(caplevel + 1))
|
||||
if host is not None:
|
||||
context['remote_addr'] = host
|
||||
with event_context.set_local(**context):
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
Display.verbose = with_verbosity(Display.verbose)
|
||||
|
||||
|
||||
def display_with_context(f):
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
log_only = args[5] if len(args) >= 6 else kwargs.get('log_only', False)
|
||||
stderr = args[3] if len(args) >= 4 else kwargs.get('stderr', False)
|
||||
fileobj = sys.stderr if stderr else sys.stdout
|
||||
event_uuid = event_context.get().get('uuid', None)
|
||||
try:
|
||||
if not log_only and not event_uuid:
|
||||
event_context.add_local(uuid=str(uuid.uuid4()))
|
||||
event_context.dump_begin(fileobj)
|
||||
return f(*args, **kwargs)
|
||||
finally:
|
||||
if not log_only and not event_uuid:
|
||||
event_context.dump_end(fileobj)
|
||||
event_context.remove_local(uuid=None)
|
||||
return wrapper
|
||||
|
||||
Display.display = display_with_context(Display.display)
|
||||
137
awx/lib/tower_display_callback/events.py
Normal file
137
awx/lib/tower_display_callback/events.py
Normal file
@ -0,0 +1,137 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import base64
|
||||
import contextlib
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
__all__ = ['event_context']
|
||||
|
||||
|
||||
class EventContext(object):
|
||||
'''
|
||||
Store global and local (per thread/process) data associated with callback
|
||||
events and other display output methods.
|
||||
'''
|
||||
|
||||
def add_local(self, **kwargs):
|
||||
if not hasattr(self, '_local'):
|
||||
self._local = threading.local()
|
||||
self._local._ctx = {}
|
||||
self._local._ctx.update(kwargs)
|
||||
|
||||
def remove_local(self, **kwargs):
|
||||
if hasattr(self, '_local'):
|
||||
for key in kwargs.keys():
|
||||
self._local._ctx.pop(key, None)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def set_local(self, **kwargs):
|
||||
try:
|
||||
self.add_local(**kwargs)
|
||||
yield
|
||||
finally:
|
||||
self.remove_local(**kwargs)
|
||||
|
||||
def get_local(self):
|
||||
return getattr(getattr(self, '_local', None), '_ctx', {})
|
||||
|
||||
def add_global(self, **kwargs):
|
||||
if not hasattr(self, '_global_ctx'):
|
||||
self._global_ctx = {}
|
||||
self._global_ctx.update(kwargs)
|
||||
|
||||
def remove_global(self, **kwargs):
|
||||
if hasattr(self, '_global_ctx'):
|
||||
for key in kwargs.keys():
|
||||
self._global_ctx.pop(key, None)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def set_global(self, **kwargs):
|
||||
try:
|
||||
self.add_global(**kwargs)
|
||||
yield
|
||||
finally:
|
||||
self.remove_global(**kwargs)
|
||||
|
||||
def get_global(self):
|
||||
return getattr(self, '_global_ctx', {})
|
||||
|
||||
def get(self):
|
||||
ctx = {}
|
||||
ctx.update(self.get_global())
|
||||
ctx.update(self.get_local())
|
||||
return ctx
|
||||
|
||||
def get_begin_dict(self):
|
||||
event_data = self.get()
|
||||
if os.getenv('JOB_ID', ''):
|
||||
event_data['job_id'] = int(os.getenv('JOB_ID', '0'))
|
||||
if os.getenv('AD_HOC_COMMAND_ID', ''):
|
||||
event_data['ad_hoc_command_id'] = int(os.getenv('AD_HOC_COMMAND_ID', '0'))
|
||||
event_data.setdefault('pid', os.getpid())
|
||||
event_data.setdefault('uuid', str(uuid.uuid4()))
|
||||
event_data.setdefault('created', datetime.datetime.utcnow().isoformat())
|
||||
if not event_data.get('parent_uuid', None) and event_data.get('job_id', None):
|
||||
for key in ('task_uuid', 'play_uuid', 'playbook_uuid'):
|
||||
parent_uuid = event_data.get(key, None)
|
||||
if parent_uuid and parent_uuid != event_data.get('uuid', None):
|
||||
event_data['parent_uuid'] = parent_uuid
|
||||
break
|
||||
|
||||
event = event_data.pop('event', None)
|
||||
if not event:
|
||||
event = 'verbose'
|
||||
for key in ('debug', 'verbose', 'deprecated', 'warning', 'system_warning', 'error'):
|
||||
if event_data.get(key, False):
|
||||
event = key
|
||||
break
|
||||
|
||||
event_dict = dict(event=event, event_data=event_data)
|
||||
for key in event_data.keys():
|
||||
if key in ('job_id', 'ad_hoc_command_id', 'uuid', 'parent_uuid', 'created', 'artifact_data'):
|
||||
event_dict[key] = event_data.pop(key)
|
||||
elif key in ('verbosity', 'pid'):
|
||||
event_dict[key] = event_data[key]
|
||||
return event_dict
|
||||
|
||||
def get_end_dict(self):
|
||||
return {}
|
||||
|
||||
def dump(self, fileobj, data, max_width=78):
|
||||
b64data = base64.b64encode(json.dumps(data))
|
||||
fileobj.write(u'\x1b[K')
|
||||
for offset in xrange(0, len(b64data), max_width):
|
||||
chunk = b64data[offset:offset + max_width]
|
||||
escaped_chunk = u'{}\x1b[{}D'.format(chunk, len(chunk))
|
||||
fileobj.write(escaped_chunk)
|
||||
fileobj.write(u'\x1b[K')
|
||||
|
||||
def dump_begin(self, fileobj):
|
||||
self.dump(fileobj, self.get_begin_dict())
|
||||
|
||||
def dump_end(self, fileobj):
|
||||
self.dump(fileobj, self.get_end_dict())
|
||||
|
||||
event_context = EventContext()
|
||||
28
awx/lib/tower_display_callback/minimal.py
Normal file
28
awx/lib/tower_display_callback/minimal.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import os
|
||||
|
||||
# Ansible
|
||||
import ansible
|
||||
|
||||
# Because of the way Ansible loads plugins, it's not possible to import
|
||||
# ansible.plugins.callback.minimal when being loaded as the minimal plugin. Ugh.
|
||||
execfile(os.path.join(os.path.dirname(ansible.__file__), 'plugins', 'callback', 'minimal.py'))
|
||||
488
awx/lib/tower_display_callback/module.py
Normal file
488
awx/lib/tower_display_callback/module.py
Normal file
@ -0,0 +1,488 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import contextlib
|
||||
import copy
|
||||
import re
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
# Ansible
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule
|
||||
|
||||
# Tower Display Callback
|
||||
from tower_display_callback.events import event_context
|
||||
from tower_display_callback.minimal import CallbackModule as MinimalCallbackModule
|
||||
|
||||
|
||||
class BaseCallbackModule(CallbackBase):
|
||||
'''
|
||||
Callback module for logging ansible/ansible-playbook events.
|
||||
'''
|
||||
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'stdout'
|
||||
|
||||
# These events should never have an associated play.
|
||||
EVENTS_WITHOUT_PLAY = [
|
||||
'playbook_on_start',
|
||||
'playbook_on_stats',
|
||||
]
|
||||
|
||||
# These events should never have an associated task.
|
||||
EVENTS_WITHOUT_TASK = EVENTS_WITHOUT_PLAY + [
|
||||
'playbook_on_setup',
|
||||
'playbook_on_notify',
|
||||
'playbook_on_import_for_host',
|
||||
'playbook_on_not_import_for_host',
|
||||
'playbook_on_no_hosts_matched',
|
||||
'playbook_on_no_hosts_remaining',
|
||||
]
|
||||
|
||||
CENSOR_FIELD_WHITELIST = [
|
||||
'msg',
|
||||
'failed',
|
||||
'changed',
|
||||
'results',
|
||||
'start',
|
||||
'end',
|
||||
'delta',
|
||||
'cmd',
|
||||
'_ansible_no_log',
|
||||
'rc',
|
||||
'failed_when_result',
|
||||
'skipped',
|
||||
'skip_reason',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
super(BaseCallbackModule, self).__init__()
|
||||
self.task_uuids = set()
|
||||
|
||||
def censor_result(self, res, no_log=False):
|
||||
if not isinstance(res, dict):
|
||||
if no_log:
|
||||
return "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
return res
|
||||
if res.get('_ansible_no_log', no_log):
|
||||
new_res = {}
|
||||
for k in self.CENSOR_FIELD_WHITELIST:
|
||||
if k in res:
|
||||
new_res[k] = res[k]
|
||||
if k == 'cmd' and k in res:
|
||||
if isinstance(res['cmd'], list):
|
||||
res['cmd'] = ' '.join(res['cmd'])
|
||||
if re.search(r'\s', res['cmd']):
|
||||
new_res['cmd'] = re.sub(r'^(([^\s\\]|\\\s)+).*$',
|
||||
r'\1 <censored>',
|
||||
res['cmd'])
|
||||
new_res['censored'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
res = new_res
|
||||
if 'results' in res:
|
||||
if isinstance(res['results'], list):
|
||||
for i in xrange(len(res['results'])):
|
||||
res['results'][i] = self.censor_result(res['results'][i], res.get('_ansible_no_log', no_log))
|
||||
elif res.get('_ansible_no_log', False):
|
||||
res['results'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
return res
|
||||
|
||||
@contextlib.contextmanager
|
||||
def capture_event_data(self, event, **event_data):
|
||||
|
||||
event_data.setdefault('uuid', str(uuid.uuid4()))
|
||||
|
||||
if 'res' in event_data:
|
||||
event_data['res'] = self.censor_result(copy.deepcopy(event_data['res']))
|
||||
res = event_data.get('res', None)
|
||||
if res and isinstance(res, dict):
|
||||
if 'artifact_data' in res:
|
||||
event_data['artifact_data'] = res['artifact_data']
|
||||
|
||||
if event not in self.EVENTS_WITHOUT_TASK:
|
||||
task = event_data.pop('task', None)
|
||||
else:
|
||||
task = None
|
||||
|
||||
try:
|
||||
event_context.add_local(event=event, **event_data)
|
||||
if task:
|
||||
self.set_task(task, local=True)
|
||||
event_context.dump_begin(sys.stdout)
|
||||
yield
|
||||
finally:
|
||||
event_context.dump_end(sys.stdout)
|
||||
if task:
|
||||
self.clear_task(local=True)
|
||||
event_context.remove_local(event=None, **event_data)
|
||||
|
||||
def set_playbook(self, playbook):
|
||||
# NOTE: Ansible doesn't generate a UUID for playbook_on_start so do it for them.
|
||||
self.playbook_uuid = str(uuid.uuid4())
|
||||
file_name = getattr(playbook, '_file_name', '???')
|
||||
event_context.add_global(playbook=file_name, playbook_uuid=self.playbook_uuid)
|
||||
self.clear_play()
|
||||
|
||||
def set_play(self, play):
|
||||
if hasattr(play, 'hosts'):
|
||||
if isinstance(play.hosts, list):
|
||||
pattern = ','.join(play.hosts)
|
||||
else:
|
||||
pattern = play.hosts
|
||||
else:
|
||||
pattern = ''
|
||||
name = play.get_name().strip() or pattern
|
||||
event_context.add_global(play=name, play_uuid=str(play._uuid), play_pattern=pattern)
|
||||
self.clear_task()
|
||||
|
||||
def clear_play(self):
|
||||
event_context.remove_global(play=None, play_uuid=None, play_pattern=None)
|
||||
self.clear_task()
|
||||
|
||||
def set_task(self, task, local=False):
|
||||
# FIXME: Task is "global" unless using free strategy!
|
||||
task_ctx = dict(
|
||||
task=(task.name or task.action),
|
||||
task_path=task.get_path(),
|
||||
task_uuid=str(task._uuid),
|
||||
task_action=task.action,
|
||||
)
|
||||
if not task.no_log:
|
||||
task_args = ', '.join(('%s=%s' % a for a in task.args.items()))
|
||||
task_ctx['task_args'] = task_args
|
||||
if getattr(task, '_role', None):
|
||||
task_role = task._role._role_name
|
||||
else:
|
||||
task_role = getattr(task, 'role_name', '')
|
||||
if task_role:
|
||||
task_ctx['role'] = task_role
|
||||
if local:
|
||||
event_context.add_local(**task_ctx)
|
||||
else:
|
||||
event_context.add_global(**task_ctx)
|
||||
|
||||
def clear_task(self, local=False):
|
||||
task_ctx = dict(task=None, task_path=None, task_uuid=None, task_action=None, task_args=None, role=None)
|
||||
if local:
|
||||
event_context.remove_local(**task_ctx)
|
||||
else:
|
||||
event_context.remove_global(**task_ctx)
|
||||
|
||||
def v2_playbook_on_start(self, playbook):
|
||||
self.set_playbook(playbook)
|
||||
event_data = dict(
|
||||
uuid=self.playbook_uuid,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_start', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_start(playbook)
|
||||
|
||||
def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None,
|
||||
encrypt=None, confirm=False, salt_size=None,
|
||||
salt=None, default=None):
|
||||
event_data = dict(
|
||||
varname=varname,
|
||||
private=private,
|
||||
prompt=prompt,
|
||||
encrypt=encrypt,
|
||||
confirm=confirm,
|
||||
salt_size=salt_size,
|
||||
salt=salt,
|
||||
default=default,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_vars_prompt', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_vars_prompt(
|
||||
varname, private, prompt, encrypt, confirm, salt_size, salt,
|
||||
default,
|
||||
)
|
||||
|
||||
def v2_playbook_on_include(self, included_file):
|
||||
event_data = dict(
|
||||
included_file=included_file,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_include', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_include(included_file)
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
self.set_play(play)
|
||||
if hasattr(play, 'hosts'):
|
||||
if isinstance(play.hosts, list):
|
||||
pattern = ','.join(play.hosts)
|
||||
else:
|
||||
pattern = play.hosts
|
||||
else:
|
||||
pattern = ''
|
||||
name = play.get_name().strip() or pattern
|
||||
event_data = dict(
|
||||
name=name,
|
||||
pattern=pattern,
|
||||
uuid=str(play._uuid),
|
||||
)
|
||||
with self.capture_event_data('playbook_on_play_start', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_play_start(play)
|
||||
|
||||
def v2_playbook_on_import_for_host(self, result, imported_file):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
with self.capture_event_data('playbook_on_import_for_host'):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_import_for_host(result, imported_file)
|
||||
|
||||
def v2_playbook_on_not_import_for_host(self, result, missing_file):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
with self.capture_event_data('playbook_on_not_import_for_host'):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_not_import_for_host(result, missing_file)
|
||||
|
||||
def v2_playbook_on_setup(self):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
with self.capture_event_data('playbook_on_setup'):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_setup()
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
# FIXME: Flag task path output as vv.
|
||||
task_uuid = str(task._uuid)
|
||||
if task_uuid in self.task_uuids:
|
||||
# FIXME: When this task UUID repeats, it means the play is using the
|
||||
# free strategy, so different hosts may be running different tasks
|
||||
# within a play.
|
||||
return
|
||||
self.task_uuids.add(task_uuid)
|
||||
self.set_task(task)
|
||||
event_data = dict(
|
||||
task=task,
|
||||
name=task.get_name(),
|
||||
is_conditional=is_conditional,
|
||||
uuid=task_uuid,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_task_start', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_task_start(task, is_conditional)
|
||||
|
||||
def v2_playbook_on_cleanup_task_start(self, task):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
self.set_task(task)
|
||||
event_data = dict(
|
||||
task=task,
|
||||
name=task.get_name(),
|
||||
uuid=str(task._uuid),
|
||||
is_conditional=True,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_task_start', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_cleanup_task_start(task)
|
||||
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
# NOTE: Re-using playbook_on_task_start event for this v2-specific
|
||||
# event, but setting is_conditional=True, which is how v1 identified a
|
||||
# task run as a handler.
|
||||
self.set_task(task)
|
||||
event_data = dict(
|
||||
task=task,
|
||||
name=task.get_name(),
|
||||
uuid=str(task._uuid),
|
||||
is_conditional=True,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_task_start', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_handler_task_start(task)
|
||||
|
||||
def v2_playbook_on_no_hosts_matched(self):
|
||||
with self.capture_event_data('playbook_on_no_hosts_matched'):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_no_hosts_matched()
|
||||
|
||||
def v2_playbook_on_no_hosts_remaining(self):
|
||||
with self.capture_event_data('playbook_on_no_hosts_remaining'):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_no_hosts_remaining()
|
||||
|
||||
def v2_playbook_on_notify(self, result, handler):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
handler=handler,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_notify', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_notify(result, handler)
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
self.clear_play()
|
||||
# FIXME: Add count of plays/tasks.
|
||||
event_data = dict(
|
||||
changed=stats.changed,
|
||||
dark=stats.dark,
|
||||
failures=stats.failures,
|
||||
ok=stats.ok,
|
||||
processed=stats.processed,
|
||||
skipped=stats.skipped,
|
||||
)
|
||||
with self.capture_event_data('playbook_on_stats', **event_data):
|
||||
super(BaseCallbackModule, self).v2_playbook_on_stats(stats)
|
||||
|
||||
def v2_runner_on_ok(self, result):
|
||||
# FIXME: Display detailed results or not based on verbosity.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
remote_addr=result._host.address,
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
event_loop=result._task.loop if hasattr(result._task, 'loop') else None,
|
||||
)
|
||||
with self.capture_event_data('runner_on_ok', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_ok(result)
|
||||
|
||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||
# FIXME: Add verbosity for exception/results output.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
remote_addr=result._host.address,
|
||||
res=result._result,
|
||||
task=result._task,
|
||||
ignore_errors=ignore_errors,
|
||||
event_loop=result._task.loop if hasattr(result._task, 'loop') else None,
|
||||
)
|
||||
with self.capture_event_data('runner_on_failed', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_failed(result, ignore_errors)
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
remote_addr=result._host.address,
|
||||
task=result._task,
|
||||
event_loop=result._task.loop if hasattr(result._task, 'loop') else None,
|
||||
)
|
||||
with self.capture_event_data('runner_on_skipped', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_skipped(result)
|
||||
|
||||
def v2_runner_on_unreachable(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
remote_addr=result._host.address,
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
)
|
||||
with self.capture_event_data('runner_on_unreachable', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_unreachable(result)
|
||||
|
||||
def v2_runner_on_no_hosts(self, task):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
task=task,
|
||||
)
|
||||
with self.capture_event_data('runner_on_no_hosts', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_no_hosts(task)
|
||||
|
||||
def v2_runner_on_async_poll(self, result):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
jid=result._result.get('ansible_job_id'),
|
||||
)
|
||||
with self.capture_event_data('runner_on_async_poll', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_async_poll(result)
|
||||
|
||||
def v2_runner_on_async_ok(self, result):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
jid=result._result.get('ansible_job_id'),
|
||||
)
|
||||
with self.capture_event_data('runner_on_async_ok', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_async_ok(result)
|
||||
|
||||
def v2_runner_on_async_failed(self, result):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
jid=result._result.get('ansible_job_id'),
|
||||
)
|
||||
with self.capture_event_data('runner_on_async_failed', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_async_failed(result)
|
||||
|
||||
def v2_runner_on_file_diff(self, result, diff):
|
||||
# NOTE: Not used by Ansible 2.x.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
diff=diff,
|
||||
)
|
||||
with self.capture_event_data('runner_on_file_diff', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_on_file_diff(result, diff)
|
||||
|
||||
def v2_on_file_diff(self, result):
|
||||
# NOTE: Logged as runner_on_file_diff.
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
diff=result._result.get('diff'),
|
||||
)
|
||||
with self.capture_event_data('runner_on_file_diff', **event_data):
|
||||
super(BaseCallbackModule, self).v2_on_file_diff(result)
|
||||
|
||||
def v2_runner_item_on_ok(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
)
|
||||
with self.capture_event_data('runner_item_on_ok', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_item_on_ok(result)
|
||||
|
||||
def v2_runner_item_on_failed(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
)
|
||||
with self.capture_event_data('runner_item_on_failed', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_item_on_failed(result)
|
||||
|
||||
def v2_runner_item_on_skipped(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
)
|
||||
with self.capture_event_data('runner_item_on_skipped', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_item_on_skipped(result)
|
||||
|
||||
def v2_runner_retry(self, result):
|
||||
event_data = dict(
|
||||
host=result._host.get_name(),
|
||||
task=result._task,
|
||||
res=result._result,
|
||||
)
|
||||
with self.capture_event_data('runner_retry', **event_data):
|
||||
super(BaseCallbackModule, self).v2_runner_retry(result)
|
||||
|
||||
|
||||
class TowerDefaultCallbackModule(BaseCallbackModule, DefaultCallbackModule):
|
||||
|
||||
CALLBACK_NAME = 'tower_display'
|
||||
|
||||
|
||||
class TowerMinimalCallbackModule(BaseCallbackModule, MinimalCallbackModule):
|
||||
|
||||
CALLBACK_NAME = 'minimal'
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
pass
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
self.set_task(task)
|
||||
@ -112,7 +112,7 @@ register(
|
||||
register(
|
||||
'AWX_PROOT_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
label=_('Enable PRoot for Job Execution'),
|
||||
label=_('Enable job isloation'),
|
||||
help_text=_('Isolates an Ansible job from protected parts of the Tower system to prevent exposing sensitive information.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
@ -121,8 +121,8 @@ register(
|
||||
register(
|
||||
'AWX_PROOT_BASE_PATH',
|
||||
field_class=fields.CharField,
|
||||
label=_('Base PRoot execution path'),
|
||||
help_text=_('The location that PRoot will create its temporary working directory.'),
|
||||
label=_('Job isolation execution path'),
|
||||
help_text=_('Create temporary working directories for isolated jobs in this location.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
@ -130,8 +130,8 @@ register(
|
||||
register(
|
||||
'AWX_PROOT_HIDE_PATHS',
|
||||
field_class=fields.StringListField,
|
||||
label=_('Paths to hide from PRoot jobs'),
|
||||
help_text=_('Extra paths to hide from PRoot isolated processes.'),
|
||||
label=_('Paths to hide from isolated jobs'),
|
||||
help_text=_('Additional paths to hide from isolated processes.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
@ -139,8 +139,8 @@ register(
|
||||
register(
|
||||
'AWX_PROOT_SHOW_PATHS',
|
||||
field_class=fields.StringListField,
|
||||
label=_('Paths to expose to PRoot jobs'),
|
||||
help_text=_('Explicit whitelist of paths to expose to PRoot jobs.'),
|
||||
label=_('Paths to expose to isolated jobs'),
|
||||
help_text=_('Whitelist of paths that would otherwise be hidden to expose to isolated jobs.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
|
||||
from kombu import Connection, Exchange, Queue
|
||||
from kombu.mixins import ConsumerMixin
|
||||
@ -12,10 +10,7 @@ from kombu.mixins import ConsumerMixin
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.core.management.base import NoArgsCommand
|
||||
from django.core.cache import cache
|
||||
from django.db import DatabaseError
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.timezone import FixedOffset
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
@ -36,112 +31,26 @@ class CallbackBrokerWorker(ConsumerMixin):
|
||||
|
||||
def process_task(self, body, message):
|
||||
try:
|
||||
if "event" not in body:
|
||||
raise Exception("Payload does not have an event")
|
||||
if "job_id" not in body:
|
||||
raise Exception("Payload does not have a job_id")
|
||||
if 'event' not in body:
|
||||
raise Exception('Payload does not have an event')
|
||||
if 'job_id' not in body and 'ad_hoc_command_id' not in body:
|
||||
raise Exception('Payload does not have a job_id or ad_hoc_command_id')
|
||||
if settings.DEBUG:
|
||||
logger.info("Body: {}".format(body))
|
||||
logger.info("Message: {}".format(message))
|
||||
self.process_job_event(body)
|
||||
logger.info('Body: {}'.format(body))
|
||||
logger.info('Message: {}'.format(message))
|
||||
try:
|
||||
if 'job_id' in body:
|
||||
JobEvent.create_from_data(**body)
|
||||
elif 'ad_hoc_command_id' in body:
|
||||
AdHocCommandEvent.create_from_data(**body)
|
||||
except DatabaseError as e:
|
||||
logger.error('Database Error Saving Job Event: {}'.format(e))
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
logger.error('Callback Task Processor Raised Exception: %r', exc)
|
||||
message.ack()
|
||||
|
||||
def process_job_event(self, payload):
|
||||
# Get the correct "verbose" value from the job.
|
||||
# If for any reason there's a problem, just use 0.
|
||||
if 'ad_hoc_command_id' in payload:
|
||||
event_type_key = 'ad_hoc_command_id'
|
||||
event_object_type = AdHocCommand
|
||||
else:
|
||||
event_type_key = 'job_id'
|
||||
event_object_type = Job
|
||||
|
||||
try:
|
||||
verbose = event_object_type.objects.get(id=payload[event_type_key]).verbosity
|
||||
except Exception as e:
|
||||
verbose=0
|
||||
# TODO: cache
|
||||
|
||||
# Convert the datetime for the job event's creation appropriately,
|
||||
# and include a time zone for it.
|
||||
#
|
||||
# In the event of any issue, throw it out, and Django will just save
|
||||
# the current time.
|
||||
try:
|
||||
if not isinstance(payload['created'], datetime.datetime):
|
||||
payload['created'] = parse_datetime(payload['created'])
|
||||
if not payload['created'].tzinfo:
|
||||
payload['created'] = payload['created'].replace(tzinfo=FixedOffset(0))
|
||||
except (KeyError, ValueError):
|
||||
payload.pop('created', None)
|
||||
|
||||
event_uuid = payload.get("uuid", '')
|
||||
parent_event_uuid = payload.get("parent_uuid", '')
|
||||
artifact_data = payload.get("artifact_data", None)
|
||||
|
||||
# Sanity check: Don't honor keys that we don't recognize.
|
||||
for key in payload.keys():
|
||||
if key not in (event_type_key, 'event', 'event_data',
|
||||
'created', 'counter', 'uuid'):
|
||||
payload.pop(key)
|
||||
|
||||
try:
|
||||
# If we're not in verbose mode, wipe out any module
|
||||
# arguments.
|
||||
res = payload['event_data'].get('res', {})
|
||||
if isinstance(res, dict):
|
||||
i = res.get('invocation', {})
|
||||
if verbose == 0 and 'module_args' in i:
|
||||
i['module_args'] = ''
|
||||
|
||||
if 'ad_hoc_command_id' in payload:
|
||||
AdHocCommandEvent.objects.create(**data)
|
||||
return
|
||||
|
||||
j = JobEvent(**payload)
|
||||
if payload['event'] == 'playbook_on_start':
|
||||
j.save()
|
||||
cache.set("{}_{}".format(payload['job_id'], event_uuid), j.id, 300)
|
||||
return
|
||||
else:
|
||||
if parent_event_uuid:
|
||||
parent_id = cache.get("{}_{}".format(payload['job_id'], parent_event_uuid), None)
|
||||
if parent_id is None:
|
||||
parent_id_obj = JobEvent.objects.filter(uuid=parent_event_uuid, job_id=payload['job_id'])
|
||||
if parent_id_obj.exists(): # Problematic if not there, means the parent hasn't been written yet... TODO
|
||||
j.parent_id = parent_id_obj[0].id
|
||||
print("Settings cache: {}_{} with value {}".format(payload['job_id'], parent_event_uuid, j.parent_id))
|
||||
cache.set("{}_{}".format(payload['job_id'], parent_event_uuid), j.parent_id, 300)
|
||||
else:
|
||||
print("Cache hit")
|
||||
j.parent_id = parent_id
|
||||
j.save(post_process=True)
|
||||
if event_uuid:
|
||||
cache.set("{}_{}".format(payload['job_id'], event_uuid), j.id, 300)
|
||||
except DatabaseError as e:
|
||||
logger.error("Database Error Saving Job Event: {}".format(e))
|
||||
|
||||
if artifact_data:
|
||||
try:
|
||||
self.process_artifacts(artifact_data, res, payload)
|
||||
except DatabaseError as e:
|
||||
logger.error("Database Error Saving Job Artifacts: {}".format(e))
|
||||
|
||||
def process_artifacts(self, artifact_data, res, payload):
|
||||
artifact_dict = json.loads(artifact_data)
|
||||
if res and isinstance(res, dict):
|
||||
if res.get('_ansible_no_log', False):
|
||||
artifact_dict['_ansible_no_log'] = True
|
||||
if artifact_data is not None:
|
||||
parent_job = Job.objects.filter(pk=payload['job_id']).first()
|
||||
if parent_job is not None and parent_job.artifacts != artifact_dict:
|
||||
parent_job.artifacts = artifact_dict
|
||||
parent_job.save(update_fields=['artifacts'])
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
'''
|
||||
@ -158,4 +67,3 @@ class Command(NoArgsCommand):
|
||||
worker.run()
|
||||
except KeyboardInterrupt:
|
||||
print('Terminating Callback Receiver')
|
||||
|
||||
|
||||
96
awx/main/migrations/0045_v310_job_event_stdout.py
Normal file
96
awx/main/migrations/0045_v310_job_event_stdout.py
Normal file
@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0044_v310_project_playbook_files'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='adhoccommandevent',
|
||||
name='end_line',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adhoccommandevent',
|
||||
name='start_line',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adhoccommandevent',
|
||||
name='stdout',
|
||||
field=models.TextField(default=b'', editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adhoccommandevent',
|
||||
name='uuid',
|
||||
field=models.CharField(default=b'', max_length=1024, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adhoccommandevent',
|
||||
name='verbosity',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobevent',
|
||||
name='end_line',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobevent',
|
||||
name='playbook',
|
||||
field=models.CharField(default=b'', max_length=1024, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobevent',
|
||||
name='start_line',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobevent',
|
||||
name='stdout',
|
||||
field=models.TextField(default=b'', editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobevent',
|
||||
name='verbosity',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adhoccommandevent',
|
||||
name='counter',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adhoccommandevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=100, choices=[(b'runner_on_failed', 'Host Failed'), (b'runner_on_ok', 'Host OK'), (b'runner_on_unreachable', 'Host Unreachable'), (b'runner_on_skipped', 'Host Skipped'), (b'debug', 'Debug'), (b'verbose', 'Verbose'), (b'deprecated', 'Deprecated'), (b'warning', 'Warning'), (b'system_warning', 'System Warning'), (b'error', 'Error')]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='jobevent',
|
||||
name='counter',
|
||||
field=models.PositiveIntegerField(default=0, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='jobevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=100, choices=[(b'runner_on_failed', 'Host Failed'), (b'runner_on_ok', 'Host OK'), (b'runner_on_error', 'Host Failure'), (b'runner_on_skipped', 'Host Skipped'), (b'runner_on_unreachable', 'Host Unreachable'), (b'runner_on_no_hosts', 'No Hosts Remaining'), (b'runner_on_async_poll', 'Host Polling'), (b'runner_on_async_ok', 'Host Async OK'), (b'runner_on_async_failed', 'Host Async Failure'), (b'runner_item_on_ok', 'Item OK'), (b'runner_item_on_failed', 'Item Failed'), (b'runner_item_on_skipped', 'Item Skipped'), (b'runner_retry', 'Host Retry'), (b'runner_on_file_diff', 'File Difference'), (b'playbook_on_start', 'Playbook Started'), (b'playbook_on_notify', 'Running Handlers'), (b'playbook_on_include', 'Including File'), (b'playbook_on_no_hosts_matched', 'No Hosts Matched'), (b'playbook_on_no_hosts_remaining', 'No Hosts Remaining'), (b'playbook_on_task_start', 'Task Started'), (b'playbook_on_vars_prompt', 'Variables Prompted'), (b'playbook_on_setup', 'Gathering Facts'), (b'playbook_on_import_for_host', 'internal: on Import for Host'), (b'playbook_on_not_import_for_host', 'internal: on Not Import for Host'), (b'playbook_on_play_start', 'Play Started'), (b'playbook_on_stats', 'Playbook Complete'), (b'debug', 'Debug'), (b'verbose', 'Verbose'), (b'deprecated', 'Deprecated'), (b'warning', 'Warning'), (b'system_warning', 'System Warning'), (b'error', 'Error')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='adhoccommandevent',
|
||||
unique_together=set([]),
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name='adhoccommandevent',
|
||||
index_together=set([('ad_hoc_command', 'event'), ('ad_hoc_command', 'uuid'), ('ad_hoc_command', 'end_line'), ('ad_hoc_command', 'start_line')]),
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name='jobevent',
|
||||
index_together=set([('job', 'event'), ('job', 'parent'), ('job', 'start_line'), ('job', 'uuid'), ('job', 'end_line')]),
|
||||
),
|
||||
]
|
||||
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
@ -10,7 +11,9 @@ from urlparse import urljoin
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.text import Truncator
|
||||
from django.utils.timezone import utc
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -257,24 +260,38 @@ class AdHocCommandEvent(CreatedModifiedModel):
|
||||
('runner_on_ok', _('Host OK'), False),
|
||||
('runner_on_unreachable', _('Host Unreachable'), True),
|
||||
# Tower won't see no_hosts (check is done earlier without callback).
|
||||
#('runner_on_no_hosts', _('No Hosts Matched'), False),
|
||||
# ('runner_on_no_hosts', _('No Hosts Matched'), False),
|
||||
# Tower will see skipped (when running in check mode for a module that
|
||||
# does not support check mode).
|
||||
('runner_on_skipped', _('Host Skipped'), False),
|
||||
# Tower does not support async for ad hoc commands.
|
||||
#('runner_on_async_poll', _('Host Polling'), False),
|
||||
#('runner_on_async_ok', _('Host Async OK'), False),
|
||||
#('runner_on_async_failed', _('Host Async Failure'), True),
|
||||
# Tower does not yet support --diff mode
|
||||
#('runner_on_file_diff', _('File Difference'), False),
|
||||
# Tower does not support async for ad hoc commands (not used in v2).
|
||||
# ('runner_on_async_poll', _('Host Polling'), False),
|
||||
# ('runner_on_async_ok', _('Host Async OK'), False),
|
||||
# ('runner_on_async_failed', _('Host Async Failure'), True),
|
||||
# Tower does not yet support --diff mode.
|
||||
# ('runner_on_file_diff', _('File Difference'), False),
|
||||
|
||||
# Additional event types for captured stdout not directly related to
|
||||
# runner events.
|
||||
('debug', _('Debug'), False),
|
||||
('verbose', _('Verbose'), False),
|
||||
('deprecated', _('Deprecated'), False),
|
||||
('warning', _('Warning'), False),
|
||||
('system_warning', _('System Warning'), False),
|
||||
('error', _('Error'), False),
|
||||
]
|
||||
FAILED_EVENTS = [x[0] for x in EVENT_TYPES if x[2]]
|
||||
EVENT_CHOICES = [(x[0], x[1]) for x in EVENT_TYPES]
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
unique_together = [('ad_hoc_command', 'host_name')]
|
||||
ordering = ('-pk',)
|
||||
index_together = [
|
||||
('ad_hoc_command', 'event'),
|
||||
('ad_hoc_command', 'uuid'),
|
||||
('ad_hoc_command', 'start_line'),
|
||||
('ad_hoc_command', 'end_line'),
|
||||
]
|
||||
|
||||
ad_hoc_command = models.ForeignKey(
|
||||
'AdHocCommand',
|
||||
@ -311,8 +328,30 @@ class AdHocCommandEvent(CreatedModifiedModel):
|
||||
default=False,
|
||||
editable=False,
|
||||
)
|
||||
uuid = models.CharField(
|
||||
max_length=1024,
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
counter = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
stdout = models.TextField(
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
verbosity = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
start_line = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
end_line = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -350,3 +389,28 @@ class AdHocCommandEvent(CreatedModifiedModel):
|
||||
except (IndexError, AttributeError):
|
||||
pass
|
||||
super(AdHocCommandEvent, self).save(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def create_from_data(self, **kwargs):
|
||||
# Convert the datetime for the ad hoc command event's creation
|
||||
# appropriately, and include a time zone for it.
|
||||
#
|
||||
# In the event of any issue, throw it out, and Django will just save
|
||||
# the current time.
|
||||
try:
|
||||
if not isinstance(kwargs['created'], datetime.datetime):
|
||||
kwargs['created'] = parse_datetime(kwargs['created'])
|
||||
if not kwargs['created'].tzinfo:
|
||||
kwargs['created'] = kwargs['created'].replace(tzinfo=utc)
|
||||
except (KeyError, ValueError):
|
||||
kwargs.pop('created', None)
|
||||
|
||||
# Sanity check: Don't honor keys that we don't recognize.
|
||||
valid_keys = {'ad_hoc_command_id', 'event', 'event_data', 'created',
|
||||
'counter', 'uuid', 'stdout', 'start_line', 'end_line',
|
||||
'verbosity'}
|
||||
for key in kwargs.keys():
|
||||
if key not in valid_keys:
|
||||
kwargs.pop(key)
|
||||
|
||||
return AdHocCommandEvent.objects.create(**kwargs)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
import hmac
|
||||
import json
|
||||
import yaml
|
||||
@ -11,8 +12,12 @@ from urlparse import urljoin
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.db.models import Q, Count
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.timezone import utc
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -931,24 +936,29 @@ class JobEvent(CreatedModifiedModel):
|
||||
# - playbook_on_vars_prompt (for each play, but before play starts, we
|
||||
# currently don't handle responding to these prompts)
|
||||
# - playbook_on_play_start (once for each play)
|
||||
# - playbook_on_import_for_host
|
||||
# - playbook_on_not_import_for_host
|
||||
# - playbook_on_import_for_host (not logged, not used for v2)
|
||||
# - playbook_on_not_import_for_host (not logged, not used for v2)
|
||||
# - playbook_on_no_hosts_matched
|
||||
# - playbook_on_no_hosts_remaining
|
||||
# - playbook_on_setup
|
||||
# - playbook_on_include (only v2 - only used for handlers?)
|
||||
# - playbook_on_setup (not used for v2)
|
||||
# - runner_on*
|
||||
# - playbook_on_task_start (once for each task within a play)
|
||||
# - runner_on_failed
|
||||
# - runner_on_ok
|
||||
# - runner_on_error
|
||||
# - runner_on_error (not used for v2)
|
||||
# - runner_on_skipped
|
||||
# - runner_on_unreachable
|
||||
# - runner_on_no_hosts
|
||||
# - runner_on_async_poll
|
||||
# - runner_on_async_ok
|
||||
# - runner_on_async_failed
|
||||
# - runner_on_file_diff
|
||||
# - playbook_on_notify (once for each notification from the play)
|
||||
# - runner_on_no_hosts (not used for v2)
|
||||
# - runner_on_async_poll (not used for v2)
|
||||
# - runner_on_async_ok (not used for v2)
|
||||
# - runner_on_async_failed (not used for v2)
|
||||
# - runner_on_file_diff (v2 event is v2_on_file_diff)
|
||||
# - runner_item_on_ok (v2 only)
|
||||
# - runner_item_on_failed (v2 only)
|
||||
# - runner_item_on_skipped (v2 only)
|
||||
# - runner_retry (v2 only)
|
||||
# - playbook_on_notify (once for each notification from the play, not used for v2)
|
||||
# - playbook_on_stats
|
||||
|
||||
EVENT_TYPES = [
|
||||
@ -962,22 +972,34 @@ class JobEvent(CreatedModifiedModel):
|
||||
(3, 'runner_on_async_poll', _('Host Polling'), False),
|
||||
(3, 'runner_on_async_ok', _('Host Async OK'), False),
|
||||
(3, 'runner_on_async_failed', _('Host Async Failure'), True),
|
||||
# AWX does not yet support --diff mode
|
||||
(3, 'runner_item_on_ok', _('Item OK'), False),
|
||||
(3, 'runner_item_on_failed', _('Item Failed'), True),
|
||||
(3, 'runner_item_on_skipped', _('Item Skipped'), False),
|
||||
(3, 'runner_retry', _('Host Retry'), False),
|
||||
# Tower does not yet support --diff mode.
|
||||
(3, 'runner_on_file_diff', _('File Difference'), False),
|
||||
(0, 'playbook_on_start', _('Playbook Started'), False),
|
||||
(2, 'playbook_on_notify', _('Running Handlers'), False),
|
||||
(2, 'playbook_on_include', _('Including File'), False),
|
||||
(2, 'playbook_on_no_hosts_matched', _('No Hosts Matched'), False),
|
||||
(2, 'playbook_on_no_hosts_remaining', _('No Hosts Remaining'), False),
|
||||
(2, 'playbook_on_task_start', _('Task Started'), False),
|
||||
# AWX does not yet support vars_prompt (and will probably hang :)
|
||||
# Tower does not yet support vars_prompt (and will probably hang :)
|
||||
(1, 'playbook_on_vars_prompt', _('Variables Prompted'), False),
|
||||
(2, 'playbook_on_setup', _('Gathering Facts'), False),
|
||||
# callback will not record this
|
||||
(2, 'playbook_on_import_for_host', _('internal: on Import for Host'), False),
|
||||
# callback will not record this
|
||||
(2, 'playbook_on_not_import_for_host', _('internal: on Not Import for Host'), False),
|
||||
(1, 'playbook_on_play_start', _('Play Started'), False),
|
||||
(1, 'playbook_on_stats', _('Playbook Complete'), False),
|
||||
|
||||
# Additional event types for captured stdout not directly related to
|
||||
# playbook or runner events.
|
||||
(0, 'debug', _('Debug'), False),
|
||||
(0, 'verbose', _('Verbose'), False),
|
||||
(0, 'deprecated', _('Deprecated'), False),
|
||||
(0, 'warning', _('Warning'), False),
|
||||
(0, 'system_warning', _('System Warning'), False),
|
||||
(0, 'error', _('Error'), True),
|
||||
]
|
||||
FAILED_EVENTS = [x[1] for x in EVENT_TYPES if x[3]]
|
||||
EVENT_CHOICES = [(x[1], x[2]) for x in EVENT_TYPES]
|
||||
@ -986,6 +1008,13 @@ class JobEvent(CreatedModifiedModel):
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
ordering = ('pk',)
|
||||
index_together = [
|
||||
('job', 'event'),
|
||||
('job', 'uuid'),
|
||||
('job', 'start_line'),
|
||||
('job', 'end_line'),
|
||||
('job', 'parent'),
|
||||
]
|
||||
|
||||
job = models.ForeignKey(
|
||||
'Job',
|
||||
@ -1032,12 +1061,17 @@ class JobEvent(CreatedModifiedModel):
|
||||
related_name='job_events',
|
||||
editable=False,
|
||||
)
|
||||
playbook = models.CharField(
|
||||
max_length=1024,
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
play = models.CharField(
|
||||
max_length=1024,
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
role = models.CharField( # FIXME: Determine from callback or task name.
|
||||
role = models.CharField(
|
||||
max_length=1024,
|
||||
default='',
|
||||
editable=False,
|
||||
@ -1057,8 +1091,24 @@ class JobEvent(CreatedModifiedModel):
|
||||
)
|
||||
counter = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
stdout = models.TextField(
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
verbosity = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
start_line = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
end_line = models.PositiveIntegerField(
|
||||
default=0,
|
||||
editable=False,
|
||||
)
|
||||
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:job_event_detail', args=(self.pk,))
|
||||
@ -1119,7 +1169,8 @@ class JobEvent(CreatedModifiedModel):
|
||||
pass
|
||||
return msg
|
||||
|
||||
def _find_parent(self):
|
||||
def _find_parent_id(self):
|
||||
# Find the (most likely) parent event for this event.
|
||||
parent_events = set()
|
||||
if self.event in ('playbook_on_play_start', 'playbook_on_stats',
|
||||
'playbook_on_vars_prompt'):
|
||||
@ -1135,101 +1186,55 @@ class JobEvent(CreatedModifiedModel):
|
||||
parent_events.add('playbook_on_setup')
|
||||
parent_events.add('playbook_on_task_start')
|
||||
if parent_events:
|
||||
try:
|
||||
qs = JobEvent.objects.filter(job_id=self.job_id)
|
||||
if self.pk:
|
||||
qs = qs.filter(pk__lt=self.pk, event__in=parent_events)
|
||||
else:
|
||||
qs = qs.filter(event__in=parent_events)
|
||||
return qs.order_by('-pk')[0]
|
||||
except IndexError:
|
||||
pass
|
||||
return None
|
||||
qs = JobEvent.objects.filter(job_id=self.job_id, event__in=parent_events).order_by('-pk')
|
||||
if self.pk:
|
||||
qs = qs.filter(pk__lt=self.pk)
|
||||
return qs.only('id').values_list('id', flat=True).first()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from awx.main.models.inventory import Host
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
# Skip normal checks on save if we're only updating failed/changed
|
||||
# flags triggered from a child event.
|
||||
from_parent_update = kwargs.pop('from_parent_update', False)
|
||||
if not from_parent_update:
|
||||
res = self.event_data.get('res', None)
|
||||
# Workaround for Ansible 1.2, where the runner_on_async_ok event is
|
||||
# created even when the async task failed. Change the event to be
|
||||
# correct.
|
||||
if self.event == 'runner_on_async_ok':
|
||||
try:
|
||||
if res.get('failed', False) or res.get('rc', 0) != 0:
|
||||
self.event = 'runner_on_async_failed'
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
if self.event in self.FAILED_EVENTS:
|
||||
if not self.event_data.get('ignore_errors', False):
|
||||
self.failed = True
|
||||
if 'failed' not in update_fields:
|
||||
update_fields.append('failed')
|
||||
if isinstance(res, dict) and res.get('changed', False):
|
||||
def _update_from_event_data(self):
|
||||
# Update job event model fields from event data.
|
||||
updated_fields = set()
|
||||
job = self.job
|
||||
verbosity = job.verbosity
|
||||
event_data = self.event_data
|
||||
res = event_data.get('res', None)
|
||||
if self.event in self.FAILED_EVENTS and not event_data.get('ignore_errors', False):
|
||||
self.failed = True
|
||||
updated_fields.add('failed')
|
||||
if isinstance(res, dict):
|
||||
if res.get('changed', False):
|
||||
self.changed = True
|
||||
if 'changed' not in update_fields:
|
||||
update_fields.append('changed')
|
||||
if self.event == 'playbook_on_stats':
|
||||
try:
|
||||
failures_dict = self.event_data.get('failures', {})
|
||||
dark_dict = self.event_data.get('dark', {})
|
||||
self.failed = bool(sum(failures_dict.values()) +
|
||||
sum(dark_dict.values()))
|
||||
if 'failed' not in update_fields:
|
||||
update_fields.append('failed')
|
||||
changed_dict = self.event_data.get('changed', {})
|
||||
self.changed = bool(sum(changed_dict.values()))
|
||||
if 'changed' not in update_fields:
|
||||
update_fields.append('changed')
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
self.play = self.event_data.get('play', '').strip()
|
||||
if 'play' not in update_fields:
|
||||
update_fields.append('play')
|
||||
self.task = self.event_data.get('task', '').strip()
|
||||
if 'task' not in update_fields:
|
||||
update_fields.append('task')
|
||||
self.role = self.event_data.get('role', '').strip()
|
||||
if 'role' not in update_fields:
|
||||
update_fields.append('role')
|
||||
self.host_name = self.event_data.get('host', '').strip()
|
||||
if 'host_name' not in update_fields:
|
||||
update_fields.append('host_name')
|
||||
# Only update job event hierarchy and related models during post
|
||||
# processing (after running job).
|
||||
post_process = kwargs.pop('post_process', False)
|
||||
if post_process:
|
||||
updated_fields.add('changed')
|
||||
# If we're not in verbose mode, wipe out any module arguments.
|
||||
invocation = res.get('invocation', None)
|
||||
if isinstance(invocation, dict) and verbosity == 0 and 'module_args' in invocation:
|
||||
event_data['res']['invocation']['module_args'] = ''
|
||||
self.event_data = event_data
|
||||
update_fields.add('event_data')
|
||||
if self.event == 'playbook_on_stats':
|
||||
try:
|
||||
if not self.host_id and self.host_name:
|
||||
host_qs = Host.objects.filter(inventory__jobs__id=self.job_id, name=self.host_name)
|
||||
host_id = host_qs.only('id').values_list('id', flat=True)
|
||||
if host_id.exists():
|
||||
self.host_id = host_id[0]
|
||||
if 'host_id' not in update_fields:
|
||||
update_fields.append('host_id')
|
||||
except (IndexError, AttributeError):
|
||||
failures_dict = event_data.get('failures', {})
|
||||
dark_dict = event_data.get('dark', {})
|
||||
self.failed = bool(sum(failures_dict.values()) +
|
||||
sum(dark_dict.values()))
|
||||
updated_fields.add('failed')
|
||||
changed_dict = event_data.get('changed', {})
|
||||
self.changed = bool(sum(changed_dict.values()))
|
||||
updated_fields.add('changed')
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
if self.parent is None:
|
||||
self.parent = self._find_parent()
|
||||
if 'parent' not in update_fields:
|
||||
update_fields.append('parent')
|
||||
super(JobEvent, self).save(*args, **kwargs)
|
||||
if post_process and not from_parent_update:
|
||||
self.update_parent_failed_and_changed()
|
||||
# FIXME: The update_hosts() call (and its queries) are the current
|
||||
# performance bottleneck....
|
||||
if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False):
|
||||
self.update_hosts()
|
||||
self.update_host_summary_from_stats()
|
||||
for field in ('playbook', 'play', 'task', 'role', 'host'):
|
||||
value = force_text(event_data.get(field, '')).strip()
|
||||
if field == 'host':
|
||||
field = 'host_name'
|
||||
if value != getattr(self, field):
|
||||
setattr(self, field, value)
|
||||
updated_fields.add(field)
|
||||
return updated_fields
|
||||
|
||||
def update_parent_failed_and_changed(self):
|
||||
# Propagage failed and changed flags to parent events.
|
||||
if self.parent:
|
||||
def _update_parent_failed_and_changed(self):
|
||||
# Propagate failed and changed flags to parent events.
|
||||
if self.parent_id:
|
||||
parent = self.parent
|
||||
update_fields = []
|
||||
if self.failed and not parent.failed:
|
||||
@ -1240,9 +1245,10 @@ class JobEvent(CreatedModifiedModel):
|
||||
update_fields.append('changed')
|
||||
if update_fields:
|
||||
parent.save(update_fields=update_fields, from_parent_update=True)
|
||||
parent.update_parent_failed_and_changed()
|
||||
parent._update_parent_failed_and_changed()
|
||||
|
||||
def update_hosts(self, extra_host_pks=None):
|
||||
def _update_hosts(self, extra_host_pks=None):
|
||||
# Update job event hosts m2m from host_name, propagate to parent events.
|
||||
from awx.main.models.inventory import Host
|
||||
extra_host_pks = set(extra_host_pks or [])
|
||||
hostnames = set()
|
||||
@ -1256,16 +1262,14 @@ class JobEvent(CreatedModifiedModel):
|
||||
pass
|
||||
qs = Host.objects.filter(inventory__jobs__id=self.job_id)
|
||||
qs = qs.filter(Q(name__in=hostnames) | Q(pk__in=extra_host_pks))
|
||||
qs = qs.exclude(job_events__pk=self.id)
|
||||
for host in qs.only('id'):
|
||||
qs = qs.exclude(job_events__pk=self.id).only('id')
|
||||
for host in qs:
|
||||
self.hosts.add(host)
|
||||
if self.parent:
|
||||
self.parent.update_hosts(self.hosts.only('id').values_list('id', flat=True))
|
||||
if self.parent_id:
|
||||
self.parent._update_hosts(qs.values_list('id', flat=True))
|
||||
|
||||
def update_host_summary_from_stats(self):
|
||||
def _update_host_summary_from_stats(self):
|
||||
from awx.main.models.inventory import Host
|
||||
if self.event != 'playbook_on_stats':
|
||||
return
|
||||
hostnames = set()
|
||||
try:
|
||||
for v in self.event_data.values():
|
||||
@ -1276,7 +1280,6 @@ class JobEvent(CreatedModifiedModel):
|
||||
qs = Host.objects.filter(inventory__jobs__id=self.job_id,
|
||||
name__in=hostnames)
|
||||
job = self.job
|
||||
#for host in qs.only('id', 'name'):
|
||||
for host in hostnames:
|
||||
host_stats = {}
|
||||
for stat in ('changed', 'dark', 'failures', 'ok', 'processed', 'skipped'):
|
||||
@ -1300,6 +1303,112 @@ class JobEvent(CreatedModifiedModel):
|
||||
job.inventory.update_computed_fields()
|
||||
emit_channel_notification('jobs-summary', dict(group_name='jobs', unified_job_id=job.id))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from awx.main.models.inventory import Host
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
# Update model fields and related objects unless we're only updating
|
||||
# failed/changed flags triggered from a child event.
|
||||
from_parent_update = kwargs.pop('from_parent_update', False)
|
||||
if not from_parent_update:
|
||||
# Update model fields from event data.
|
||||
updated_fields = self._update_from_event_data()
|
||||
for field in updated_fields:
|
||||
if field not in update_fields:
|
||||
update_fields.append(field)
|
||||
# Update host related field from host_name.
|
||||
if not self.host_id and self.host_name:
|
||||
host_qs = Host.objects.filter(inventory__jobs__id=self.job_id, name=self.host_name)
|
||||
host_id = host_qs.only('id').values_list('id', flat=True).first()
|
||||
if host_id != self.host_id:
|
||||
self.host_id = host_id
|
||||
if 'host_id' not in update_fields:
|
||||
update_fields.append('host_id')
|
||||
# Update parent related field if not set.
|
||||
if self.parent_id is None:
|
||||
self.parent_id = self._find_parent_id()
|
||||
if self.parent_id and 'parent_id' not in update_fields:
|
||||
update_fields.append('parent_id')
|
||||
super(JobEvent, self).save(*args, **kwargs)
|
||||
# Update related objects after this event is saved.
|
||||
if not from_parent_update:
|
||||
if self.parent_id:
|
||||
self._update_parent_failed_and_changed()
|
||||
# FIXME: The update_hosts() call (and its queries) are the current
|
||||
# performance bottleneck....
|
||||
if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False):
|
||||
self._update_hosts()
|
||||
if self.event == 'playbook_on_stats':
|
||||
self._update_host_summary_from_stats()
|
||||
|
||||
@classmethod
|
||||
def create_from_data(self, **kwargs):
|
||||
# Must have a job_id specified.
|
||||
if not kwargs.get('job_id', None):
|
||||
return
|
||||
|
||||
# Convert the datetime for the job event's creation appropriately,
|
||||
# and include a time zone for it.
|
||||
#
|
||||
# In the event of any issue, throw it out, and Django will just save
|
||||
# the current time.
|
||||
try:
|
||||
if not isinstance(kwargs['created'], datetime.datetime):
|
||||
kwargs['created'] = parse_datetime(kwargs['created'])
|
||||
if not kwargs['created'].tzinfo:
|
||||
kwargs['created'] = kwargs['created'].replace(tzinfo=utc)
|
||||
except (KeyError, ValueError):
|
||||
kwargs.pop('created', None)
|
||||
|
||||
# Save UUID and parent UUID for determining parent-child relationship.
|
||||
job_event_uuid = kwargs.get('uuid', None)
|
||||
parent_event_uuid = kwargs.get('parent_uuid', None)
|
||||
artifact_data = kwargs.get('artifact_data', None)
|
||||
|
||||
# Sanity check: Don't honor keys that we don't recognize.
|
||||
valid_keys = {'job_id', 'event', 'event_data', 'playbook', 'play',
|
||||
'role', 'task', 'created', 'counter', 'uuid', 'stdout',
|
||||
'start_line', 'end_line', 'verbosity'}
|
||||
for key in kwargs.keys():
|
||||
if key not in valid_keys:
|
||||
kwargs.pop(key)
|
||||
|
||||
# Try to find a parent event based on UUID.
|
||||
if parent_event_uuid:
|
||||
cache_key = '{}_{}'.format(kwargs['job_id'], parent_event_uuid)
|
||||
parent_id = cache.get(cache_key)
|
||||
if parent_id is None:
|
||||
parent_id = JobEvent.objects.filter(job_id=kwargs['job_id'], uuid=parent_event_uuid).only('id').values_list('id', flat=True).first()
|
||||
if parent_id:
|
||||
print("Settings cache: {} with value {}".format(cache_key, parent_id))
|
||||
cache.set(cache_key, parent_id, 300)
|
||||
if parent_id:
|
||||
kwargs['parent_id'] = parent_id
|
||||
|
||||
job_event = JobEvent.objects.create(**kwargs)
|
||||
|
||||
# Cache this job event ID vs. UUID for future parent lookups.
|
||||
if job_event_uuid:
|
||||
cache_key = '{}_{}'.format(kwargs['job_id'], job_event_uuid)
|
||||
cache.set(cache_key, job_event.id, 300)
|
||||
|
||||
# Save artifact data to parent job (if provided).
|
||||
if artifact_data:
|
||||
artifact_dict = json.loads(artifact_data)
|
||||
event_data = kwargs.get('event_data', None)
|
||||
if event_data and isinstance(event_data, dict):
|
||||
res = event_data.get('res', None)
|
||||
if res and isinstance(res, dict):
|
||||
if res.get('_ansible_no_log', False):
|
||||
artifact_dict['_ansible_no_log'] = True
|
||||
parent_job = Job.objects.filter(pk=kwargs['job_id']).first()
|
||||
if parent_job and parent_job.artifacts != artifact_dict:
|
||||
parent_job.artifacts = artifact_dict
|
||||
parent_job.save(update_fields=['artifacts'])
|
||||
|
||||
return job_event
|
||||
|
||||
@classmethod
|
||||
def get_startevent_queryset(cls, parent_task, starting_events, ordering=None):
|
||||
'''
|
||||
|
||||
@ -696,8 +696,11 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
||||
return StringIO(msg['missing' if self.finished else 'pending'])
|
||||
|
||||
def _escape_ascii(self, content):
|
||||
ansi_escape = re.compile(r'\x1b[^m]*m')
|
||||
return ansi_escape.sub('', content)
|
||||
# Remove ANSI escape sequences used to embed event data.
|
||||
content = re.sub(r'\x1b\[K(?:[A-Za-z0-9+/=]+\x1b\[\d+D)+\x1b\[K', '', content)
|
||||
# Remove ANSI color escape sequences.
|
||||
content = re.sub(r'\x1b[^m]*m', '', content)
|
||||
return content
|
||||
|
||||
def _result_stdout_raw(self, redact_sensitive=False, escape_ascii=False):
|
||||
content = self.result_stdout_raw_handle().read()
|
||||
|
||||
@ -93,6 +93,22 @@ class WorkflowNodeBase(CreatedModifiedModel):
|
||||
data[fd] = self.char_prompts[fd]
|
||||
return data
|
||||
|
||||
@property
|
||||
def job_type(self):
|
||||
return self.char_prompts.get('job_type', None)
|
||||
|
||||
@property
|
||||
def job_tags(self):
|
||||
return self.char_prompts.get('job_tags', None)
|
||||
|
||||
@property
|
||||
def skip_tags(self):
|
||||
return self.char_prompts.get('skip_tags', None)
|
||||
|
||||
@property
|
||||
def limit(self):
|
||||
return self.char_prompts.get('limit', None)
|
||||
|
||||
def get_prompts_warnings(self):
|
||||
ujt_obj = self.unified_job_template
|
||||
if ujt_obj is None:
|
||||
@ -382,6 +398,9 @@ class WorkflowJob(UnifiedJob, WorkflowJobOptions, JobNotificationMixin, Workflow
|
||||
from awx.main.tasks import RunWorkflowJob
|
||||
return RunWorkflowJob
|
||||
|
||||
def _has_failed(self):
|
||||
return False
|
||||
|
||||
def socketio_emit_data(self):
|
||||
return {}
|
||||
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
|
||||
# Kombu
|
||||
from kombu import Connection, Exchange, Producer
|
||||
|
||||
__all__ = ['FifoQueue', 'CallbackQueueDispatcher']
|
||||
|
||||
__all__ = ['FifoQueue']
|
||||
|
||||
# TODO: Figure out wtf to do with this class
|
||||
class FifoQueue(object):
|
||||
@ -33,3 +43,39 @@ class FifoQueue(object):
|
||||
answer = None
|
||||
if answer:
|
||||
return json.loads(answer)
|
||||
|
||||
|
||||
class CallbackQueueDispatcher(object):
|
||||
|
||||
def __init__(self):
|
||||
self.callback_connection = getattr(settings, 'CALLBACK_CONNECTION', None)
|
||||
self.connection_queue = getattr(settings, 'CALLBACK_QUEUE', '')
|
||||
self.connection = None
|
||||
self.exchange = None
|
||||
self.logger = logging.getLogger('awx.main.queue.CallbackQueueDispatcher')
|
||||
|
||||
def dispatch(self, obj):
|
||||
if not self.callback_connection or not self.connection_queue:
|
||||
return
|
||||
active_pid = os.getpid()
|
||||
for retry_count in xrange(4):
|
||||
try:
|
||||
if not hasattr(self, 'connection_pid'):
|
||||
self.connection_pid = active_pid
|
||||
if self.connection_pid != active_pid:
|
||||
self.connection = None
|
||||
if self.connection is None:
|
||||
self.connection = Connection(self.callback_connection)
|
||||
self.exchange = Exchange(self.connection_queue, type='direct')
|
||||
|
||||
producer = Producer(self.connection)
|
||||
producer.publish(obj,
|
||||
serializer='json',
|
||||
compression='bzip2',
|
||||
exchange=self.exchange,
|
||||
declare=[self.exchange],
|
||||
routing_key=self.connection_queue)
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.info('Publish Job Event Exception: %r, retry=%d', e,
|
||||
retry_count, exc_info=True)
|
||||
|
||||
@ -73,10 +73,12 @@ def process_finished_workflow_jobs(workflow_jobs):
|
||||
dag = WorkflowDAG(workflow_job)
|
||||
if dag.is_workflow_done():
|
||||
with transaction.atomic():
|
||||
# TODO: detect if wfj failed
|
||||
workflow_job.status = 'completed'
|
||||
if workflow_job._has_failed():
|
||||
workflow_job.status = 'failed'
|
||||
else:
|
||||
workflow_job.status = 'successful'
|
||||
workflow_job.save()
|
||||
workflow_job.websocket_emit_status('completed')
|
||||
workflow_job.websocket_emit_status(workflow_job.status)
|
||||
|
||||
def rebuild_graph():
|
||||
"""Regenerate the task graph by refreshing known tasks from Tower, purging
|
||||
|
||||
@ -48,9 +48,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from awx.main.constants import CLOUD_PROVIDERS
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.models import UnifiedJob
|
||||
from awx.main.queue import CallbackQueueDispatcher
|
||||
from awx.main.task_engine import TaskEnhancer
|
||||
from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url,
|
||||
check_proot_installed, build_proot_temp_dir, wrap_args_with_proot)
|
||||
check_proot_installed, build_proot_temp_dir, wrap_args_with_proot,
|
||||
OutputEventFilter)
|
||||
from awx.main.consumers import emit_channel_notification
|
||||
|
||||
__all__ = ['RunJob', 'RunSystemJob', 'RunProjectUpdate', 'RunInventoryUpdate',
|
||||
@ -398,6 +400,8 @@ class BaseTask(Task):
|
||||
if os.path.isdir(os.path.join(venv_libdir, python_ver)):
|
||||
env['PYTHONPATH'] = os.path.join(venv_libdir, python_ver, "site-packages") + ":"
|
||||
break
|
||||
# Add awx/lib to PYTHONPATH.
|
||||
env['PYTHONPATH'] = ':'.join(filter(None, [self.get_path_to('..', 'lib'), env.get('PYTHONPATH', '')]))
|
||||
return env
|
||||
|
||||
def add_tower_venv(self, env):
|
||||
@ -495,6 +499,17 @@ class BaseTask(Task):
|
||||
'''
|
||||
return OrderedDict()
|
||||
|
||||
def get_stdout_handle(self, instance):
|
||||
'''
|
||||
Return an open file object for capturing stdout.
|
||||
'''
|
||||
if not os.path.exists(settings.JOBOUTPUT_ROOT):
|
||||
os.makedirs(settings.JOBOUTPUT_ROOT)
|
||||
stdout_filename = os.path.join(settings.JOBOUTPUT_ROOT, "%d-%s.out" % (instance.pk, str(uuid.uuid1())))
|
||||
stdout_handle = codecs.open(stdout_filename, 'w', encoding='utf-8')
|
||||
assert stdout_handle.name == stdout_filename
|
||||
return stdout_handle
|
||||
|
||||
def run_pexpect(self, instance, args, cwd, env, passwords, stdout_handle,
|
||||
output_replacements=None, extra_update_fields=None):
|
||||
'''
|
||||
@ -644,10 +659,7 @@ class BaseTask(Task):
|
||||
cwd = self.build_cwd(instance, **kwargs)
|
||||
env = self.build_env(instance, **kwargs)
|
||||
safe_env = self.build_safe_env(instance, **kwargs)
|
||||
if not os.path.exists(settings.JOBOUTPUT_ROOT):
|
||||
os.makedirs(settings.JOBOUTPUT_ROOT)
|
||||
stdout_filename = os.path.join(settings.JOBOUTPUT_ROOT, "%d-%s.out" % (pk, str(uuid.uuid1())))
|
||||
stdout_handle = codecs.open(stdout_filename, 'w', encoding='utf-8')
|
||||
stdout_handle = self.get_stdout_handle(instance)
|
||||
if self.should_use_proot(instance, **kwargs):
|
||||
if not check_proot_installed():
|
||||
raise RuntimeError('proot is not installed')
|
||||
@ -661,7 +673,7 @@ class BaseTask(Task):
|
||||
args = self.wrap_args_with_ssh_agent(args, ssh_key_path, ssh_auth_sock)
|
||||
safe_args = self.wrap_args_with_ssh_agent(safe_args, ssh_key_path, ssh_auth_sock)
|
||||
instance = self.update_model(pk, job_args=json.dumps(safe_args),
|
||||
job_cwd=cwd, job_env=safe_env, result_stdout_file=stdout_filename)
|
||||
job_cwd=cwd, job_env=safe_env, result_stdout_file=stdout_handle.name)
|
||||
status, rc = self.run_pexpect(instance, args, cwd, env, kwargs['passwords'], stdout_handle,
|
||||
extra_update_fields=extra_update_fields)
|
||||
except Exception:
|
||||
@ -780,6 +792,7 @@ class RunJob(BaseTask):
|
||||
if job.project:
|
||||
env['PROJECT_REVISION'] = job.project.scm_revision
|
||||
env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_path
|
||||
env['ANSIBLE_STDOUT_CALLBACK'] = 'tower_display'
|
||||
env['REST_API_URL'] = settings.INTERNAL_API_URL
|
||||
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
||||
env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE
|
||||
@ -975,6 +988,25 @@ class RunJob(BaseTask):
|
||||
d[re.compile(r'^Vault password:\s*?$', re.M)] = 'vault_password'
|
||||
return d
|
||||
|
||||
def get_stdout_handle(self, instance):
|
||||
'''
|
||||
Wrap stdout file object to capture events.
|
||||
'''
|
||||
stdout_handle = super(RunJob, self).get_stdout_handle(instance)
|
||||
|
||||
if getattr(settings, 'USE_CALLBACK_QUEUE', False):
|
||||
dispatcher = CallbackQueueDispatcher()
|
||||
|
||||
def job_event_callback(event_data):
|
||||
event_data.setdefault('job_id', instance.id)
|
||||
dispatcher.dispatch(event_data)
|
||||
else:
|
||||
def job_event_callback(event_data):
|
||||
event_data.setdefault('job_id', instance.id)
|
||||
JobEvent.create_from_data(**event_data)
|
||||
|
||||
return OutputEventFilter(stdout_handle, job_event_callback)
|
||||
|
||||
def get_ssh_key_path(self, instance, **kwargs):
|
||||
'''
|
||||
If using an SSH key, return the path for use by ssh-agent.
|
||||
@ -1020,11 +1052,6 @@ class RunJob(BaseTask):
|
||||
pass
|
||||
else:
|
||||
update_inventory_computed_fields.delay(inventory.id, True)
|
||||
# Update job event fields after job has completed (only when using REST
|
||||
# API callback).
|
||||
if not getattr(settings, 'CALLBACK_CONSUMER_PORT', None) and not getattr(settings, 'CALLBACK_QUEUE', None):
|
||||
for job_event in job.job_events.order_by('pk'):
|
||||
job_event.save(post_process=True)
|
||||
|
||||
|
||||
class RunProjectUpdate(BaseTask):
|
||||
@ -1598,6 +1625,7 @@ class RunAdHocCommand(BaseTask):
|
||||
env['INVENTORY_HOSTVARS'] = str(True)
|
||||
env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_dir
|
||||
env['ANSIBLE_LOAD_CALLBACK_PLUGINS'] = '1'
|
||||
env['ANSIBLE_STDOUT_CALLBACK'] = 'minimal' # Hardcoded by Ansible for ad-hoc commands (either minimal or oneline).
|
||||
env['REST_API_URL'] = settings.INTERNAL_API_URL
|
||||
env['REST_API_TOKEN'] = ad_hoc_command.task_auth_token or ''
|
||||
env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE
|
||||
@ -1694,6 +1722,25 @@ class RunAdHocCommand(BaseTask):
|
||||
d[re.compile(r'^Password:\s*?$', re.M)] = 'ssh_password'
|
||||
return d
|
||||
|
||||
def get_stdout_handle(self, instance):
|
||||
'''
|
||||
Wrap stdout file object to capture events.
|
||||
'''
|
||||
stdout_handle = super(RunAdHocCommand, self).get_stdout_handle(instance)
|
||||
|
||||
if getattr(settings, 'USE_CALLBACK_QUEUE', False):
|
||||
dispatcher = CallbackQueueDispatcher()
|
||||
|
||||
def ad_hoc_command_event_callback(event_data):
|
||||
event_data.setdefault('ad_hoc_command_id', instance.id)
|
||||
dispatcher.dispatch(event_data)
|
||||
else:
|
||||
def ad_hoc_command_event_callback(event_data):
|
||||
event_data.setdefault('ad_hoc_command_id', instance.id)
|
||||
AdHocCommandEvent.create_from_data(**event_data)
|
||||
|
||||
return OutputEventFilter(stdout_handle, ad_hoc_command_event_callback)
|
||||
|
||||
def get_ssh_key_path(self, instance, **kwargs):
|
||||
'''
|
||||
If using an SSH key, return the path for use by ssh-agent.
|
||||
|
||||
@ -90,3 +90,35 @@ class TestWorkflowJobTemplate:
|
||||
assert len(parent_qs) == 1
|
||||
assert parent_qs[0] == wfjt.workflow_job_template_nodes.all()[1]
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestWorkflowJobFailure:
|
||||
"""
|
||||
Tests to re-implement if workflow failure status is introduced in
|
||||
a future Tower version.
|
||||
"""
|
||||
@pytest.fixture
|
||||
def wfj(self):
|
||||
return WorkflowJob.objects.create(name='test-wf-job')
|
||||
|
||||
def test_workflow_not_failed_unran_job(self, wfj):
|
||||
"""
|
||||
Test that an un-ran node will not mark workflow job as failed
|
||||
"""
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
def test_workflow_not_failed_successful_job(self, wfj):
|
||||
"""
|
||||
Test that a sucessful node will not mark workflow job as failed
|
||||
"""
|
||||
job = Job.objects.create(name='test-job', status='successful')
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj, job=job)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
def test_workflow_not_failed_failed_job_but_okay(self, wfj):
|
||||
"""
|
||||
Test that a failed node will not mark workflow job as failed
|
||||
"""
|
||||
job = Job.objects.create(name='test-job', status='failed')
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj, job=job)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
@ -319,19 +319,19 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
|
||||
self.assertIn('ssh-agent', ad_hoc_command.job_args)
|
||||
self.assertNotIn('Bad passphrase', ad_hoc_command.result_stdout)
|
||||
|
||||
def test_run_with_proot(self):
|
||||
# Only run test if proot is installed
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '--version']
|
||||
def test_run_with_bubblewrap(self):
|
||||
# Only run test if bubblewrap is installed
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version']
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
proc.communicate()
|
||||
has_proot = bool(proc.returncode == 0)
|
||||
has_bubblewrap = bool(proc.returncode == 0)
|
||||
except (OSError, ValueError):
|
||||
has_proot = False
|
||||
if not has_proot:
|
||||
self.skipTest('proot is not installed')
|
||||
# Enable proot for this test.
|
||||
has_bubblewrap = False
|
||||
if not has_bubblewrap:
|
||||
self.skipTest('bubblewrap is not installed')
|
||||
# Enable bubblewrap for this test.
|
||||
settings.AWX_PROOT_ENABLED = True
|
||||
# Hide local settings path.
|
||||
settings.AWX_PROOT_HIDE_PATHS = [os.path.join(settings.BASE_DIR, 'settings')]
|
||||
@ -362,8 +362,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
|
||||
self.check_ad_hoc_command_events(ad_hoc_command, 'ok')
|
||||
|
||||
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('failed', 0))
|
||||
def test_run_with_proot_not_installed(self, ignore):
|
||||
# Enable proot for this test, specify invalid proot cmd.
|
||||
def test_run_with_bubblewrap_not_installed(self, ignore):
|
||||
# Enable bubblewrap for this test, specify invalid bubblewrap cmd.
|
||||
settings.AWX_PROOT_ENABLED = True
|
||||
settings.AWX_PROOT_CMD = 'PR00T'
|
||||
ad_hoc_command = self.create_test_ad_hoc_command()
|
||||
|
||||
@ -150,7 +150,7 @@ TEST_ASYNC_NOWAIT_PLAYBOOK = '''
|
||||
'''
|
||||
|
||||
TEST_PROOT_PLAYBOOK = '''
|
||||
- name: test proot environment
|
||||
- name: test bubblewrap environment
|
||||
hosts: test-group
|
||||
gather_facts: false
|
||||
connection: local
|
||||
@ -1177,19 +1177,19 @@ class RunJobTest(BaseJobExecutionTest):
|
||||
|
||||
@unittest.skipUnless(settings.BROKER_URL == 'redis://localhost/',
|
||||
'Non-default Redis setup.')
|
||||
def test_run_job_with_proot(self):
|
||||
# Only run test if proot is installed
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '--version']
|
||||
def test_run_job_with_bubblewrap(self):
|
||||
# Only run test if bubblewrap is installed
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version']
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
proc.communicate()
|
||||
has_proot = bool(proc.returncode == 0)
|
||||
has_bubblewrap = bool(proc.returncode == 0)
|
||||
except (OSError, ValueError):
|
||||
has_proot = False
|
||||
if not has_proot:
|
||||
self.skipTest('proot is not installed')
|
||||
# Enable proot for this test.
|
||||
has_bubblewrap = False
|
||||
if not has_bubblewrap:
|
||||
self.skipTest('bubblewrap is not installed')
|
||||
# Enable bubblewrap for this test.
|
||||
settings.AWX_PROOT_ENABLED = True
|
||||
# Hide local settings path.
|
||||
settings.AWX_PROOT_HIDE_PATHS = [os.path.join(settings.BASE_DIR, 'settings')]
|
||||
@ -1227,8 +1227,8 @@ class RunJobTest(BaseJobExecutionTest):
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
self.check_job_result(job, 'successful')
|
||||
|
||||
def test_run_job_with_proot_not_installed(self):
|
||||
# Enable proot for this test, specify invalid proot cmd.
|
||||
def test_run_job_with_bubblewrap_not_installed(self):
|
||||
# Enable bubblewrap for this test, specify invalid bubblewrap cmd.
|
||||
settings.AWX_PROOT_ENABLED = True
|
||||
settings.AWX_PROOT_CMD = 'PR00T'
|
||||
self.create_test_credential()
|
||||
|
||||
@ -215,7 +215,7 @@ class TestWorkflowWarnings:
|
||||
|
||||
def test_warn_scan_errors_node_prompts(self, job_node_with_prompts):
|
||||
job_node_with_prompts.unified_job_template.job_type = 'scan'
|
||||
job_node_with_prompts.job_type = 'run'
|
||||
job_node_with_prompts.char_prompts['job_type'] = 'run'
|
||||
job_node_with_prompts.inventory = Inventory(name='different-inventory', pk=23)
|
||||
assert 'ignored' in job_node_with_prompts.get_prompts_warnings()
|
||||
assert 'job_type' in job_node_with_prompts.get_prompts_warnings()['ignored']
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
# Python
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@ -39,7 +40,7 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
|
||||
'get_type_for_model', 'get_model_for_type', 'cache_list_capabilities', 'to_python_boolean',
|
||||
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
|
||||
'_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided',
|
||||
'get_current_apps', 'set_current_apps']
|
||||
'get_current_apps', 'set_current_apps', 'OutputEventFilter']
|
||||
|
||||
|
||||
def get_object_or_400(klass, *args, **kwargs):
|
||||
@ -528,7 +529,7 @@ def check_proot_installed():
|
||||
Check that proot is installed.
|
||||
'''
|
||||
from django.conf import settings
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '--version']
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version']
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
@ -556,8 +557,7 @@ def wrap_args_with_proot(args, cwd, **kwargs):
|
||||
- /tmp (except for own tmp files)
|
||||
'''
|
||||
from django.conf import settings
|
||||
new_args = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '-v',
|
||||
str(getattr(settings, 'AWX_PROOT_VERBOSITY', '0')), '-r', '/']
|
||||
new_args = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--dev-bind', '/', '/']
|
||||
hide_paths = ['/etc/tower', '/var/lib/awx', '/var/log',
|
||||
tempfile.gettempdir(), settings.PROJECTS_ROOT,
|
||||
settings.JOBOUTPUT_ROOT]
|
||||
@ -572,7 +572,7 @@ def wrap_args_with_proot(args, cwd, **kwargs):
|
||||
handle, new_path = tempfile.mkstemp(dir=kwargs['proot_temp_dir'])
|
||||
os.close(handle)
|
||||
os.chmod(new_path, stat.S_IRUSR | stat.S_IWUSR)
|
||||
new_args.extend(['-b', '%s:%s' % (new_path, path)])
|
||||
new_args.extend(['--bind', '%s' %(new_path,), '%s' % (path,)])
|
||||
if 'private_data_dir' in kwargs:
|
||||
show_paths = [cwd, kwargs['private_data_dir']]
|
||||
else:
|
||||
@ -585,8 +585,8 @@ def wrap_args_with_proot(args, cwd, **kwargs):
|
||||
for path in sorted(set(show_paths)):
|
||||
if not os.path.exists(path):
|
||||
continue
|
||||
new_args.extend(['-b', '%s:%s' % (path, path)])
|
||||
new_args.extend(['-w', cwd])
|
||||
new_args.extend(['--bind', '%s' % (path,), '%s' % (path,)])
|
||||
new_args.extend(['--chdir', cwd])
|
||||
new_args.extend(args)
|
||||
return new_args
|
||||
|
||||
@ -644,3 +644,71 @@ def set_current_apps(apps):
|
||||
def get_current_apps():
|
||||
global current_apps
|
||||
return current_apps
|
||||
|
||||
|
||||
class OutputEventFilter(object):
|
||||
'''
|
||||
File-like object that looks for encoded job events in stdout data.
|
||||
'''
|
||||
|
||||
EVENT_DATA_RE = re.compile(r'\x1b\[K((?:[A-Za-z0-9+/=]+\x1b\[\d+D)+)\x1b\[K')
|
||||
|
||||
def __init__(self, fileobj=None, event_callback=None):
|
||||
self._fileobj = fileobj
|
||||
self._event_callback = event_callback
|
||||
self._counter = 1
|
||||
self._start_line = 0
|
||||
self._buffer = ''
|
||||
self._current_event_data = None
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._fileobj, attr)
|
||||
|
||||
def write(self, data):
|
||||
if self._fileobj:
|
||||
self._fileobj.write(data)
|
||||
self._buffer += data
|
||||
while True:
|
||||
match = self.EVENT_DATA_RE.search(self._buffer)
|
||||
if not match:
|
||||
break
|
||||
try:
|
||||
base64_data = re.sub(r'\x1b\[\d+D', '', match.group(1))
|
||||
event_data = json.loads(base64.b64decode(base64_data))
|
||||
except ValueError:
|
||||
event_data = {}
|
||||
self._emit_event(self._buffer[:match.start()], event_data)
|
||||
self._buffer = self._buffer[match.end():]
|
||||
|
||||
def close(self):
|
||||
if self._fileobj:
|
||||
self._fileobj.close()
|
||||
if self._buffer:
|
||||
self._emit_event(self._buffer)
|
||||
self._buffer = ''
|
||||
|
||||
def _emit_event(self, buffered_stdout, next_event_data=None):
|
||||
if self._current_event_data:
|
||||
event_data = self._current_event_data
|
||||
stdout_chunks = [buffered_stdout]
|
||||
elif buffered_stdout:
|
||||
event_data = dict(event='verbose')
|
||||
stdout_chunks = buffered_stdout.splitlines(True)
|
||||
else:
|
||||
stdout_chunks = []
|
||||
|
||||
for stdout_chunk in stdout_chunks:
|
||||
event_data['counter'] = self._counter
|
||||
self._counter += 1
|
||||
event_data['stdout'] = stdout_chunk
|
||||
n_lines = stdout_chunk.count('\n')
|
||||
event_data['start_line'] = self._start_line
|
||||
event_data['end_line'] = self._start_line + n_lines
|
||||
self._start_line += n_lines
|
||||
if self._event_callback:
|
||||
self._event_callback(event_data)
|
||||
|
||||
if next_event_data.get('uuid', None):
|
||||
self._current_event_data = next_event_data
|
||||
else:
|
||||
self._current_event_data = None
|
||||
|
||||
@ -1,579 +0,0 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# This file is a utility Ansible plugin that is not part of the AWX or Ansible
|
||||
# packages. It does not import any code from either package, nor does its
|
||||
# license apply to Ansible or AWX.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# Neither the name of the <ORGANIZATION> nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import urlparse
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from uuid import uuid4
|
||||
|
||||
# Kombu
|
||||
from kombu import Connection, Exchange, Producer
|
||||
|
||||
# Requests
|
||||
import requests
|
||||
|
||||
import psutil
|
||||
|
||||
CENSOR_FIELD_WHITELIST = [
|
||||
'msg',
|
||||
'failed',
|
||||
'changed',
|
||||
'results',
|
||||
'start',
|
||||
'end',
|
||||
'delta',
|
||||
'cmd',
|
||||
'_ansible_no_log',
|
||||
'rc',
|
||||
'failed_when_result',
|
||||
'skipped',
|
||||
'skip_reason',
|
||||
]
|
||||
|
||||
def censor(obj, no_log=False):
|
||||
if not isinstance(obj, dict):
|
||||
if no_log:
|
||||
return "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
return obj
|
||||
if obj.get('_ansible_no_log', no_log):
|
||||
new_obj = {}
|
||||
for k in CENSOR_FIELD_WHITELIST:
|
||||
if k in obj:
|
||||
new_obj[k] = obj[k]
|
||||
if k == 'cmd' and k in obj:
|
||||
if isinstance(obj['cmd'], list):
|
||||
obj['cmd'] = ' '.join(obj['cmd'])
|
||||
if re.search(r'\s', obj['cmd']):
|
||||
new_obj['cmd'] = re.sub(r'^(([^\s\\]|\\\s)+).*$',
|
||||
r'\1 <censored>',
|
||||
obj['cmd'])
|
||||
new_obj['censored'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
obj = new_obj
|
||||
if 'results' in obj:
|
||||
if isinstance(obj['results'], list):
|
||||
for i in xrange(len(obj['results'])):
|
||||
obj['results'][i] = censor(obj['results'][i], obj.get('_ansible_no_log', no_log))
|
||||
elif obj.get('_ansible_no_log', False):
|
||||
obj['results'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
|
||||
return obj
|
||||
|
||||
|
||||
class TokenAuth(requests.auth.AuthBase):
|
||||
|
||||
def __init__(self, token):
|
||||
self.token = token
|
||||
|
||||
def __call__(self, request):
|
||||
request.headers['Authorization'] = 'Token %s' % self.token
|
||||
return request
|
||||
|
||||
|
||||
# TODO: non v2_ events are deprecated and should be purge/refactored out
|
||||
class BaseCallbackModule(object):
|
||||
'''
|
||||
Callback module for logging ansible-playbook job events via the REST API.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
self.base_url = os.getenv('REST_API_URL', '')
|
||||
self.auth_token = os.getenv('REST_API_TOKEN', '')
|
||||
self.callback_connection = os.getenv('CALLBACK_CONNECTION', None)
|
||||
self.connection_queue = os.getenv('CALLBACK_QUEUE', '')
|
||||
self.connection = None
|
||||
self.exchange = None
|
||||
self._init_logging()
|
||||
self._init_connection()
|
||||
self.counter = 0
|
||||
self.active_playbook = None
|
||||
self.active_play = None
|
||||
self.active_task = None
|
||||
|
||||
def _init_logging(self):
|
||||
try:
|
||||
self.job_callback_debug = int(os.getenv('JOB_CALLBACK_DEBUG', '0'))
|
||||
except ValueError:
|
||||
self.job_callback_debug = 0
|
||||
self.logger = logging.getLogger('awx.plugins.callback.job_event_callback')
|
||||
if self.job_callback_debug >= 2:
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
elif self.job_callback_debug >= 1:
|
||||
self.logger.setLevel(logging.INFO)
|
||||
else:
|
||||
self.logger.setLevel(logging.WARNING)
|
||||
handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter('%(levelname)-8s %(process)-8d %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
self.logger.addHandler(handler)
|
||||
self.logger.propagate = False
|
||||
|
||||
def _init_connection(self):
|
||||
self.connection = None
|
||||
|
||||
def _start_connection(self):
|
||||
self.connection = Connection(self.callback_connection)
|
||||
self.exchange = Exchange(self.connection_queue, type='direct')
|
||||
|
||||
def _post_job_event_queue_msg(self, event, event_data):
|
||||
self.counter += 1
|
||||
msg = {
|
||||
'event': event,
|
||||
'event_data': event_data,
|
||||
'counter': self.counter,
|
||||
'created': datetime.datetime.utcnow().isoformat(),
|
||||
}
|
||||
if event in ('playbook_on_play_start',
|
||||
'playbook_on_stats',
|
||||
'playbook_on_vars_prompt'):
|
||||
msg['parent_uuid'] = str(self.active_playbook)
|
||||
elif event in ('playbook_on_notify',
|
||||
'playbook_on_setup',
|
||||
'playbook_on_task_start',
|
||||
'playbook_on_no_hosts_matched',
|
||||
'playbook_on_no_hosts_remaining',
|
||||
'playbook_on_include',
|
||||
'playbook_on_import_for_host',
|
||||
'playbook_on_not_import_for_host'):
|
||||
msg['parent_uuid'] = str(self.active_play)
|
||||
elif event.startswith('runner_on_') or event.startswith('runner_item_on_'):
|
||||
msg['parent_uuid'] = str(self.active_task)
|
||||
else:
|
||||
msg['parent_uuid'] = ''
|
||||
|
||||
if "uuid" in event_data:
|
||||
msg['uuid'] = str(event_data['uuid'])
|
||||
else:
|
||||
msg['uuid'] = ''
|
||||
|
||||
if getattr(self, 'job_id', None):
|
||||
msg['job_id'] = self.job_id
|
||||
if getattr(self, 'ad_hoc_command_id', None):
|
||||
msg['ad_hoc_command_id'] = self.ad_hoc_command_id
|
||||
|
||||
if getattr(self, 'artifact_data', None):
|
||||
msg['artifact_data'] = self.artifact_data
|
||||
|
||||
active_pid = os.getpid()
|
||||
if self.job_callback_debug:
|
||||
msg.update({
|
||||
'pid': active_pid,
|
||||
})
|
||||
for retry_count in xrange(4):
|
||||
try:
|
||||
if not hasattr(self, 'connection_pid'):
|
||||
self.connection_pid = active_pid
|
||||
if self.connection_pid != active_pid:
|
||||
self._init_connection()
|
||||
if self.connection is None:
|
||||
self._start_connection()
|
||||
|
||||
producer = Producer(self.connection)
|
||||
producer.publish(msg,
|
||||
serializer='json',
|
||||
compression='bzip2',
|
||||
exchange=self.exchange,
|
||||
declare=[self.exchange],
|
||||
routing_key=self.connection_queue)
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.info('Publish Job Event Exception: %r, retry=%d', e,
|
||||
retry_count, exc_info=True)
|
||||
retry_count += 1
|
||||
if retry_count >= 3:
|
||||
break
|
||||
|
||||
def _post_rest_api_event(self, event, event_data):
|
||||
data = json.dumps({
|
||||
'event': event,
|
||||
'event_data': event_data,
|
||||
})
|
||||
parts = urlparse.urlsplit(self.base_url)
|
||||
if parts.username and parts.password:
|
||||
auth = (parts.username, parts.password)
|
||||
elif self.auth_token:
|
||||
auth = TokenAuth(self.auth_token)
|
||||
else:
|
||||
auth = None
|
||||
port = parts.port or (443 if parts.scheme == 'https' else 80)
|
||||
url = urlparse.urlunsplit([parts.scheme,
|
||||
'%s:%d' % (parts.hostname, port),
|
||||
parts.path, parts.query, parts.fragment])
|
||||
url = urlparse.urljoin(url, self.rest_api_path)
|
||||
headers = {'content-type': 'application/json'}
|
||||
response = requests.post(url, data=data, headers=headers, auth=auth)
|
||||
response.raise_for_status()
|
||||
|
||||
def _log_event(self, event, **event_data):
|
||||
if 'res' in event_data:
|
||||
event_data['res'] = censor(deepcopy(event_data['res']))
|
||||
|
||||
if self.callback_connection:
|
||||
self._post_job_event_queue_msg(event, event_data)
|
||||
else:
|
||||
self._post_rest_api_event(event, event_data)
|
||||
|
||||
def on_any(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||
self._log_event('runner_on_failed', host=host, res=res,
|
||||
ignore_errors=ignore_errors)
|
||||
|
||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||
event_is_loop = result._task.loop if hasattr(result._task, 'loop') else None
|
||||
self._log_event('runner_on_failed', host=result._host.name,
|
||||
res=result._result, task=result._task,
|
||||
ignore_errors=ignore_errors, event_loop=event_is_loop)
|
||||
|
||||
def runner_on_ok(self, host, res):
|
||||
self._log_event('runner_on_ok', host=host, res=res)
|
||||
|
||||
def v2_runner_on_ok(self, result):
|
||||
event_is_loop = result._task.loop if hasattr(result._task, 'loop') else None
|
||||
self._log_event('runner_on_ok', host=result._host.name,
|
||||
task=result._task, res=result._result,
|
||||
event_loop=event_is_loop)
|
||||
|
||||
def runner_on_error(self, host, msg):
|
||||
self._log_event('runner_on_error', host=host, msg=msg)
|
||||
|
||||
def v2_runner_on_error(self, result):
|
||||
pass # Currently not implemented in v2
|
||||
|
||||
def runner_on_skipped(self, host, item=None):
|
||||
self._log_event('runner_on_skipped', host=host, item=item)
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
event_is_loop = result._task.loop if hasattr(result._task, 'loop') else None
|
||||
self._log_event('runner_on_skipped', host=result._host.name,
|
||||
task=result._task, event_loop=event_is_loop)
|
||||
|
||||
def runner_on_unreachable(self, host, res):
|
||||
self._log_event('runner_on_unreachable', host=host, res=res)
|
||||
|
||||
def v2_runner_on_unreachable(self, result):
|
||||
self._log_event('runner_on_unreachable', host=result._host.name,
|
||||
task=result._task, res=result._result)
|
||||
|
||||
def runner_on_no_hosts(self):
|
||||
self._log_event('runner_on_no_hosts')
|
||||
|
||||
def v2_runner_on_no_hosts(self, task):
|
||||
self._log_event('runner_on_no_hosts', task=task)
|
||||
|
||||
# V2 does not use the _on_async callbacks (yet).
|
||||
|
||||
def runner_on_async_poll(self, host, res, jid, clock):
|
||||
self._log_event('runner_on_async_poll', host=host, res=res, jid=jid,
|
||||
clock=clock)
|
||||
|
||||
def runner_on_async_ok(self, host, res, jid):
|
||||
self._log_event('runner_on_async_ok', host=host, res=res, jid=jid)
|
||||
|
||||
def runner_on_async_failed(self, host, res, jid):
|
||||
self._log_event('runner_on_async_failed', host=host, res=res, jid=jid)
|
||||
|
||||
def runner_on_file_diff(self, host, diff):
|
||||
self._log_event('runner_on_file_diff', host=host, diff=diff)
|
||||
|
||||
def v2_runner_on_file_diff(self, result, diff):
|
||||
self._log_event('runner_on_file_diff', host=result._host.name,
|
||||
task=result._task, diff=diff)
|
||||
|
||||
def v2_runner_item_on_ok(self, result):
|
||||
self._log_event('runner_item_on_ok', res=result._result, host=result._host.name,
|
||||
task=result._task)
|
||||
|
||||
def v2_runner_item_on_failed(self, result):
|
||||
self._log_event('runner_item_on_failed', res=result._result, host=result._host.name,
|
||||
task=result._task)
|
||||
|
||||
def v2_runner_item_on_skipped(self, result):
|
||||
self._log_event('runner_item_on_skipped', res=result._result, host=result._host.name,
|
||||
task=result._task)
|
||||
|
||||
@staticmethod
|
||||
def terminate_ssh_control_masters():
|
||||
# Determine if control persist is being used and if any open sockets
|
||||
# exist after running the playbook.
|
||||
cp_path = os.environ.get('ANSIBLE_SSH_CONTROL_PATH', '')
|
||||
if not cp_path:
|
||||
return
|
||||
cp_dir = os.path.dirname(cp_path)
|
||||
if not os.path.exists(cp_dir):
|
||||
return
|
||||
cp_pattern = os.path.join(cp_dir, 'ansible-ssh-*')
|
||||
cp_files = glob.glob(cp_pattern)
|
||||
if not cp_files:
|
||||
return
|
||||
|
||||
# Attempt to find any running control master processes.
|
||||
username = pwd.getpwuid(os.getuid())[0]
|
||||
ssh_cm_procs = []
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
pname = proc.name()
|
||||
pcmdline = proc.cmdline()
|
||||
pusername = proc.username()
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
if pusername != username:
|
||||
continue
|
||||
if pname != 'ssh':
|
||||
continue
|
||||
for cp_file in cp_files:
|
||||
if pcmdline and cp_file in pcmdline[0]:
|
||||
ssh_cm_procs.append(proc)
|
||||
break
|
||||
|
||||
# Terminate then kill control master processes. Workaround older
|
||||
# version of psutil that may not have wait_procs implemented.
|
||||
for proc in ssh_cm_procs:
|
||||
proc.terminate()
|
||||
procs_gone, procs_alive = psutil.wait_procs(ssh_cm_procs, timeout=5)
|
||||
for proc in procs_alive:
|
||||
proc.kill()
|
||||
|
||||
|
||||
class JobCallbackModule(BaseCallbackModule):
|
||||
'''
|
||||
Callback module for logging ansible-playbook job events via the REST API.
|
||||
'''
|
||||
|
||||
# These events should never have an associated play.
|
||||
EVENTS_WITHOUT_PLAY = [
|
||||
'playbook_on_start',
|
||||
'playbook_on_stats',
|
||||
]
|
||||
# These events should never have an associated task.
|
||||
EVENTS_WITHOUT_TASK = EVENTS_WITHOUT_PLAY + [
|
||||
'playbook_on_setup',
|
||||
'playbook_on_notify',
|
||||
'playbook_on_import_for_host',
|
||||
'playbook_on_not_import_for_host',
|
||||
'playbook_on_no_hosts_matched',
|
||||
'playbook_on_no_hosts_remaining',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.job_id = int(os.getenv('JOB_ID', '0'))
|
||||
self.rest_api_path = '/api/v1/jobs/%d/job_events/' % self.job_id
|
||||
super(JobCallbackModule, self).__init__()
|
||||
|
||||
def _log_event(self, event, **event_data):
|
||||
play = getattr(self, 'play', None)
|
||||
play_name = getattr(play, 'name', '')
|
||||
if play_name and event not in self.EVENTS_WITHOUT_PLAY:
|
||||
event_data['play'] = play_name
|
||||
task = event_data.pop('task', None) or getattr(self, 'task', None)
|
||||
task_name = None
|
||||
role_name = None
|
||||
if task:
|
||||
if hasattr(task, 'get_name'):
|
||||
# in v2, the get_name() method creates the name
|
||||
task_name = task.get_name()
|
||||
else:
|
||||
# v1 datastructure
|
||||
task_name = getattr(task, 'name', '')
|
||||
if hasattr(task, '_role') and task._role:
|
||||
# v2 datastructure
|
||||
role_name = task._role._role_name
|
||||
else:
|
||||
# v1 datastructure
|
||||
role_name = getattr(task, 'role_name', '')
|
||||
if task_name and event not in self.EVENTS_WITHOUT_TASK:
|
||||
event_data['task'] = task_name
|
||||
if role_name and event not in self.EVENTS_WITHOUT_TASK:
|
||||
event_data['role'] = role_name
|
||||
self.artifact_data = None
|
||||
if 'res' in event_data and 'artifact_data' in event_data['res']:
|
||||
self.artifact_data = event_data['res']['artifact_data']
|
||||
super(JobCallbackModule, self)._log_event(event, **event_data)
|
||||
|
||||
def playbook_on_start(self):
|
||||
self._log_event('playbook_on_start')
|
||||
|
||||
def v2_playbook_on_start(self, playbook):
|
||||
# NOTE: the playbook parameter was added late in Ansible 2.0 development
|
||||
# so we don't currently utilize but could later.
|
||||
# NOTE: Ansible doesn't generate a UUID for playbook_on_start so we'll do it for them
|
||||
self.active_playbook = str(uuid4())
|
||||
self._log_event('playbook_on_start', uuid=self.active_playbook)
|
||||
|
||||
def playbook_on_notify(self, host, handler):
|
||||
self._log_event('playbook_on_notify', host=host, handler=handler)
|
||||
|
||||
def v2_playbook_on_notify(self, result, handler):
|
||||
self._log_event('playbook_on_notify', host=result._host.name,
|
||||
task=result._task, handler=handler)
|
||||
|
||||
def playbook_on_no_hosts_matched(self):
|
||||
self._log_event('playbook_on_no_hosts_matched')
|
||||
|
||||
def v2_playbook_on_no_hosts_matched(self):
|
||||
# since there is no task/play info, this is currently identical
|
||||
# to the v1 callback which does the same thing
|
||||
self.playbook_on_no_hosts_matched()
|
||||
|
||||
def playbook_on_no_hosts_remaining(self):
|
||||
self._log_event('playbook_on_no_hosts_remaining')
|
||||
|
||||
def v2_playbook_on_no_hosts_remaining(self):
|
||||
# since there is no task/play info, this is currently identical
|
||||
# to the v1 callback which does the same thing
|
||||
self.playbook_on_no_hosts_remaining()
|
||||
|
||||
def playbook_on_task_start(self, name, is_conditional):
|
||||
self._log_event('playbook_on_task_start', name=name,
|
||||
is_conditional=is_conditional)
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
self.active_task = task._uuid
|
||||
self._log_event('playbook_on_task_start', task=task, uuid=str(task._uuid),
|
||||
name=task.get_name(), is_conditional=is_conditional)
|
||||
|
||||
def v2_playbook_on_cleanup_task_start(self, task):
|
||||
# re-using playbook_on_task_start event here for this v2-specific
|
||||
# event, though we may consider any changes necessary to distinguish
|
||||
# this from a normal task
|
||||
self.active_task = task._uuid
|
||||
self._log_event('playbook_on_task_start', task=task, uuid=str(task._uuid),
|
||||
name=task.get_name())
|
||||
|
||||
def playbook_on_vars_prompt(self, varname, private=True, prompt=None,
|
||||
encrypt=None, confirm=False, salt_size=None,
|
||||
salt=None, default=None):
|
||||
self._log_event('playbook_on_vars_prompt', varname=varname,
|
||||
private=private, prompt=prompt, encrypt=encrypt,
|
||||
confirm=confirm, salt_size=salt_size, salt=salt,
|
||||
default=default)
|
||||
|
||||
def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None,
|
||||
encrypt=None, confirm=False, salt_size=None,
|
||||
salt=None, default=None):
|
||||
pass # not currently used in v2 (yet)
|
||||
|
||||
def playbook_on_setup(self):
|
||||
self._log_event('playbook_on_setup')
|
||||
|
||||
def v2_playbook_on_setup(self):
|
||||
pass # not currently used in v2 (yet)
|
||||
|
||||
def playbook_on_import_for_host(self, host, imported_file):
|
||||
# don't care about recording this one
|
||||
# self._log_event('playbook_on_import_for_host', host=host,
|
||||
# imported_file=imported_file)
|
||||
pass
|
||||
|
||||
def v2_playbook_on_import_for_host(self, result, imported_file):
|
||||
pass # not currently used in v2 (yet)
|
||||
|
||||
def playbook_on_not_import_for_host(self, host, missing_file):
|
||||
# don't care about recording this one
|
||||
#self._log_event('playbook_on_not_import_for_host', host=host,
|
||||
# missing_file=missing_file)
|
||||
pass
|
||||
|
||||
def v2_playbook_on_not_import_for_host(self, result, missing_file):
|
||||
pass # not currently used in v2 (yet)
|
||||
|
||||
def playbook_on_play_start(self, name):
|
||||
# Only play name is passed via callback, get host pattern from the play.
|
||||
pattern = getattr(getattr(self, 'play', None), 'hosts', name)
|
||||
self._log_event('playbook_on_play_start', name=name, pattern=pattern)
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
setattr(self, 'play', play)
|
||||
# Ansible 2.0.0.2 doesn't default .name to hosts like it did in 1.9.4,
|
||||
# though that default will likely return in a future version of Ansible.
|
||||
if (not hasattr(play, 'name') or not play.name) and hasattr(play, 'hosts'):
|
||||
if isinstance(play.hosts, list):
|
||||
play.name = ','.join(play.hosts)
|
||||
else:
|
||||
play.name = play.hosts
|
||||
self.active_play = play._uuid
|
||||
self._log_event('playbook_on_play_start', name=play.name, uuid=str(play._uuid),
|
||||
pattern=play.hosts)
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
d = {}
|
||||
for attr in ('changed', 'dark', 'failures', 'ok', 'processed', 'skipped'):
|
||||
d[attr] = getattr(stats, attr)
|
||||
self._log_event('playbook_on_stats', **d)
|
||||
self.terminate_ssh_control_masters()
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
self.playbook_on_stats(stats)
|
||||
|
||||
def v2_playbook_on_include(self, included_file):
|
||||
self._log_event('playbook_on_include', included_file=included_file)
|
||||
|
||||
class AdHocCommandCallbackModule(BaseCallbackModule):
|
||||
'''
|
||||
Callback module for logging ansible ad hoc events via ZMQ or the REST API.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
self.ad_hoc_command_id = int(os.getenv('AD_HOC_COMMAND_ID', '0'))
|
||||
self.rest_api_path = '/api/v1/ad_hoc_commands/%d/events/' % self.ad_hoc_command_id
|
||||
self.skipped_hosts = set()
|
||||
super(AdHocCommandCallbackModule, self).__init__()
|
||||
|
||||
def _log_event(self, event, **event_data):
|
||||
# Ignore task for ad hoc commands (with v2).
|
||||
event_data.pop('task', None)
|
||||
super(AdHocCommandCallbackModule, self)._log_event(event, **event_data)
|
||||
|
||||
def runner_on_file_diff(self, host, diff):
|
||||
pass # Ignore file diff for ad hoc commands.
|
||||
|
||||
def runner_on_ok(self, host, res):
|
||||
# When running in check mode using a module that does not support check
|
||||
# mode, Ansible v1.9 will call runner_on_skipped followed by
|
||||
# runner_on_ok for the same host; only capture the skipped event and
|
||||
# ignore the ok event.
|
||||
if host not in self.skipped_hosts:
|
||||
super(AdHocCommandCallbackModule, self).runner_on_ok(host, res)
|
||||
|
||||
def runner_on_skipped(self, host, item=None):
|
||||
super(AdHocCommandCallbackModule, self).runner_on_skipped(host, item)
|
||||
self.skipped_hosts.add(host)
|
||||
|
||||
if os.getenv('JOB_ID', ''):
|
||||
CallbackModule = JobCallbackModule
|
||||
elif os.getenv('AD_HOC_COMMAND_ID', ''):
|
||||
CallbackModule = AdHocCommandCallbackModule
|
||||
30
awx/plugins/callback/minimal.py
Normal file
30
awx/plugins/callback/minimal.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add awx/lib to sys.path.
|
||||
awx_lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'lib'))
|
||||
if awx_lib_path not in sys.path:
|
||||
sys.path.insert(0, awx_lib_path)
|
||||
|
||||
# Tower Display Callback
|
||||
from tower_display_callback import TowerMinimalCallbackModule as CallbackModule # noqa
|
||||
30
awx/plugins/callback/tower_display.py
Normal file
30
awx/plugins/callback/tower_display.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2016 Ansible by Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible Tower, but depends on code imported from Ansible.
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
# Python
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add awx/lib to sys.path.
|
||||
awx_lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'lib'))
|
||||
if awx_lib_path not in sys.path:
|
||||
sys.path.insert(0, awx_lib_path)
|
||||
|
||||
# Tower Display Callback
|
||||
from tower_display_callback import TowerDefaultCallbackModule as CallbackModule # noqa
|
||||
@ -511,25 +511,25 @@ JOB_EVENT_MAX_QUEUE_SIZE = 100
|
||||
# Flag to enable/disable updating hosts M2M when saving job events.
|
||||
CAPTURE_JOB_EVENT_HOSTS = False
|
||||
|
||||
# Enable proot support for running jobs (playbook runs only).
|
||||
# Enable bubblewrap support for running jobs (playbook runs only).
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_ENABLED = False
|
||||
|
||||
# Command/path to proot.
|
||||
AWX_PROOT_CMD = 'proot'
|
||||
# Command/path to bubblewrap.
|
||||
AWX_PROOT_CMD = 'bwrap'
|
||||
|
||||
# Additional paths to hide from jobs using proot.
|
||||
# Additional paths to hide from jobs using bubblewrap.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_HIDE_PATHS = []
|
||||
|
||||
# Additional paths to show for jobs using proot.
|
||||
# Additional paths to show for jobs using bubbelwrap.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_SHOW_PATHS = []
|
||||
|
||||
# Number of jobs to show as part of the job template history
|
||||
AWX_JOB_TEMPLATE_HISTORY = 10
|
||||
|
||||
# The directory in which proot will create new temporary directories for its root
|
||||
# The directory in which bubblewrap will create new temporary directories for its root
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_BASE_PATH = "/tmp"
|
||||
|
||||
|
||||
@ -729,64 +729,7 @@ legend {
|
||||
.navigation {
|
||||
margin: 15px 0 15px 0;
|
||||
}
|
||||
|
||||
.page-number {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-number-small {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.page-label {
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 0;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.pagination>li>a,
|
||||
.pagination>li>span {
|
||||
border: 1px solid @grey-border;
|
||||
padding: 3px 6px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.pagination li {
|
||||
a#next-page {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
a#previous-page {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.pagination > li > a {
|
||||
border: none;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.pagination > .active > a {
|
||||
background-color: @default-bg;
|
||||
color: #428bca;
|
||||
border-color: none;
|
||||
border: 1px solid @default-link;
|
||||
}
|
||||
.alert {
|
||||
padding: 0;
|
||||
border: none;
|
||||
@ -1623,6 +1566,10 @@ a.btn-disabled:hover {
|
||||
|
||||
/* Sort link styles */
|
||||
|
||||
.list-header-noSort:hover.list-header:hover{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.list-header:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -19,13 +19,7 @@
|
||||
}
|
||||
|
||||
.job-list {
|
||||
.pagination li {
|
||||
|
||||
}
|
||||
.pagination li a {
|
||||
font-size: 12px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
i[class*="icon-job-"] {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -116,44 +116,6 @@ table, tbody {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
/* -- Pagination -- */
|
||||
.List-pagination {
|
||||
margin-top: 20px;
|
||||
font-size: 12px;
|
||||
color: @list-pagin-text;
|
||||
text-transform: uppercase;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.List-paginationPagerHolder {
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.List-paginationPager {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.List-paginationPager--pageof {
|
||||
line-height: 22px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.List-paginationPager--item {
|
||||
border-color: @list-pagin-bord;
|
||||
}
|
||||
|
||||
.List-paginationPager--active {
|
||||
border-color: @list-pagin-bord-act!important;
|
||||
background-color: @list-pagin-bg-act!important;
|
||||
}
|
||||
|
||||
.List-paginationItemsOf {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.List-header {
|
||||
display: flex;
|
||||
min-height: 34px;
|
||||
|
||||
@ -11,23 +11,12 @@
|
||||
* Controller for handling permissions adding
|
||||
*/
|
||||
|
||||
export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'ProcessErrors', function (rootScope, scope, GetBasePath, Rest, $q, Wait, ProcessErrors) {
|
||||
var manuallyUpdateChecklists = function(list, id, isSelected) {
|
||||
var elemScope = angular
|
||||
.element("#" +
|
||||
list + "s_table #" + id + ".List-tableRow input")
|
||||
.scope();
|
||||
if (elemScope) {
|
||||
elemScope.isSelected = !!isSelected;
|
||||
}
|
||||
};
|
||||
export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'ProcessErrors', function(rootScope, scope, GetBasePath, Rest, $q, Wait, ProcessErrors) {
|
||||
|
||||
scope.allSelected = [];
|
||||
|
||||
// the object permissions are being added to
|
||||
scope.object = scope[scope.$parent.list
|
||||
.iterator + "_obj"];
|
||||
|
||||
scope.object = scope.resourceData.data;
|
||||
// array for all possible roles for the object
|
||||
scope.roles = Object
|
||||
.keys(scope.object.summary_fields.object_roles)
|
||||
@ -36,7 +25,8 @@ export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'Pr
|
||||
value: scope.object.summary_fields
|
||||
.object_roles[key].id,
|
||||
label: scope.object.summary_fields
|
||||
.object_roles[key].name };
|
||||
.object_roles[key].name
|
||||
};
|
||||
});
|
||||
|
||||
// TODO: get working with api
|
||||
@ -48,7 +38,8 @@ export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'Pr
|
||||
name: scope.object.summary_fields
|
||||
.object_roles[key].name,
|
||||
description: scope.object.summary_fields
|
||||
.object_roles[key].description };
|
||||
.object_roles[key].description
|
||||
};
|
||||
});
|
||||
|
||||
scope.showKeyPane = false;
|
||||
@ -63,90 +54,44 @@ export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'Pr
|
||||
scope.teamsSelected = !scope.usersSelected;
|
||||
};
|
||||
|
||||
// manually handle selection/deselection of user/team checkboxes
|
||||
scope.$on("selectedOrDeselected", function(e, val) {
|
||||
val = val.value;
|
||||
if (val.isSelected) {
|
||||
// deselected, so remove from the allSelected list
|
||||
scope.allSelected = scope.allSelected.filter(function(i) {
|
||||
// return all but the object who has the id and type
|
||||
// of the element to deselect
|
||||
return (!(val.id === i.id && val.type === i.type));
|
||||
});
|
||||
// pop/push into unified collection of selected users & teams
|
||||
scope.$on("selectedOrDeselected", function(e, value) {
|
||||
let item = value.value;
|
||||
|
||||
function buildName(user) {
|
||||
return (user.first_name &&
|
||||
user.last_name) ?
|
||||
user.first_name + " " +
|
||||
user.last_name :
|
||||
user.username;
|
||||
}
|
||||
|
||||
if (item.isSelected) {
|
||||
if (item.type === 'user') {
|
||||
item.name = buildName(item);
|
||||
}
|
||||
scope.allSelected.push(item);
|
||||
} else {
|
||||
// selected, so add to the allSelected list
|
||||
var getName = function(val) {
|
||||
if (val.type === "user") {
|
||||
return (val.first_name &&
|
||||
val.last_name) ?
|
||||
val.first_name + " " +
|
||||
val.last_name :
|
||||
val.username;
|
||||
} else {
|
||||
return val.name;
|
||||
}
|
||||
};
|
||||
scope.allSelected.push({
|
||||
name: getName(val),
|
||||
type: val.type,
|
||||
roles: [],
|
||||
id: val.id
|
||||
});
|
||||
scope.allSelected = _.remove(scope.allSelected, { id: item.id });
|
||||
}
|
||||
});
|
||||
|
||||
// used to handle changes to the itemsSelected scope var on "next page",
|
||||
// "sorting etc."
|
||||
scope.$on("itemsSelected", function(e, inList) {
|
||||
// compile a list of objects that needed to be checked in the lists
|
||||
scope.updateLists = scope.allSelected.filter(function(inMemory) {
|
||||
var notInList = true;
|
||||
inList.forEach(function(val) {
|
||||
// if the object is part of the allSelected list and is
|
||||
// selected,
|
||||
// you don't need to add it updateLists
|
||||
if (inMemory.id === val.id &&
|
||||
inMemory.type === val.type) {
|
||||
notInList = false;
|
||||
}
|
||||
});
|
||||
return notInList;
|
||||
});
|
||||
});
|
||||
|
||||
// handle changes to the updatedLists by manually selected those values in
|
||||
// the UI
|
||||
scope.$watch("updateLists", function(toUpdate) {
|
||||
(toUpdate || []).forEach(function(obj) {
|
||||
manuallyUpdateChecklists(obj.type, obj.id, true);
|
||||
});
|
||||
|
||||
delete scope.updateLists;
|
||||
});
|
||||
|
||||
// remove selected user/team
|
||||
scope.removeObject = function(obj) {
|
||||
manuallyUpdateChecklists(obj.type, obj.id, false);
|
||||
|
||||
scope.allSelected = scope.allSelected.filter(function(i) {
|
||||
return (!(obj.id === i.id && obj.type === i.type));
|
||||
});
|
||||
};
|
||||
|
||||
// update post url list
|
||||
scope.$watch("allSelected", function(val) {
|
||||
scope.posts = _
|
||||
.flatten((val || [])
|
||||
.map(function (owner) {
|
||||
var url = GetBasePath(owner.type + "s") + owner.id +
|
||||
"/roles/";
|
||||
.map(function(owner) {
|
||||
var url = GetBasePath(owner.type + "s") + owner.id +
|
||||
"/roles/";
|
||||
|
||||
return (owner.roles || [])
|
||||
.map(function (role) {
|
||||
return {url: url,
|
||||
id: role.value};
|
||||
});
|
||||
}));
|
||||
return (owner.roles || [])
|
||||
.map(function(role) {
|
||||
return {
|
||||
url: url,
|
||||
id: role.value
|
||||
};
|
||||
});
|
||||
}));
|
||||
}, true);
|
||||
|
||||
// post roles to api
|
||||
@ -156,22 +101,22 @@ export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'Pr
|
||||
var requests = scope.posts
|
||||
.map(function(post) {
|
||||
Rest.setUrl(post.url);
|
||||
return Rest.post({"id": post.id});
|
||||
return Rest.post({ "id": post.id });
|
||||
});
|
||||
|
||||
$q.all(requests)
|
||||
.then(function () {
|
||||
.then(function() {
|
||||
Wait('stop');
|
||||
rootScope.$broadcast("refreshList", "permission");
|
||||
scope.closeModal();
|
||||
}, function (error) {
|
||||
}, function(error) {
|
||||
Wait('stop');
|
||||
rootScope.$broadcast("refreshList", "permission");
|
||||
scope.closeModal();
|
||||
ProcessErrors(null, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to post role(s): POST returned status' +
|
||||
error.status
|
||||
error.status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -6,55 +6,28 @@
|
||||
import addPermissionsController from './addPermissions.controller';
|
||||
|
||||
/* jshint unused: vars */
|
||||
export default
|
||||
[ 'templateUrl',
|
||||
'Wait',
|
||||
function(templateUrl, Wait) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: true,
|
||||
controller: addPermissionsController,
|
||||
templateUrl: templateUrl('access/addPermissions/addPermissions'),
|
||||
link: function(scope, element, attrs, ctrl) {
|
||||
scope.withoutTeamPermissions = attrs.withoutTeamPermissions;
|
||||
scope.toggleFormTabs('users');
|
||||
export default ['templateUrl', '$state',
|
||||
'Wait', 'addPermissionsUsersList', 'addPermissionsTeamsList',
|
||||
function(templateUrl, $state, Wait, usersList, teamsList) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
usersDataset: '=',
|
||||
teamsDataset: '=',
|
||||
resourceData: '=',
|
||||
},
|
||||
controller: addPermissionsController,
|
||||
templateUrl: templateUrl('access/addPermissions/addPermissions'),
|
||||
link: function(scope, element, attrs) {
|
||||
scope.toggleFormTabs('users');
|
||||
$('#add-permissions-modal').modal('show');
|
||||
|
||||
$("body").addClass("is-modalOpen");
|
||||
scope.closeModal = function() {
|
||||
$state.go('^', null, {reload: true});
|
||||
};
|
||||
|
||||
$("body").append(element);
|
||||
|
||||
Wait('start');
|
||||
|
||||
|
||||
scope.$broadcast("linkLists");
|
||||
|
||||
setTimeout(function() {
|
||||
$('#add-permissions-modal').modal("show");
|
||||
}, 200);
|
||||
|
||||
$('.modal[aria-hidden=false]').each(function () {
|
||||
if ($(this).attr('id') !== 'add-permissions-modal') {
|
||||
$(this).modal('hide');
|
||||
}
|
||||
});
|
||||
|
||||
scope.closeModal = function() {
|
||||
$("body").removeClass("is-modalOpen");
|
||||
$('#add-permissions-modal').on('hidden.bs.modal',
|
||||
function () {
|
||||
$('.AddPermissions').remove();
|
||||
});
|
||||
$('#add-permissions-modal').modal('hide');
|
||||
};
|
||||
|
||||
scope.$on('closePermissionsModal', function() {
|
||||
scope.closeModal();
|
||||
});
|
||||
|
||||
Wait('stop');
|
||||
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
@ -45,13 +45,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="AddPermissions-list" ng-show="usersSelected">
|
||||
<add-permissions-list all-selected="allSelected" type="users">
|
||||
</add-permissions-list>
|
||||
<div id="AddPermissions-users" class="AddPermissions-list" ng-show="usersSelected">
|
||||
<add-permissions-list view="Users" all-selected="allSelected" dataset="usersDataset"></add-permissions-list>
|
||||
</div>
|
||||
<div class="AddPermissions-list" ng-show="teamsSelected">
|
||||
<add-permissions-list all-selected="allSelected" type="teams">
|
||||
</add-permissions-list>
|
||||
<div id="AddPermissions-teams" class="AddPermissions-list" ng-show="teamsSelected">
|
||||
<add-permissions-list view="Teams" all-selected="allSelected" dataset="teamsDataset"></add-permissions-list>
|
||||
</div>
|
||||
|
||||
<div class="AddPermissions-separator"
|
||||
|
||||
@ -5,55 +5,43 @@
|
||||
*************************************************/
|
||||
|
||||
/* jshint unused: vars */
|
||||
export default
|
||||
['addPermissionsTeamsList', 'addPermissionsUsersList', 'generateList', 'GetBasePath', 'SelectionInit', 'SearchInit',
|
||||
'PaginateInit', function(addPermissionsTeamsList,
|
||||
addPermissionsUsersList, generateList,
|
||||
GetBasePath, SelectionInit, SearchInit, PaginateInit) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
allSelected: '='
|
||||
},
|
||||
template: "<div class='addPermissionsList-inner'></div>",
|
||||
link: function(scope, element, attrs, ctrl) {
|
||||
scope.$on("linkLists", function(e) {
|
||||
var generator = generateList,
|
||||
list = addPermissionsTeamsList,
|
||||
url = GetBasePath("teams"),
|
||||
set = "teams",
|
||||
id = "addPermissionsTeamsList",
|
||||
mode = "edit";
|
||||
export default ['addPermissionsTeamsList', 'addPermissionsUsersList', '$compile', 'generateList', 'GetBasePath', 'SelectionInit', function(addPermissionsTeamsList,
|
||||
addPermissionsUsersList, $compile, generateList,
|
||||
GetBasePath, SelectionInit) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
allSelected: '=',
|
||||
view: '@',
|
||||
dataset: '='
|
||||
},
|
||||
template: "<div class='addPermissionsList-inner'></div>",
|
||||
link: function(scope, element, attrs, ctrl) {
|
||||
let listMap, list, list_html;
|
||||
|
||||
if (attrs.type === 'users') {
|
||||
list = addPermissionsUsersList;
|
||||
url = GetBasePath("users") + "?is_superuser=false";
|
||||
set = "users";
|
||||
id = "addPermissionsUsersList";
|
||||
mode = "edit";
|
||||
}
|
||||
listMap = {Teams: addPermissionsTeamsList, Users: addPermissionsUsersList};
|
||||
list = listMap[scope.view];
|
||||
list_html = generateList.build({
|
||||
mode: 'edit',
|
||||
list: list
|
||||
});
|
||||
|
||||
scope.id = id;
|
||||
scope.list = listMap[scope.view];
|
||||
scope[`${list.iterator}_dataset`] = scope.dataset.data;
|
||||
scope[`${list.name}`] = scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
scope.$watch("selectedItems", function() {
|
||||
scope.$emit("itemsSelected", scope.selectedItems);
|
||||
});
|
||||
scope.$watch(list.name, function(){
|
||||
_.forEach(scope[`${list.name}`], isSelected);
|
||||
});
|
||||
|
||||
element.find(".addPermissionsList-inner")
|
||||
.attr("id", id);
|
||||
|
||||
generator.inject(list, { id: id,
|
||||
title: false, mode: mode, scope: scope });
|
||||
|
||||
SearchInit({ scope: scope, set: set,
|
||||
list: list, url: url });
|
||||
|
||||
PaginateInit({ scope: scope,
|
||||
list: list, url: url, pageSize: 5 });
|
||||
|
||||
scope.search(list.iterator);
|
||||
});
|
||||
function isSelected(item){
|
||||
if(_.find(scope.allSelected, {id: item.id})){
|
||||
item.isSelected = true;
|
||||
}
|
||||
};
|
||||
return item;
|
||||
}
|
||||
element.append(list_html);
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
];
|
||||
};
|
||||
}];
|
||||
|
||||
@ -7,9 +7,14 @@
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
searchSize: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
|
||||
name: 'users',
|
||||
iterator: 'user',
|
||||
defaultSearchParams: function(term){
|
||||
return {or__username__icontains: term,
|
||||
or__first_name__icontains: term,
|
||||
or__last_name__icontains: term
|
||||
};
|
||||
},
|
||||
title: false,
|
||||
listTitleBadge: false,
|
||||
multiSelect: true,
|
||||
@ -17,7 +22,6 @@
|
||||
index: false,
|
||||
hover: true,
|
||||
emptyListText : 'No Users exist',
|
||||
|
||||
fields: {
|
||||
first_name: {
|
||||
label: 'First Name',
|
||||
|
||||
@ -8,22 +8,31 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Activity Stream
|
||||
* @description This controller controls the activity stream.
|
||||
*/
|
||||
function activityStreamController($scope, $state, subTitle, Stream, GetTargetTitle) {
|
||||
*/
|
||||
function activityStreamController($scope, $state, subTitle, Stream, GetTargetTitle, list, Dataset) {
|
||||
|
||||
// subTitle is passed in via a resolve on the route. If there is no subtitle
|
||||
// generated in the resolve then we go get the targets generic title.
|
||||
init();
|
||||
|
||||
// Get the streams sub-title based on the target. This scope variable is leveraged
|
||||
// when we define the activity stream list. Specifically it is included in the list
|
||||
// title.
|
||||
$scope.streamSubTitle = subTitle ? subTitle : GetTargetTitle($state.params.target);
|
||||
function init() {
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
// Open the stream
|
||||
Stream({
|
||||
scope: $scope
|
||||
});
|
||||
// subTitle is passed in via a resolve on the route. If there is no subtitle
|
||||
// generated in the resolve then we go get the targets generic title.
|
||||
|
||||
// Get the streams sub-title based on the target. This scope variable is leveraged
|
||||
// when we define the activity stream list. Specifically it is included in the list
|
||||
// title.
|
||||
$scope.streamSubTitle = subTitle ? subTitle : GetTargetTitle($state.params.target);
|
||||
|
||||
// Open the stream
|
||||
Stream({
|
||||
scope: $scope
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ['$scope', '$state', 'subTitle', 'Stream', 'GetTargetTitle', activityStreamController];
|
||||
export default ['$scope', '$state', 'subTitle', 'Stream', 'GetTargetTitle', 'StreamList', 'Dataset', activityStreamController];
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
<div class="Panel" id="stream-container">
|
||||
<div id="stream-content"></div>
|
||||
</div>
|
||||
@ -4,49 +4,73 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'activityStream',
|
||||
route: '/activity_stream?target&id',
|
||||
templateUrl: templateUrl('activity-stream/activitystream'),
|
||||
controller: 'activityStreamController',
|
||||
searchPrefix: 'activity',
|
||||
data: {
|
||||
activityStream: true
|
||||
},
|
||||
params: {
|
||||
activity_search: {
|
||||
value: {
|
||||
// default params will not generate search tags
|
||||
order_by: '-timestamp',
|
||||
or__object1: null,
|
||||
or__object2: null
|
||||
}
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "ACTIVITY STREAM"
|
||||
},
|
||||
onExit: function(){
|
||||
onExit: function() {
|
||||
$('#stream-detail-modal').modal('hide');
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', 'ProcessErrors', '$state', '$rootScope',
|
||||
function(FeaturesService, ProcessErrors, $state, $rootScope) {
|
||||
var features = FeaturesService.get();
|
||||
if(features){
|
||||
if(FeaturesService.featureEnabled('activity_streams')) {
|
||||
return features;
|
||||
}
|
||||
else {
|
||||
$state.go('dashboard');
|
||||
}
|
||||
views: {
|
||||
'list@': {
|
||||
controller: 'activityStreamController',
|
||||
templateProvider: function(StreamList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: StreamList,
|
||||
mode: 'edit'
|
||||
});
|
||||
html = generateList.wrapPanel(html);
|
||||
return html;
|
||||
}
|
||||
$rootScope.featuresConfigured.promise.then(function(features){
|
||||
if(features){
|
||||
if(FeaturesService.featureEnabled('activity_streams')) {
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['StreamList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $stateParams, GetBasePath) {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
],
|
||||
features: ['FeaturesService', 'ProcessErrors', '$state', '$rootScope',
|
||||
function(FeaturesService, ProcessErrors, $state, $rootScope) {
|
||||
var features = FeaturesService.get();
|
||||
if (features) {
|
||||
if (FeaturesService.featureEnabled('activity_streams')) {
|
||||
return features;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$state.go('dashboard');
|
||||
}
|
||||
}
|
||||
});
|
||||
}],
|
||||
subTitle:
|
||||
[ '$stateParams',
|
||||
$rootScope.featuresConfigured.promise.then(function(features) {
|
||||
if (features) {
|
||||
if (FeaturesService.featureEnabled('activity_streams')) {
|
||||
return features;
|
||||
} else {
|
||||
$state.go('dashboard');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
subTitle: ['$stateParams',
|
||||
'Rest',
|
||||
'ModelToBasePathKey',
|
||||
'GetBasePath',
|
||||
@ -65,15 +89,14 @@ export default {
|
||||
.then(function(data) {
|
||||
// Return the name or the username depending on which is available.
|
||||
return (data.data.name || data.data.username);
|
||||
}).catch(function (response) {
|
||||
ProcessErrors(null, response.data, response.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get title info. GET returned status: ' +
|
||||
response.status
|
||||
}).catch(function(response) {
|
||||
ProcessErrors(null, response.data, response.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get title info. GET returned status: ' +
|
||||
response.status
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export default ['templateUrl', function(templateUrl) {
|
||||
scope: true,
|
||||
replace: true,
|
||||
templateUrl: templateUrl('activity-stream/streamDropdownNav/stream-dropdown-nav'),
|
||||
controller: ['$scope', '$state', 'CreateSelect2', function($scope, $state, CreateSelect2) {
|
||||
controller: ['$scope', '$state', '$stateParams','CreateSelect2', function($scope, $state, $stateParams, CreateSelect2) {
|
||||
|
||||
$scope.streamTarget = ($state.params && $state.params.target) ? $state.params.target : 'dashboard';
|
||||
|
||||
@ -35,14 +35,17 @@ export default ['templateUrl', function(templateUrl) {
|
||||
});
|
||||
|
||||
$scope.changeStreamTarget = function(){
|
||||
|
||||
if($scope.streamTarget && $scope.streamTarget === 'dashboard') {
|
||||
// Just navigate to the base activity stream
|
||||
$state.go('activityStream', {}, {inherit: false});
|
||||
$state.go('activityStream');
|
||||
}
|
||||
else {
|
||||
let search = _.merge($stateParams.activity_search, {
|
||||
or__object1: $scope.streamTarget,
|
||||
or__object2: $scope.streamTarget
|
||||
});
|
||||
// Attach the taget to the query parameters
|
||||
$state.go('activityStream', {target: $scope.streamTarget}, {inherit: false});
|
||||
$state.go('activityStream', {target: $scope.streamTarget, activity_search: search});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -15,6 +15,12 @@ import 'jquery.resize';
|
||||
import 'codemirror';
|
||||
import 'js-yaml';
|
||||
import 'select2';
|
||||
import uiRouter from 'angular-ui-router';
|
||||
// backwards compatibility for $stateChange* events
|
||||
import 'angular-ui-router/release/stateEvents';
|
||||
// ui-router debugging
|
||||
//import { trace } from 'angular-ui-router';
|
||||
//trace.enable();
|
||||
|
||||
// Configuration dependencies
|
||||
global.$AnsibleConfig = null;
|
||||
@ -27,7 +33,7 @@ if ($basePath) {
|
||||
|
||||
// Modules
|
||||
import './helpers';
|
||||
import './forms';
|
||||
import * as forms from './forms';
|
||||
import './lists';
|
||||
import './widgets';
|
||||
import './filters';
|
||||
@ -40,12 +46,10 @@ import systemTracking from './system-tracking/main';
|
||||
import inventories from './inventories/main';
|
||||
import inventoryScripts from './inventory-scripts/main';
|
||||
import organizations from './organizations/main';
|
||||
import permissions from './permissions/main';
|
||||
import managementJobs from './management-jobs/main';
|
||||
import jobDetail from './job-detail/main';
|
||||
import jobSubmission from './job-submission/main';
|
||||
import notifications from './notifications/main';
|
||||
import access from './access/main';
|
||||
import about from './about/main';
|
||||
import license from './license/main';
|
||||
import setupMenu from './setup-menu/main';
|
||||
@ -54,23 +58,17 @@ import breadCrumb from './bread-crumb/main';
|
||||
import browserData from './browser-data/main';
|
||||
import dashboard from './dashboard/main';
|
||||
import moment from './shared/moment/main';
|
||||
import templateUrl from './shared/template-url/main';
|
||||
import login from './login/main';
|
||||
import activityStream from './activity-stream/main';
|
||||
import standardOut from './standard-out/main';
|
||||
import JobTemplates from './job-templates/main';
|
||||
import search from './search/main';
|
||||
import credentials from './credentials/main';
|
||||
import { ProjectsList, ProjectsAdd, ProjectsEdit } from './controllers/Projects';
|
||||
import OrganizationsList from './organizations/list/organizations-list.controller';
|
||||
import OrganizationsAdd from './organizations/add/organizations-add.controller';
|
||||
import { UsersList, UsersAdd, UsersEdit } from './controllers/Users';
|
||||
import { TeamsList, TeamsAdd, TeamsEdit } from './controllers/Teams';
|
||||
|
||||
import RestServices from './rest/main';
|
||||
import './lookup/main';
|
||||
import './shared/api-loader';
|
||||
import './shared/form-generator';
|
||||
import access from './access/main';
|
||||
import './shared/Modal';
|
||||
import './shared/prompt-dialog';
|
||||
import './shared/directives';
|
||||
@ -80,7 +78,7 @@ import config from './shared/config/main';
|
||||
import './login/authenticationServices/pendo/ng-pendo';
|
||||
import footer from './footer/main';
|
||||
import scheduler from './scheduler/main';
|
||||
import {N_} from './i18n';
|
||||
import { N_ } from './i18n';
|
||||
|
||||
var tower = angular.module('Tower', [
|
||||
// how to add CommonJS / AMD third-party dependencies:
|
||||
@ -88,17 +86,17 @@ var tower = angular.module('Tower', [
|
||||
// 2. add package name to ./grunt-tasks/webpack.vendorFiles
|
||||
require('angular-breadcrumb'),
|
||||
require('angular-codemirror'),
|
||||
require('angular-cookies'),
|
||||
require('angular-drag-and-drop-lists'),
|
||||
require('angular-ui-router'),
|
||||
require('angular-sanitize'),
|
||||
require('angular-scheduler').name,
|
||||
require('angular-tz-extensions'),
|
||||
require('lr-infinite-scroll'),
|
||||
require('ng-toast'),
|
||||
|
||||
uiRouter,
|
||||
'ui.router.state.events',
|
||||
|
||||
about.name,
|
||||
access.name,
|
||||
license.name,
|
||||
RestServices.name,
|
||||
browserData.name,
|
||||
@ -106,14 +104,13 @@ var tower = angular.module('Tower', [
|
||||
inventories.name,
|
||||
inventoryScripts.name,
|
||||
organizations.name,
|
||||
permissions.name,
|
||||
//permissions.name,
|
||||
managementJobs.name,
|
||||
setupMenu.name,
|
||||
mainMenu.name,
|
||||
breadCrumb.name,
|
||||
dashboard.name,
|
||||
moment.name,
|
||||
templateUrl.name,
|
||||
login.name,
|
||||
activityStream.name,
|
||||
footer.name,
|
||||
@ -121,27 +118,19 @@ var tower = angular.module('Tower', [
|
||||
jobSubmission.name,
|
||||
notifications.name,
|
||||
standardOut.name,
|
||||
access.name,
|
||||
JobTemplates.name,
|
||||
portalMode.name,
|
||||
search.name,
|
||||
config.name,
|
||||
credentials.name,
|
||||
//'templates',
|
||||
'Utilities',
|
||||
'OrganizationFormDefinition',
|
||||
'UserFormDefinition',
|
||||
'FormGenerator',
|
||||
'OrganizationListDefinition',
|
||||
'jobTemplates',
|
||||
'UserListDefinition',
|
||||
'UserHelper',
|
||||
'PromptDialog',
|
||||
'ApiLoader',
|
||||
'RelatedSearchHelper',
|
||||
'SearchHelper',
|
||||
'PaginationHelpers',
|
||||
'RefreshHelper',
|
||||
'AWDirectives',
|
||||
'InventoriesListDefinition',
|
||||
'InventoryFormDefinition',
|
||||
@ -161,7 +150,6 @@ var tower = angular.module('Tower', [
|
||||
'TeamHelper',
|
||||
'CredentialsListDefinition',
|
||||
'CredentialFormDefinition',
|
||||
'LookUpHelper',
|
||||
'JobTemplatesListDefinition',
|
||||
'PortalJobTemplatesListDefinition',
|
||||
'JobTemplateFormDefinition',
|
||||
@ -223,10 +211,12 @@ var tower = angular.module('Tower', [
|
||||
timeout: 4000
|
||||
});
|
||||
}])
|
||||
.config(['$stateProvider', '$urlRouterProvider', '$breadcrumbProvider',
|
||||
'$urlMatcherFactoryProvider',
|
||||
function($stateProvider, $urlRouterProvider, $breadcrumbProvider,
|
||||
$urlMatcherFactoryProvider) {
|
||||
.config(['$urlRouterProvider', '$breadcrumbProvider', 'QuerySetProvider',
|
||||
'$urlMatcherFactoryProvider', 'stateDefinitionsProvider', '$stateProvider', '$stateExtenderProvider',
|
||||
function($urlRouterProvider, $breadcrumbProvider, QuerySet,
|
||||
$urlMatcherFactoryProvider, stateDefinitionsProvider, $stateProvider, $stateExtenderProvider) {
|
||||
let $stateExtender = $stateExtenderProvider.$get(),
|
||||
stateDefinitions = stateDefinitionsProvider.$get();
|
||||
$urlMatcherFactoryProvider.strictMode(false);
|
||||
$breadcrumbProvider.setOptions({
|
||||
templateUrl: urlPrefix + 'partials/breadcrumb.html'
|
||||
@ -234,202 +224,133 @@ var tower = angular.module('Tower', [
|
||||
|
||||
// route to the details pane of /job/:id/host-event/:eventId if no other child specified
|
||||
$urlRouterProvider.when('/jobs/*/host-event/*', '/jobs/*/host-event/*/details');
|
||||
$urlRouterProvider.otherwise('/home');
|
||||
|
||||
// $urlRouterProvider.otherwise("/home");
|
||||
$urlRouterProvider.otherwise(function($injector) {
|
||||
var $state = $injector.get("$state");
|
||||
$state.go('dashboard');
|
||||
$urlMatcherFactoryProvider.type('queryset', {
|
||||
// encoding
|
||||
// from {operator__key1__comparator=value, ... }
|
||||
// to "_search=operator:key:compator=value& ... "
|
||||
encode: function(item) {
|
||||
return QuerySet.$get().encodeArr(item);
|
||||
},
|
||||
// decoding
|
||||
// from "_search=operator:key:compator=value& ... "
|
||||
// to "_search=operator:key:compator=value& ... "
|
||||
decode: function(item) {
|
||||
return QuerySet.$get().decodeArr(item);
|
||||
},
|
||||
// directionality - are we encoding or decoding?
|
||||
is: function(item) {
|
||||
// true: encode to uri
|
||||
// false: decode to $stateParam
|
||||
return angular.isObject(item);
|
||||
}
|
||||
});
|
||||
|
||||
/* Mark translatable strings with N_() and
|
||||
* extract them by 'grunt nggettext_extract'
|
||||
* but angular.config() cannot get gettextCatalog.
|
||||
*/
|
||||
$stateProvider.
|
||||
state('teams', {
|
||||
url: '/teams',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: TeamsList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'team'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: N_("TEAMS")
|
||||
}
|
||||
}).
|
||||
|
||||
state('teams.add', {
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: TeamsAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "teams",
|
||||
label: N_("CREATE TEAM")
|
||||
}
|
||||
}).
|
||||
// Handy hook for debugging register/deregister of lazyLoad'd states
|
||||
// $stateProvider.stateRegistry.onStatesChanged((event, states) =>{
|
||||
// console.log(event, states)
|
||||
// })
|
||||
|
||||
state('teams.edit', {
|
||||
url: '/:team_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: TeamsEdit,
|
||||
data: {
|
||||
activityStreamId: 'team_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "teams",
|
||||
label: "{{team_obj.name}}"
|
||||
}
|
||||
}).
|
||||
|
||||
state('teamUsers', {
|
||||
url: '/teams/:team_id/users',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: UsersList
|
||||
}).
|
||||
// lazily generate a tree of substates which will replace this node in ui-router's stateRegistry
|
||||
// see: stateDefinition.factory for usage documentation
|
||||
$stateProvider.state({
|
||||
name: 'projects',
|
||||
url: '/projects',
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'projects', // top-most node in the generated tree (will replace this state definition)
|
||||
modes: ['add', 'edit'],
|
||||
list: 'ProjectList',
|
||||
form: 'ProjectsForm',
|
||||
controllers: {
|
||||
list: ProjectsList, // DI strings or objects
|
||||
add: ProjectsAdd,
|
||||
edit: ProjectsEdit
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'project',
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
state('teamUserEdit', {
|
||||
url: '/teams/:team_id/users/:user_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: UsersEdit
|
||||
}).
|
||||
|
||||
state('teamProjects', {
|
||||
url: '/teams/:team_id/projects',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: ProjectsList
|
||||
}).
|
||||
|
||||
state('teamProjectAdd', {
|
||||
url: '/teams/:team_id/projects/add',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: ProjectsAdd
|
||||
}).
|
||||
|
||||
state('teamProjectEdit', {
|
||||
url: '/teams/:team_id/projects/:project_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: ProjectsEdit
|
||||
}).
|
||||
|
||||
state('teamCredentials', {
|
||||
url: '/teams/:team_id/credentials',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsList
|
||||
}).
|
||||
|
||||
state('teamCredentialAdd', {
|
||||
url: '/teams/:team_id/credentials/add',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsAdd
|
||||
}).
|
||||
|
||||
state('teamCredentialEdit', {
|
||||
url: '/teams/:team_id/credentials/:credential_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsEdit
|
||||
}).
|
||||
|
||||
state('credentials', {
|
||||
$stateProvider.state({
|
||||
name: 'credentials',
|
||||
url: '/credentials',
|
||||
templateUrl: urlPrefix + 'partials/credentials.html',
|
||||
controller: CredentialsList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'credential'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: N_("CREDENTIALS")
|
||||
}
|
||||
}).
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'credentials',
|
||||
modes: ['add', 'edit'],
|
||||
list: 'CredentialList',
|
||||
form: 'CredentialForm',
|
||||
controllers: {
|
||||
list: CredentialsList,
|
||||
add: CredentialsAdd,
|
||||
edit: CredentialsEdit
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'credential'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'CREDENTIALS'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
state('credentials.add', {
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/credentials.html',
|
||||
controller: CredentialsAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "credentials",
|
||||
label: N_("CREATE CREDENTIAL")
|
||||
}
|
||||
}).
|
||||
$stateProvider.state({
|
||||
name: 'teams',
|
||||
url: '/teams',
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'teams',
|
||||
modes: ['add', 'edit'],
|
||||
list: 'TeamList',
|
||||
form: 'TeamForm',
|
||||
controllers: {
|
||||
list: TeamsList,
|
||||
add: TeamsAdd,
|
||||
edit: TeamsEdit
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'team'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'TEAMS'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
state('credentials.edit', {
|
||||
url: '/:credential_id',
|
||||
templateUrl: urlPrefix + 'partials/credentials.html',
|
||||
controller: CredentialsEdit,
|
||||
data: {
|
||||
activityStreamId: 'credential_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "credentials",
|
||||
label: "{{credential_obj.name}}"
|
||||
}
|
||||
}).
|
||||
|
||||
state('users', {
|
||||
$stateProvider.state({
|
||||
name: 'users',
|
||||
url: '/users',
|
||||
templateUrl: urlPrefix + 'partials/users.html',
|
||||
controller: UsersList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'user'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: N_("USERS")
|
||||
}
|
||||
}).
|
||||
|
||||
state('users.add', {
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/users.html',
|
||||
controller: UsersAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "users",
|
||||
label: N_("CREATE USER")
|
||||
}
|
||||
}).
|
||||
|
||||
state('users.edit', {
|
||||
url: '/:user_id',
|
||||
templateUrl: urlPrefix + 'partials/users.html',
|
||||
controller: UsersEdit,
|
||||
data: {
|
||||
activityStreamId: 'user_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "users",
|
||||
label: "{{user_obj.username}}"
|
||||
}
|
||||
}).
|
||||
|
||||
state('userCredentials', {
|
||||
url: '/users/:user_id/credentials',
|
||||
templateUrl: urlPrefix + 'partials/users.html',
|
||||
controller: CredentialsList
|
||||
}).
|
||||
|
||||
state('userCredentialAdd', {
|
||||
url: '/users/:user_id/credentials/add',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsAdd
|
||||
}).
|
||||
|
||||
state('teamUserCredentialEdit', {
|
||||
url: '/teams/:user_id/credentials/:credential_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsEdit
|
||||
}).
|
||||
|
||||
state('sockets', {
|
||||
url: '/sockets',
|
||||
templateUrl: urlPrefix + 'partials/sockets.html',
|
||||
controller: SocketsController,
|
||||
ncyBreadcrumb: {
|
||||
label: N_("SOCKETS")
|
||||
}
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'users',
|
||||
modes: ['add', 'edit'],
|
||||
list: 'UserList',
|
||||
form: 'UserForm',
|
||||
controllers: {
|
||||
list: UsersList,
|
||||
add: UsersAdd,
|
||||
edit: UsersEdit
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'user'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'USERS'
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
])
|
||||
@ -447,17 +368,23 @@ var tower = angular.module('Tower', [
|
||||
}]);
|
||||
}])
|
||||
|
||||
.run(['$stateExtender', '$q', '$compile', '$cookieStore', '$rootScope', '$log',
|
||||
.run(['$stateExtender', '$q', '$compile', '$cookieStore', '$rootScope', '$log', '$stateParams',
|
||||
'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
|
||||
'ClearScope', 'LoadConfig', 'Store', 'pendoService', 'Prompt', 'Rest',
|
||||
'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'ConfigService',
|
||||
'FeaturesService', '$filter', 'SocketService', 'I18NInit',
|
||||
function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log,
|
||||
function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log, $stateParams,
|
||||
CheckLicense, $location, Authorization, LoadBasePaths, Timer,
|
||||
ClearScope, LoadConfig, Store, pendoService, Prompt, Rest, Wait,
|
||||
ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService,
|
||||
$filter, SocketService, I18NInit) {
|
||||
|
||||
$rootScope.$state = $state;
|
||||
$rootScope.$state.matches = function(stateName) {
|
||||
return $state.current.name.search(stateName) > 0;
|
||||
};
|
||||
$rootScope.$stateParams = $stateParams;
|
||||
|
||||
I18NInit();
|
||||
$stateExtender.addState({
|
||||
name: 'dashboard',
|
||||
@ -465,14 +392,14 @@ var tower = angular.module('Tower', [
|
||||
templateUrl: urlPrefix + 'partials/home.html',
|
||||
controller: Home,
|
||||
params: { licenseMissing: null },
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
refreshButton: true
|
||||
refreshButton: true,
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
},
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_("DASHBOARD")
|
||||
@ -491,94 +418,94 @@ var tower = angular.module('Tower', [
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
searchPrefix: 'job',
|
||||
name: 'jobs',
|
||||
url: '/jobs',
|
||||
templateUrl: urlPrefix + 'partials/jobs.html',
|
||||
controller: JobsListController,
|
||||
ncyBreadcrumb: {
|
||||
label: N_("JOBS")
|
||||
},
|
||||
params: {
|
||||
search: {
|
||||
value: {order_by:'-finished'}
|
||||
job_search: {
|
||||
value: { order_by: '-finished' }
|
||||
}
|
||||
},
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"],
|
||||
"schedules": ["changed"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'projects',
|
||||
url: '/projects?{status}',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
controller: ProjectsList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'project'
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"],
|
||||
"schedules": ["changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_("PROJECTS")
|
||||
resolve: {
|
||||
Dataset: ['AllJobsList', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}]
|
||||
},
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"]
|
||||
views: {
|
||||
'list@': {
|
||||
templateUrl: urlPrefix + 'partials/jobs.html',
|
||||
},
|
||||
'list@jobs': {
|
||||
templateProvider: function(AllJobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: AllJobsList,
|
||||
mode: 'edit'
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: JobsListController
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'projects.add',
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
controller: ProjectsAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "projects",
|
||||
label: N_("CREATE PROJECT")
|
||||
},
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
name: 'teamUsers',
|
||||
url: '/teams/:team_id/users',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: UsersList,
|
||||
resolve: {
|
||||
Users: ['UsersList', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'projects.edit',
|
||||
url: '/:id',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
controller: ProjectsEdit,
|
||||
data: {
|
||||
activityStreamId: 'id'
|
||||
},
|
||||
name: 'userCredentials',
|
||||
url: '/users/:user_id/credentials',
|
||||
templateUrl: urlPrefix + 'partials/users.html',
|
||||
controller: CredentialsList
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'userCredentialAdd',
|
||||
url: '/users/:user_id/credentials/add',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsAdd
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'teamUserCredentialEdit',
|
||||
url: '/teams/:user_id/credentials/:credential_id',
|
||||
templateUrl: urlPrefix + 'partials/teams.html',
|
||||
controller: CredentialsEdit
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'sockets',
|
||||
url: '/sockets',
|
||||
templateUrl: urlPrefix + 'partials/sockets.html',
|
||||
controller: SocketsController,
|
||||
ncyBreadcrumb: {
|
||||
parent: 'projects',
|
||||
label: '{{name}}'
|
||||
},
|
||||
socket: {
|
||||
"groups":{
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
label: 'SOCKETS'
|
||||
}
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'projectOrganizations',
|
||||
url: '/projects/:project_id/organizations',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
controller: OrganizationsList
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'projectOrganizationAdd',
|
||||
url: '/projects/:project_id/organizations/add',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
controller: OrganizationsAdd
|
||||
});
|
||||
|
||||
$rootScope.addPermission = function(scope) {
|
||||
$compile("<add-permissions class='AddPermissions'></add-permissions>")(scope);
|
||||
};
|
||||
@ -604,7 +531,7 @@ var tower = angular.module('Tower', [
|
||||
Rest.post({ "disassociate": true, "id": entry.id })
|
||||
.success(function() {
|
||||
Wait('stop');
|
||||
$rootScope.$broadcast("refreshList", "permission");
|
||||
$state.go('.', null, { reload: true });
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, {
|
||||
|
||||
@ -25,18 +25,29 @@ export default
|
||||
if(streamConfig && streamConfig.activityStream) {
|
||||
if(streamConfig.activityStreamTarget) {
|
||||
stateGoParams.target = streamConfig.activityStreamTarget;
|
||||
stateGoParams.activity_search = {
|
||||
or__object1: streamConfig.activityStreamTarget,
|
||||
or__object2: streamConfig.activityStreamTarget,
|
||||
order_by: '-timestamp',
|
||||
page_size: '20',
|
||||
};
|
||||
}
|
||||
else {
|
||||
stateGoParams.activity_search = {
|
||||
order_by: '-timestamp',
|
||||
page_size: '20',
|
||||
};
|
||||
}
|
||||
if(streamConfig.activityStreamId) {
|
||||
stateGoParams.id = $state.params[streamConfig.activityStreamId];
|
||||
}
|
||||
|
||||
}
|
||||
originalRoute = $state.current;
|
||||
$state.go('activityStream', stateGoParams);
|
||||
}
|
||||
// The user is navigating away from the activity stream - take them back from whence they came
|
||||
else {
|
||||
// Pull the previous state out of local storage
|
||||
|
||||
if(originalRoute) {
|
||||
$state.go(originalRoute.name, originalRoute.fromParams);
|
||||
}
|
||||
@ -51,14 +62,13 @@ export default
|
||||
};
|
||||
|
||||
scope.$on("$stateChangeStart", function updateActivityStreamButton(event, toState, toParams, fromState, fromParams) {
|
||||
|
||||
if(fromState && !Empty(fromState.name)) {
|
||||
// Go ahead and attach the from params to the state object so that it can all be stored together
|
||||
fromState.fromParams = fromParams ? fromParams : {};
|
||||
|
||||
// Store the state that we're coming from in local storage to be accessed when navigating away from the
|
||||
// activity stream
|
||||
Store('previous_state', fromState);
|
||||
//Store('previous_state', fromState);
|
||||
}
|
||||
|
||||
streamConfig = (toState && toState.data) ? toState.data : {};
|
||||
|
||||
@ -8,114 +8,63 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Credentials
|
||||
* @description This controller's for the credentials page
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
$stateParams, Rest, Alert, CredentialList, GenerateList, Prompt, SearchInit,
|
||||
PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath,
|
||||
SelectionInit, GetChoices, Wait, $state, $filter, rbacUiControlService) {
|
||||
$stateParams, Rest, Alert, CredentialList, Prompt, ClearScope,
|
||||
ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
rbacUiControlService.canAdd('credentials')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
var list = CredentialList,
|
||||
defaultUrl = GetBasePath('credentials'),
|
||||
view = GenerateList,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
mode = (base === 'credentials') ? 'edit' : 'select',
|
||||
url;
|
||||
defaultUrl = GetBasePath('credentials');
|
||||
|
||||
view.inject(list, { mode: mode, scope: $scope });
|
||||
init();
|
||||
|
||||
$scope.selected = [];
|
||||
$scope.credentialLoading = true;
|
||||
function init() {
|
||||
rbacUiControlService.canAdd('credentials')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
url = GetBasePath(base) + ( (base === 'users') ? $stateParams.user_id + '/credentials/' : $stateParams.team_id + '/credentials/' );
|
||||
if (mode === 'select') {
|
||||
SelectionInit({ scope: $scope, list: list, url: url, returnToCaller: 1 });
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$scope.selected = [];
|
||||
}
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
var i, j;
|
||||
|
||||
// Cleanup after a delete
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
|
||||
list.fields.kind.searchOptions = $scope.credential_kind_options_list;
|
||||
|
||||
// Translate the kind value
|
||||
for (i = 0; i < $scope.credentials.length; i++) {
|
||||
for (j = 0; j < $scope.credential_kind_options_list.length; j++) {
|
||||
if ($scope.credential_kind_options_list[j].value === $scope.credentials[i].kind) {
|
||||
$scope.credentials[i].kind = $scope.credential_kind_options_list[j].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyCredential', function () {
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'credentials',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
$scope.search(list.iterator);
|
||||
});
|
||||
|
||||
// Load the list of options for Kind
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options_list',
|
||||
callback: 'choicesReadyCredential'
|
||||
});
|
||||
|
||||
$scope.addCredential = function () {
|
||||
$state.transitionTo('credentials.add');
|
||||
$scope.addCredential = function() {
|
||||
$state.go('credentials.add');
|
||||
};
|
||||
|
||||
$scope.editCredential = function (id) {
|
||||
$state.transitionTo('credentials.edit', {credential_id: id});
|
||||
$scope.editCredential = function(id) {
|
||||
$state.go('credentials.edit', { credential_id: id });
|
||||
};
|
||||
|
||||
$scope.deleteCredential = function (id, name) {
|
||||
var action = function () {
|
||||
$scope.deleteCredential = function(id, name) {
|
||||
var action = function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
.success(function() {
|
||||
|
||||
if (parseInt($state.params.credential_id) === id) {
|
||||
$state.go("^", null, {reload: true});
|
||||
$state.go("^", null, { reload: true });
|
||||
} else {
|
||||
$scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(list.iterator);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -126,97 +75,74 @@ export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$emit('choicesReadyCredential');
|
||||
}
|
||||
|
||||
CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'CredentialList', 'generateList', 'Prompt',
|
||||
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'GetChoices', 'Wait',
|
||||
'$state', '$filter', 'rbacUiControlService'
|
||||
'$stateParams', 'Rest', 'Alert', 'CredentialList', 'Prompt', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset'
|
||||
];
|
||||
|
||||
|
||||
export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, GenerateList, SearchInit, PaginateInit,
|
||||
LookUpInit, OrganizationList, GetBasePath, GetChoices, Empty, KindChange,
|
||||
ClearScope, GetBasePath, GetChoices, Empty, KindChange,
|
||||
OwnerChange, FormSave, $state, CreateSelect2) {
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var form = CredentialForm,
|
||||
generator = GenerateForm,
|
||||
defaultUrl = GetBasePath('credentials'),
|
||||
url;
|
||||
|
||||
$scope.keyEntered = false;
|
||||
$scope.permissionsTooltip = 'Please save before assigning permissions';
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
generator.reset();
|
||||
init();
|
||||
|
||||
// Load the list of options for Kind
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options'
|
||||
});
|
||||
function init() {
|
||||
// Load the list of options for Kind
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'become_method',
|
||||
variable: 'become_options'
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'become_method',
|
||||
variable: 'become_options'
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: '#credential_become_method',
|
||||
multiple: false
|
||||
});
|
||||
CreateSelect2({
|
||||
element: '#credential_become_method',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: '#credential_kind',
|
||||
multiple: false
|
||||
});
|
||||
CreateSelect2({
|
||||
element: '#credential_kind',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.canShareCredential = false;
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
$rootScope.$watch('current_user', function(){
|
||||
try {
|
||||
if ($rootScope.current_user.is_superuser) {
|
||||
$scope.canShareCredential = true;
|
||||
} else {
|
||||
Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.canShareCredential = (data.count) ? true : false;
|
||||
}).error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status });
|
||||
});
|
||||
}
|
||||
$scope.keyEntered = false;
|
||||
$scope.permissionsTooltip = 'Please save before assigning permissions';
|
||||
|
||||
|
||||
var orgUrl = ($rootScope.current_user.is_superuser) ?
|
||||
GetBasePath("organizations") :
|
||||
$rootScope.current_user.url + "admin_of_organizations?";
|
||||
|
||||
// Create LookUpInit for organizations
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: orgUrl,
|
||||
form: form,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio',
|
||||
autopopulateLookup: false
|
||||
});
|
||||
// determine if the currently logged-in user may share this credential
|
||||
// previous commentary said: "$rootScope.current_user isn't available because a call to the config endpoint hasn't finished resolving yet"
|
||||
// I'm 99% sure this state's will never resolve block will be rejected if setup surrounding config endpoint hasn't completed
|
||||
if ($rootScope.current_user && $rootScope.current_user.is_superuser) {
|
||||
$scope.canShareCredential = true;
|
||||
} else {
|
||||
Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.canShareCredential = (data.count) ? true : false;
|
||||
}).error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status });
|
||||
});
|
||||
}
|
||||
catch(err){
|
||||
// $rootScope.current_user isn't available because a call to the config endpoint hasn't finished resolving yet
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Empty($stateParams.user_id)) {
|
||||
// Get the username based on incoming route
|
||||
@ -226,10 +152,10 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
url = GetBasePath('users') + $stateParams.user_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
$scope.user_username = data.username;
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status });
|
||||
});
|
||||
} else if (!Empty($stateParams.team_id)) {
|
||||
@ -240,10 +166,10 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
url = GetBasePath('teams') + $stateParams.team_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
$scope.team_name = data.name;
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status });
|
||||
});
|
||||
} else {
|
||||
@ -263,32 +189,30 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
});
|
||||
|
||||
// Handle Kind change
|
||||
$scope.kindChange = function () {
|
||||
$scope.kindChange = function() {
|
||||
KindChange({ scope: $scope, form: form, reset: true });
|
||||
};
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
generator.clearApiErrors();
|
||||
generator.checkAutoFill();
|
||||
$scope.formSave = function() {
|
||||
if ($scope[form.name + '_form'].$valid) {
|
||||
FormSave({ scope: $scope, mode: 'add' });
|
||||
}
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('credentials');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('credentials');
|
||||
};
|
||||
|
||||
// Password change
|
||||
$scope.clearPWConfirm = function (fld) {
|
||||
$scope.clearPWConfirm = function(fld) {
|
||||
// If password value changes, make sure password_confirm must be re-entered
|
||||
$scope[fld] = '';
|
||||
$scope[form.name + '_form'][fld].$setValidity('awpassmatch', false);
|
||||
};
|
||||
|
||||
// Respond to 'Ask at runtime?' checkbox
|
||||
$scope.ask = function (fld, associated) {
|
||||
$scope.ask = function(fld, associated) {
|
||||
if ($scope[fld + '_ask']) {
|
||||
$scope[fld] = 'ASK';
|
||||
$("#" + form.name + "_" + fld + "_input").attr("type", "text");
|
||||
@ -313,7 +237,7 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
};
|
||||
|
||||
// Click clear button
|
||||
$scope.clear = function (fld, associated) {
|
||||
$scope.clear = function(fld, associated) {
|
||||
$scope[fld] = '';
|
||||
$scope[associated] = '';
|
||||
$scope[form.name + '_form'][associated].$setValidity('awpassmatch', true);
|
||||
@ -324,54 +248,88 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
|
||||
CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'SearchInit', 'PaginateInit', 'LookUpInit', 'OrganizationList',
|
||||
'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'OwnerChange',
|
||||
'FormSave', '$state', 'CreateSelect2'
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange',
|
||||
'OwnerChange', 'FormSave', '$state', 'CreateSelect2'
|
||||
];
|
||||
|
||||
|
||||
export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, Prompt,
|
||||
GetBasePath, GetChoices, KindChange, OrganizationList, LookUpInit, Empty,
|
||||
OwnerChange, FormSave, Wait, $state, CreateSelect2, Authorization) {
|
||||
if (!$rootScope.current_user) {
|
||||
Authorization.restoreUserInfo();
|
||||
}
|
||||
$stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt,
|
||||
GetBasePath, GetChoices, KindChange, Empty, OwnerChange, FormSave, Wait,
|
||||
$state, CreateSelect2, Authorization) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var defaultUrl = GetBasePath('credentials'),
|
||||
generator = GenerateForm,
|
||||
form = CredentialForm,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
master = {},
|
||||
id = $stateParams.credential_id,
|
||||
relatedSets = {};
|
||||
id = $stateParams.credential_id;
|
||||
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
generator.reset();
|
||||
$scope.id = id;
|
||||
init();
|
||||
|
||||
$scope.$watch('credential_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
function init() {
|
||||
$scope.id = id;
|
||||
$scope.$watch('credential_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.canShareCredential = false;
|
||||
Wait('start');
|
||||
if (!$rootScope.current_user) {
|
||||
Authorization.restoreUserInfo();
|
||||
}
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options',
|
||||
callback: 'choicesReadyCredential'
|
||||
});
|
||||
|
||||
$scope.canShareCredential = false;
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'become_method',
|
||||
variable: 'become_options'
|
||||
});
|
||||
|
||||
if ($rootScope.current_user.is_superuser) {
|
||||
$scope.canShareCredential = true;
|
||||
} else {
|
||||
Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.canShareCredential = (data.count) ? true : false;
|
||||
}).error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status });
|
||||
});
|
||||
if ($rootScope.current_user && $rootScope.current_user.is_superuser) {
|
||||
$scope.canShareCredential = true;
|
||||
} else {
|
||||
Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.canShareCredential = (data.count) ? true : false;
|
||||
}).error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status });
|
||||
});
|
||||
}
|
||||
|
||||
// if the credential is assigned to an organization, allow permission delegation
|
||||
// do NOT use $scope.organization in a view directive to determine if a credential is associated with an org
|
||||
// @todo why not? ^ and what is this type check for a number doing - should this be a type check for undefined?
|
||||
$scope.disablePermissionAssignment = typeof($scope.organization) === 'number' ? false : true;
|
||||
if ($scope.disablePermissionAssignment) {
|
||||
$scope.permissionsTooltip = 'Credentials are only shared within an organization. Assign credentials to an organization to delegate credential permissions. The organization cannot be edited after credentials are assigned.';
|
||||
}
|
||||
setAskCheckboxes();
|
||||
KindChange({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
reset: false
|
||||
});
|
||||
OwnerChange({ scope: $scope });
|
||||
$scope.$watch("ssh_key_data", function(val) {
|
||||
if (val === "" || val === null || val === undefined) {
|
||||
$scope.keyEntered = false;
|
||||
$scope.ssh_key_unlock_ask = false;
|
||||
$scope.ssh_key_unlock = "";
|
||||
} else {
|
||||
$scope.keyEntered = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setAskCheckboxes() {
|
||||
@ -398,64 +356,14 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.removeCredentialLoaded) {
|
||||
$scope.removeCredentialLoaded();
|
||||
}
|
||||
$scope.removeCredentialLoaded = $scope.$on('credentialLoaded', function () {
|
||||
// if the credential is assigned to an organization, allow permission delegation
|
||||
// do NOT use $scope.organization in a view directive to determine if a credential is associated with an org
|
||||
$scope.disablePermissionAssignment = typeof($scope.organization) === 'number' ? false : true;
|
||||
if ($scope.disablePermissionAssignment){
|
||||
$scope.permissionsTooltip = 'Credentials are only shared within an organization. Assign credentials to an organization to delegate credential permissions. The organization cannot be edited after credentials are assigned.';
|
||||
}
|
||||
var set;
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
var orgUrl = ($rootScope.current_user.is_superuser) ?
|
||||
GetBasePath("organizations") :
|
||||
$rootScope.current_user.url + "admin_of_organizations?";
|
||||
|
||||
// create LookUpInit for organizations
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: orgUrl,
|
||||
form: form,
|
||||
current_item: $scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio',
|
||||
autopopulateLookup: false
|
||||
});
|
||||
|
||||
setAskCheckboxes();
|
||||
KindChange({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
reset: false
|
||||
});
|
||||
OwnerChange({ scope: $scope });
|
||||
$scope.$watch("ssh_key_data", function(val) {
|
||||
if (val === "" || val === null || val === undefined) {
|
||||
$scope.keyEntered = false;
|
||||
$scope.ssh_key_unlock_ask = false;
|
||||
$scope.ssh_key_unlock = "";
|
||||
} else {
|
||||
$scope.keyEntered = true;
|
||||
}
|
||||
});
|
||||
Wait('stop');
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyCredential', function () {
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyCredential', function() {
|
||||
// Retrieve detail record and prepopulate the form
|
||||
Rest.setUrl(defaultUrl + ':id/');
|
||||
Rest.get({ params: { id: id } })
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
if (data && data.summary_fields &&
|
||||
data.summary_fields.organization &&
|
||||
data.summary_fields.organization.id) {
|
||||
@ -481,7 +389,6 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
relatedSets = form.relatedSets(data.related);
|
||||
|
||||
if (!Empty($scope.user)) {
|
||||
$scope.owner = 'user';
|
||||
@ -530,118 +437,93 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
});
|
||||
|
||||
switch (data.kind) {
|
||||
case 'aws':
|
||||
$scope.access_key = data.username;
|
||||
$scope.secret_key = data.password;
|
||||
master.access_key = $scope.access_key;
|
||||
master.secret_key = $scope.secret_key;
|
||||
break;
|
||||
case 'ssh':
|
||||
$scope.ssh_password = data.password;
|
||||
master.ssh_password = $scope.ssh_password;
|
||||
break;
|
||||
case 'rax':
|
||||
$scope.api_key = data.password;
|
||||
master.api_key = $scope.api_key;
|
||||
break;
|
||||
case 'gce':
|
||||
$scope.email_address = data.username;
|
||||
$scope.project = data.project;
|
||||
break;
|
||||
case 'azure':
|
||||
$scope.subscription = data.username;
|
||||
break;
|
||||
case 'aws':
|
||||
$scope.access_key = data.username;
|
||||
$scope.secret_key = data.password;
|
||||
master.access_key = $scope.access_key;
|
||||
master.secret_key = $scope.secret_key;
|
||||
break;
|
||||
case 'ssh':
|
||||
$scope.ssh_password = data.password;
|
||||
master.ssh_password = $scope.ssh_password;
|
||||
break;
|
||||
case 'rax':
|
||||
$scope.api_key = data.password;
|
||||
master.api_key = $scope.api_key;
|
||||
break;
|
||||
case 'gce':
|
||||
$scope.email_address = data.username;
|
||||
$scope.project = data.project;
|
||||
break;
|
||||
case 'azure':
|
||||
$scope.subscription = data.username;
|
||||
break;
|
||||
}
|
||||
$scope.credential_obj = data;
|
||||
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
$scope.$emit('credentialLoaded');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve Credential: ' + $stateParams.id + '. GET status: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve Credential: ' + $stateParams.id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options',
|
||||
callback: 'choicesReadyCredential'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'become_method',
|
||||
variable: 'become_options'
|
||||
});
|
||||
|
||||
// Save changes to the parent
|
||||
$scope.formSave = function () {
|
||||
generator.clearApiErrors();
|
||||
generator.checkAutoFill({ scope: $scope });
|
||||
$scope.formSave = function() {
|
||||
if ($scope[form.name + '_form'].$valid) {
|
||||
FormSave({ scope: $scope, mode: 'edit' });
|
||||
}
|
||||
};
|
||||
|
||||
// Handle Owner change
|
||||
$scope.ownerChange = function () {
|
||||
$scope.ownerChange = function() {
|
||||
OwnerChange({ scope: $scope });
|
||||
};
|
||||
|
||||
// Handle Kind change
|
||||
$scope.kindChange = function () {
|
||||
$scope.kindChange = function() {
|
||||
KindChange({ scope: $scope, form: form, reset: true });
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$scope.formCancel = function() {
|
||||
$state.transitionTo('credentials');
|
||||
};
|
||||
|
||||
// Related set: Add button
|
||||
$scope.add = function (set) {
|
||||
$scope.add = function(set) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + base + '/' + $stateParams.id + '/' + set + '/add');
|
||||
};
|
||||
|
||||
// Related set: Edit button
|
||||
$scope.edit = function (set, id) {
|
||||
$scope.edit = function(set, id) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + base + '/' + $stateParams.id + '/' + set + '/' + id);
|
||||
};
|
||||
|
||||
// Related set: Delete button
|
||||
$scope['delete'] = function (set, itm_id, name, title) {
|
||||
$scope['delete'] = function(set, itm_id, name, title) {
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
var action = function () {
|
||||
var action = function() {
|
||||
var url = defaultUrl + id + '/' + set + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post({
|
||||
id: itm_id,
|
||||
disassociate: 1
|
||||
})
|
||||
.success(function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.search(form.related[set].iterator);
|
||||
id: itm_id,
|
||||
disassociate: 1
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.success(function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(form.related[set].iterator);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#prompt-modal').modal('hide');
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
@ -657,14 +539,14 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
};
|
||||
|
||||
// Password change
|
||||
$scope.clearPWConfirm = function (fld) {
|
||||
$scope.clearPWConfirm = function(fld) {
|
||||
// If password value changes, make sure password_confirm must be re-entered
|
||||
$scope[fld] = '';
|
||||
$scope[form.name + '_form'][fld].$setValidity('awpassmatch', false);
|
||||
};
|
||||
|
||||
// Respond to 'Ask at runtime?' checkbox
|
||||
$scope.ask = function (fld, associated) {
|
||||
$scope.ask = function(fld, associated) {
|
||||
if ($scope[fld + '_ask']) {
|
||||
$scope[fld] = 'ASK';
|
||||
$("#" + form.name + "_" + fld + "_input").attr("type", "text");
|
||||
@ -688,7 +570,7 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.clear = function (fld, associated) {
|
||||
$scope.clear = function(fld, associated) {
|
||||
$scope[fld] = '';
|
||||
$scope[associated] = '';
|
||||
$scope[form.name + '_form'][associated].$setValidity('awpassmatch', true);
|
||||
@ -698,9 +580,8 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
|
||||
CredentialsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit',
|
||||
'ReturnToCaller', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices',
|
||||
'KindChange', 'OrganizationList', 'LookUpInit', 'Empty', 'OwnerChange',
|
||||
'$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices',
|
||||
'KindChange', 'Empty', 'OwnerChange',
|
||||
'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization'
|
||||
];
|
||||
|
||||
@ -12,15 +12,17 @@
|
||||
|
||||
|
||||
export function JobEventsList($sce, $filter, $scope, $rootScope, $location, $log, $stateParams, Rest, Alert, JobEventList, GenerateList,
|
||||
Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, LookUpInit, ToggleChildren,
|
||||
FormatDate, EventView, Refresh, Wait) {
|
||||
Prompt, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, LookUpInit, ToggleChildren,
|
||||
FormatDate, EventView, Wait) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var list = JobEventList,
|
||||
defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_events/', //?parent__isnull=1';
|
||||
generator = GenerateList,
|
||||
page;
|
||||
generator = GenerateList;
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// var defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_events/', //?parent__isnull=1';
|
||||
// page;
|
||||
|
||||
list.base = $location.path();
|
||||
$scope.job_id = $stateParams.id;
|
||||
@ -191,30 +193,31 @@ export function JobEventsList($sce, $filter, $scope, $rootScope, $location, $log
|
||||
});
|
||||
});
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'jobevents',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
page = ($stateParams.page) ? parseInt($stateParams.page,10) - 1 : null;
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl,
|
||||
page: page
|
||||
});
|
||||
|
||||
// Called from Inventories tab, host failed events link:
|
||||
if ($stateParams.host) {
|
||||
$scope[list.iterator + 'SearchField'] = 'host';
|
||||
$scope[list.iterator + 'SearchValue'] = $stateParams.host;
|
||||
$scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label;
|
||||
}
|
||||
|
||||
$scope.search(list.iterator, $stateParams.page);
|
||||
// @issue: OLD SEARCH
|
||||
// SearchInit({
|
||||
// scope: $scope,
|
||||
// set: 'jobevents',
|
||||
// list: list,
|
||||
// url: defaultUrl
|
||||
// });
|
||||
//
|
||||
// page = ($stateParams.page) ? parseInt($stateParams.page,10) - 1 : null;
|
||||
//
|
||||
// PaginateInit({
|
||||
// scope: $scope,
|
||||
// list: list,
|
||||
// url: defaultUrl,
|
||||
// page: page
|
||||
// });
|
||||
//
|
||||
// // Called from Inventories tab, host failed events link:
|
||||
// if ($stateParams.host) {
|
||||
// $scope[list.iterator + 'SearchField'] = 'host';
|
||||
// $scope[list.iterator + 'SearchValue'] = $stateParams.host;
|
||||
// $scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label;
|
||||
// }
|
||||
//
|
||||
// $scope.search(list.iterator, $stateParams.page);
|
||||
|
||||
$scope.toggle = function (id) {
|
||||
ToggleChildren({
|
||||
@ -231,21 +234,24 @@ export function JobEventsList($sce, $filter, $scope, $rootScope, $location, $log
|
||||
};
|
||||
|
||||
$scope.refresh = function () {
|
||||
$scope.jobSearchSpin = true;
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.jobSearchSpin = true;
|
||||
$scope.jobLoading = true;
|
||||
Wait('start');
|
||||
Refresh({
|
||||
scope: $scope,
|
||||
set: 'jobevents',
|
||||
iterator: 'jobevent',
|
||||
url: $scope.current_url
|
||||
});
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// Refresh({
|
||||
// scope: $scope,
|
||||
// set: 'jobevents',
|
||||
// iterator: 'jobevent',
|
||||
// url: $scope.current_url
|
||||
// });
|
||||
};
|
||||
}
|
||||
|
||||
JobEventsList.$inject = ['$sce', '$filter', '$scope', '$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'JobEventList',
|
||||
'generateList', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
|
||||
'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Refresh', 'Wait'
|
||||
'generateList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
|
||||
'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Wait'
|
||||
];
|
||||
|
||||
export function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $stateParams, JobEventsForm, GenerateForm,
|
||||
|
||||
@ -12,13 +12,14 @@
|
||||
|
||||
|
||||
export function JobHostSummaryList($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, JobHostList, GenerateList,
|
||||
Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, Refresh,
|
||||
Prompt, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath,
|
||||
JobStatusToolTip) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var list = JobHostList,
|
||||
defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_host_summaries/',
|
||||
// @issue: OLD SEARCH
|
||||
// defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_host_summaries/',
|
||||
view = GenerateList,
|
||||
inventory;
|
||||
|
||||
@ -58,26 +59,27 @@ export function JobHostSummaryList($scope, $rootScope, $location, $log, $statePa
|
||||
$scope.removeJobReady = $scope.$on('JobReady', function() {
|
||||
view.inject(list, { mode: 'edit', scope: $scope });
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'jobhosts',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
// Called from Inventories tab, host failed events link:
|
||||
if ($stateParams.host_name) {
|
||||
$scope[list.iterator + 'SearchField'] = 'host';
|
||||
$scope[list.iterator + 'SearchValue'] = $stateParams.host_name;
|
||||
$scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label;
|
||||
}
|
||||
$scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// SearchInit({
|
||||
// scope: $scope,
|
||||
// set: 'jobhosts',
|
||||
// list: list,
|
||||
// url: defaultUrl
|
||||
// });
|
||||
//
|
||||
// PaginateInit({
|
||||
// scope: $scope,
|
||||
// list: list,
|
||||
// url: defaultUrl
|
||||
// });
|
||||
//
|
||||
// // Called from Inventories tab, host failed events link:
|
||||
// if ($stateParams.host_name) {
|
||||
// $scope[list.iterator + 'SearchField'] = 'host';
|
||||
// $scope[list.iterator + 'SearchValue'] = $stateParams.host_name;
|
||||
// $scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label;
|
||||
// }
|
||||
// $scope.search(list.iterator);
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath('jobs') + $scope.job_id);
|
||||
@ -107,12 +109,13 @@ export function JobHostSummaryList($scope, $rootScope, $location, $log, $statePa
|
||||
};
|
||||
|
||||
$scope.refresh = function () {
|
||||
$scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(list.iterator);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
JobHostSummaryList.$inject = ['$scope', '$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'JobHostList',
|
||||
'generateList', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
|
||||
'GetBasePath', 'Refresh', 'JobStatusToolTip', 'Wait'
|
||||
'generateList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
|
||||
'GetBasePath', 'JobStatusToolTip', 'Wait'
|
||||
];
|
||||
|
||||
@ -8,148 +8,105 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Jobs
|
||||
* @description This controller's for the jobs page
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
export function JobsListController ($rootScope, $log, $scope, $compile, $stateParams,
|
||||
ClearScope, LoadSchedulesScope,
|
||||
LoadJobsScope, AllJobsList, ScheduledJobsList, GetChoices, GetBasePath, Wait, $state) {
|
||||
|
||||
export function JobsListController($state, $rootScope, $log, $scope, $compile, $stateParams,
|
||||
ClearScope, Find, DeleteJob, RelaunchJob, AllJobsList, ScheduledJobsList, GetBasePath, Dataset) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var jobs_scope, scheduled_scope,
|
||||
choicesCount = 0,
|
||||
listCount = 0,
|
||||
api_complete = false,
|
||||
scheduledJobsList = _.cloneDeep(ScheduledJobsList);
|
||||
var list = AllJobsList;
|
||||
|
||||
$scope.jobsSelected = true;
|
||||
init();
|
||||
|
||||
if ($scope.removeListLoaded) {
|
||||
$scope.removeListLoaded();
|
||||
function init() {
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$scope.showJobType = true;
|
||||
|
||||
_.forEach($scope[list.name], buildTooltips);
|
||||
}
|
||||
$scope.removeListLoaded = $scope.$on('listLoaded', function() {
|
||||
listCount++;
|
||||
if (listCount === 2) {
|
||||
api_complete = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// After all choices are ready, load up the lists and populate the page
|
||||
if ($scope.removeBuildJobsList) {
|
||||
$scope.removeBuildJobsList();
|
||||
function buildTooltips(job) {
|
||||
job.status_tip = 'Job ' + job.status + ". Click for details.";
|
||||
}
|
||||
$scope.removeBuildJobsList = $scope.$on('buildJobsList', function() {
|
||||
var opt, search_params={};
|
||||
|
||||
if (AllJobsList.fields.type) {
|
||||
AllJobsList.fields.type.searchOptions = $scope.type_choices;
|
||||
}
|
||||
if ($stateParams.status) {
|
||||
search_params[AllJobsList.iterator + 'SearchField'] = 'status';
|
||||
search_params[AllJobsList.iterator + 'SelectShow'] = true;
|
||||
search_params[AllJobsList.iterator + 'SearchSelectOpts'] = AllJobsList.fields.status.searchOptions;
|
||||
search_params[AllJobsList.iterator + 'SearchFieldLabel'] = AllJobsList.fields.status.label.replace(/<br\>/g,' ');
|
||||
search_params[AllJobsList.iterator + 'SearchType'] = '';
|
||||
for (opt in AllJobsList.fields.status.searchOptions) {
|
||||
if (AllJobsList.fields.status.searchOptions[opt].value === $stateParams.status) {
|
||||
search_params[AllJobsList.iterator + 'SearchSelectValue'] = AllJobsList.fields.status.searchOptions[opt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
jobs_scope = $scope.$new(true);
|
||||
$scope.deleteJob = function(id) {
|
||||
DeleteJob({ scope: $scope, id: id });
|
||||
};
|
||||
|
||||
jobs_scope.viewJob = function (id) {
|
||||
$state.transitionTo('jobDetail', {id: id});
|
||||
$scope.relaunchJob = function(event, id) {
|
||||
var list, job, typeId;
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
} catch (e) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
job = Find({ list: list, key: 'id', val: id });
|
||||
if (job.type === 'inventory_update') {
|
||||
typeId = job.inventory_source;
|
||||
} else if (job.type === 'project_update') {
|
||||
typeId = job.project;
|
||||
} else if (job.type === 'job' || job.type === "system_job" || job.type === 'ad_hoc_command') {
|
||||
typeId = job.id;
|
||||
}
|
||||
RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
|
||||
};
|
||||
|
||||
$scope.refreshJobs = function() {
|
||||
$state.go('.', null, { reload: true });
|
||||
};
|
||||
|
||||
$scope.viewJobDetails = function(job) {
|
||||
|
||||
var goToJobDetails = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
|
||||
jobs_scope.showJobType = true;
|
||||
LoadJobsScope({
|
||||
parent_scope: $scope,
|
||||
scope: jobs_scope,
|
||||
list: AllJobsList,
|
||||
id: 'active-jobs',
|
||||
url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled,new&order_by=-finished',
|
||||
pageSize: 20,
|
||||
searchParams: search_params,
|
||||
spinner: false
|
||||
});
|
||||
|
||||
|
||||
scheduled_scope = $scope.$new(true);
|
||||
scheduledJobsList.basePath = GetBasePath('schedules') + '?next_run__isnull=false';
|
||||
LoadSchedulesScope({
|
||||
parent_scope: $scope,
|
||||
scope: scheduled_scope,
|
||||
list: scheduledJobsList,
|
||||
pageSize: 20,
|
||||
id: 'scheduled-jobs-tab',
|
||||
searchSize: 'col-lg-4 col-md-4 col-sm-4 col-xs-12',
|
||||
url: scheduledJobsList.basePath
|
||||
});
|
||||
|
||||
$scope.refreshJobs = function() {
|
||||
jobs_scope.search('all_job');
|
||||
scheduled_scope.search('schedule');
|
||||
};
|
||||
|
||||
function clearTabs() {
|
||||
$scope.jobsSelected = false;
|
||||
$scope.schedulesSelected = false;
|
||||
}
|
||||
|
||||
$scope.toggleTab = function(tab) {
|
||||
clearTabs();
|
||||
if (tab === "jobs") {
|
||||
$scope.jobsSelected = true;
|
||||
} else if (tab === "scheduled") {
|
||||
$scope.schedulesSelected = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on('ws-jobs', function() {
|
||||
$scope.refreshJobs();
|
||||
});
|
||||
|
||||
$scope.$on('ws-schedules', function() {
|
||||
if (api_complete) {
|
||||
scheduled_scope.search('schedule');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
choicesCount++;
|
||||
if (choicesCount === 2) {
|
||||
$scope.$emit('buildJobsList');
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goToJobDetails('jobDetail');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goToJobDetails('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goToJobDetails('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goToJobDetails('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.refreshJobs = function() {
|
||||
$state.reload();
|
||||
};
|
||||
|
||||
if ($rootScope.removeJobStatusChange) {
|
||||
$rootScope.removeJobStatusChange();
|
||||
}
|
||||
$rootScope.removeJobStatusChange = $rootScope.$on('JobStatusChange-jobs', function() {
|
||||
$scope.refreshJobs();
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('unified_jobs'),
|
||||
field: 'status',
|
||||
variable: 'status_choices',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('unified_jobs'),
|
||||
field: 'type',
|
||||
variable: 'type_choices',
|
||||
callback: 'choicesReady'
|
||||
if ($rootScope.removeScheduleStatusChange) {
|
||||
$rootScope.removeScheduleStatusChange();
|
||||
}
|
||||
$rootScope.removeScheduleStatusChange = $rootScope.$on('ScheduleStatusChange', function() {
|
||||
$state.reload();
|
||||
});
|
||||
}
|
||||
|
||||
JobsListController.$inject = ['$rootScope', '$log', '$scope', '$compile', '$stateParams',
|
||||
'ClearScope', 'LoadSchedulesScope', 'LoadJobsScope',
|
||||
'AllJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', '$state'];
|
||||
JobsListController.$inject = ['$state', '$rootScope', '$log', '$scope', '$compile', '$stateParams',
|
||||
'ClearScope', 'Find', 'DeleteJob', 'RelaunchJob', 'AllJobsList', 'ScheduledJobsList', 'GetBasePath', 'Dataset'
|
||||
];
|
||||
|
||||
@ -8,85 +8,64 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Projects
|
||||
* @description This controller's for the projects page
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
Rest, Alert, ProjectList, GenerateList, Prompt, SearchInit,
|
||||
PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath,
|
||||
SelectionInit, ProjectUpdate, Refresh, Wait, GetChoices, Empty,
|
||||
Find, GetProjectIcon, GetProjectToolTip, $filter, $state, rbacUiControlService,
|
||||
i18n) {
|
||||
ClearScope();
|
||||
|
||||
$scope.canAdd = false;
|
||||
|
||||
rbacUiControlService.canAdd('projects')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
export function ProjectsList($scope, $rootScope, $location, $log, $stateParams,
|
||||
Rest, Alert, ProjectList, Prompt, ReturnToCaller, ClearScope, ProcessErrors,
|
||||
GetBasePath, ProjectUpdate, Wait, GetChoices, Empty, Find, GetProjectIcon,
|
||||
GetProjectToolTip, $filter, $state, rbacUiControlService, Dataset, i18n) {
|
||||
|
||||
var list = ProjectList,
|
||||
defaultUrl = GetBasePath('projects') + ($stateParams.status ? '?status__in=' + $stateParams.status : ''),
|
||||
view = GenerateList,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
mode = (base === 'projects') ? 'edit' : 'select',
|
||||
url = (base === 'teams') ? GetBasePath('teams') + $stateParams.team_id + '/projects/' : defaultUrl,
|
||||
choiceCount = 0;
|
||||
view.inject(list, { mode: mode, scope: $scope });
|
||||
defaultUrl = GetBasePath('projects');
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
$scope.projectLoading = true;
|
||||
init();
|
||||
|
||||
if (mode === 'select') {
|
||||
SelectionInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: url,
|
||||
returnToCaller: 1
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
$scope.canAdd = false;
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
Wait('stop');
|
||||
if ($scope.projects) {
|
||||
$scope.projects.forEach(function(project, i) {
|
||||
$scope.projects[i].statusIcon = GetProjectIcon(project.status);
|
||||
$scope.projects[i].statusTip = GetProjectToolTip(project.status);
|
||||
$scope.projects[i].scm_update_tooltip = i18n._("Start an SCM update");
|
||||
$scope.projects[i].scm_schedule_tooltip = i18n._("Schedule future SCM updates");
|
||||
$scope.projects[i].scm_type_class = "";
|
||||
|
||||
if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') {
|
||||
$scope.projects[i].statusTip = i18n._('Canceled. Click for details');
|
||||
}
|
||||
|
||||
if (project.status === 'running' || project.status === 'updating') {
|
||||
$scope.projects[i].scm_update_tooltip = i18n._("SCM update currently running");
|
||||
$scope.projects[i].scm_type_class = "btn-disabled";
|
||||
}
|
||||
|
||||
$scope.project_scm_type_options.forEach(function(type) {
|
||||
if (type.value === project.scm_type) {
|
||||
$scope.projects[i].scm_type = type.label;
|
||||
if (type.label === 'Manual') {
|
||||
$scope.projects[i].scm_update_tooltip = i18n._('Manual projects do not require an SCM update');
|
||||
$scope.projects[i].scm_schedule_tooltip = i18n._('Manual projects do not require a schedule');
|
||||
$scope.projects[i].scm_type_class = 'btn-disabled';
|
||||
$scope.projects[i].statusTip = i18n._('Not configured for SCM');
|
||||
$scope.projects[i].statusIcon = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
rbacUiControlService.canAdd('projects')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
}
|
||||
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
_.forEach($scope[list.name], buildTooltips);
|
||||
$rootScope.flashMessage = null;
|
||||
}
|
||||
|
||||
$scope.$watch(`${list.name}`, function() {
|
||||
_.forEach($scope[list.name], buildTooltips);
|
||||
});
|
||||
|
||||
function buildTooltips(project) {
|
||||
project.statusIcon = GetProjectIcon(project.status);
|
||||
project.statusTip = GetProjectToolTip(project.status);
|
||||
project.scm_update_tooltip = "Start an SCM update";
|
||||
project.scm_schedule_tooltip = i18n._("Schedule future SCM updates");
|
||||
project.scm_type_class = "";
|
||||
|
||||
if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') {
|
||||
project.statusTip = i18n._('Canceled. Click for details');
|
||||
}
|
||||
|
||||
if (project.status === 'running' || project.status === 'updating') {
|
||||
project.scm_update_tooltip = i18n._("SCM update currently running");
|
||||
project.scm_type_class = "btn-disabled";
|
||||
}
|
||||
if (project.scm_type === 'manual') {
|
||||
project.scm_update_tooltip = i18n._('Manual projects do not require an SCM update');
|
||||
project.scm_schedule_tooltip = i18n._('Manual projects do not require a schedule');
|
||||
project.scm_type_class = 'btn-disabled';
|
||||
project.statusTip = i18n._('Not configured for SCM');
|
||||
project.statusIcon = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$on(`ws-jobs`, function(e, data) {
|
||||
var project;
|
||||
$log.debug(data);
|
||||
@ -98,9 +77,9 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
$log.debug('Received event for project: ' + project.name);
|
||||
$log.debug('Status changed to: ' + data.status);
|
||||
if (data.status === 'successful' || data.status === 'failed') {
|
||||
$scope.search(list.iterator, null, null, null, null, false);
|
||||
}
|
||||
else {
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(list.iterator, null, null, null, null, false);
|
||||
} else {
|
||||
project.scm_update_tooltip = "SCM update currently running";
|
||||
project.scm_type_class = "btn-disabled";
|
||||
}
|
||||
@ -111,95 +90,12 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesHere) {
|
||||
$scope.removeChoicesHere();
|
||||
}
|
||||
$scope.removeChoicesHere = $scope.$on('choicesCompleteProjectList', function () {
|
||||
var opt;
|
||||
|
||||
list.fields.scm_type.searchOptions = $scope.project_scm_type_options;
|
||||
list.fields.status.searchOptions = $scope.project_status_options;
|
||||
|
||||
if ($stateParams.scm_type && $stateParams.status) {
|
||||
// Request coming from home page. User wants all errors for an scm_type
|
||||
defaultUrl += '?status=' + $stateParams.status;
|
||||
}
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'projects',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
if ($stateParams.scm_type) {
|
||||
$scope[list.iterator + 'SearchType'] = '';
|
||||
$scope[list.iterator + 'SearchField'] = 'scm_type';
|
||||
$scope[list.iterator + 'SelectShow'] = true;
|
||||
$scope[list.iterator + 'SearchSelectOpts'] = list.fields.scm_type.searchOptions;
|
||||
$scope[list.iterator + 'SearchFieldLabel'] = list.fields.scm_type.label.replace(/<br\>/g, ' ');
|
||||
for (opt in list.fields.scm_type.searchOptions) {
|
||||
if (list.fields.scm_type.searchOptions[opt].value === $stateParams.scm_type) {
|
||||
$scope[list.iterator + 'SearchSelectValue'] = list.fields.scm_type.searchOptions[opt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ($stateParams.status) {
|
||||
$scope[list.iterator + 'SearchType'] = '';
|
||||
$scope[list.iterator + 'SearchValue'] = $stateParams.status;
|
||||
$scope[list.iterator + 'SearchField'] = 'status';
|
||||
$scope[list.iterator + 'SelectShow'] = true;
|
||||
$scope[list.iterator + 'SearchFieldLabel'] = list.fields.status.label;
|
||||
$scope[list.iterator + 'SearchSelectOpts'] = list.fields.status.searchOptions;
|
||||
for (opt in list.fields.status.searchOptions) {
|
||||
if (list.fields.status.searchOptions[opt].value === $stateParams.status) {
|
||||
$scope[list.iterator + 'SearchSelectValue'] = list.fields.status.searchOptions[opt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$scope.search(list.iterator);
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReadyList) {
|
||||
$scope.removeChoicesReadyList();
|
||||
}
|
||||
$scope.removeChoicesReadyList = $scope.$on('choicesReadyProjectList', function () {
|
||||
choiceCount++;
|
||||
if (choiceCount === 2) {
|
||||
$scope.$emit('choicesCompleteProjectList');
|
||||
}
|
||||
});
|
||||
|
||||
// Load options for status --used in search
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'status',
|
||||
variable: 'project_status_options',
|
||||
callback: 'choicesReadyProjectList'
|
||||
});
|
||||
|
||||
// Load the list of options for Kind
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'scm_type',
|
||||
variable: 'project_scm_type_options',
|
||||
callback: 'choicesReadyProjectList'
|
||||
});
|
||||
|
||||
$scope.addProject = function () {
|
||||
$state.transitionTo('projects.add');
|
||||
$scope.addProject = function() {
|
||||
$state.go('projects.add');
|
||||
};
|
||||
|
||||
$scope.editProject = function (id) {
|
||||
$state.transitionTo('projects.edit', {id: id});
|
||||
$scope.editProject = function(id) {
|
||||
$state.go('projects.edit', { project_id: id });
|
||||
};
|
||||
|
||||
if ($scope.removeGoToJobDetails) {
|
||||
@ -213,7 +109,7 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
// Grab the id from summary_fields
|
||||
var id = (data.summary_fields.current_update) ? data.summary_fields.current_update.id : data.summary_fields.last_update.id;
|
||||
|
||||
$state.go('scmUpdateStdout', {id: id});
|
||||
$state.go('scmUpdateStdout', { id: id });
|
||||
|
||||
} else {
|
||||
Alert(i18n._('No Updates Available'), i18n._('There is no SCM update information available for this project. An update has not yet been ' +
|
||||
@ -221,7 +117,7 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
}
|
||||
});
|
||||
|
||||
$scope.showSCMStatus = function (id) {
|
||||
$scope.showSCMStatus = function(id) {
|
||||
// Refresh the project list
|
||||
var project = Find({ list: $scope.projects, key: 'id', val: id });
|
||||
if (Empty(project.scm_type) || project.scm_type === 'Manual') {
|
||||
@ -241,18 +137,19 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteProject = function (id, name) {
|
||||
var action = function () {
|
||||
$scope.deleteProject = function(id, name) {
|
||||
var action = function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
if (parseInt($state.params.id) === id) {
|
||||
$state.go("^", null, {reload: true});
|
||||
.success(function() {
|
||||
if (parseInt($state.params.project_id) === id) {
|
||||
$state.go("^", null, { reload: true });
|
||||
} else {
|
||||
$scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(list.iterator);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
@ -272,7 +169,7 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
if ($scope.removeCancelUpdate) {
|
||||
$scope.removeCancelUpdate();
|
||||
}
|
||||
$scope.removeCancelUpdate = $scope.$on('Cancel_Update', function (e, url) {
|
||||
$scope.removeCancelUpdate = $scope.$on('Cancel_Update', function(e, url) {
|
||||
// Cancel the project update process
|
||||
Rest.setUrl(url);
|
||||
Rest.post()
|
||||
@ -288,12 +185,12 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
if ($scope.removeCheckCancel) {
|
||||
$scope.removeCheckCancel();
|
||||
}
|
||||
$scope.removeCheckCancel = $scope.$on('Check_Cancel', function (e, data) {
|
||||
$scope.removeCheckCancel = $scope.$on('Check_Cancel', function(e, data) {
|
||||
// Check that we 'can' cancel the update
|
||||
var url = data.related.cancel;
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
if (data.can_cancel) {
|
||||
$scope.$emit('Cancel_Update', url);
|
||||
} else {
|
||||
@ -306,14 +203,14 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
});
|
||||
});
|
||||
|
||||
$scope.cancelUpdate = function (id, name) {
|
||||
$scope.cancelUpdate = function(id, name) {
|
||||
Rest.setUrl(GetBasePath("projects") + id);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
if (data.related.current_update) {
|
||||
Rest.setUrl(data.related.current_update);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
$scope.$emit('Check_Cancel', data);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
@ -331,15 +228,10 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refresh = function () {
|
||||
$scope.search(list.iterator);
|
||||
};
|
||||
|
||||
$scope.SCMUpdate = function (project_id, event) {
|
||||
$scope.SCMUpdate = function(project_id, event) {
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
$scope.projects.every(function(project) {
|
||||
@ -362,62 +254,52 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
|
||||
$scope.editSchedules = function(id) {
|
||||
var project = Find({ list: $scope.projects, key: 'id', val: id });
|
||||
if (!(project.scm_type === "Manual" || Empty(project.scm_type)) && !(project.status === 'updating' || project.status === 'running' || project.status === 'pending')) {
|
||||
$state.go('projectSchedules', {id: id});
|
||||
$state.go('projectSchedules', { id: id });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'ProjectList', 'generateList', 'Prompt',
|
||||
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'ProjectUpdate',
|
||||
'Refresh', 'Wait', 'GetChoices', 'Empty', 'Find',
|
||||
'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService',
|
||||
'i18n'
|
||||
ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$stateParams',
|
||||
'Rest', 'Alert', 'ProjectList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
|
||||
'GetBasePath', 'ProjectUpdate', 'Wait', 'GetChoices', 'Empty', 'Find', 'GetProjectIcon',
|
||||
'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService', 'Dataset', 'i18n'
|
||||
];
|
||||
|
||||
export function ProjectsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, GenerateForm, ProjectsForm, Rest, Alert, ProcessErrors,
|
||||
GetBasePath, GetProjectPath, GetChoices, Wait, $state, CreateSelect2) {
|
||||
|
||||
export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, ProjectsForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit,
|
||||
OrganizationList, CredentialList, GetChoices, DebugForm, Wait, $state,
|
||||
CreateSelect2, i18n) {
|
||||
|
||||
Rest.setUrl(GetBasePath('projects'));
|
||||
Rest.options()
|
||||
.success(function(data) {
|
||||
if (!data.actions.POST) {
|
||||
$state.go("^");
|
||||
Alert('Permission Error', 'You do not have permission to add a project.', 'alert-info');
|
||||
}
|
||||
});
|
||||
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var form = ProjectsForm(),
|
||||
generator = GenerateForm,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
defaultUrl = GetBasePath('projects'),
|
||||
master = {};
|
||||
|
||||
// remove "type" field from search options
|
||||
CredentialList = _.cloneDeep(CredentialList);
|
||||
CredentialList.fields.kind.noSearch = true;
|
||||
init();
|
||||
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
generator.reset();
|
||||
function init() {
|
||||
Rest.setUrl(GetBasePath('projects'));
|
||||
Rest.options()
|
||||
.success(function(data) {
|
||||
if (!data.actions.POST) {
|
||||
$state.go("^");
|
||||
Alert('Permission Error', 'You do not have permission to add a project.', 'alert-info');
|
||||
}
|
||||
});
|
||||
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
}
|
||||
|
||||
GetProjectPath({ scope: $scope, master: master });
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function () {
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
var i;
|
||||
for (i = 0; i < $scope.scm_type_options.length; i++) {
|
||||
if ($scope.scm_type_options[i].value === '') {
|
||||
$scope.scm_type_options[i].value="manual";
|
||||
$scope.scm_type_options[i].value = "manual";
|
||||
//$scope.scm_type = $scope.scm_type_options[i];
|
||||
break;
|
||||
}
|
||||
@ -440,33 +322,14 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
variable: 'scm_type_options',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + '?kind=scm',
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: '#local-path-select',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
var i, fld, url, data={};
|
||||
generator.clearApiErrors();
|
||||
$scope.formSave = function() {
|
||||
var i, fld, url, data = {};
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].type === 'checkbox_group') {
|
||||
@ -480,8 +343,8 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
}
|
||||
}
|
||||
|
||||
if($scope.scm_type.value === "manual"){
|
||||
data.scm_type = "" ;
|
||||
if ($scope.scm_type.value === "manual") {
|
||||
data.scm_type = "";
|
||||
data.local_path = $scope.local_path.value;
|
||||
} else {
|
||||
data.scm_type = $scope.scm_type.value;
|
||||
@ -492,26 +355,18 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
Wait('start');
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
$scope.addedItem = data.id;
|
||||
|
||||
Refresh({
|
||||
scope: $scope,
|
||||
set: 'projects',
|
||||
iterator: 'project',
|
||||
url: $scope.current_url
|
||||
});
|
||||
|
||||
$state.go('projects.edit', {id: data.id}, {reload: true});
|
||||
$state.go('projects.edit', { id: data.id }, { reload: true });
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'),
|
||||
msg: i18n._('Failed to create new project. POST returned status: ') + status });
|
||||
});
|
||||
};
|
||||
|
||||
$scope.scmChange = function () {
|
||||
$scope.scmChange = function() {
|
||||
// When an scm_type is set, path is not required
|
||||
if ($scope.scm_type) {
|
||||
$scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false;
|
||||
@ -520,7 +375,7 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
}
|
||||
|
||||
// Dynamically update popover values
|
||||
if($scope.scm_type.value) {
|
||||
if ($scope.scm_type.value) {
|
||||
switch ($scope.scm_type.value) {
|
||||
case 'git':
|
||||
$scope.urlPopover = i18n._('<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
|
||||
@ -528,7 +383,7 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
'<p><strong>Note:</strong> When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
|
||||
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
|
||||
'SSH. GIT read only protocol (git://) does not use username or password information.');
|
||||
break;
|
||||
break;
|
||||
case 'svn':
|
||||
$scope.urlPopover = i18n._('<p>Example URLs for Subversion SCM include:</p>' +
|
||||
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
|
||||
@ -548,89 +403,47 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('projects');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('projects');
|
||||
};
|
||||
}
|
||||
|
||||
ProjectsAdd.$inject = ['Refresh', '$scope', '$rootScope', '$compile', '$location', '$log',
|
||||
'$stateParams', 'ProjectsForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ReturnToCaller',
|
||||
'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList',
|
||||
'GetChoices', 'DebugForm', 'Wait', '$state', 'CreateSelect2', 'i18n'
|
||||
];
|
||||
ProjectsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
|
||||
'$stateParams', 'GenerateForm', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath',
|
||||
'GetProjectPath', 'GetChoices', 'Wait', '$state', 'CreateSelect2', 'i18n'];
|
||||
|
||||
|
||||
export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, ProjectsForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
RelatedSearchInit, RelatedPaginateInit, Prompt, ClearScope, GetBasePath,
|
||||
ReturnToCaller, GetProjectPath, Authorization, CredentialList, LookUpInit,
|
||||
GetChoices, Empty, DebugForm, Wait, SchedulesControllerInit,
|
||||
SchedulesListInit, SchedulesList, ProjectUpdate, $state, CreateSelect2,
|
||||
OrganizationList, NotificationsListInit, ToggleNotification, i18n) {
|
||||
$stateParams, ProjectsForm, Rest, Alert, ProcessErrors,
|
||||
Prompt, ClearScope, GetBasePath, GetProjectPath, Authorization,
|
||||
GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n) {
|
||||
|
||||
ClearScope('htmlTemplate');
|
||||
|
||||
var form = ProjectsForm(),
|
||||
defaultUrl = GetBasePath('projects') + $stateParams.project_id + '/',
|
||||
master = {},
|
||||
id = $stateParams.project_id;
|
||||
|
||||
init();
|
||||
|
||||
function init() {
|
||||
$scope.project_local_paths = [];
|
||||
$scope.base_dir = '';
|
||||
}
|
||||
|
||||
$scope.$watch('project_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Inject dynamic view
|
||||
var form = ProjectsForm(),
|
||||
generator = GenerateForm,
|
||||
defaultUrl = GetBasePath('projects') + $stateParams.id + '/',
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
master = {}, i,
|
||||
id = $stateParams.id,
|
||||
relatedSets = {};
|
||||
|
||||
// remove "type" field from search options
|
||||
CredentialList = _.cloneDeep(CredentialList);
|
||||
CredentialList.fields.kind.noSearch = true;
|
||||
|
||||
|
||||
SchedulesList.well = false;
|
||||
generator.inject(form, {
|
||||
mode: 'edit',
|
||||
related: true,
|
||||
scope: $scope
|
||||
});
|
||||
generator.reset();
|
||||
|
||||
$scope.project_local_paths = [];
|
||||
$scope.base_dir = '';
|
||||
|
||||
if ($scope.removerelatedschedules) {
|
||||
$scope.removerelatedschedules();
|
||||
}
|
||||
$scope.removerelatedschedules = $scope.$on('relatedschedules', function() {
|
||||
SchedulesListInit({
|
||||
scope: $scope,
|
||||
list: SchedulesList,
|
||||
choices: null,
|
||||
related: true
|
||||
});
|
||||
});
|
||||
|
||||
// After the project is loaded, retrieve each related set
|
||||
if ($scope.projectLoadedRemove) {
|
||||
$scope.projectLoadedRemove();
|
||||
}
|
||||
$scope.projectLoadedRemove = $scope.$on('projectLoaded', function () {
|
||||
var set, opts=[];
|
||||
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
|
||||
SchedulesControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
iterator: 'schedule'
|
||||
});
|
||||
$scope.projectLoadedRemove = $scope.$on('projectLoaded', function() {
|
||||
var opts = [];
|
||||
|
||||
if (Authorization.getUserInfo('is_superuser') === true) {
|
||||
GetProjectPath({ scope: $scope, master: master });
|
||||
@ -644,42 +457,19 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$scope.base_dir = 'You do not have access to view this property';
|
||||
}
|
||||
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?kind=scm',
|
||||
scope: $scope,
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: $scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
$scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false;
|
||||
$scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false;
|
||||
$scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch';
|
||||
Wait('stop');
|
||||
|
||||
NotificationsListInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('projects'),
|
||||
id: $scope.project_obj.id
|
||||
});
|
||||
|
||||
$scope.scmChange();
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function () {
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
let i;
|
||||
for (i = 0; i < $scope.scm_type_options.length; i++) {
|
||||
if ($scope.scm_type_options[i].value === '') {
|
||||
$scope.scm_type_options[i].value = "manual";
|
||||
@ -689,7 +479,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
// Retrieve detail record and prepopulate the form
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.get({ params: { id: id } })
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
var fld, i;
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].type === 'checkbox_group') {
|
||||
@ -705,15 +495,12 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
relatedSets = form.relatedSets(data.related);
|
||||
|
||||
|
||||
|
||||
data.scm_type = (Empty(data.scm_type)) ? 'manual' : data.scm_type;
|
||||
for (i = 0; i < $scope.scm_type_options.length; i++) {
|
||||
@ -743,18 +530,6 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
});
|
||||
|
||||
$scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch';
|
||||
|
||||
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
$scope.scm_update_tooltip = "Start an SCM update";
|
||||
$scope.scm_type_class = "";
|
||||
if (data.status === 'running' || data.status === 'updating') {
|
||||
@ -791,14 +566,12 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
var notifier = this.notification;
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
ToggleNotification({
|
||||
scope: $scope,
|
||||
url: $scope.project_url,
|
||||
id: $scope.project_obj.id,
|
||||
url: $scope.project_obj.url,
|
||||
notifier: notifier,
|
||||
column: column,
|
||||
callback: 'NotificationRefresh'
|
||||
@ -806,9 +579,9 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
};
|
||||
|
||||
// Save changes to the parent
|
||||
$scope.formSave = function () {
|
||||
$scope.formSave = function() {
|
||||
var fld, i, params;
|
||||
generator.clearApiErrors();
|
||||
//generator.clearApiErrors();
|
||||
Wait('start');
|
||||
$rootScope.flashMessage = null;
|
||||
params = {};
|
||||
@ -824,8 +597,8 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
}
|
||||
|
||||
if($scope.scm_type.value === "manual"){
|
||||
params.scm_type = "" ;
|
||||
if ($scope.scm_type.value === "manual") {
|
||||
params.scm_type = "";
|
||||
params.local_path = $scope.local_path.value;
|
||||
} else {
|
||||
params.scm_type = $scope.scm_type.value;
|
||||
@ -836,37 +609,26 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
Rest.put(params)
|
||||
.success(function() {
|
||||
Wait('stop');
|
||||
$state.go($state.current, {}, {reload: true});
|
||||
$state.go($state.current, {}, { reload: true });
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update project: ' + id + '. PUT status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
// Related set: Add button
|
||||
$scope.add = function (set) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + base + '/' + $stateParams.id + '/' + set);
|
||||
};
|
||||
|
||||
// Related set: Edit button
|
||||
$scope.edit = function (set, id) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + set + '/' + id);
|
||||
};
|
||||
|
||||
// Related set: Delete button
|
||||
$scope['delete'] = function (set, itm_id, name, title) {
|
||||
var action = function () {
|
||||
$scope['delete'] = function(set, itm_id, name, title) {
|
||||
var action = function() {
|
||||
var url = GetBasePath('projects') + id + '/' + set + '/';
|
||||
$rootScope.flashMessage = null;
|
||||
Rest.setUrl(url);
|
||||
Rest.post({ id: itm_id, disassociate: 1 })
|
||||
.success(function () {
|
||||
.success(function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.search(form.related[set].iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(form.related[set].iterator);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
$('#prompt-modal').modal('hide');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status });
|
||||
});
|
||||
@ -880,7 +642,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
});
|
||||
};
|
||||
|
||||
$scope.scmChange = function () {
|
||||
$scope.scmChange = function() {
|
||||
if ($scope.scm_type) {
|
||||
$scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false;
|
||||
$scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false;
|
||||
@ -888,7 +650,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
|
||||
// Dynamically update popover values
|
||||
if($scope.scm_type.value) {
|
||||
if ($scope.scm_type.value) {
|
||||
switch ($scope.scm_type.value) {
|
||||
case 'git':
|
||||
$scope.urlPopover = i18n._('<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
|
||||
@ -896,7 +658,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
'<p><strong>Note:</strong> When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
|
||||
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
|
||||
'SSH. GIT read only protocol (git://) does not use username or password information.');
|
||||
break;
|
||||
break;
|
||||
case 'svn':
|
||||
$scope.urlPopover = i18n._('<p>Example URLs for Subversion SCM include:</p>' +
|
||||
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
|
||||
@ -916,7 +678,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.SCMUpdate = function () {
|
||||
$scope.SCMUpdate = function() {
|
||||
if ($scope.project_obj.scm_type === "Manual" || Empty($scope.project_obj.scm_type)) {
|
||||
// ignore
|
||||
} else if ($scope.project_obj.status === 'updating' || $scope.project_obj.status === 'running' || $scope.project_obj.status === 'pending') {
|
||||
@ -926,17 +688,12 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$scope.formCancel = function() {
|
||||
$state.transitionTo('projects');
|
||||
};
|
||||
}
|
||||
|
||||
ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
|
||||
'$stateParams', 'ProjectsForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
|
||||
'ClearScope', 'GetBasePath', 'ReturnToCaller', 'GetProjectPath',
|
||||
'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
|
||||
'DebugForm', 'Wait', 'SchedulesControllerInit', 'SchedulesListInit',
|
||||
'SchedulesList', 'ProjectUpdate', '$state', 'CreateSelect2',
|
||||
'OrganizationList', 'NotificationsListInit', 'ToggleNotification', 'i18n'
|
||||
];
|
||||
'$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'Prompt',
|
||||
'ClearScope', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty',
|
||||
'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n'];
|
||||
|
||||
@ -18,33 +18,33 @@ GetBasePath, Wait, Find, LoadSchedulesScope, GetChoices) {
|
||||
|
||||
var base, id, url, parentObject;
|
||||
|
||||
base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
// base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function() {
|
||||
var list = $scope.schedules;
|
||||
list.forEach(function(element, idx) {
|
||||
list[idx].play_tip = (element.enabled) ? 'Schedule is Active. Click to temporarily stop.' : 'Schedule is temporarily stopped. Click to activate.';
|
||||
});
|
||||
});
|
||||
// if ($scope.removePostRefresh) {
|
||||
// $scope.removePostRefresh();
|
||||
// }
|
||||
// $scope.removePostRefresh = $scope.$on('PostRefresh', function() {
|
||||
// var list = $scope.schedules;
|
||||
// list.forEach(function(element, idx) {
|
||||
// list[idx].play_tip = (element.enabled) ? 'Schedule is Active. Click to temporarily stop.' : 'Schedule is temporarily stopped. Click to activate.';
|
||||
// });
|
||||
// });
|
||||
|
||||
if ($scope.removeParentLoaded) {
|
||||
$scope.removeParentLoaded();
|
||||
}
|
||||
$scope.removeParentLoaded = $scope.$on('ParentLoaded', function() {
|
||||
url += "schedules/";
|
||||
SchedulesList.well = true;
|
||||
LoadSchedulesScope({
|
||||
parent_scope: $scope,
|
||||
scope: $scope,
|
||||
list: SchedulesList,
|
||||
id: 'schedule-list-target',
|
||||
url: url,
|
||||
pageSize: 20
|
||||
});
|
||||
});
|
||||
// if ($scope.removeParentLoaded) {
|
||||
// $scope.removeParentLoaded();
|
||||
// }
|
||||
// $scope.removeParentLoaded = $scope.$on('ParentLoaded', function() {
|
||||
// url += "schedules/";
|
||||
// SchedulesList.well = true;
|
||||
// LoadSchedulesScope({
|
||||
// parent_scope: $scope,
|
||||
// scope: $scope,
|
||||
// list: SchedulesList,
|
||||
// id: 'schedule-list-target',
|
||||
// url: url,
|
||||
// pageSize: 20
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
@ -67,7 +67,8 @@ GetBasePath, Wait, Find, LoadSchedulesScope, GetChoices) {
|
||||
});
|
||||
|
||||
$scope.refreshJobs = function() {
|
||||
$scope.search(SchedulesList.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(SchedulesList.iterator);
|
||||
};
|
||||
|
||||
Wait('start');
|
||||
|
||||
@ -8,108 +8,62 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Teams
|
||||
* @description This controller's for teams
|
||||
*/
|
||||
*/
|
||||
|
||||
export function TeamsList($scope, $rootScope, $log, $stateParams,
|
||||
Rest, Alert, TeamList, Prompt, ClearScope, ProcessErrors,
|
||||
GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset) {
|
||||
|
||||
export function TeamsList($scope, $rootScope, $location, $log, $stateParams,
|
||||
Rest, Alert, TeamList, GenerateList, Prompt, SearchInit, PaginateInit,
|
||||
ReturnToCaller, ClearScope, ProcessErrors, SetTeamListeners, GetBasePath,
|
||||
SelectionInit, Wait, $state, Refresh, $filter, rbacUiControlService) {
|
||||
ClearScope();
|
||||
|
||||
$scope.canAdd = false;
|
||||
|
||||
rbacUiControlService.canAdd('teams')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
var list = TeamList,
|
||||
defaultUrl = GetBasePath('teams'),
|
||||
generator = GenerateList,
|
||||
paths = $location.path().replace(/^\//, '').split('/'),
|
||||
mode = (paths[0] === 'teams') ? 'edit' : 'select',
|
||||
url;
|
||||
defaultUrl = GetBasePath('teams');
|
||||
|
||||
var injectForm = function() {
|
||||
generator.inject(list, { mode: mode, scope: $scope });
|
||||
};
|
||||
init();
|
||||
|
||||
injectForm();
|
||||
function init() {
|
||||
$scope.canAdd = false;
|
||||
|
||||
$scope.$on("RefreshTeamsList", function() {
|
||||
injectForm();
|
||||
Refresh({
|
||||
scope: $scope,
|
||||
set: 'teams',
|
||||
iterator: 'team',
|
||||
url: GetBasePath('teams') + "?order_by=name&page_size=" + $scope.team_page_size
|
||||
rbacUiControlService.canAdd('teams')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
_.forEach($scope[list.name], (team) => {
|
||||
team.organization_name = team.summary_fields.organization.name;
|
||||
});
|
||||
});
|
||||
|
||||
$scope.selected = [];
|
||||
|
||||
url = GetBasePath('base') + $location.path() + '/';
|
||||
SelectionInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: url,
|
||||
returnToCaller: 1
|
||||
});
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
$scope.selected = [];
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
// After a refresh, populate the organization name on each row
|
||||
var i;
|
||||
if ($scope.teams) {
|
||||
for (i = 0; i < $scope.teams.length; i++) {
|
||||
if ($scope.teams[i].summary_fields.organization) {
|
||||
$scope.teams[i].organization_name = $scope.teams[i].summary_fields.organization.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'teams',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
$scope.search(list.iterator);
|
||||
|
||||
$scope.addTeam = function () {
|
||||
$state.transitionTo('teams.add');
|
||||
$scope.addTeam = function() {
|
||||
$state.go('teams.add');
|
||||
};
|
||||
|
||||
$scope.editTeam = function (id) {
|
||||
$state.transitionTo('teams.edit', {team_id: id});
|
||||
$scope.editTeam = function(id) {
|
||||
$state.go('teams.edit', { team_id: id });
|
||||
};
|
||||
|
||||
$scope.deleteTeam = function (id, name) {
|
||||
$scope.deleteTeam = function(id, name) {
|
||||
|
||||
var action = function () {
|
||||
var action = function() {
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
.success(function() {
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
if (parseInt($state.params.team_id) === id) {
|
||||
$state.go("^", null, {reload: true});
|
||||
$state.go('^', null, { reload: true });
|
||||
} else {
|
||||
$scope.search(list.iterator);
|
||||
$state.go('.', null, { reload: true });
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
@ -128,18 +82,15 @@ export function TeamsList($scope, $rootScope, $location, $log, $stateParams,
|
||||
};
|
||||
}
|
||||
|
||||
TeamsList.$inject = ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'TeamList', 'generateList', 'Prompt',
|
||||
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors', 'SetTeamListeners', 'GetBasePath', 'SelectionInit', 'Wait',
|
||||
'$state', 'Refresh', '$filter', 'rbacUiControlService'
|
||||
|
||||
TeamsList.$inject = ['$scope', '$rootScope', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'TeamList', 'Prompt', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset'
|
||||
];
|
||||
|
||||
|
||||
export function TeamsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, GenerateList, OrganizationList, SearchInit,
|
||||
PaginateInit, GetBasePath, LookUpInit, Wait, $state) {
|
||||
export function TeamsAdd($scope, $rootScope, $stateParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, Wait, $state) {
|
||||
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
|
||||
//$scope.
|
||||
|
||||
@ -155,176 +106,128 @@ export function TeamsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('teams'),
|
||||
form = TeamForm,
|
||||
generator = GenerateForm,
|
||||
scope = generator.inject(form, { mode: 'add', related: false });
|
||||
generator = GenerateForm;
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
generator.reset();
|
||||
init();
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: null,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
function init() {
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
}
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
$scope.formSave = function() {
|
||||
var fld, data;
|
||||
generator.clearApiErrors();
|
||||
Wait('start');
|
||||
Rest.setUrl(defaultUrl);
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
data[fld] = scope[fld];
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
Wait('stop');
|
||||
$rootScope.flashMessage = "New team successfully created!";
|
||||
$rootScope.$broadcast("EditIndicatorChange", "users", data.id);
|
||||
$state.go('teams.edit', {team_id: data.id}, {reload: true});
|
||||
$state.go('teams.edit', { team_id: data.id }, { reload: true });
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new team. Post returned status: ' +
|
||||
status });
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add new team. Post returned status: ' +
|
||||
status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('teams');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('teams');
|
||||
};
|
||||
}
|
||||
|
||||
TeamsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
|
||||
'$stateParams', 'TeamForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'OrganizationList', 'SearchInit', 'PaginateInit', 'GetBasePath',
|
||||
'LookUpInit', 'Wait', '$state'
|
||||
TeamsAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'GenerateForm',
|
||||
'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state'
|
||||
];
|
||||
|
||||
|
||||
export function TeamsEdit($scope, $rootScope, $location,
|
||||
$stateParams, TeamForm, GenerateForm, Rest, ProcessErrors,
|
||||
RelatedSearchInit, RelatedPaginateInit, ClearScope,
|
||||
LookUpInit, GetBasePath, OrganizationList, Wait, $state) {
|
||||
export function TeamsEdit($scope, $rootScope, $stateParams,
|
||||
TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var defaultUrl = GetBasePath('teams'),
|
||||
generator = GenerateForm,
|
||||
form = TeamForm,
|
||||
var form = TeamForm,
|
||||
id = $stateParams.team_id,
|
||||
relatedSets = {},
|
||||
set;
|
||||
defaultUrl = GetBasePath('teams') + id;
|
||||
|
||||
$scope.team_id = id;
|
||||
init();
|
||||
|
||||
$scope.$watch('team_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
function init() {
|
||||
$scope.team_id = id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
Wait('start');
|
||||
Rest.get(defaultUrl).success(function(data) {
|
||||
setScopeFields(data);
|
||||
$scope.organization_name = data.summary_fields.organization.name;
|
||||
|
||||
$scope.team_obj = data;
|
||||
});
|
||||
|
||||
$scope.$watch('team_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
generator.reset();
|
||||
}
|
||||
|
||||
var setScopeFields = function(data){
|
||||
// @issue I think all this really want to do is _.forEach(form.fields, (field) =>{ $scope[field] = data[field]})
|
||||
function setScopeFields(data) {
|
||||
_(data)
|
||||
.pick(function(value, key){
|
||||
return form.fields.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key){
|
||||
$scope[key] = value;
|
||||
})
|
||||
.value();
|
||||
.pick(function(value, key) {
|
||||
return form.fields.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key) {
|
||||
$scope[key] = value;
|
||||
})
|
||||
.value();
|
||||
return;
|
||||
};
|
||||
var setScopeRelated = function(data, related){
|
||||
_(related)
|
||||
.pick(function(value, key){
|
||||
return data.related.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key){
|
||||
relatedSets[key] = {
|
||||
url: data.related[key],
|
||||
iterator: value.iterator
|
||||
};
|
||||
})
|
||||
.value();
|
||||
};
|
||||
}
|
||||
|
||||
// prepares a data payload for a PUT request to the API
|
||||
var processNewData = function(fields){
|
||||
function processNewData(fields) {
|
||||
var data = {};
|
||||
_.forEach(fields, function(value, key){
|
||||
if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined){
|
||||
data[key] = $scope[key];
|
||||
_.forEach(fields, function(value, key) {
|
||||
if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) {
|
||||
data[key] = $scope[key];
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
$scope.formCancel = function() {
|
||||
$state.go('teams', null, { reload: true });
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
var url = defaultUrl + id;
|
||||
Rest.setUrl(url);
|
||||
Wait('start');
|
||||
Rest.get(url).success(function(data){
|
||||
setScopeFields(data);
|
||||
setScopeRelated(data, form.related);
|
||||
$scope.organization_name = data.summary_fields.organization.name;
|
||||
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
|
||||
$scope.team_obj = data;
|
||||
|
||||
LookUpInit({
|
||||
url: GetBasePath('organizations'),
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: $scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.formCancel = function(){
|
||||
$state.go('teams', null, {reload: true});
|
||||
};
|
||||
|
||||
$scope.formSave = function(){
|
||||
generator.clearApiErrors();
|
||||
generator.checkAutoFill();
|
||||
$scope.formSave = function() {
|
||||
$rootScope.flashMessage = null;
|
||||
if ($scope[form.name + '_form'].$valid){
|
||||
if ($scope[form.name + '_form'].$valid) {
|
||||
Rest.setUrl(defaultUrl + id + '/');
|
||||
var data = processNewData(form.fields);
|
||||
Rest.put(data).success(function(){
|
||||
$state.go($state.current, null, {reload: true});
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status });
|
||||
});
|
||||
Rest.put(data).success(function() {
|
||||
$state.go($state.current, null, { reload: true });
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -337,13 +240,8 @@ export function TeamsEdit($scope, $rootScope, $location,
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/* Related Set implementation TDB */
|
||||
}
|
||||
|
||||
TeamsEdit.$inject = ['$scope', '$rootScope', '$location',
|
||||
'$stateParams', 'TeamForm', 'GenerateForm', 'Rest',
|
||||
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit',
|
||||
'ClearScope', 'LookUpInit', 'GetBasePath',
|
||||
'OrganizationList', 'Wait', '$state'
|
||||
TeamsEdit.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state'
|
||||
];
|
||||
|
||||
@ -8,14 +8,14 @@
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Users
|
||||
* @description This controller's the Users page
|
||||
*/
|
||||
*/
|
||||
|
||||
import {N_} from "../i18n";
|
||||
import { N_ } from "../i18n";
|
||||
|
||||
const user_type_options = [
|
||||
{type: 'normal' , label: N_('Normal User') },
|
||||
{type: 'system_auditor' , label: N_('System Auditor') },
|
||||
{type: 'system_administrator', label: N_('System Administrator') },
|
||||
{ type: 'normal', label: N_('Normal User') },
|
||||
{ type: 'system_auditor', label: N_('System Auditor') },
|
||||
{ type: 'system_administrator', label: N_('System Administrator') },
|
||||
];
|
||||
|
||||
function user_type_sync($scope) {
|
||||
@ -25,18 +25,17 @@ function user_type_sync($scope) {
|
||||
switch (type_option.type) {
|
||||
case 'system_administrator':
|
||||
$scope.is_superuser = true;
|
||||
break;
|
||||
break;
|
||||
case 'system_auditor':
|
||||
$scope.is_system_auditor = true;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function UsersList($scope, $rootScope, $location, $log, $stateParams,
|
||||
Rest, Alert, UserList, GenerateList, Prompt, SearchInit, PaginateInit,
|
||||
ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit,
|
||||
Wait, $state, Refresh, $filter, rbacUiControlService, i18n) {
|
||||
export function UsersList($scope, $rootScope, $stateParams,
|
||||
Rest, Alert, UserList, Prompt, ClearScope, ProcessErrors, GetBasePath,
|
||||
Wait, $state, $filter, rbacUiControlService, Dataset, i18n) {
|
||||
|
||||
for (var i = 0; i < user_type_options.length; i++) {
|
||||
user_type_options[i].label = i18n._(user_type_options[i].label);
|
||||
@ -44,95 +43,57 @@ export function UsersList($scope, $rootScope, $location, $log, $stateParams,
|
||||
|
||||
ClearScope();
|
||||
|
||||
$scope.canAdd = false;
|
||||
|
||||
rbacUiControlService.canAdd('users')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
var list = UserList,
|
||||
defaultUrl = GetBasePath('users'),
|
||||
generator = GenerateList,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
mode = (base === 'users') ? 'edit' : 'select',
|
||||
url = (base === 'organizations') ? GetBasePath('organizations') + $stateParams.organization_id + '/users/' :
|
||||
GetBasePath('teams') + $stateParams.team_id + '/users/';
|
||||
defaultUrl = GetBasePath('users');
|
||||
|
||||
var injectForm = function() {
|
||||
generator.inject(UserList, { mode: mode, scope: $scope });
|
||||
};
|
||||
init();
|
||||
|
||||
injectForm();
|
||||
function init() {
|
||||
$scope.canAdd = false;
|
||||
|
||||
$scope.$on("RefreshUsersList", function() {
|
||||
injectForm();
|
||||
Refresh({
|
||||
scope: $scope,
|
||||
set: 'users',
|
||||
iterator: 'user',
|
||||
url: GetBasePath('users') + "?order_by=username&page_size=" + $scope.user_page_size
|
||||
});
|
||||
});
|
||||
rbacUiControlService.canAdd('users')
|
||||
.then(function(canAdd) {
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
$scope.selected = [];
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
if (mode === 'select') {
|
||||
SelectionInit({ scope: $scope, list: list, url: url, returnToCaller: 1 });
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
$scope.selected = [];
|
||||
}
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
// Cleanup after a delete
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
});
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'users',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
$scope.search(list.iterator);
|
||||
|
||||
$scope.addUser = function () {
|
||||
$state.transitionTo('users.add');
|
||||
$scope.addUser = function() {
|
||||
$state.go('users.add');
|
||||
};
|
||||
|
||||
$scope.editUser = function (id) {
|
||||
$state.transitionTo('users.edit', {user_id: id});
|
||||
$scope.editUser = function(id) {
|
||||
$state.go('users.edit', { user_id: id });
|
||||
};
|
||||
|
||||
$scope.deleteUser = function (id, name) {
|
||||
$scope.deleteUser = function(id, name) {
|
||||
|
||||
var action = function () {
|
||||
//$('#prompt-modal').on('hidden.bs.modal', function () {
|
||||
// Wait('start');
|
||||
//});
|
||||
var action = function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
.success(function() {
|
||||
if (parseInt($state.params.user_id) === id) {
|
||||
$state.go("^", null, {reload: true});
|
||||
$state.go('^', null, { reload: true });
|
||||
} else {
|
||||
$scope.search(list.iterator);
|
||||
$state.go('.', null, { reload: true });
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -145,91 +106,54 @@ export function UsersList($scope, $rootScope, $location, $log, $stateParams,
|
||||
};
|
||||
}
|
||||
|
||||
UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'UserList', 'generateList', 'Prompt',
|
||||
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'Wait', '$state',
|
||||
'Refresh', '$filter', 'rbacUiControlService', 'i18n'
|
||||
UsersList.$inject = ['$scope', '$rootScope', '$stateParams',
|
||||
'Rest', 'Alert', 'UserList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath',
|
||||
'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n'
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export function UsersAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, GetBasePath, LookUpInit, OrganizationList,
|
||||
ResetForm, Wait, CreateSelect2, $state, i18n) {
|
||||
|
||||
for (var i = 0; i < user_type_options.length; i++) {
|
||||
user_type_options[i].label = i18n._(user_type_options[i].label);
|
||||
}
|
||||
|
||||
Rest.setUrl(GetBasePath('users'));
|
||||
Rest.options()
|
||||
.success(function(data) {
|
||||
if (!data.actions.POST) {
|
||||
$state.go("^");
|
||||
Alert('Permission Error', 'You do not have permission to add a user.', 'alert-info');
|
||||
}
|
||||
});
|
||||
export function UsersAdd($scope, $rootScope, $stateParams, UserForm,
|
||||
GenerateForm, Rest, Alert, ProcessErrors, ReturnToCaller, ClearScope,
|
||||
GetBasePath, ResetForm, Wait, CreateSelect2, $state, i18n) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('organizations'),
|
||||
form = UserForm,
|
||||
generator = GenerateForm;
|
||||
form = UserForm;
|
||||
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
ResetForm();
|
||||
init();
|
||||
|
||||
$scope.ldap_user = false;
|
||||
$scope.not_ldap_user = !$scope.ldap_user;
|
||||
$scope.ldap_dn = null;
|
||||
$scope.socialAuthUser = false;
|
||||
$scope.external_account = null;
|
||||
function init() {
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
generator.reset();
|
||||
$scope.ldap_user = false;
|
||||
$scope.not_ldap_user = !$scope.ldap_user;
|
||||
$scope.ldap_dn = null;
|
||||
$scope.socialAuthUser = false;
|
||||
$scope.external_account = null;
|
||||
|
||||
$scope.user_type_options = user_type_options;
|
||||
$scope.user_type = user_type_options[0];
|
||||
$scope.$watch('user_type', user_type_sync($scope));
|
||||
|
||||
CreateSelect2({
|
||||
element: '#user_user_type',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
// Configure the lookup dialog. If we're adding a user through the Organizations tab,
|
||||
// default the Organization value.
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: ($stateParams.organization_id !== undefined) ? $stateParams.organization_id : null,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
if ($stateParams.organization_id) {
|
||||
$scope.organization = $stateParams.organization_id;
|
||||
Rest.setUrl(GetBasePath('organizations') + $stateParams.organization_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.organization_name = data.name;
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to lookup Organization: ' + data.id + '. GET returned status: ' + status });
|
||||
Rest.setUrl(GetBasePath('users'));
|
||||
Rest.options()
|
||||
.success(function(data) {
|
||||
if (!data.actions.POST) {
|
||||
$state.go("^");
|
||||
Alert('Permission Error', 'You do not have permission to add a user.', 'alert-info');
|
||||
}
|
||||
});
|
||||
|
||||
$scope.user_type_options = user_type_options;
|
||||
$scope.user_type = user_type_options[0];
|
||||
$scope.$watch('user_type', user_type_sync($scope));
|
||||
CreateSelect2({
|
||||
element: '#user_user_type',
|
||||
multiple: false
|
||||
});
|
||||
}
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
$scope.formSave = function() {
|
||||
var fld, data = {};
|
||||
generator.clearApiErrors();
|
||||
generator.checkAutoFill();
|
||||
if ($scope[form.name + '_form'].$valid) {
|
||||
if ($scope.organization !== undefined && $scope.organization !== null && $scope.organization !== '') {
|
||||
Rest.setUrl(defaultUrl + $scope.organization + '/users/');
|
||||
@ -244,18 +168,17 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
|
||||
data.is_system_auditor = $scope.is_system_auditor;
|
||||
Wait('start');
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
if (base === 'users') {
|
||||
$rootScope.flashMessage = 'New user successfully created!';
|
||||
$rootScope.$broadcast("EditIndicatorChange", "users", data.id);
|
||||
$state.go('users.edit', {user_id: data.id}, {reload: true});
|
||||
}
|
||||
else {
|
||||
$state.go('users.edit', { user_id: data.id }, { reload: true });
|
||||
} else {
|
||||
ReturnToCaller(1);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new user. POST returned status: ' + status });
|
||||
});
|
||||
} else {
|
||||
@ -264,69 +187,102 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('users');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('users');
|
||||
};
|
||||
|
||||
// Password change
|
||||
$scope.clearPWConfirm = function (fld) {
|
||||
$scope.clearPWConfirm = function(fld) {
|
||||
// If password value changes, make sure password_confirm must be re-entered
|
||||
$scope[fld] = '';
|
||||
$scope[form.name + '_form'][fld].$setValidity('awpassmatch', false);
|
||||
};
|
||||
}
|
||||
|
||||
UsersAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
|
||||
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
|
||||
'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state',
|
||||
'i18n'
|
||||
UsersAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'UserForm', 'GenerateForm',
|
||||
'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
|
||||
'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n'
|
||||
];
|
||||
|
||||
|
||||
export function UsersEdit($scope, $rootScope, $location,
|
||||
$stateParams, UserForm, GenerateForm, Rest, ProcessErrors,
|
||||
RelatedSearchInit, RelatedPaginateInit, ClearScope,
|
||||
GetBasePath, ResetForm, Wait, CreateSelect2 ,$state, i18n) {
|
||||
$stateParams, UserForm, Rest, ProcessErrors,
|
||||
ClearScope, GetBasePath, ResetForm, Wait, CreateSelect2, $state, i18n) {
|
||||
|
||||
for (var i = 0; i < user_type_options.length; i++) {
|
||||
user_type_options[i].label = i18n._(user_type_options[i].label);
|
||||
}
|
||||
|
||||
ClearScope();
|
||||
|
||||
var defaultUrl = GetBasePath('users'),
|
||||
generator = GenerateForm,
|
||||
form = UserForm,
|
||||
var form = UserForm,
|
||||
master = {},
|
||||
id = $stateParams.user_id,
|
||||
relatedSets = {},
|
||||
set;
|
||||
defaultUrl = GetBasePath('users') + id;
|
||||
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
generator.reset();
|
||||
init();
|
||||
|
||||
$scope.user_type_options = user_type_options;
|
||||
$scope.user_type = user_type_options[0];
|
||||
$scope.$watch('user_type', user_type_sync($scope));
|
||||
function init() {
|
||||
$scope.user_type_options = user_type_options;
|
||||
$scope.user_type = user_type_options[0];
|
||||
$scope.$watch('user_type', user_type_sync($scope));
|
||||
Rest.setUrl(defaultUrl);
|
||||
Wait('start');
|
||||
Rest.get(defaultUrl).success(function(data) {
|
||||
$scope.user_id = id;
|
||||
$scope.ldap_user = (data.ldap_dn !== null && data.ldap_dn !== undefined && data.ldap_dn !== '') ? true : false;
|
||||
$scope.not_ldap_user = !$scope.ldap_user;
|
||||
master.ldap_user = $scope.ldap_user;
|
||||
$scope.socialAuthUser = (data.auth.length > 0) ? true : false;
|
||||
$scope.external_account = data.external_account;
|
||||
|
||||
$scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
$scope.user_type = $scope.user_type_options[0];
|
||||
$scope.is_system_auditor = false;
|
||||
$scope.is_superuser = false;
|
||||
if (data.is_system_auditor) {
|
||||
$scope.user_type = $scope.user_type_options[1];
|
||||
$scope.is_system_auditor = true;
|
||||
}
|
||||
if (data.is_superuser) {
|
||||
$scope.user_type = $scope.user_type_options[2];
|
||||
$scope.is_superuser = true;
|
||||
}
|
||||
|
||||
var setScopeFields = function(data){
|
||||
$scope.user_obj = data;
|
||||
|
||||
CreateSelect2({
|
||||
element: '#user_user_type',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
|
||||
setScopeFields(data);
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function setScopeFields(data) {
|
||||
_(data)
|
||||
.pick(function(value, key){
|
||||
return form.fields.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key){
|
||||
$scope[key] = value;
|
||||
})
|
||||
.value();
|
||||
.pick(function(value, key) {
|
||||
return form.fields.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key) {
|
||||
$scope[key] = value;
|
||||
})
|
||||
.value();
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$scope.convertApiUrl = function(str) {
|
||||
if (str) {
|
||||
@ -336,25 +292,12 @@ export function UsersEdit($scope, $rootScope, $location,
|
||||
}
|
||||
};
|
||||
|
||||
var setScopeRelated = function(data, related){
|
||||
_(related)
|
||||
.pick(function(value, key){
|
||||
return data.related.hasOwnProperty(key) === true;
|
||||
})
|
||||
.forEach(function(value, key){
|
||||
relatedSets[key] = {
|
||||
url: data.related[key],
|
||||
iterator: value.iterator
|
||||
};
|
||||
})
|
||||
.value();
|
||||
};
|
||||
// prepares a data payload for a PUT request to the API
|
||||
var processNewData = function(fields){
|
||||
var processNewData = function(fields) {
|
||||
var data = {};
|
||||
_.forEach(fields, function(value, key){
|
||||
if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined){
|
||||
data[key] = $scope[key];
|
||||
_.forEach(fields, function(value, key) {
|
||||
if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) {
|
||||
data[key] = $scope[key];
|
||||
}
|
||||
});
|
||||
data.is_superuser = $scope.is_superuser;
|
||||
@ -362,98 +305,37 @@ export function UsersEdit($scope, $rootScope, $location,
|
||||
return data;
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
var url = defaultUrl + id;
|
||||
Rest.setUrl(url);
|
||||
Wait('start');
|
||||
Rest.get(url).success(function(data){
|
||||
$scope.user_id = id;
|
||||
$scope.ldap_user = (data.ldap_dn !== null && data.ldap_dn !== undefined && data.ldap_dn !== '') ? true : false;
|
||||
$scope.not_ldap_user = !$scope.ldap_user;
|
||||
master.ldap_user = $scope.ldap_user;
|
||||
$scope.socialAuthUser = (data.auth.length > 0) ? true : false;
|
||||
$scope.external_account = data.external_account;
|
||||
|
||||
$scope.user_type = $scope.user_type_options[0];
|
||||
$scope.is_system_auditor = false;
|
||||
$scope.is_superuser = false;
|
||||
if (data.is_system_auditor) {
|
||||
$scope.user_type = $scope.user_type_options[1];
|
||||
$scope.is_system_auditor = true;
|
||||
}
|
||||
if (data.is_superuser) {
|
||||
$scope.user_type = $scope.user_type_options[2];
|
||||
$scope.is_superuser = true;
|
||||
}
|
||||
|
||||
$scope.user_obj = data;
|
||||
|
||||
CreateSelect2({
|
||||
element: '#user_user_type',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
|
||||
setScopeFields(data);
|
||||
setScopeRelated(data, form.related);
|
||||
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status });
|
||||
});
|
||||
$scope.formCancel = function() {
|
||||
$state.go('users', null, { reload: true });
|
||||
};
|
||||
|
||||
$scope.formCancel = function(){
|
||||
$state.go('users', null, {reload: true});
|
||||
};
|
||||
|
||||
$scope.formSave = function(){
|
||||
generator.clearApiErrors();
|
||||
generator.checkAutoFill();
|
||||
$scope.formSave = function() {
|
||||
$rootScope.flashMessage = null;
|
||||
if ($scope[form.name + '_form'].$valid){
|
||||
if ($scope[form.name + '_form'].$valid) {
|
||||
Rest.setUrl(defaultUrl + id + '/');
|
||||
var data = processNewData(form.fields);
|
||||
Rest.put(data).success(function(){
|
||||
$state.go($state.current, null, {reload: true});
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status });
|
||||
});
|
||||
Rest.put(data).success(function() {
|
||||
$state.go($state.current, null, { reload: true });
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve user: ' +
|
||||
$stateParams.id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.clearPWConfirm = function (fld) {
|
||||
$scope.clearPWConfirm = function(fld) {
|
||||
// If password value changes, make sure password_confirm must be re-entered
|
||||
$scope[fld] = '';
|
||||
$scope[form.name + '_form'][fld].$setValidity('awpassmatch', false);
|
||||
$rootScope.flashMessage = null;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
/* Related Set implementation TDB */
|
||||
}
|
||||
|
||||
UsersEdit.$inject = ['$scope', '$rootScope', '$location',
|
||||
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'ProcessErrors',
|
||||
'RelatedSearchInit', 'RelatedPaginateInit', 'ClearScope', 'GetBasePath',
|
||||
'$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath',
|
||||
'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n'
|
||||
];
|
||||
|
||||
@ -39,7 +39,7 @@ export default
|
||||
label: i18n._("Hosts")
|
||||
},
|
||||
{
|
||||
url: "/#/home/hosts?active-failures=true",
|
||||
url: "/#/home/hosts?host_search=has_active_failures:true",
|
||||
number: scope.data.hosts.failed,
|
||||
label: i18n._("Failed Hosts"),
|
||||
isFailureCount: true
|
||||
@ -50,7 +50,7 @@ export default
|
||||
label: i18n._("Inventories"),
|
||||
},
|
||||
{
|
||||
url: "/#/inventories?status=sync-failed",
|
||||
url: "/#/inventories?inventory_search=inventory_sources_with_failures__gt:0",
|
||||
number: scope.data.inventories.inventory_failed,
|
||||
label: i18n._("Inventory Sync Failures"),
|
||||
isFailureCount: true
|
||||
@ -61,7 +61,7 @@ export default
|
||||
label: i18n._("Projects")
|
||||
},
|
||||
{
|
||||
url: "/#/projects?status=failed,canceled",
|
||||
url: "/#/projects?project_search=status__in:failed,canceled",
|
||||
number: scope.data.projects.failed,
|
||||
label: i18n._("Project Sync Failures"),
|
||||
isFailureCount: true
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
<div class="tab-pane" id="organizations">
|
||||
<div ui-view></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
</div>
|
||||
@ -4,86 +4,52 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$scope', '$state', '$stateParams', 'PageRangeSetup', 'GetBasePath', 'DashboardHostsList',
|
||||
'generateList', 'PaginateInit', 'SetStatus', 'DashboardHostService', 'hosts', '$rootScope', 'SearchInit',
|
||||
function($scope, $state, $stateParams, PageRangeSetup, GetBasePath, DashboardHostsList, GenerateList, PaginateInit, SetStatus, DashboardHostService, hosts, $rootScope, SearchInit){
|
||||
var setJobStatus = function(){
|
||||
_.forEach($scope.hosts, function(value){
|
||||
SetStatus({
|
||||
scope: $scope,
|
||||
host: value
|
||||
});
|
||||
});
|
||||
};
|
||||
var generator = GenerateList,
|
||||
list = DashboardHostsList,
|
||||
defaultUrl = GetBasePath('hosts');
|
||||
$scope.hostPageSize = 10;
|
||||
$scope.editHost = function(id){
|
||||
$state.go('dashboardHosts.edit', {id: id});
|
||||
};
|
||||
$scope.toggleHostEnabled = function(host){
|
||||
DashboardHostService.setHostStatus(host, !host.enabled)
|
||||
.then(function(res){
|
||||
var index = _.findIndex($scope.hosts, function(o) {return o.id === res.data.id;});
|
||||
$scope.hosts[index].enabled = res.data.enabled;
|
||||
});
|
||||
};
|
||||
$scope.$on('PostRefresh', function(){
|
||||
$scope.hosts = _.map($scope.hosts, function(value){
|
||||
value.inventory_name = value.summary_fields.inventory.name;
|
||||
value.inventory_id = value.summary_fields.inventory.id;
|
||||
return value;
|
||||
});
|
||||
setJobStatus();
|
||||
});
|
||||
var cleanUpStateChangeListener = $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
|
||||
if (toState.name === "dashboardHosts.edit") {
|
||||
$scope.rowBeingEdited = toParams.id;
|
||||
$scope.listBeingEdited = "hosts";
|
||||
}
|
||||
else {
|
||||
delete $scope.rowBeingEdited;
|
||||
delete $scope.listBeingEdited;
|
||||
}
|
||||
});
|
||||
// Remove the listener when the scope is destroyed to avoid a memory leak
|
||||
$scope.$on('$destroy', function() {
|
||||
cleanUpStateChangeListener();
|
||||
});
|
||||
var init = function(){
|
||||
$scope.list = list;
|
||||
$scope.host_active_search = false;
|
||||
$scope.host_total_rows = hosts.results.length;
|
||||
$scope.hosts = hosts.results;
|
||||
setJobStatus();
|
||||
generator.inject(list, {mode: 'edit', scope: $scope});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'hosts',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl,
|
||||
pageSize: 10
|
||||
});
|
||||
PageRangeSetup({
|
||||
scope: $scope,
|
||||
count: hosts.count,
|
||||
next: hosts.next,
|
||||
previous: hosts.previous,
|
||||
iterator: list.iterator
|
||||
export default ['$scope', '$state', '$stateParams', 'GetBasePath', 'DashboardHostsList',
|
||||
'generateList', 'SetStatus', 'DashboardHostService', '$rootScope', 'Dataset',
|
||||
function($scope, $state, $stateParams, GetBasePath, DashboardHostsList,
|
||||
GenerateList, SetStatus, DashboardHostService, $rootScope, Dataset) {
|
||||
|
||||
let list = DashboardHostsList;
|
||||
init();
|
||||
|
||||
function init() {
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$scope.$watchCollection(list.name, function() {
|
||||
$scope[list.name] = _.map($scope.hosts, function(value) {
|
||||
value.inventory_name = value.summary_fields.inventory.name;
|
||||
value.inventory_id = value.summary_fields.inventory.id;
|
||||
return value;
|
||||
});
|
||||
setJobStatus();
|
||||
});
|
||||
$scope.hostLoading = false;
|
||||
if($state.current.name === "dashboardHosts.edit") {
|
||||
$scope.rowBeingEdited = $state.params.id;
|
||||
$scope.listBeingEdited = "hosts";
|
||||
}
|
||||
$scope.search(list.iterator);
|
||||
};
|
||||
init();
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
function setJobStatus(){
|
||||
_.forEach($scope.hosts, function(value) {
|
||||
SetStatus({
|
||||
scope: $scope,
|
||||
host: value
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$scope.editHost = function(id) {
|
||||
$state.go('dashboardHosts.edit', { id: id });
|
||||
};
|
||||
|
||||
$scope.toggleHostEnabled = function(host) {
|
||||
DashboardHostService.setHostStatus(host, !host.enabled)
|
||||
.then(function(res) {
|
||||
var index = _.findIndex($scope.hosts, function(o) {
|
||||
return o.id === res.data.id;
|
||||
});
|
||||
$scope.hosts[index].enabled = res.data.enabled;
|
||||
});
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
<div class="tab-pane" id="HomeHosts">
|
||||
<div ui-view></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
</div>
|
||||
@ -18,7 +18,7 @@ export default function(){
|
||||
class: 'Form-header-field',
|
||||
ngClick: 'toggleHostEnabled()',
|
||||
type: 'toggle',
|
||||
editRequired: false,
|
||||
|
||||
awToolTip: "<p>Indicates if a host is available and should be included in running jobs.</p><p>For hosts that " +
|
||||
"are part of an external inventory, this flag cannot be changed. It will be set by the inventory sync process.</p>",
|
||||
dataTitle: 'Host Enabled'
|
||||
@ -28,7 +28,7 @@ export default function(){
|
||||
name: {
|
||||
label: 'Host Name',
|
||||
type: 'text',
|
||||
editRequired: true,
|
||||
|
||||
value: '{{name}}',
|
||||
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
|
||||
"<blockquote>myserver.domain.com<br/>" +
|
||||
@ -43,12 +43,10 @@ export default function(){
|
||||
description: {
|
||||
label: 'Description',
|
||||
type: 'text',
|
||||
editRequired: false
|
||||
},
|
||||
variables: {
|
||||
label: 'Variables',
|
||||
type: 'textarea',
|
||||
editRequired: false,
|
||||
rows: 6,
|
||||
class: 'modal-input-xlarge Form-textArea Form-formGroup--fullWidth',
|
||||
dataTitle: 'Host Variables',
|
||||
|
||||
@ -21,11 +21,7 @@ export default [ 'i18n', function(i18n){
|
||||
basePath: 'unified_jobs',
|
||||
label: '',
|
||||
iconOnly: true,
|
||||
searchable: false,
|
||||
searchType: 'select',
|
||||
nosort: true,
|
||||
searchOptions: [],
|
||||
searchLabel: 'Job Status',
|
||||
icon: 'icon-job-{{ host.active_failures }}',
|
||||
awToolTip: '{{ host.badgeToolTip }}',
|
||||
awTipPlacement: 'right',
|
||||
@ -54,24 +50,9 @@ export default [ 'i18n', function(i18n){
|
||||
columnClass: 'List-staticColumn--toggle',
|
||||
type: 'toggle',
|
||||
ngClick: 'toggleHostEnabled(host)',
|
||||
searchable: false,
|
||||
nosort: true,
|
||||
awToolTip: "<p>Indicates if a host is available and should be included in running jobs.</p><p>For hosts that are part of an external inventory, this flag cannot be changed. It will be set by the inventory sync process.</p>",
|
||||
dataTitle: 'Host Enabled',
|
||||
},
|
||||
has_active_failures: {
|
||||
label: 'Has failed jobs?',
|
||||
searchSingleValue: true,
|
||||
searchType: 'boolean',
|
||||
searchValue: 'true',
|
||||
searchOnly: true
|
||||
},
|
||||
has_inventory_sources: {
|
||||
label: 'Has external source?',
|
||||
searchSingleValue: true,
|
||||
searchType: 'boolean',
|
||||
searchValue: 'true',
|
||||
searchOnly: true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import listController from './dashboard-hosts-list.controller';
|
||||
import editController from './dashboard-hosts-edit.controller';
|
||||
|
||||
var dashboardHostsList = {
|
||||
name: 'dashboardHosts',
|
||||
url: '/home/hosts?:active-failures',
|
||||
controller: listController,
|
||||
templateUrl: templateUrl('dashboard/hosts/dashboard-hosts-list'),
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'host'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'dashboard',
|
||||
label: "HOSTS"
|
||||
},
|
||||
resolve: {
|
||||
hosts: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams){
|
||||
var defaultUrl = GetBasePath('hosts') + '?page_size=10' + ($stateParams['active-failures'] ? '&has_active_failures=true' : '' );
|
||||
Rest.setUrl(defaultUrl);
|
||||
return Rest.get().then(function(res){
|
||||
var results = _.map(res.data.results, function(value){
|
||||
value.inventory_name = value.summary_fields.inventory.name;
|
||||
value.inventory_id = value.summary_fields.inventory.id;
|
||||
return value;
|
||||
});
|
||||
res.data.results = results;
|
||||
return res.data;
|
||||
});
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var dashboardHostsEdit = {
|
||||
name: 'dashboardHosts.edit',
|
||||
url: '/:id',
|
||||
controller: editController,
|
||||
templateUrl: templateUrl('dashboard/hosts/dashboard-hosts-edit'),
|
||||
ncyBreadcrumb: {
|
||||
parent: 'dashboardHosts',
|
||||
label: "{{host.name}}"
|
||||
},
|
||||
resolve: {
|
||||
host: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
|
||||
var defaultUrl = GetBasePath('hosts') + '?id=' + $stateParams.id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
return Rest.get().then(function(res){
|
||||
return res.data.results[0];
|
||||
});
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
export {dashboardHostsList, dashboardHostsEdit};
|
||||
@ -4,17 +4,43 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {dashboardHostsList, dashboardHostsEdit} from './dashboard-hosts.route';
|
||||
import list from './dashboard-hosts.list';
|
||||
import form from './dashboard-hosts.form';
|
||||
import listController from './dashboard-hosts-list.controller';
|
||||
import editController from './dashboard-hosts-edit.controller';
|
||||
import service from './dashboard-hosts.service';
|
||||
|
||||
export default
|
||||
angular.module('dashboardHosts', [])
|
||||
angular.module('dashboardHosts', [])
|
||||
.service('DashboardHostService', service)
|
||||
.factory('DashboardHostsList', list)
|
||||
.factory('DashboardHostsForm', form)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(dashboardHostsList);
|
||||
$stateExtender.addState(dashboardHostsEdit);
|
||||
}]);
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider',
|
||||
function($stateProvider, stateDefinitionsProvider) {
|
||||
let stateDefinitions = stateDefinitionsProvider.$get();
|
||||
|
||||
$stateProvider.state({
|
||||
name: 'dashboardHosts',
|
||||
url: '/home/hosts',
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
url: '/home/hosts',
|
||||
parent: 'dashboardHosts',
|
||||
modes: ['edit'],
|
||||
list: 'DashboardHostsList',
|
||||
form: 'DashboardHostsForm',
|
||||
controllers: {
|
||||
list: listController,
|
||||
edit: editController
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'host'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'dashboard',
|
||||
label: "HOSTS"
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
]);
|
||||
|
||||
@ -18,6 +18,8 @@ export default
|
||||
addTitle: i18n._('Create Credential'), //Legend in add mode
|
||||
editTitle: '{{ name }}', //Legend in edit mode
|
||||
name: 'credential',
|
||||
// the top-most node of generated state tree
|
||||
stateTree: 'credentials',
|
||||
forceListeners: true,
|
||||
subFormTitles: {
|
||||
credentialSubForm: i18n._('Type Details'),
|
||||
@ -31,24 +33,22 @@ export default
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
autocomplete: false,
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
description: {
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
organization: {
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
// interpolated with $rootScope
|
||||
basePath: "{{$rootScope.current_user.is_superuser ? 'api/v1/organizations' : $rootScope.current_user.url + 'admin_of_organizations'}}",
|
||||
ngShow: 'canShareCredential',
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
list: 'OrganizationList',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpOrganization()',
|
||||
@ -56,7 +56,7 @@ export default
|
||||
dataTitle: i18n._('Organization') + ' ',
|
||||
dataPlacement: 'bottom',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
kind: {
|
||||
label: i18n._('Type'),
|
||||
@ -64,8 +64,7 @@ export default
|
||||
type: 'select',
|
||||
ngOptions: 'kind.label for kind in credential_kind_options track by kind.value', // select as label for value in array 'kind.label for kind in credential_kind_options',
|
||||
ngChange: 'kindChange()',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
awPopOver: i18n._('<dl>\n' +
|
||||
'<dt>Machine</dt>\n' +
|
||||
'<dd>Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
|
||||
@ -88,7 +87,7 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
hasSubForm: true,
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
access_key: {
|
||||
label: i18n._('Access Key'),
|
||||
@ -101,7 +100,7 @@ export default
|
||||
autocomplete: false,
|
||||
apiField: 'username',
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
secret_key: {
|
||||
label: i18n._('Secret Key'),
|
||||
@ -130,7 +129,7 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"host": {
|
||||
labelBind: 'hostLabel',
|
||||
@ -147,7 +146,7 @@ export default
|
||||
init: false
|
||||
},
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"subscription": {
|
||||
label: i18n._("Subscription ID"),
|
||||
@ -157,15 +156,15 @@ export default
|
||||
reqExpression: 'subscription_required',
|
||||
init: false
|
||||
},
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
autocomplete: false,
|
||||
awPopOver: i18n._('<p>Subscription ID is an Azure construct, which is mapped to a username.</p>'),
|
||||
dataTitle: i18n._('Subscription ID'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"username": {
|
||||
labelBind: 'usernameLabel',
|
||||
@ -178,7 +177,7 @@ export default
|
||||
},
|
||||
autocomplete: false,
|
||||
subForm: "credentialSubForm",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"email_address": {
|
||||
labelBind: 'usernameLabel',
|
||||
@ -194,7 +193,7 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"api_key": {
|
||||
label: i18n._('API Key'),
|
||||
@ -208,7 +207,7 @@ export default
|
||||
hasShowInputButton: true,
|
||||
clear: false,
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"password": {
|
||||
labelBind: 'passwordLabel',
|
||||
@ -222,15 +221,13 @@ export default
|
||||
init: false
|
||||
},
|
||||
subForm: "credentialSubForm",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"ssh_password": {
|
||||
label: i18n._('Password'),
|
||||
type: 'sensitive',
|
||||
ngShow: "kind.value == 'ssh'",
|
||||
ngDisabled: "ssh_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
subCheckbox: {
|
||||
variable: 'ssh_password_ask',
|
||||
text: i18n._('Ask at runtime?'),
|
||||
@ -251,8 +248,8 @@ export default
|
||||
},
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
elementClass: 'Form-monospace',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
awDropFile: true,
|
||||
rows: 10,
|
||||
awPopOver: i18n._("SSH key description"),
|
||||
@ -261,14 +258,12 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
subForm: "credentialSubForm",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"ssh_key_unlock": {
|
||||
label: i18n._('Private Key Passphrase'),
|
||||
type: 'sensitive',
|
||||
ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: "keyEntered === false || ssh_key_unlock_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
|
||||
subCheckbox: {
|
||||
variable: 'ssh_key_unlock_ask',
|
||||
@ -293,25 +288,23 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"become_username": {
|
||||
labelBind: 'becomeUsernameLabel',
|
||||
type: 'text',
|
||||
ngShow: "(kind.value == 'ssh' && (become_method && become_method.value)) ",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
autocomplete: false,
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"become_password": {
|
||||
labelBind: 'becomePasswordLabel',
|
||||
type: 'sensitive',
|
||||
ngShow: "(kind.value == 'ssh' && (become_method && become_method.value)) ",
|
||||
ngDisabled: "become_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
subCheckbox: {
|
||||
variable: 'become_password_ask',
|
||||
text: i18n._('Ask at runtime?'),
|
||||
@ -326,7 +319,7 @@ export default
|
||||
label: i18n._('Client ID'),
|
||||
subForm: 'credentialSubForm',
|
||||
ngShow: "kind.value === 'azure_rm'",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
secret:{
|
||||
type: 'sensitive',
|
||||
@ -335,14 +328,14 @@ export default
|
||||
label: i18n._('Client Secret'),
|
||||
subForm: 'credentialSubForm',
|
||||
ngShow: "kind.value === 'azure_rm'",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
tenant: {
|
||||
type: 'text',
|
||||
label: i18n._('Tenant ID'),
|
||||
subForm: 'credentialSubForm',
|
||||
ngShow: "kind.value === 'azure_rm'",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
authorize: {
|
||||
label: i18n._('Authorize'),
|
||||
@ -350,7 +343,7 @@ export default
|
||||
ngChange: "toggleCallback('host_config_key')",
|
||||
subForm: 'credentialSubForm',
|
||||
ngShow: "kind.value === 'net'",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
authorize_password: {
|
||||
label: i18n._('Authorize Password'),
|
||||
@ -359,7 +352,7 @@ export default
|
||||
autocomplete: false,
|
||||
subForm: 'credentialSubForm',
|
||||
ngShow: "authorize && authorize !== 'false'",
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"project": {
|
||||
labelBind: 'projectLabel',
|
||||
@ -370,14 +363,12 @@ export default
|
||||
dataTitle: i18n._('Project Name'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awRequiredWhen: {
|
||||
reqExpression: 'project_required',
|
||||
init: false
|
||||
},
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
"domain": {
|
||||
labelBind: 'domainLabel',
|
||||
@ -391,18 +382,14 @@ export default
|
||||
dataTitle: i18n._('Domain Name'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
subForm: 'credentialSubForm',
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
subForm: 'credentialSubForm'
|
||||
},
|
||||
"vault_password": {
|
||||
label: i18n._("Vault Password"),
|
||||
type: 'sensitive',
|
||||
ngShow: "kind.value == 'ssh'",
|
||||
ngDisabled: "vault_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
subCheckbox: {
|
||||
variable: 'vault_password_ask',
|
||||
text: i18n._('Ask at runtime?'),
|
||||
@ -417,17 +404,17 @@ export default
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
label: 'Save',
|
||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||
ngDisabled: true,
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)' //Disable when $pristine or $invalid, optional
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || !canAdd)' //Disable when $pristine or $invalid, optional
|
||||
}
|
||||
},
|
||||
|
||||
@ -437,24 +424,25 @@ export default
|
||||
awToolTip: '{{permissionsTooltip}}',
|
||||
dataTipWatch: 'permissionsTooltip',
|
||||
dataPlacement: 'top',
|
||||
basePath: 'credentials/:id/access_list/',
|
||||
basePath: 'api/v1/credentials/{{$stateParams.credential_id}}/access_list/',
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
type: 'collection',
|
||||
title: i18n._('Permissions'),
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
searchType: 'select',
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermission",
|
||||
ngClick: "$state.go('.add')",
|
||||
label: 'Add',
|
||||
awToolTip: i18n._('Add a permission'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: i18n._('+ ADD'),
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
fields: {
|
||||
username: {
|
||||
key: true,
|
||||
|
||||
@ -18,31 +18,31 @@ export default
|
||||
editTitle: '{{ name }}',
|
||||
showTitle: true,
|
||||
name: 'group',
|
||||
basePath: 'groups',
|
||||
// the parent node this generated state definition tree expects to attach to
|
||||
stateTree: 'inventoryManage',
|
||||
// form generator inspects the current state name to determine whether or not to set an active (.is-selected) class on a form tab
|
||||
// this setting is optional on most forms, except where the form's edit state name is not parentStateName.edit
|
||||
activeEditState: 'inventoryManage.editGroup',
|
||||
well: false,
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
tab: 'properties',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '(!group_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
tab: 'properties'
|
||||
},
|
||||
description: {
|
||||
label: 'Description',
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
tab: 'properties',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '(!group_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
tab: 'properties'
|
||||
},
|
||||
variables: {
|
||||
label: 'Variables',
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
rows: 12,
|
||||
'default': '---',
|
||||
dataTitle: 'Group Variables',
|
||||
@ -65,23 +65,23 @@ export default
|
||||
type: 'select',
|
||||
ngOptions: 'source.label for source in source_type_options track by source.value',
|
||||
ngChange: 'sourceChange(source)',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngModel: 'source',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '(!group_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
ngModel: 'source'
|
||||
},
|
||||
credential: {
|
||||
label: 'Cloud Credential',
|
||||
type: 'lookup',
|
||||
list: 'CredentialList',
|
||||
basePath: 'credentials',
|
||||
ngShow: "source && source.value !== '' && source.value !== 'custom'",
|
||||
sourceModel: 'credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCredential()',
|
||||
ngClick: 'lookupCredential()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "cloudCredentialRequired",
|
||||
init: "false"
|
||||
},
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '(!group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
source_regions: {
|
||||
label: 'Regions',
|
||||
@ -89,22 +89,20 @@ export default
|
||||
ngOptions: 'source.label for source in source_region_choices track by source.value',
|
||||
multiSelect: true,
|
||||
ngShow: "source && (source.value == 'rax' || source.value == 'ec2' || source.value == 'gce' || source.value == 'azure' || source.value == 'azure_rm')",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
dataTitle: 'Source Regions',
|
||||
dataPlacement: 'right',
|
||||
awPopOver: "<p>Click on the regions field to see a list of regions for your cloud provider. You can select multiple regions, " +
|
||||
"or choose <em>All</em> to include all regions. Tower will only be updated with Hosts associated with the selected regions." +
|
||||
"</p>",
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
instance_filters: {
|
||||
label: 'Instance Filters',
|
||||
type: 'text',
|
||||
ngShow: "source && source.value == 'ec2'",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
dataTitle: 'Instance Filters',
|
||||
dataPlacement: 'right',
|
||||
awPopOver: "<p>Provide a comma-separated list of filter expressions. " +
|
||||
@ -118,15 +116,13 @@ export default
|
||||
"<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',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
group_by: {
|
||||
label: 'Only Group By',
|
||||
type: 'select',
|
||||
ngShow: "source && source.value == 'ec2'",
|
||||
ngOptions: 'source.label for source in group_by_choices track by source.value',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
multiSelect: true,
|
||||
dataTitle: 'Only Group By',
|
||||
dataPlacement: 'right',
|
||||
@ -144,19 +140,19 @@ export default
|
||||
"<li>Tag None: <strong>tags » tag_none</strong></li>" +
|
||||
"</ul><p>If blank, all groups above are created except <em>Instance ID</em>.</p>",
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
inventory_script: {
|
||||
label : "Custom Inventory Script",
|
||||
type: 'lookup',
|
||||
basePath: 'inventory_scripts',
|
||||
list: 'InventoryScriptList',
|
||||
ngShow: "source && source.value === 'custom'",
|
||||
sourceModel: 'inventory_script',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpInventory_script()' ,
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
ngRequired: "source && source.value === 'custom'",
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
},
|
||||
custom_variables: {
|
||||
id: 'custom_variables',
|
||||
@ -164,8 +160,6 @@ export default
|
||||
ngShow: "source && source.value=='custom' ",
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
rows: 6,
|
||||
'default': '---',
|
||||
parseTypeName: 'envParseType',
|
||||
@ -187,8 +181,6 @@ export default
|
||||
ngShow: "source && source.value == 'ec2'",
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
'default': '---',
|
||||
parseTypeName: 'envParseType',
|
||||
@ -209,12 +201,9 @@ export default
|
||||
vmware_variables: {
|
||||
id: 'vmware_variables',
|
||||
label: 'Source Variables', //"{{vars_label}}" ,
|
||||
|
||||
ngShow: "source && source.value == 'vmware'",
|
||||
type: 'textarea',
|
||||
addRequired: false,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
'default': '---',
|
||||
parseTypeName: 'envParseType',
|
||||
@ -235,12 +224,9 @@ export default
|
||||
openstack_variables: {
|
||||
id: 'openstack_variables',
|
||||
label: 'Source Variables', //"{{vars_label}}" ,
|
||||
|
||||
ngShow: "source && source.value == 'openstack'",
|
||||
type: 'textarea',
|
||||
addRequired: false,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
'default': '---',
|
||||
parseTypeName: 'envParseType',
|
||||
@ -263,14 +249,13 @@ export default
|
||||
type: 'checkbox_group',
|
||||
ngShow: "source && (source.value !== '' && source.value !== null)",
|
||||
class: 'Form-checkbox--stacked',
|
||||
|
||||
fields: [{
|
||||
name: 'overwrite',
|
||||
label: 'Overwrite',
|
||||
type: 'checkbox',
|
||||
ngShow: "source.value !== '' && source.value !== null",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
awPopOver: '<p>If checked, all child groups and hosts not found on the external source will be deleted from ' +
|
||||
'the local inventory.</p><p>When not checked, local child hosts and groups not found on the external source will ' +
|
||||
'remain untouched by the inventory update process.</p>',
|
||||
@ -278,14 +263,14 @@ export default
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}, {
|
||||
name: 'overwrite_vars',
|
||||
label: 'Overwrite Variables',
|
||||
type: 'checkbox',
|
||||
ngShow: "source.value !== '' && source.value !== null",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
awPopOver: '<p>If checked, all variables for child groups and hosts will be removed and replaced by those ' +
|
||||
'found on the external source.</p><p>When not checked, a merge will be performed, combining local variables with ' +
|
||||
'those found on the external source.</p>',
|
||||
@ -293,21 +278,19 @@ export default
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}, {
|
||||
name: 'update_on_launch',
|
||||
label: 'Update on Launch',
|
||||
type: 'checkbox',
|
||||
ngShow: "source.value !== '' && source.value !== null",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: '<p>Each time a job runs using this inventory, refresh the inventory from the selected source before ' +
|
||||
'executing job tasks.</p>',
|
||||
dataTitle: 'Update on Launch',
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options',
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}]
|
||||
},
|
||||
update_cache_timeout: {
|
||||
@ -319,8 +302,6 @@ export default
|
||||
ngShow: "source && source.value !== '' && update_on_launch",
|
||||
spinner: true,
|
||||
"default": 0,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: '<p>Time in seconds to consider an inventory sync to be current. During job runs and callbacks the task system will ' +
|
||||
'evaluate the timestamp of the latest sync. If it is older than Cache Timeout, it is not considered current, ' +
|
||||
'and a new inventory sync will be performed.</p>',
|
||||
@ -333,16 +314,16 @@ export default
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(group_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -26,8 +26,7 @@ export default
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
ngOptions: 'group.name for group in inventory_groups track by group.value',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
|
||||
"<blockquote>myserver.domain.com<br/>" +
|
||||
"127.0.0.1<br />" +
|
||||
|
||||
@ -17,6 +17,7 @@ export default
|
||||
addTitle: 'Create Host',
|
||||
editTitle: '{{ host.name }}',
|
||||
name: 'host',
|
||||
basePath: 'hosts',
|
||||
well: false,
|
||||
formLabelSize: 'col-lg-3',
|
||||
formFieldSize: 'col-lg-9',
|
||||
@ -26,7 +27,6 @@ export default
|
||||
class: 'Form-header-field',
|
||||
ngClick: 'toggleHostEnabled(host)',
|
||||
type: 'toggle',
|
||||
editRequired: false,
|
||||
awToolTip: "<p>Indicates if a host is available and should be included in running jobs.</p><p>For hosts that " +
|
||||
"are part of an external inventory, this flag cannot be changed. It will be set by the inventory sync process.</p>",
|
||||
dataTitle: 'Host Enabled',
|
||||
@ -36,8 +36,7 @@ export default
|
||||
name: {
|
||||
label: 'Host Name',
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
|
||||
"<blockquote>myserver.domain.com<br/>" +
|
||||
"127.0.0.1<br />" +
|
||||
@ -47,22 +46,18 @@ export default
|
||||
dataTitle: 'Host Name',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(host.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(host.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
description: {
|
||||
label: 'Description',
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(host.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(host.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
type: 'text'
|
||||
},
|
||||
variables: {
|
||||
label: 'Variables',
|
||||
type: 'textarea',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
"class": "modal-input-xlarge Form-textArea Form-formGroup--fullWidth",
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
"default": "---",
|
||||
awPopOver: "<p>Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
|
||||
"JSON:<br />\n" +
|
||||
@ -85,19 +80,16 @@ export default
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(host.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(host.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(host.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(host.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(host.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(host.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {}
|
||||
|
||||
});
|
||||
|
||||
@ -4,91 +4,136 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name forms.function:Inventories
|
||||
* @description This form is for adding/editing an inventory
|
||||
*/
|
||||
*/
|
||||
|
||||
export default
|
||||
angular.module('InventoryFormDefinition', ['ScanJobsListDefinition'])
|
||||
.factory('InventoryFormObject', ['i18n', function(i18n) {
|
||||
angular.module('InventoryFormDefinition', ['ScanJobsListDefinition'])
|
||||
.factory('InventoryFormObject', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
addTitle: i18n._('New Inventory'),
|
||||
editTitle: '{{ inventory_name }}',
|
||||
name: 'inventory',
|
||||
tabs: true,
|
||||
addTitle: 'New Inventory',
|
||||
editTitle: '{{ inventory_name }}',
|
||||
name: 'inventory',
|
||||
basePath: 'inventory',
|
||||
// the top-most node of this generated state tree
|
||||
stateTree: 'inventories',
|
||||
tabs: true,
|
||||
|
||||
fields: {
|
||||
inventory_name: {
|
||||
realName: 'name',
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: false,
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
fields: {
|
||||
inventory_name: {
|
||||
realName: 'name',
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
required: true,
|
||||
capitalize: false,
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
inventory_description: {
|
||||
realName: 'description',
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
organization: {
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
basePath: 'organizations',
|
||||
list: 'OrganizationList',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "organizationrequired",
|
||||
init: "true"
|
||||
},
|
||||
inventory_description: {
|
||||
realName: 'description',
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
variables: {
|
||||
label: i18n._('Variables'),
|
||||
type: 'textarea',
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
rows: 6,
|
||||
"default": "---",
|
||||
awPopOver: "<p>Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
|
||||
"JSON:<br />\n" +
|
||||
"<blockquote>{<br /> \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
||||
"YAML:<br />\n" +
|
||||
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
|
||||
'<p>View JSON examples at <a href="http://www.json.org" target="_blank">www.json.org</a></p>' +
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||
dataTitle: 'Inventory Variables',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)' // TODO: get working
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngHide: '(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
related: {
|
||||
permissions: {
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
dataPlacement: 'top',
|
||||
basePath: 'api/v1/inventories/{{$stateParams.inventory_id}}/access_list/',
|
||||
type: 'collection',
|
||||
title: 'Permissions',
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
organization: {
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpOrganization()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "organizationrequired",
|
||||
init: "true"
|
||||
actions: {
|
||||
add: {
|
||||
label: i18n._('Add'),
|
||||
ngClick: "$state.go('.add')",
|
||||
awToolTip: 'Add a permission',
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: '+ ADD',
|
||||
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
|
||||
}
|
||||
},
|
||||
fields: {
|
||||
username: {
|
||||
label: i18n._('User'),
|
||||
linkBase: 'users',
|
||||
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
|
||||
},
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
variables: {
|
||||
label: i18n._('Variables'),
|
||||
type: 'textarea',
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
"default": "---",
|
||||
awPopOver: i18n._("<p>Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
|
||||
"JSON:<br />\n" +
|
||||
"<blockquote>{<br /> \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
||||
"YAML:<br />\n" +
|
||||
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
|
||||
'<p>View JSON examples at <a href="http://www.json.org" target="_blank">www.json.org</a></p>' +
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>'),
|
||||
dataTitle: i18n._('Inventory Variables'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
|
||||
role: {
|
||||
label: i18n._('Role'),
|
||||
type: 'role',
|
||||
noSort: true,
|
||||
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
||||
},
|
||||
team_roles: {
|
||||
label: i18n._('Team Roles'),
|
||||
type: 'team_roles',
|
||||
noSort: true,
|
||||
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngHide: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
relatedSets: function(urls) {
|
||||
return {
|
||||
permissions: {
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
dataPlacement: 'top',
|
||||
@ -102,7 +147,7 @@ export default
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermission",
|
||||
label: 'Add',
|
||||
label: i18n._('Add'),
|
||||
awToolTip: i18n._('Add a permission'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: i18n._('+ ADD'),
|
||||
@ -133,28 +178,22 @@ export default
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
relatedSets: function(urls) {
|
||||
return {
|
||||
permissions: {
|
||||
iterator: 'permission',
|
||||
url: urls.access_list
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};}])
|
||||
.factory('InventoryForm', ['InventoryFormObject', 'ScanJobsList',
|
||||
|
||||
.factory('InventoryForm', ['InventoryFormObject', 'ScanJobsList',
|
||||
function(InventoryFormObject, ScanJobsList) {
|
||||
return function() {
|
||||
var itm;
|
||||
for (itm in InventoryFormObject.related) {
|
||||
if (InventoryFormObject.related[itm].include === "ScanJobsList") {
|
||||
InventoryFormObject.related[itm] = ScanJobsList;
|
||||
InventoryFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||
InventoryFormObject.related[itm] = ScanJobsList;
|
||||
InventoryFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||
}
|
||||
}
|
||||
return InventoryFormObject;
|
||||
};
|
||||
}]);
|
||||
}
|
||||
]);
|
||||
|
||||
@ -19,26 +19,27 @@ export default
|
||||
|
||||
addTitle: i18n._('New Job Template'),
|
||||
editTitle: '{{ name }}',
|
||||
name: 'job_templates',
|
||||
base: 'job_templates',
|
||||
name: 'job_template',
|
||||
basePath: 'job_templates',
|
||||
// the top-most node of generated state tree
|
||||
stateTree: 'jobTemplates',
|
||||
tabs: true,
|
||||
// (optional) array of supporting templates to ng-include inside generated html
|
||||
include: ['/static/partials/survey-maker-modal.html'],
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
column: 1,
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
column: 1
|
||||
},
|
||||
description: {
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
column: 1,
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
job_type: {
|
||||
label: i18n._('Job Type'),
|
||||
@ -46,8 +47,7 @@ export default
|
||||
ngOptions: 'type.label for type in job_type_options track by type.value',
|
||||
ngChange: 'jobTypeChange()',
|
||||
"default": 0,
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
column: 1,
|
||||
awPopOver: i18n._("<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " +
|
||||
" on the selected hosts.</p> <p>Setting the type to <em>check</em> will not execute the playbook. Instead, <code>ansible</code> will check playbook " +
|
||||
@ -61,14 +61,15 @@ export default
|
||||
ngShow: "!job_type.value || job_type.value !== 'scan'",
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
inventory: {
|
||||
label: i18n._('Inventory'),
|
||||
type: 'lookup',
|
||||
basePath: 'inventory',
|
||||
list: 'InventoryList',
|
||||
sourceModel: 'inventory',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpInventory()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: '!ask_inventory_on_launch',
|
||||
alwaysShowAsterisk: true
|
||||
@ -84,7 +85,7 @@ export default
|
||||
ngShow: "!job_type.value || job_type.value !== 'scan'",
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
project: {
|
||||
label: i18n._('Project'),
|
||||
@ -94,9 +95,10 @@ export default
|
||||
'class': "{{!(job_type.value === 'scan' && project_name !== 'Default') ? 'hidden' : ''}}",
|
||||
},
|
||||
type: 'lookup',
|
||||
list: 'ProjectList',
|
||||
basePath: 'projects',
|
||||
sourceModel: 'project',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpProject()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "projectrequired",
|
||||
init: "true"
|
||||
@ -106,7 +108,7 @@ export default
|
||||
dataTitle: i18n._('Project'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
playbook: {
|
||||
label: i18n._('Playbook'),
|
||||
@ -128,9 +130,13 @@ export default
|
||||
credential: {
|
||||
label: i18n._('Machine Credential'),
|
||||
type: 'lookup',
|
||||
list: 'CredentialList',
|
||||
basePath: 'credentials',
|
||||
search: {
|
||||
kind: 'ssh'
|
||||
},
|
||||
sourceModel: 'credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCredential()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: '!ask_credential_on_launch',
|
||||
alwaysShowAsterisk: true
|
||||
@ -146,38 +152,42 @@ export default
|
||||
variable: 'ask_credential_on_launch',
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
cloud_credential: {
|
||||
label: i18n._('Cloud Credential'),
|
||||
type: 'lookup',
|
||||
list: 'CredentialList',
|
||||
basePath: 'credentials',
|
||||
search: {
|
||||
cloud: 'true'
|
||||
},
|
||||
sourceModel: 'cloud_credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCloudcredential()',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
column: 1,
|
||||
awPopOver: i18n._("<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
|
||||
"running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.</p>"),
|
||||
dataTitle: i18n._('Cloud Credential'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
network_credential: {
|
||||
label: i18n._('Network Credential'),
|
||||
type: 'lookup',
|
||||
list: 'CredentialList',
|
||||
basePath: 'credentials',
|
||||
search: {
|
||||
kind: 'net'
|
||||
},
|
||||
sourceModel: 'network_credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpNetworkcredential()',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
column: 1,
|
||||
awPopOver: i18n._("<p>Network credentials are used by Ansible networking modules to connect to and manage networking devices.</p>"),
|
||||
dataTitle: i18n._('Network Credential'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
forks: {
|
||||
label: i18n._('Forks'),
|
||||
@ -187,8 +197,6 @@ export default
|
||||
min: 0,
|
||||
spinner: true,
|
||||
"default": '0',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
'class': "input-small",
|
||||
column: 1,
|
||||
awPopOver: i18n._('<p>The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
|
||||
@ -197,13 +205,11 @@ export default
|
||||
dataTitle: i18n._('Forks'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)' // TODO: get working
|
||||
},
|
||||
limit: {
|
||||
label: i18n._('Limit'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
column: 1,
|
||||
awPopOver: i18n._("<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
|
||||
"Multiple patterns can be separated by ; : or ,</p><p>For more information and examples see " +
|
||||
@ -215,28 +221,25 @@ export default
|
||||
variable: 'ask_limit_on_launch',
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
verbosity: {
|
||||
label: i18n._('Verbosity'),
|
||||
type: 'select',
|
||||
ngOptions: 'v.label for v in verbosity_options track by v.value',
|
||||
"default": 1,
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
column: 1,
|
||||
awPopOver: i18n._("<p>Control the level of output ansible will produce as the playbook executes.</p>"),
|
||||
dataTitle: i18n._('Verbosity'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
job_tags: {
|
||||
label: i18n._('Job Tags'),
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
'elementClass': 'Form-textInput',
|
||||
column: 2,
|
||||
awPopOver: i18n._("<p>Provide a comma separated list of tags.</p>\n" +
|
||||
@ -249,14 +252,12 @@ export default
|
||||
variable: 'ask_tags_on_launch',
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
skip_tags: {
|
||||
label: i18n._('Skip Tags'),
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
'elementClass': 'Form-textInput',
|
||||
column: 2,
|
||||
awPopOver: i18n._("<p>Provide a comma separated list of tags.</p>\n" +
|
||||
@ -269,7 +270,7 @@ export default
|
||||
variable: 'ask_skip_tags_on_launch',
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
checkbox_group: {
|
||||
label: i18n._('Options'),
|
||||
@ -278,21 +279,17 @@ export default
|
||||
name: 'become_enabled',
|
||||
label: i18n._('Enable Privilege Escalation'),
|
||||
type: 'checkbox',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
column: 2,
|
||||
awPopOver: i18n._("<p>If enabled, run this playbook as an administrator. This is the equivalent of passing the <code>--become</code> option to the <code>ansible-playbook</code> command. </p>"),
|
||||
dataPlacement: 'right',
|
||||
dataTitle: i18n._('Become Privilege Escalation'),
|
||||
dataContainer: "body",
|
||||
labelClass: 'stack-inline',
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}, {
|
||||
name: 'allow_callbacks',
|
||||
label: i18n._('Allow Provisioning Callbacks'),
|
||||
type: 'checkbox',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
ngChange: "toggleCallback('host_config_key')",
|
||||
column: 2,
|
||||
awPopOver: i18n._("<p>Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update " +
|
||||
@ -301,14 +298,12 @@ export default
|
||||
dataTitle: i18n._('Allow Provisioning Callbacks'),
|
||||
dataContainer: "body",
|
||||
labelClass: 'stack-inline',
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}]
|
||||
},
|
||||
callback_url: {
|
||||
label: i18n._('Provisioning Callback URL'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
readonly: true,
|
||||
ngShow: "allow_callbacks && allow_callbacks !== 'false'",
|
||||
column: 2,
|
||||
@ -317,7 +312,7 @@ export default
|
||||
dataPlacement: 'top',
|
||||
dataTitle: i18n._('Provisioning Callback URL'),
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
host_config_key: {
|
||||
label: i18n._('Host Config Key'),
|
||||
@ -331,7 +326,7 @@ export default
|
||||
dataPlacement: 'right',
|
||||
dataTitle: i18n._("Host Config Key"),
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
labels: {
|
||||
label: i18n._('Labels'),
|
||||
@ -339,21 +334,17 @@ export default
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
ngOptions: 'label.label for label in labelOptions track by label.value',
|
||||
multiSelect: true,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
dataTitle: i18n._('Labels'),
|
||||
dataPlacement: 'right',
|
||||
awPopOver: i18n._("<p>Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.</p>"),
|
||||
dataContainer: 'body',
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
variables: {
|
||||
label: i18n._('Extra Variables'),
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
rows: 6,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
"default": "---",
|
||||
column: 2,
|
||||
awPopOver: i18n._("<p>Pass extra command line variables to the playbook. This is the <code>-e</code> or <code>--extra-vars</code> command line parameter " +
|
||||
@ -369,14 +360,14 @@ export default
|
||||
variable: 'ask_variables_on_launch',
|
||||
text: i18n._('Prompt on launch')
|
||||
},
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)' // TODO: get working
|
||||
}
|
||||
},
|
||||
|
||||
buttons: { //for now always generates <button> tags
|
||||
add_survey: {
|
||||
ngClick: 'addSurvey()',
|
||||
ngShow: 'job_type.value !== "scan" && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngShow: 'job_type.value !== "scan" && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
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.',
|
||||
dataPlacement: 'top'
|
||||
@ -384,25 +375,25 @@ export default
|
||||
edit_survey: {
|
||||
ngClick: 'editSurvey()',
|
||||
awFeature: 'surveys',
|
||||
ngShow: 'job_type.value !== "scan" && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: 'job_type.value !== "scan" && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
view_survey: {
|
||||
ngClick: 'editSurvey()',
|
||||
awFeature: 'surveys',
|
||||
ngShow: 'job_type.value !== "scan" && survey_exists && !(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: 'job_type.value !== "scan" && survey_exists && !(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||
ngDisabled: "job_templates_form.$invalid",//true //Disable when $pristine or $invalid, optional and when can_edit = false, for permission reasons
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
@ -413,21 +404,23 @@ export default
|
||||
permissions: {
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
dataPlacement: 'top',
|
||||
basePath: 'job_templates/:id/access_list/',
|
||||
basePath: 'api/v1/job_templates/{{$stateParams.job_template_id}}/access_list/',
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
type: 'collection',
|
||||
title: i18n._('Permissions'),
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
searchType: 'select',
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermission",
|
||||
ngClick: "$state.go('.add')",
|
||||
label: 'Add',
|
||||
awToolTip: 'Add a permission',
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: '+ ADD',
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
@ -443,14 +436,12 @@ export default
|
||||
type: 'role',
|
||||
noSort: true,
|
||||
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
||||
searchable: false
|
||||
},
|
||||
team_roles: {
|
||||
label: 'Team Roles',
|
||||
type: 'team_roles',
|
||||
noSort: true,
|
||||
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
||||
searchable: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name forms.function:JobVarsPrompt
|
||||
@ -27,8 +27,8 @@ export default
|
||||
label: null,
|
||||
type: 'textarea',
|
||||
rows: 6,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
|
||||
|
||||
"default": "---"
|
||||
}
|
||||
},
|
||||
|
||||
@ -17,6 +17,7 @@ export default
|
||||
addTitle: 'Create Job',
|
||||
editTitle: '{{ id }} - {{ name }}',
|
||||
name: 'jobs',
|
||||
stateTree: 'jobs',
|
||||
well: true,
|
||||
base: 'jobs',
|
||||
tabs: true,
|
||||
|
||||
@ -18,46 +18,47 @@ export default
|
||||
addTitle: i18n._('New Organization'), //Title in add mode
|
||||
editTitle: '{{ name }}', //Title in edit mode
|
||||
name: 'organization', //entity or model name in singular form
|
||||
stateTree: 'organizations',
|
||||
tabs: true,
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: false,
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
capitalize: false
|
||||
},
|
||||
description: {
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: { //for now always generates <button> tags
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(organization_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||
ngDisabled: true,
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
permissions: {
|
||||
basePath: 'organizations/:id/access_list/',
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
basePath: 'api/v1/organizations/{{$stateParams.organization_id}}/access_list/',
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
dataPlacement: 'top',
|
||||
type: 'collection',
|
||||
title: i18n._('Permissions'),
|
||||
@ -68,11 +69,11 @@ export default
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermission",
|
||||
label: 'Add',
|
||||
label: i18n._('Add'),
|
||||
awToolTip: i18n._('Add a permission'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: i18n._('+ ADD'),
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -18,43 +18,39 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
addTitle: i18n._('New Project'),
|
||||
editTitle: '{{ name }}',
|
||||
name: 'project',
|
||||
basePath: 'projects',
|
||||
// the top-most node of generated state tree
|
||||
stateTree: 'projects',
|
||||
forceListeners: true,
|
||||
tabs: true,
|
||||
subFormTitles: {
|
||||
sourceSubForm: i18n._('Source Details'),
|
||||
},
|
||||
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: false,
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
capitalize: false
|
||||
},
|
||||
description: {
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
organization: {
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
list: 'OrganizationList',
|
||||
sourceModel: 'organization',
|
||||
basePath: 'organizations',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpOrganization()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "organizationrequired",
|
||||
init: "true"
|
||||
},
|
||||
dataTitle: i18n._('Organization'),
|
||||
required: true,
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
scm_type: {
|
||||
label: i18n._('SCM Type'),
|
||||
@ -62,10 +58,9 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
class: 'Form-dropDown--scmType',
|
||||
ngOptions: 'type.label for type in scm_type_options track by type.value',
|
||||
ngChange: 'scmChange()',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
required: true,
|
||||
hasSubForm: true,
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
missing_path_alert: {
|
||||
type: 'alertblock',
|
||||
@ -88,7 +83,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
dataTitle: i18n._('Project Base Path'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
local_path: {
|
||||
label: i18n._('Playbook Directory'),
|
||||
@ -106,7 +101,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
dataTitle: i18n._('Project Path'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
scm_url: {
|
||||
label: 'SCM URL',
|
||||
@ -123,28 +118,29 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
dataTitle: 'SCM URL',
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
scm_branch: {
|
||||
labelBind: "scmBranchLabel",
|
||||
type: 'text',
|
||||
ngShow: "scm_type && scm_type.value !== 'manual'",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
subForm: 'sourceSubForm',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
credential: {
|
||||
label: i18n._('SCM Credential'),
|
||||
type: 'lookup',
|
||||
basePath: 'credentials',
|
||||
list: 'CredentialList',
|
||||
// apply a default search filter to show only scm credentials
|
||||
search: {
|
||||
kind: 'scm'
|
||||
},
|
||||
ngShow: "scm_type && scm_type.value !== 'manual'",
|
||||
sourceModel: 'credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCredential()',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
subForm: 'sourceSubForm',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
subForm: 'sourceSubForm'
|
||||
},
|
||||
checkbox_group: {
|
||||
label: i18n._('SCM Update Options'),
|
||||
@ -155,39 +151,33 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
name: 'scm_clean',
|
||||
label: i18n._('Clean'),
|
||||
type: 'checkbox',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: i18n._('<p>Remove any local modifications prior to performing an update.</p>'),
|
||||
dataTitle: i18n._('SCM Clean'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options stack-inline',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}, {
|
||||
name: 'scm_delete_on_update',
|
||||
label: i18n._('Delete on Update'),
|
||||
type: 'checkbox',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: i18n._('<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
|
||||
'repository this may significantly increase the amount of time required to complete an update.</p>'),
|
||||
dataTitle: i18n._('SCM Delete'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options stack-inline',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}, {
|
||||
name: 'scm_update_on_launch',
|
||||
label: i18n._('Update on Launch'),
|
||||
type: 'checkbox',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: i18n._('<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>'),
|
||||
dataTitle: i18n._('SCM Update'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
labelClass: 'checkbox-options stack-inline',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}]
|
||||
},
|
||||
scm_update_cache_timeout: {
|
||||
@ -199,61 +189,61 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
ngShow: "scm_update_on_launch && projectSelected && scm_type.value !== 'manual'",
|
||||
spinner: true,
|
||||
"default": '0',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: i18n._('<p>Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
|
||||
'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
|
||||
'and a new project update will be performed.</p>'),
|
||||
dataTitle: i18n._('Cache Timeout'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
permissions: {
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
djangoModel: 'access_list',
|
||||
dataPlacement: 'top',
|
||||
basePath: 'projects/:id/access_list/',
|
||||
basePath: 'api/v1/projects/{{$stateParams.project_id}}/access_list/',
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
type: 'collection',
|
||||
title: i18n._('Permissions'),
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
searchType: 'select',
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermission",
|
||||
ngClick: "$state.go('.add')",
|
||||
label: 'Add',
|
||||
awToolTip: i18n._('Add a permission'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: i18n._('+ ADD'),
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(project_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
fields: {
|
||||
username: {
|
||||
key: true,
|
||||
label: 'User',
|
||||
linkBase: 'users',
|
||||
uiSref: 'users({user_id: field.id})',
|
||||
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
|
||||
},
|
||||
role: {
|
||||
@ -261,14 +251,12 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
||||
type: 'role',
|
||||
noSort: true,
|
||||
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
||||
noSearch: true
|
||||
},
|
||||
team_roles: {
|
||||
label: 'Team Roles',
|
||||
type: 'team_roles',
|
||||
noSort: true,
|
||||
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
||||
noSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -18,75 +18,73 @@ export default
|
||||
addTitle: i18n._('New Team'), //Legend in add mode
|
||||
editTitle: '{{ name }}', //Legend in edit mode
|
||||
name: 'team',
|
||||
// the top-most node of generated state tree
|
||||
stateTree: 'teams',
|
||||
tabs: true,
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: false,
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
capitalize: false
|
||||
},
|
||||
description: {
|
||||
label: i18n._('Description'),
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
organization: {
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
list: 'OrganizationsList',
|
||||
sourceModel: 'organization',
|
||||
basePath: 'organizations',
|
||||
sourceField: 'name',
|
||||
addRequired: true,
|
||||
editRequire: false,
|
||||
ngClick: 'lookUpOrganization()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "orgrequired",
|
||||
init: true
|
||||
},
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(team_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
access_list: {
|
||||
permissions: {
|
||||
dataPlacement: 'top',
|
||||
awToolTip: i18n._('Please save before adding users'),
|
||||
basePath: 'teams/:id/access_list/',
|
||||
basePath: 'api/v1/teams/{{$stateParams.team_id}}/access_list/',
|
||||
search: {
|
||||
order_by: 'username'
|
||||
},
|
||||
type: 'collection',
|
||||
title: i18n._('Users'),
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
searchType: 'select',
|
||||
actions: {
|
||||
add: {
|
||||
ngClick: "addPermissionWithoutTeamTab",
|
||||
// @issue https://github.com/ansible/ansible-tower/issues/3487
|
||||
//ngClick: "addPermissionWithoutTeamTab",
|
||||
label: 'Add',
|
||||
awToolTip: i18n._('Add user to team'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: i18n._('+ ADD'),
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(team_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
@ -110,7 +108,12 @@ export default
|
||||
hideSearchAndActions: true,
|
||||
dataPlacement: 'top',
|
||||
awToolTip: i18n._('Please save before assigning permissions'),
|
||||
basePath: 'teams/:id/roles/',
|
||||
basePath: 'api/v1/teams/{{$stateParams.team_id}}/roles/',
|
||||
search: {
|
||||
page_size: '10',
|
||||
// @todo ask about name field / serializer on this endpoint
|
||||
order_by: 'id'
|
||||
},
|
||||
type: 'collection',
|
||||
title: i18n._('Granted Permissions'),
|
||||
iterator: 'role',
|
||||
@ -147,7 +150,7 @@ export default
|
||||
ngShow: 'permission.summary_fields.user_capabilities.unattach'
|
||||
}
|
||||
},
|
||||
hideOnSuperuser: true
|
||||
//hideOnSuperuser: true // defunct with RBAC
|
||||
}
|
||||
},
|
||||
};}]); //InventoryForm
|
||||
|
||||
@ -18,6 +18,8 @@ export default
|
||||
addTitle: i18n._('New User'),
|
||||
editTitle: '{{ username }}',
|
||||
name: 'user',
|
||||
// the top-most node of generated state tree
|
||||
stateTree: 'users',
|
||||
forceListeners: true,
|
||||
tabs: true,
|
||||
|
||||
@ -25,26 +27,23 @@ export default
|
||||
first_name: {
|
||||
label: i18n._('First Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: true,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
capitalize: true
|
||||
},
|
||||
last_name: {
|
||||
label: i18n._('Last Name'),
|
||||
type: 'text',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
capitalize: true,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
capitalize: true
|
||||
},
|
||||
email: {
|
||||
label: i18n._('Email'),
|
||||
type: 'email',
|
||||
addRequired: true,
|
||||
editRequired: true,
|
||||
autocomplete: false,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)',
|
||||
required: true,
|
||||
autocomplete: false
|
||||
},
|
||||
username: {
|
||||
label: i18n._('Username'),
|
||||
@ -54,46 +53,42 @@ export default
|
||||
init: true
|
||||
},
|
||||
autocomplete: false,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
organization: {
|
||||
label: i18n._('Organization'),
|
||||
type: 'lookup',
|
||||
list: 'OrganizationList',
|
||||
basePath: 'organizations',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name',
|
||||
addRequired: true,
|
||||
editRequired: false,
|
||||
required: true,
|
||||
excludeMode: 'edit',
|
||||
ngClick: 'lookUpOrganization()',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "orgrequired",
|
||||
init: true
|
||||
},
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
password: {
|
||||
label: i18n._('Password'),
|
||||
type: 'sensitive',
|
||||
hasShowInputButton: true,
|
||||
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
|
||||
addRequired: true,
|
||||
editRequired: false,
|
||||
ngRequired: "$state.match('add')",
|
||||
labelNGClass: "{'prepend-asterisk' : $state.matches('add')}",
|
||||
ngChange: "clearPWConfirm('password_confirm')",
|
||||
autocomplete: false,
|
||||
chkPass: true,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
password_confirm: {
|
||||
label: i18n._('Confirm Password'),
|
||||
type: 'sensitive',
|
||||
hasShowInputButton: true,
|
||||
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
|
||||
addRequired: true,
|
||||
editRequired: false,
|
||||
ngRequired: "$state.match('add')",
|
||||
labelNGClass: "{'prepend-asterisk' : $state.matches('add')}",
|
||||
awPassMatch: true,
|
||||
associated: 'password',
|
||||
autocomplete: false,
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
user_type: {
|
||||
label: i18n._('User Type'),
|
||||
@ -102,30 +97,33 @@ export default
|
||||
disableChooseOption: true,
|
||||
ngModel: 'user_type',
|
||||
ngShow: 'current_user["is_superuser"]',
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
},
|
||||
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'formCancel()',
|
||||
ngShow: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '!(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'formSave()',
|
||||
ngDisabled: true,
|
||||
ngShow: '(user_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngShow: '(user_obj.summary_fields.user_capabilities.edit || !canAdd)'
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
organizations: {
|
||||
basePath: 'users/:id/organizations',
|
||||
awToolTip: i18n._('Please save before assigning to organizations'),
|
||||
basePath: 'api/v1/users/{{$stateParams.user_id}}/organizations',
|
||||
search: {
|
||||
page_size: '10'
|
||||
},
|
||||
dataPlacement: 'top',
|
||||
type: 'collection',
|
||||
title: i18n._('Organizations'),
|
||||
@ -144,11 +142,14 @@ export default
|
||||
label: 'Description'
|
||||
}
|
||||
},
|
||||
hideOnSuperuser: true
|
||||
//hideOnSuperuser: true // RBAC defunct
|
||||
},
|
||||
teams: {
|
||||
basePath: 'users/:id/teams',
|
||||
awToolTip: i18n._('Please save before assigning to teams'),
|
||||
basePath: 'api/v1/users/{{$stateParams.user_id}}/teams',
|
||||
search: {
|
||||
page_size: '10'
|
||||
},
|
||||
dataPlacement: 'top',
|
||||
type: 'collection',
|
||||
title: i18n._('Teams'),
|
||||
@ -166,9 +167,15 @@ export default
|
||||
label: 'Description'
|
||||
}
|
||||
},
|
||||
hideOnSuperuser: true
|
||||
//hideOnSuperuser: true // RBAC defunct
|
||||
},
|
||||
roles: {
|
||||
permissions: {
|
||||
basePath: 'api/v1/users/{{$stateParams.user_id}}/roles/',
|
||||
search: {
|
||||
page_size: '10',
|
||||
// @todo ask about name field / serializer on this endpoint
|
||||
order_by: 'id'
|
||||
},
|
||||
awToolTip: i18n._('Please save before assigning to organizations'),
|
||||
dataPlacement: 'top',
|
||||
hideSearchAndActions: true,
|
||||
@ -196,6 +203,12 @@ export default
|
||||
noSort: true
|
||||
},
|
||||
},
|
||||
// @issue https://github.com/ansible/ansible-tower/issues/3487
|
||||
// actions: {
|
||||
// add: {
|
||||
|
||||
// }
|
||||
// }
|
||||
fieldActions: {
|
||||
"delete": {
|
||||
label: i18n._('Remove'),
|
||||
@ -205,7 +218,7 @@ export default
|
||||
ngShow: 'permission.summary_fields.user_capabilities.unattach'
|
||||
}
|
||||
},
|
||||
hideOnSuperuser: true
|
||||
//hideOnSuperuser: true // RBAC defunct
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,6 @@ import JobSubmission from "./helpers/JobSubmission";
|
||||
import JobTemplates from "./helpers/JobTemplates";
|
||||
import Jobs from "./helpers/Jobs";
|
||||
import LoadConfig from "./helpers/LoadConfig";
|
||||
import PaginationHelpers from "./helpers/PaginationHelpers";
|
||||
import Parse from "./helpers/Parse";
|
||||
import ProjectPath from "./helpers/ProjectPath";
|
||||
import Projects from "./helpers/Projects";
|
||||
@ -28,10 +27,6 @@ import Variables from "./helpers/Variables";
|
||||
import ApiDefaults from "./helpers/api-defaults";
|
||||
import inventory from "./helpers/inventory";
|
||||
import MD5 from "./helpers/md5";
|
||||
import RefreshRelated from "./helpers/refresh-related";
|
||||
import Refresh from "./helpers/refresh";
|
||||
import RelatedSearch from "./helpers/related-search";
|
||||
import Search from "./helpers/search";
|
||||
import Teams from "./helpers/teams";
|
||||
import AdhocHelper from "./helpers/Adhoc";
|
||||
import ApiModelHelper from "./helpers/ApiModel";
|
||||
@ -48,7 +43,6 @@ export
|
||||
JobTemplates,
|
||||
Jobs,
|
||||
LoadConfig,
|
||||
PaginationHelpers,
|
||||
Parse,
|
||||
ProjectPath,
|
||||
Projects,
|
||||
@ -59,10 +53,6 @@ export
|
||||
ApiDefaults,
|
||||
inventory,
|
||||
MD5,
|
||||
RefreshRelated,
|
||||
Refresh,
|
||||
RelatedSearch,
|
||||
Search,
|
||||
Teams,
|
||||
AdhocHelper,
|
||||
ApiModelHelper,
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
export default
|
||||
angular.module('AdhocHelper', ['RestServices', 'Utilities',
|
||||
'CredentialFormDefinition', 'CredentialsListDefinition', 'LookUpHelper',
|
||||
'CredentialFormDefinition', 'CredentialsListDefinition',
|
||||
'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog',
|
||||
'FormGenerator', 'JobVarsPromptFormDefinition'])
|
||||
|
||||
|
||||
@ -223,8 +223,8 @@ angular.module('CredentialsHelper', ['Utilities'])
|
||||
}
|
||||
])
|
||||
|
||||
.factory('FormSave', ['$rootScope', 'Refresh', '$location', 'Alert', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait', '$state',
|
||||
function ($rootScope, Refresh, $location, Alert, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait, $state) {
|
||||
.factory('FormSave', ['$rootScope', '$location', 'Alert', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait', '$state',
|
||||
function ($rootScope, $location, Alert, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait, $state) {
|
||||
return function (params) {
|
||||
var scope = params.scope,
|
||||
mode = params.mode,
|
||||
@ -279,12 +279,13 @@ angular.module('CredentialsHelper', ['Utilities'])
|
||||
.success(function (data) {
|
||||
scope.addedItem = data.id;
|
||||
|
||||
Refresh({
|
||||
scope: scope,
|
||||
set: 'credentials',
|
||||
iterator: 'credential',
|
||||
url: url
|
||||
});
|
||||
// @issue: OLD SEARCH
|
||||
// Refresh({
|
||||
// scope: scope,
|
||||
// set: 'credentials',
|
||||
// iterator: 'credential',
|
||||
// url: url
|
||||
// });
|
||||
|
||||
Wait('stop');
|
||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
|
||||
@ -15,9 +15,8 @@
|
||||
import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'GroupListDefinition', 'SearchHelper',
|
||||
'PaginationHelpers', listGenerator.name, 'GroupsHelper', 'InventoryHelper', 'SelectionHelper',
|
||||
'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition',
|
||||
angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'GroupListDefinition', listGenerator.name, 'GroupsHelper', 'InventoryHelper', 'SelectionHelper',
|
||||
'JobSubmissionHelper', 'PromptDialog', 'CredentialsListDefinition',
|
||||
'InventoryStatusDefinition', 'VariablesHelper', 'SchedulesListDefinition', 'StandardOutHelper',
|
||||
'SchedulesHelper'
|
||||
])
|
||||
@ -325,10 +324,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
||||
*/
|
||||
.factory('GroupsEdit', ['$filter', '$rootScope', '$location', '$log', '$stateParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
|
||||
'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find',
|
||||
'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find',
|
||||
'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SetSchedulesInnerDialogSize', 'CreateSelect2',
|
||||
function ($filter, $rootScope, $location, $log, $stateParams, $compile, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
|
||||
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, Empty, Wait,
|
||||
GetChoices, UpdateGroup, SourceChange, Find, ParseVariableString, ToJSON, GroupsScheduleListInit,
|
||||
SetSchedulesInnerDialogSize, CreateSelect2) {
|
||||
return function (params) {
|
||||
@ -842,9 +841,11 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
||||
// Clean up
|
||||
Wait('stop');
|
||||
|
||||
if (modal_scope.searchCleanUp) {
|
||||
modal_scope.searchCleanup();
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (modal_scope.searchCleanUp) {
|
||||
// modal_scope.searchCleanup();
|
||||
// }
|
||||
|
||||
try {
|
||||
$('#group-modal-dialog').dialog('close');
|
||||
}
|
||||
@ -947,15 +948,19 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
||||
catch(e) {
|
||||
//ignore
|
||||
}
|
||||
if (modal_scope.searchCleanup) {
|
||||
modal_scope.searchCleanup();
|
||||
}
|
||||
if (parent_scope.restoreSearch) {
|
||||
parent_scope.restoreSearch();
|
||||
}
|
||||
else {
|
||||
Wait('stop');
|
||||
}
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// if (modal_scope.searchCleanup) {
|
||||
// modal_scope.searchCleanup();
|
||||
// }
|
||||
// if (parent_scope.restoreSearch) {
|
||||
// parent_scope.restoreSearch();
|
||||
// }
|
||||
// else {
|
||||
// Wait('stop');
|
||||
// }
|
||||
|
||||
Wait('stop');
|
||||
};
|
||||
|
||||
// Save
|
||||
|
||||
@ -18,8 +18,8 @@ import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'HostListDefinition',
|
||||
'SearchHelper', 'PaginationHelpers', listGenerator.name, 'HostsHelper',
|
||||
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',
|
||||
listGenerator.name, 'HostsHelper',
|
||||
'InventoryHelper', 'InventoryFormDefinition', 'SelectionHelper',
|
||||
'HostGroupsFormDefinition', 'VariablesHelper', 'ModalDialog', 'StandardOutHelper',
|
||||
'GroupListDefinition'
|
||||
])
|
||||
@ -160,23 +160,26 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name,
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('HostsReload', [ '$stateParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait',
|
||||
.factory('HostsReload', [ '$stateParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'Wait',
|
||||
'SetHostStatus', 'SetStatus', 'ApplyEllipsis',
|
||||
function($stateParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait, SetHostStatus, SetStatus,
|
||||
function($stateParams, Empty, InventoryHosts, GetBasePath, Wait, SetHostStatus, SetStatus,
|
||||
ApplyEllipsis) {
|
||||
return function(params) {
|
||||
|
||||
var scope = params.scope,
|
||||
parent_scope = params.parent_scope,
|
||||
group_id = params.group_id,
|
||||
inventory_id = params.inventory_id,
|
||||
list = InventoryHosts,
|
||||
pageSize = (params.pageSize) ? params.pageSize : 20,
|
||||
parent_scope = params.parent_scope;
|
||||
|
||||
url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' :
|
||||
GetBasePath('inventory') + inventory_id + '/hosts/';
|
||||
// @issue: OLD SEARCH
|
||||
// var list = InventoryHosts,
|
||||
// group_id = params.group_id,
|
||||
// inventory_id = params.inventory_id;
|
||||
// pageSize = (params.pageSize) ? params.pageSize : 20,
|
||||
//
|
||||
// url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' :
|
||||
// GetBasePath('inventory') + inventory_id + '/hosts/';
|
||||
|
||||
scope.search_place_holder='Search ' + scope.selected_group_name;
|
||||
// @issue: OLD SEARCH
|
||||
// scope.search_place_holder='Search ' + scope.selected_group_name;
|
||||
|
||||
if (scope.removeHostsReloadPostRefresh) {
|
||||
scope.removeHostsReloadPostRefresh();
|
||||
@ -196,31 +199,31 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name,
|
||||
}
|
||||
});
|
||||
|
||||
SearchInit({ scope: scope, set: 'hosts', list: list, url: url });
|
||||
PaginateInit({ scope: scope, list: list, url: url, pageSize: pageSize });
|
||||
|
||||
if ($stateParams.host_name) {
|
||||
scope[list.iterator + 'InputDisable'] = false;
|
||||
scope[list.iterator + 'SearchValue'] = $stateParams.host_name;
|
||||
scope[list.iterator + 'SearchField'] = 'name';
|
||||
scope[list.iterator + 'SearchFieldLabel'] = list.fields.name.label;
|
||||
scope[list.iterator + 'SearchSelectValue'] = null;
|
||||
}
|
||||
|
||||
if (scope.show_failures) {
|
||||
scope[list.iterator + 'InputDisable'] = true;
|
||||
scope[list.iterator + 'SearchValue'] = 'true';
|
||||
scope[list.iterator + 'SearchField'] = 'has_active_failures';
|
||||
scope[list.iterator + 'SearchFieldLabel'] = list.fields.has_active_failures.label;
|
||||
scope[list.iterator + 'SearchSelectValue'] = { value: 1 };
|
||||
}
|
||||
scope.search(list.iterator, null, true);
|
||||
// @issue: OLD SEARCH
|
||||
// SearchInit({ scope: scope, set: 'hosts', list: list, url: url });
|
||||
// PaginateInit({ scope: scope, list: list, url: url, pageSize: pageSize });
|
||||
//
|
||||
// if ($stateParams.host_name) {
|
||||
// scope[list.iterator + 'InputDisable'] = false;
|
||||
// scope[list.iterator + 'SearchValue'] = $stateParams.host_name;
|
||||
// scope[list.iterator + 'SearchField'] = 'name';
|
||||
// scope[list.iterator + 'SearchFieldLabel'] = list.fields.name.label;
|
||||
// scope[list.iterator + 'SearchSelectValue'] = null;
|
||||
// }
|
||||
//
|
||||
// if (scope.show_failures) {
|
||||
// scope[list.iterator + 'InputDisable'] = true;
|
||||
// scope[list.iterator + 'SearchValue'] = 'true';
|
||||
// scope[list.iterator + 'SearchField'] = 'has_active_failures';
|
||||
// scope[list.iterator + 'SearchFieldLabel'] = list.fields.has_active_failures.label;
|
||||
// scope[list.iterator + 'SearchSelectValue'] = { value: 1 };
|
||||
// }
|
||||
// scope.search(list.iterator, null, true);
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit',
|
||||
'PaginateInit',
|
||||
function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit) {
|
||||
.factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList',
|
||||
function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList) {
|
||||
return function(params) {
|
||||
|
||||
var host_id = params.host_id,
|
||||
@ -271,7 +274,9 @@ return function(params) {
|
||||
scope.removeHostCopyDialogReady();
|
||||
}
|
||||
scope.removeCopyDialogReady = scope.$on('HostCopyDialogReady', function() {
|
||||
var url = GetBasePath('inventory') + group_scope.inventory.id + '/groups/';
|
||||
// @issue: OLD SEARCH
|
||||
// var url = GetBasePath('inventory') + group_scope.inventory.id + '/groups/';
|
||||
|
||||
GenerateList.inject(GroupList, {
|
||||
mode: 'lookup',
|
||||
id: 'copy-host-select-container',
|
||||
@ -279,19 +284,21 @@ return function(params) {
|
||||
//,
|
||||
//instructions: instructions
|
||||
});
|
||||
SearchInit({
|
||||
scope: scope,
|
||||
set: GroupList.name,
|
||||
list: GroupList,
|
||||
url: url
|
||||
});
|
||||
PaginateInit({
|
||||
scope: scope,
|
||||
list: GroupList,
|
||||
url: url,
|
||||
mode: 'lookup'
|
||||
});
|
||||
scope.search(GroupList.iterator, null, true, false);
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// SearchInit({
|
||||
// scope: scope,
|
||||
// set: GroupList.name,
|
||||
// list: GroupList,
|
||||
// url: url
|
||||
// });
|
||||
// PaginateInit({
|
||||
// scope: scope,
|
||||
// list: GroupList,
|
||||
// url: url,
|
||||
// mode: 'lookup'
|
||||
// });
|
||||
// scope.search(GroupList.iterator, null, true, false);
|
||||
});
|
||||
|
||||
if (scope.removeShowDialog) {
|
||||
@ -341,8 +348,12 @@ return function(params) {
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
scope.searchCleanup();
|
||||
group_scope.restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// scope.searchCleanup();
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// group_scope.restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists
|
||||
scope.$destroy();
|
||||
};
|
||||
|
||||
|
||||
@ -686,9 +686,11 @@ export default
|
||||
|
||||
scope.plays = [];
|
||||
|
||||
url = scope.job.url + 'job_plays/?page_size=' + scope.playsMaxRows + '&order=id';
|
||||
url += (scope.search_play_name) ? '&play__icontains=' + encodeURIComponent(scope.search_play_name) : '';
|
||||
url += (scope.search_play_status === 'failed') ? '&failed=true' : '';
|
||||
// @issue: OLD SEARCH - factory needs refactoring! will be completely rehauled for job details 3.1 update
|
||||
// url = scope.job.url + 'job_plays/?page_size=' + scope.playsMaxRows + '&order=id';
|
||||
// url += (scope.search_play_name) ? '&play__icontains=' + encodeURIComponent(scope.search_play_name) : '';
|
||||
// url += (scope.search_play_status === 'failed') ? '&failed=true' : '';
|
||||
|
||||
scope.playsLoading = true;
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
@ -786,9 +788,12 @@ export default
|
||||
scope.tasks = [];
|
||||
if (scope.selectedPlay) {
|
||||
url = scope.job.url + 'job_tasks/?event_id=' + scope.selectedPlay;
|
||||
url += (scope.search_task_name) ? '&task__icontains=' + encodeURIComponent(scope.search_task_name) : '';
|
||||
url += (scope.search_task_status === 'failed') ? '&failed=true' : '';
|
||||
url += '&page_size=' + scope.tasksMaxRows + '&order=id';
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// url += (scope.search_task_name) ? '&task__icontains=' + encodeURIComponent(scope.search_task_name) : '';
|
||||
// url += (scope.search_task_status === 'failed') ? '&failed=true' : '';
|
||||
// url += '&page_size=' + scope.tasksMaxRows + '&order=id';
|
||||
|
||||
scope.plays.every(function(p, idx) {
|
||||
if (p.id === scope.selectedPlay) {
|
||||
play = scope.plays[idx];
|
||||
@ -918,9 +923,12 @@ export default
|
||||
page_size: scope.hostResultsMaxRows,
|
||||
order: 'host_name,counter',
|
||||
};
|
||||
if (scope.search_host_status === 'failed'){
|
||||
params.failed = true;
|
||||
}
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_host_status === 'failed'){
|
||||
// params.failed = true;
|
||||
// }
|
||||
|
||||
JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){
|
||||
scope.hostResults = JobDetailService.processHostEvents(res.results);
|
||||
scope.hostResultsLoading = false;
|
||||
@ -1052,27 +1060,30 @@ export default
|
||||
filteredListX[key] = plays[key];
|
||||
}
|
||||
}
|
||||
if (scope.search_play_name) {
|
||||
for (key in plays) {
|
||||
if (filteredListX[key].name.indexOf(scope.search_play_name) > 0) {
|
||||
filteredListA[key] = filteredListX[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListA = filteredListX;
|
||||
}
|
||||
|
||||
if (scope.search_play_status === 'failed') {
|
||||
for (key in filteredListA) {
|
||||
if (filteredListA[key].status === 'failed') {
|
||||
filteredListB[key] = plays[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListB = filteredListA;
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_play_name) {
|
||||
// for (key in plays) {
|
||||
// if (filteredListX[key].name.indexOf(scope.search_play_name) > 0) {
|
||||
// filteredListA[key] = filteredListX[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListA = filteredListX;
|
||||
// }
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_play_status === 'failed') {
|
||||
// for (key in filteredListA) {
|
||||
// if (filteredListA[key].status === 'failed') {
|
||||
// filteredListB[key] = plays[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListB = filteredListA;
|
||||
// }
|
||||
|
||||
keys = Object.keys(filteredListB);
|
||||
keys.sort(function(a,b) { return listSort(a,b); }).reverse();
|
||||
@ -1130,27 +1141,29 @@ export default
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.search_task_name) {
|
||||
for (key in filteredListX) {
|
||||
if (filteredListX[key].name.indexOf(scope.search_task_name) > 0) {
|
||||
filteredListA[key] = filteredListX[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListA = filteredListX;
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_task_name) {
|
||||
// for (key in filteredListX) {
|
||||
// if (filteredListX[key].name.indexOf(scope.search_task_name) > 0) {
|
||||
// filteredListA[key] = filteredListX[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListA = filteredListX;
|
||||
// }
|
||||
|
||||
if (scope.search_task_status === 'failed') {
|
||||
for (key in filteredListA) {
|
||||
if (filteredListA[key].status === 'failed') {
|
||||
filteredListB[key] = tasks[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListB = filteredListA;
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_task_status === 'failed') {
|
||||
// for (key in filteredListA) {
|
||||
// if (filteredListA[key].status === 'failed') {
|
||||
// filteredListB[key] = tasks[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListB = filteredListA;
|
||||
// }
|
||||
|
||||
keys = Object.keys(filteredListB);
|
||||
keys.sort(function(a,b) { return listSort(a,b); }).reverse();
|
||||
@ -1196,27 +1209,30 @@ export default
|
||||
//hostResults = JSON.parse(JSON.stringify(scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].hostResults));
|
||||
hostResults = scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].hostResults;
|
||||
|
||||
if (scope.search_host_name) {
|
||||
for (key in hostResults) {
|
||||
if (hostResults[key].name.indexOf(scope.search_host_name) > 0) {
|
||||
filteredListA[key] = hostResults[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListA = hostResults;
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_host_name) {
|
||||
// for (key in hostResults) {
|
||||
// if (hostResults[key].name.indexOf(scope.search_host_name) > 0) {
|
||||
// filteredListA[key] = hostResults[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListA = hostResults;
|
||||
// }
|
||||
|
||||
if (scope.search_host_status === 'failed' || scope.search_host_status === 'unreachable') {
|
||||
for (key in filteredListA) {
|
||||
if (filteredListA[key].status === 'failed') {
|
||||
filteredListB[key] = filteredListA[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
filteredListB = filteredListA;
|
||||
}
|
||||
// @issue: OLD SEARCH
|
||||
// if (scope.search_host_status === 'failed' || scope.search_host_status === 'unreachable') {
|
||||
// for (key in filteredListA) {
|
||||
// if (filteredListA[key].status === 'failed') {
|
||||
// filteredListB[key] = filteredListA[key];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// filteredListB = filteredListA;
|
||||
// }
|
||||
|
||||
keys = Object.keys(filteredListB);
|
||||
keys.sort(function compare(a, b) {
|
||||
if (filteredListB[a].name === filteredListB[b].name) {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
export default
|
||||
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
|
||||
'LookUpHelper', 'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog', 'FormGenerator', 'JobVarsPromptFormDefinition'])
|
||||
'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog', 'FormGenerator', 'JobVarsPromptFormDefinition'])
|
||||
|
||||
.factory('CreateLaunchDialog', ['$compile', 'CreateDialog', 'Wait', 'ParseTypeChange',
|
||||
function($compile, CreateDialog, Wait, ParseTypeChange) {
|
||||
|
||||
@ -19,9 +19,9 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
||||
*/
|
||||
|
||||
.factory('CallbackHelpInit', ['$location', 'GetBasePath', 'Rest', 'JobTemplateForm', 'GenerateForm', '$stateParams', 'ProcessErrors', 'ParseTypeChange',
|
||||
'ParseVariableString', 'Empty', 'LookUpInit', 'InventoryList', 'CredentialList','ProjectList', 'RelatedSearchInit', 'RelatedPaginateInit', 'Wait',
|
||||
function($location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors,ParseTypeChange,
|
||||
ParseVariableString, Empty, LookUpInit, InventoryList, CredentialList, ProjectList, RelatedSearchInit, RelatedPaginateInit, Wait) {
|
||||
'ParseVariableString', 'Empty', 'InventoryList', 'CredentialList','ProjectList', 'Wait',
|
||||
function($location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors, ParseTypeChange,
|
||||
ParseVariableString, Empty, InventoryList, CredentialList, ProjectList, Wait) {
|
||||
return function(params) {
|
||||
|
||||
var scope = params.scope,
|
||||
@ -31,7 +31,7 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
||||
// loadingFinishedCount = 0,
|
||||
// base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
master = {},
|
||||
id = $stateParams.id,
|
||||
id = $stateParams.job_template_id,
|
||||
relatedSets = {};
|
||||
// checkSCMStatus, getPlaybooks, callback,
|
||||
// choicesCount = 0;
|
||||
@ -158,74 +158,11 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
||||
|
||||
scope.can_edit = data.summary_fields.can_edit;
|
||||
|
||||
LookUpInit({
|
||||
scope: scope,
|
||||
form: form,
|
||||
current_item: data.inventory,
|
||||
list: InventoryList,
|
||||
field: 'inventory',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
CredentialList.basePath = GetBasePath('credentials') + '?kind=ssh';
|
||||
// remove "type" field from search options
|
||||
CredentialList.fields.kind.noSearch = true;
|
||||
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?kind=ssh',
|
||||
scope: scope,
|
||||
form: form,
|
||||
current_item: data.credential,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
hdr: 'Select Machine Credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
var NetworkCredentialList = {};
|
||||
// Clone the CredentialList object for use with network_credential. Cloning
|
||||
// and changing properties to avoid collision.
|
||||
jQuery.extend(true, NetworkCredentialList, CredentialList);
|
||||
NetworkCredentialList.name = 'networkcredentials';
|
||||
NetworkCredentialList.iterator = 'networkcredential';
|
||||
NetworkCredentialList.basePath = '/api/v1/credentials?kind=net';
|
||||
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?kind=net',
|
||||
scope: scope,
|
||||
form: form,
|
||||
current_item: data.network_credential,
|
||||
list: NetworkCredentialList,
|
||||
field: 'network_credential',
|
||||
hdr: 'Select Network Credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: scope,
|
||||
form: form,
|
||||
current_item: data.project,
|
||||
list: ProjectList,
|
||||
field: 'project',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
|
||||
if (scope.project === "" && scope.playbook === "") {
|
||||
scope.resetProjectToDefault();
|
||||
}
|
||||
|
||||
RelatedSearchInit({
|
||||
scope: scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
RelatedPaginateInit({
|
||||
scope: scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
scope.$emit('jobTemplateLoaded', data.related.cloud_credential, master, relatedSets);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
|
||||
@ -14,92 +14,7 @@ import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper', 'GeneratorHelpers',
|
||||
'JobSubmissionHelper', 'StandardOutHelper', 'SearchHelper', 'PaginationHelpers', 'AdhocHelper', listGenerator.name])
|
||||
|
||||
/**
|
||||
* JobsControllerInit({ scope: $scope });
|
||||
*
|
||||
* Initialize calling scope with all the bits required to support a jobs list
|
||||
*
|
||||
*/
|
||||
.factory('JobsControllerInit', ['$state', 'Find', 'DeleteJob', 'RelaunchJob',
|
||||
function($state, Find, DeleteJob, RelaunchJob) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
iterator = (params.iterator) ? params.iterator : scope.iterator;
|
||||
|
||||
scope.deleteJob = function(id) {
|
||||
DeleteJob({ scope: scope, id: id });
|
||||
};
|
||||
|
||||
scope.relaunchJob = function(event, id) {
|
||||
var list, job, typeId;
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
//ignore
|
||||
}
|
||||
if (scope.completed_jobs) {
|
||||
list = scope.completed_jobs;
|
||||
}
|
||||
else if (scope.running_jobs) {
|
||||
list = scope.running_jobs;
|
||||
}
|
||||
else if (scope.queued_jobs) {
|
||||
list = scope.queued_jobs;
|
||||
}
|
||||
else if (scope.jobs) {
|
||||
list = scope.jobs;
|
||||
}
|
||||
else if(scope.all_jobs){
|
||||
list = scope.all_jobs;
|
||||
}
|
||||
job = Find({ list: list, key: 'id', val: id });
|
||||
if (job.type === 'inventory_update') {
|
||||
typeId = job.inventory_source;
|
||||
}
|
||||
else if (job.type === 'project_update') {
|
||||
typeId = job.project;
|
||||
}
|
||||
else if (job.type === 'job' || job.type === "system_job" || job.type === 'ad_hoc_command') {
|
||||
typeId = job.id;
|
||||
}
|
||||
RelaunchJob({ scope: scope, id: typeId, type: job.type, name: job.name });
|
||||
};
|
||||
|
||||
scope.refreshJobs = function() {
|
||||
scope.search(iterator);
|
||||
};
|
||||
|
||||
scope.viewJobDetails = function(job) {
|
||||
|
||||
var goToJobDetails = function(state) {
|
||||
$state.go(state, {id: job.id}, {reload:true});
|
||||
};
|
||||
|
||||
switch(job.type) {
|
||||
case 'job':
|
||||
goToJobDetails('jobDetail');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goToJobDetails('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goToJobDetails('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goToJobDetails('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
])
|
||||
'JobSubmissionHelper', 'StandardOutHelper', 'AdhocHelper', listGenerator.name])
|
||||
|
||||
.factory('RelaunchJob', ['RelaunchInventory', 'RelaunchPlaybook', 'RelaunchSCM', 'RelaunchAdhoc',
|
||||
function(RelaunchInventory, RelaunchPlaybook, RelaunchSCM, RelaunchAdhoc) {
|
||||
@ -210,83 +125,6 @@ export default
|
||||
};
|
||||
}])
|
||||
|
||||
/**
|
||||
*
|
||||
* Called from JobsList controller to load each section or list on the page
|
||||
*
|
||||
*/
|
||||
.factory('LoadJobsScope', ['$stateParams', '$location', '$compile',
|
||||
'SearchInit', 'PaginateInit', 'generateList', 'JobsControllerInit',
|
||||
'JobsListUpdate',
|
||||
function($stateParams, $location, $compile, SearchInit, PaginateInit,
|
||||
GenerateList, JobsControllerInit, JobsListUpdate) {
|
||||
return function(params) {
|
||||
var parent_scope = params.parent_scope,
|
||||
scope = params.scope,
|
||||
list = params.list,
|
||||
id = params.id,
|
||||
url = params.url,
|
||||
pageSize = params.pageSize || 10,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
search_params = params.searchParams,
|
||||
spinner = (params.spinner === undefined) ? true : params.spinner, key;
|
||||
|
||||
var buildTooltips = function(data){
|
||||
data.forEach((val) => {
|
||||
val.status_tip = 'Job ' + val.status + ". Click for details.";
|
||||
});
|
||||
};
|
||||
|
||||
GenerateList.inject(list, {
|
||||
mode: 'edit',
|
||||
id: id,
|
||||
scope: scope,
|
||||
title: false
|
||||
});
|
||||
|
||||
SearchInit({
|
||||
scope: scope,
|
||||
set: list.name,
|
||||
list: list,
|
||||
url: url
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: scope,
|
||||
list: list,
|
||||
url: url,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
scope.iterator = list.iterator;
|
||||
|
||||
if (scope.removePostRefresh) {
|
||||
scope.removePostRefresh();
|
||||
}
|
||||
scope.$on('PostRefresh', function(){
|
||||
JobsControllerInit({ scope: scope, parent_scope: parent_scope });
|
||||
JobsListUpdate({ scope: scope, parent_scope: parent_scope, list: list });
|
||||
buildTooltips(scope.jobs);
|
||||
parent_scope.$emit('listLoaded');
|
||||
});
|
||||
|
||||
if (base === 'jobs' && list.name === 'all_jobs') {
|
||||
if ($stateParams.id__int) {
|
||||
scope[list.iterator + 'SearchField'] = 'id';
|
||||
scope[list.iterator + 'SearchValue'] = $stateParams.id__int;
|
||||
scope[list.iterator + 'SearchFieldLabel'] = 'Job ID';
|
||||
}
|
||||
}
|
||||
|
||||
if (search_params) {
|
||||
for (key in search_params) {
|
||||
scope[key] = search_params[key];
|
||||
}
|
||||
}
|
||||
scope.search(list.iterator, null, null, null, null, spinner);
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('DeleteJob', ['Find', 'GetBasePath', 'Rest', 'Wait',
|
||||
'ProcessErrors', 'Prompt', 'Alert', '$filter',
|
||||
function(Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert,
|
||||
@ -338,7 +176,8 @@ export default
|
||||
scope.$emit(callback, action_label);
|
||||
}
|
||||
else {
|
||||
scope.search(scope.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// scope.search(scope.iterator);
|
||||
}
|
||||
})
|
||||
.error(function(obj, status) {
|
||||
@ -359,7 +198,8 @@ export default
|
||||
scope.$emit(callback, action_label);
|
||||
}
|
||||
else {
|
||||
scope.search(scope.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// scope.search(scope.iterator);
|
||||
}
|
||||
})
|
||||
.error(function (obj, status) {
|
||||
|
||||
@ -1,182 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:PaginationHelpers
|
||||
* @description pagination
|
||||
*/
|
||||
|
||||
export default
|
||||
angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelatedHelper'])
|
||||
|
||||
.factory('PageRangeSetup', ['Empty',
|
||||
function (Empty) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
count = params.count,
|
||||
next = params.next,
|
||||
previous = params.previous,
|
||||
iterator = params.iterator,
|
||||
i, first, last;
|
||||
|
||||
scope[iterator + '_page'] = 1;
|
||||
scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size']));
|
||||
scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages'];
|
||||
scope[iterator + '_total_rows'] = count;
|
||||
$('#'+iterator+'-pagination #pagination-links li:eq(1)').removeAttr('class');
|
||||
// Which page are we on?
|
||||
if (Empty(next) && previous) {
|
||||
// no next page, but there is a previous page
|
||||
scope[iterator + '_page'] = /page=\d+/.test(previous) ? parseInt(previous.match(/page=(\d+)/)[1]) + 1 : 2;
|
||||
} else if (next && Empty(previous)) {
|
||||
// next page available, but no previous page
|
||||
scope[iterator + '_page'] = 1;
|
||||
$('#'+iterator+'-pagination #pagination-links li:eq(1)').attr('class', 'disabled');
|
||||
} else if (next && previous) {
|
||||
// we're in between next and previous
|
||||
scope[iterator + '_page'] = /page=\d+/.test(previous) ? parseInt(previous.match(/page=(\d+)/)[1]) + 1 : 2;
|
||||
}
|
||||
|
||||
// Calc the range of up to 10 pages to show
|
||||
scope[iterator + '_page_range'] = [];
|
||||
first = (scope[iterator + '_page'] > 5) ? scope[iterator + '_page'] - 5 : 1;
|
||||
if (scope[iterator + '_page'] < 6) {
|
||||
last = (10 <= scope[iterator + '_num_pages']) ? 10 : scope[iterator + '_num_pages'];
|
||||
} else {
|
||||
last = (scope[iterator + '_page'] + 4 < scope[iterator + '_num_pages']) ?
|
||||
scope[iterator + '_page'] + 4 : scope[iterator + '_num_pages'];
|
||||
}
|
||||
for (i = first; i <= last; i++) {
|
||||
scope[iterator + '_page_range'].push(i);
|
||||
}
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.factory('RelatedPaginateInit', ['RefreshRelated', '$cookieStore', 'Wait',
|
||||
function (RefreshRelated, $cookieStore, Wait) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
relatedSets = params.relatedSets,
|
||||
pageSize = (params.pageSize) ? params.pageSize : 10,
|
||||
key;
|
||||
|
||||
for (key in relatedSets) {
|
||||
scope[relatedSets[key].iterator + '_url'] = relatedSets[key].url;
|
||||
scope[relatedSets[key].iterator + '_page'] = 0;
|
||||
scope[relatedSets[key].iterator + '_page_size'] = pageSize;
|
||||
}
|
||||
|
||||
scope.getPage = function (page, set, iterator) {
|
||||
var new_url = scope[iterator + '_url'].replace(/.page\=\d+/, ''),
|
||||
connect = (/\/$/.test(new_url)) ? '?' : '&';
|
||||
if(scope[iterator + '_page'] === 1 && page === 0){
|
||||
return;
|
||||
}
|
||||
new_url += connect + 'page=' + page;
|
||||
if (scope[iterator + 'SearchFilters']){
|
||||
new_url += _.reduce(scope[iterator+'SearchFilters'], (result, filter) => result + '&' + filter.url, '');
|
||||
}
|
||||
if (scope[iterator + 'SearchParams']){
|
||||
new_url += '&' + scope[iterator + 'SearchParams'];
|
||||
}
|
||||
new_url += '&page_size=' + scope[iterator + '_page_size'];
|
||||
Wait('start');
|
||||
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: new_url });
|
||||
};
|
||||
|
||||
scope.pageIsActive = function (page, iterator) {
|
||||
return (page === scope[iterator + '_page']) ? 'active' : '';
|
||||
};
|
||||
|
||||
scope.changePageSize = function (set, iterator) {
|
||||
// Called when a new page size is selected
|
||||
|
||||
scope[iterator + '_page'] = 1;
|
||||
var url = scope[iterator + '_url'];
|
||||
|
||||
// Using the session cookie, keep track of user rows per page selection
|
||||
$cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']);
|
||||
|
||||
url = url.replace(/\/\?.*$/, '/');
|
||||
url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size'] :
|
||||
'?page_size=' + scope[iterator + '_page_size'];
|
||||
|
||||
RefreshRelated({
|
||||
scope: scope,
|
||||
set: set,
|
||||
iterator: iterator,
|
||||
url: url
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
.factory('PaginateInit', ['Refresh', '$cookieStore', 'Wait',
|
||||
function (Refresh, $cookieStore, Wait) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
list = params.list,
|
||||
iterator = (params.iterator) ? params.iterator : list.iterator,
|
||||
pageSize = params.pageSize,
|
||||
mode = (params.mode) ? params.mode : null;
|
||||
|
||||
scope[iterator + '_page'] = (params.page) ? params.page : 1;
|
||||
scope[iterator + '_url'] = params.url;
|
||||
scope[iterator + '_mode'] = mode;
|
||||
|
||||
if (pageSize) {
|
||||
scope[iterator + '_page_size'] = params.pageSize;
|
||||
} else if (mode === 'lookup') {
|
||||
scope[iterator + '_page_size'] = 5;
|
||||
} else {
|
||||
scope[iterator + '_page_size'] = 20;
|
||||
}
|
||||
|
||||
scope.getPage = function (page, set, iterator) {
|
||||
var new_url = scope[iterator + '_url'].replace(/.page\=\d+/, ''),
|
||||
connect = (/\/$/.test(new_url)) ? '?' : '&';
|
||||
if(scope[iterator + '_page'] === 1 && page === 0){
|
||||
return;
|
||||
}
|
||||
new_url += connect + 'page=' + page;
|
||||
if (scope[iterator + 'SearchFilters']){
|
||||
new_url += _.reduce(scope[iterator+'SearchFilters'], (result, filter) => result + '&' + filter.url, '');
|
||||
}
|
||||
if (scope[iterator + 'SearchParams']){
|
||||
new_url += '&' + scope[iterator + 'SearchParams'];
|
||||
}
|
||||
new_url += '&page_size=' + scope[iterator + '_page_size'];
|
||||
Wait('start');
|
||||
scope.getNewPage = true;
|
||||
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
|
||||
};
|
||||
|
||||
scope.pageIsActive = function (page, iterator) {
|
||||
return (page === scope[iterator + '_page']) ? 'active' : '';
|
||||
};
|
||||
|
||||
scope.changePageSize = function (set, iterator, spinner) {
|
||||
// Called whenever a new page size is selected
|
||||
scope[iterator + '_page'] = 1;
|
||||
var new_url = scope[iterator + '_url'].replace(/\?page_size\=\d+/, ''),
|
||||
connect = (/\/$/.test(new_url)) ? '?' : '&';
|
||||
new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size'] :
|
||||
connect + 'page_size=' + scope[iterator + '_page_size'];
|
||||
if (spinner === undefined || spinner === true) {
|
||||
Wait('start');
|
||||
}
|
||||
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
|
||||
};
|
||||
};
|
||||
}
|
||||
]);
|
||||
@ -17,7 +17,7 @@
|
||||
import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ModalDialog',
|
||||
angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', listGenerator.name, 'ModalDialog',
|
||||
'GeneratorHelpers'])
|
||||
|
||||
.factory('EditSchedule', ['SchedulerInit', '$rootScope', 'Wait', 'Rest',
|
||||
@ -272,7 +272,7 @@ export default
|
||||
if (callback) {
|
||||
scope.$emit(callback, data);
|
||||
}
|
||||
$state.go("^");
|
||||
$state.go("^", null, {reload: true});
|
||||
});
|
||||
scope.saveSchedule = function() {
|
||||
SchedulePost({
|
||||
@ -376,7 +376,8 @@ export default
|
||||
* });
|
||||
*
|
||||
*/
|
||||
.factory('ToggleSchedule', ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest', function(Wait, GetBasePath, ProcessErrors, Rest) {
|
||||
.factory('ToggleSchedule', ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest', '$state',
|
||||
function(Wait, GetBasePath, ProcessErrors, Rest, $state) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
id = params.id,
|
||||
@ -391,12 +392,8 @@ export default
|
||||
data.enabled = (data.enabled) ? false : true;
|
||||
Rest.put(data)
|
||||
.success( function() {
|
||||
if (callback) {
|
||||
scope.$emit(callback, id);
|
||||
}
|
||||
else {
|
||||
Wait('stop');
|
||||
}
|
||||
Wait('stop');
|
||||
$state.go('.', null, {reload: true});
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
@ -429,9 +426,9 @@ export default
|
||||
* })
|
||||
*
|
||||
*/
|
||||
.factory('DeleteSchedule', ['GetBasePath','Rest', 'Wait',
|
||||
.factory('DeleteSchedule', ['GetBasePath','Rest', 'Wait', '$state',
|
||||
'ProcessErrors', 'Prompt', 'Find', '$location', '$filter',
|
||||
function(GetBasePath, Rest, Wait, ProcessErrors, Prompt, Find,
|
||||
function(GetBasePath, Rest, Wait, $state, ProcessErrors, Prompt, Find,
|
||||
$location, $filter) {
|
||||
return function(params) {
|
||||
|
||||
@ -461,6 +458,9 @@ export default
|
||||
if (new RegExp('/' + id + '$').test($location.$$url)) {
|
||||
$location.url($location.url().replace(/[/][0-9]+$/, "")); // go to list view
|
||||
}
|
||||
else{
|
||||
$state.go('.', null, {reload: true});
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
try {
|
||||
@ -497,214 +497,4 @@ export default
|
||||
});
|
||||
return response;
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('SchedulesControllerInit', ['$state', '$location',
|
||||
'ToggleSchedule', 'DeleteSchedule', 'ParamPass',
|
||||
function($state, $location, ToggleSchedule, DeleteSchedule,
|
||||
ParamPass) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
parent_scope = params.parent_scope,
|
||||
iterator = (params.iterator) ? params.iterator : scope.iterator,
|
||||
base = params.base || $location.path().replace(/^\//, '').split('/')[0];
|
||||
|
||||
scope.toggleSchedule = function(event, id) {
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
ToggleSchedule({
|
||||
scope: scope,
|
||||
id: id,
|
||||
callback: 'SchedulesRefresh'
|
||||
});
|
||||
};
|
||||
|
||||
scope.deleteSchedule = function(id) {
|
||||
DeleteSchedule({
|
||||
scope: scope,
|
||||
id: id,
|
||||
callback: 'SchedulesRefresh'
|
||||
});
|
||||
};
|
||||
|
||||
scope.editSchedule = function(id) {
|
||||
if ($state.includes('inventoryManage')){
|
||||
$state.go('inventoryManage.schedules.edit', {schedule_id: id});
|
||||
}
|
||||
else if ($state.current.name === 'jobs'){
|
||||
// id === schedule object in this case
|
||||
var stateDictionary = {
|
||||
// type: stateName
|
||||
job: 'jobTemplateSchedules.edit',
|
||||
system_job: 'managementJobSchedules.edit',
|
||||
project_update: 'projectSchedules.edit',
|
||||
};
|
||||
$state.go(stateDictionary[id.type], {schedule_id: id.id, id: id.summary_fields.unified_job_template.id});
|
||||
}
|
||||
else{
|
||||
var base = $state.current.name.split(".")[0];
|
||||
$state.go(base + ".edit", {schedule_id: id});
|
||||
}
|
||||
};
|
||||
|
||||
scope.addSchedule = function() {
|
||||
if ($state.includes('inventoryManage')){
|
||||
scope.schedule_url = parent_scope.current_url.split('?')[0];
|
||||
ParamPass.set(scope.schedule_url);
|
||||
$state.go('inventoryManage.schedules.add');
|
||||
}
|
||||
else{
|
||||
var base = $state.current.name.split(".")[0];
|
||||
ParamPass.set(scope.schedule_url);
|
||||
$state.go(base + ".add");
|
||||
}
|
||||
};
|
||||
|
||||
scope.refreshSchedules = function() {
|
||||
if (base === 'jobs') {
|
||||
parent_scope.refreshJobs();
|
||||
}
|
||||
else {
|
||||
scope.search(iterator);
|
||||
}
|
||||
};
|
||||
|
||||
if (scope.removeSchedulesRefresh) {
|
||||
scope.removeSchedulesRefresh();
|
||||
}
|
||||
scope.removeSchedulesRefresh = scope.$on('SchedulesRefresh', function() {
|
||||
scope.search(iterator);
|
||||
});
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('SchedulesListInit', [ function() {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
list = params.list,
|
||||
choices = params.choices;
|
||||
scope[list.name].forEach(function(item, item_idx) {
|
||||
var fld,
|
||||
field,
|
||||
itm = scope[list.name][item_idx],
|
||||
job = item.summary_fields.unified_job_template;
|
||||
|
||||
itm.enabled = (itm.enabled) ? true : false;
|
||||
if (itm.enabled) {
|
||||
itm.play_tip = 'Schedule is active. Click to stop.';
|
||||
itm.status = 'active';
|
||||
itm.status_tip = 'Schedule is active. Click to stop.';
|
||||
}
|
||||
else {
|
||||
itm.play_tip = 'Schedule is stopped. Click to activate.';
|
||||
itm.status = 'stopped';
|
||||
itm.status_tip = 'Schedule is stopped. Click to activate.';
|
||||
}
|
||||
itm.nameTip = item.name;
|
||||
// include the word schedule if the schedule name does not include the word schedule
|
||||
if (item.name.indexOf("schedule") === -1 && item.name.indexOf("Schedule") === -1) {
|
||||
itm.nameTip += " schedule";
|
||||
}
|
||||
itm.nameTip += " for ";
|
||||
if (job.name.indexOf("job") === -1 && job.name.indexOf("Job") === -1) {
|
||||
itm.nameTip += "job ";
|
||||
}
|
||||
itm.nameTip += job.name;
|
||||
itm.nameTip += ". Click to edit schedule.";
|
||||
// Copy summary_field values
|
||||
for (field in list.fields) {
|
||||
fld = list.fields[field];
|
||||
if (fld.sourceModel) {
|
||||
if (itm.summary_fields[fld.sourceModel]) {
|
||||
itm[field] = itm.summary_fields[fld.sourceModel][fld.sourceField];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the item type label
|
||||
if (list.fields.type) {
|
||||
choices.every(function(choice) {
|
||||
if (choice.value === item.type) {
|
||||
itm.type_label = choice.label;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}])
|
||||
|
||||
/**
|
||||
*
|
||||
* Called from a controller to setup the scope for a schedules list
|
||||
*
|
||||
*/
|
||||
.factory('LoadSchedulesScope', ['$compile', '$location', '$stateParams','SearchInit', 'PaginateInit', 'generateList', 'SchedulesControllerInit',
|
||||
'SchedulesListInit',
|
||||
function($compile, $location, $stateParams, SearchInit, PaginateInit, GenerateList, SchedulesControllerInit, SchedulesListInit) {
|
||||
return function(params) {
|
||||
var parent_scope = params.parent_scope,
|
||||
scope = params.scope,
|
||||
list = params.list,
|
||||
id = params.id,
|
||||
url = params.url,
|
||||
searchSize = params.searchSize,
|
||||
pageSize = params.pageSize || 10,
|
||||
spinner = (params.spinner === undefined) ? true : params.spinner;
|
||||
|
||||
|
||||
GenerateList.inject(list, {
|
||||
mode: 'edit',
|
||||
id: id,
|
||||
scope: scope,
|
||||
searchSize: (searchSize) ? searchSize : 'col-lg-6 col-md-6 col-sm-6 col-xs-12',
|
||||
showSearch: true,
|
||||
title: true,
|
||||
});
|
||||
|
||||
SearchInit({
|
||||
scope: scope,
|
||||
set: list.name,
|
||||
list: list,
|
||||
url: url
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: scope,
|
||||
list: list,
|
||||
url: url,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
scope.iterator = list.iterator;
|
||||
|
||||
if (scope.removePostRefresh) {
|
||||
scope.removePostRefresh();
|
||||
}
|
||||
scope.$on('PostRefresh', function(){
|
||||
SchedulesControllerInit({
|
||||
scope: scope,
|
||||
parent_scope: parent_scope,
|
||||
list: list
|
||||
});
|
||||
SchedulesListInit({
|
||||
scope: scope,
|
||||
list: list,
|
||||
choices: parent_scope.type_choices
|
||||
});
|
||||
parent_scope.$emit('listLoaded');
|
||||
});
|
||||
|
||||
if ($stateParams.id__int) {
|
||||
scope[list.iterator + 'SearchField'] = 'id';
|
||||
scope[list.iterator + 'SearchValue'] = $stateParams.id__int;
|
||||
scope[list.iterator + 'SearchFieldLabel'] = 'ID';
|
||||
}
|
||||
|
||||
scope.search(list.iterator, null, null, null, null, spinner);
|
||||
};
|
||||
}]);
|
||||
|
||||
@ -17,12 +17,12 @@ import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', listGenerator.name,
|
||||
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper', 'VariablesHelper',
|
||||
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'VariablesHelper',
|
||||
])
|
||||
|
||||
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
|
||||
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'OrganizationList',
|
||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON',
|
||||
function (InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait,
|
||||
function (InventoryForm, Rest, Alert, ProcessErrors, OrganizationList, GetBasePath, ParseTypeChange, Wait,
|
||||
ToJSON) {
|
||||
return function (params) {
|
||||
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:refresh-related
|
||||
* @description
|
||||
* discussion
|
||||
* RefreshRelatedHelper
|
||||
*
|
||||
* Used to refresh a related set whenever pagination or filter options change.
|
||||
*
|
||||
* RefreshRelated({
|
||||
* scope: <current scope>,
|
||||
* set: <model>,
|
||||
* iterator: <model name in singular form (i.e. organization),
|
||||
* url: <the api url to call>
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export default
|
||||
angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
|
||||
.factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup',
|
||||
function (ProcessErrors, Rest, Wait, PageRangeSetup) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
set = params.set,
|
||||
iterator = params.iterator,
|
||||
url = params.url;
|
||||
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
PageRangeSetup({
|
||||
scope: scope,
|
||||
count: data.count,
|
||||
next: data.next,
|
||||
previous: data.previous,
|
||||
iterator: iterator
|
||||
});
|
||||
scope[set] = data.results;
|
||||
scope[iterator + 'Loading'] = false;
|
||||
scope[iterator + 'HoldInput'] = false;
|
||||
Wait('stop');
|
||||
scope.$emit('related' + set);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
|
||||
});
|
||||
};
|
||||
}
|
||||
]);
|
||||
@ -1,104 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:refresh
|
||||
* @description
|
||||
* RefreshHelper
|
||||
*
|
||||
* Used to refresh a related set whenever pagination or filter options change.
|
||||
*
|
||||
* RefreshRelated({
|
||||
* scope: <current scope>,
|
||||
* set: <model>,
|
||||
* iterator: <model name in singular form (i.e. organization),
|
||||
* url: <the api url to call>
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export default
|
||||
angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
|
||||
.factory('Refresh', ['$rootScope', '$location', 'ProcessErrors', 'Rest', 'Wait', 'Empty', 'PageRangeSetup', 'pagination', function ($rootScope, $location, ProcessErrors, Rest, Wait, Empty, PageRangeSetup, pagination) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
set = params.set,
|
||||
iterator = params.iterator,
|
||||
deferWaitStop = params.deferWaitStop;
|
||||
|
||||
var getPage = function(url) {
|
||||
scope.current_url = url;
|
||||
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var i, modifier;
|
||||
PageRangeSetup({
|
||||
scope: scope,
|
||||
count: data.count,
|
||||
next: data.next,
|
||||
previous: data.previous,
|
||||
iterator: iterator
|
||||
});
|
||||
for (i = 1; i <= 3; i++) {
|
||||
modifier = (i === 1) ? '' : i;
|
||||
scope[iterator + 'HoldInput' + modifier] = false;
|
||||
}
|
||||
scope[set] = data.results;
|
||||
scope[iterator + 'Loading'] = false;
|
||||
scope[iterator + 'HidePaginator'] = false;
|
||||
if (!deferWaitStop) {
|
||||
Wait('stop');
|
||||
}
|
||||
scope.$emit('PostRefresh', set);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
scope[iterator + 'HoldInput'] = false;
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var id, restUrl, pageSize;
|
||||
|
||||
// if you're editing an object, make sure you're on the right
|
||||
// page to display the element you are editing
|
||||
if (scope.addedItem) {
|
||||
id = scope.addedItem + "";
|
||||
delete scope.addedItem;
|
||||
$rootScope.rowBeingEdited = id;
|
||||
$rootScope.listBeingEdited = set;
|
||||
$rootScope.addedAnItem = true;
|
||||
restUrl = params.url.split("?")[0];
|
||||
pageSize = scope[iterator + '_page_size'];
|
||||
pagination.getInitialPageForList(id, restUrl, pageSize)
|
||||
.then(function (currentPage) {
|
||||
scope.getPage(currentPage, set, iterator);
|
||||
});
|
||||
} else if ($location.$$url.split("/")[1] === params.set && $location.$$url.split("/")[2] && $location.$$url.split("/")[2] !== "add" && !scope.getNewPage) {
|
||||
id = $location.$$url.split("/")[2];
|
||||
restUrl = params.url.split("?")[0];
|
||||
pageSize = scope[iterator + '_page_size'];
|
||||
pagination.getInitialPageForList(id, restUrl, pageSize)
|
||||
.then(function (currentPage) {
|
||||
scope[iterator + '_page'] = currentPage;
|
||||
params.url = params.url + "&page=" + currentPage;
|
||||
getPage(params.url);
|
||||
|
||||
});
|
||||
} else {
|
||||
getPage(params.url);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
]);
|
||||
@ -1,295 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:related-search
|
||||
* @description
|
||||
* RelatedSearchHelper
|
||||
*
|
||||
* All the parts for controlling the search widget on
|
||||
* related collections.
|
||||
*
|
||||
* SearchInit({
|
||||
* scope: <scope>,
|
||||
* relatedSets: <array of related collections {model_name, url, iterator}>,
|
||||
* form: <form object used by FormGenerator>
|
||||
* });
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export default
|
||||
angular.module('RelatedSearchHelper', ['RestServices', 'Utilities', 'RefreshRelatedHelper'])
|
||||
.factory('RelatedSearchInit', ['$timeout', 'Alert', 'Rest', 'RefreshRelated', 'Wait', 'Empty',
|
||||
function ($timeout, Alert, Rest, RefreshRelated, Wait, Empty) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
relatedSets = params.relatedSets,
|
||||
form = params.form, f;
|
||||
|
||||
|
||||
|
||||
// add 'selected' class to the selected li element
|
||||
function setSelectedItem(iterator, label) {
|
||||
$('#' + iterator + 'SearchDropdown' + ' li').each(function() {
|
||||
$(this).removeClass('selected');
|
||||
var link = $(this).find('a');
|
||||
if (label === link.text()) {
|
||||
$(this).addClass('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set default values
|
||||
function setDefaults(inIterator) {
|
||||
var iterator, f, fld, set;
|
||||
for (set in form.related) {
|
||||
if (form.related[set].type !== 'tree' && (inIterator === undefined || inIterator === form.related[set].iterator)) {
|
||||
iterator = form.related[set].iterator;
|
||||
for (fld in form.related[set].fields) {
|
||||
if (form.related[set].fields[fld].key) {
|
||||
if (form.related[set].fields[fld].searchable === undefined || form.related[set].fields[fld].searchable === true) {
|
||||
scope[iterator + 'SearchField'] = fld;
|
||||
scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Empty(scope[iterator + 'SearchField'])) {
|
||||
// A field marked as key may not be 'searchable'. Find the first searchable field.
|
||||
for (fld in form.related[set].fields) {
|
||||
if (form.related[set].fields[fld].searchable === undefined || form.related[set].fields[fld].searchable === true) {
|
||||
scope[iterator + 'SearchField'] = fld;
|
||||
scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope[iterator + 'SortOrder'] = null;
|
||||
scope[iterator + 'SearchType'] = 'icontains';
|
||||
scope[iterator + 'SearchTypeLabel'] = 'Contains';
|
||||
scope[iterator + 'SearchValue'] = null;
|
||||
scope[iterator + 'SelectShow'] = false;
|
||||
//scope[iterator + 'HideSearchType'] = false;
|
||||
scope[iterator + 'ShowStartBtn'] = true;
|
||||
scope[iterator + 'HideAllStartBtn'] = false;
|
||||
|
||||
f = scope[iterator + 'SearchField'];
|
||||
if (form.related[set].fields[f].searchType &&
|
||||
(form.related[set].fields[f].searchType === 'boolean' || form.related[set].fields[f].searchType === 'select')) {
|
||||
scope[iterator + 'SelectShow'] = true;
|
||||
scope[iterator + 'SearchSelectOpts'] = form.related[set].fields[f].searchOptions;
|
||||
}
|
||||
if (form.related[set].fields[f].searchType && form.related[set].fields[f].searchType === 'gtzero') {
|
||||
scope[iterator + "InputHide"] = true;
|
||||
}
|
||||
|
||||
setSelectedItem(iterator, scope[iterator + 'SearchFieldLabel']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDefaults();
|
||||
|
||||
scope.resetSearch = function (iterator) {
|
||||
setDefaults(iterator);
|
||||
scope.search(iterator);
|
||||
};
|
||||
|
||||
// Functions to handle search widget changes
|
||||
scope.setSearchField = function (iterator, fld, label) {
|
||||
|
||||
var f, related;
|
||||
|
||||
for (related in form.related) {
|
||||
if (form.related[related].iterator === iterator) {
|
||||
f = form.related[related].fields[fld];
|
||||
}
|
||||
}
|
||||
|
||||
scope[iterator + 'SearchFieldLabel'] = label;
|
||||
scope[iterator + 'SearchField'] = fld;
|
||||
scope[iterator + 'SearchValue'] = '';
|
||||
scope[iterator + 'SelectShow'] = false;
|
||||
//scope[iterator + 'HideSearchType'] = false;
|
||||
scope[iterator + 'InputHide'] = false;
|
||||
scope[iterator + 'ShowStartBtn'] = true;
|
||||
|
||||
if (f.searchType !== undefined && f.searchType === 'gtzero') {
|
||||
scope[iterator + "InputHide"] = true;
|
||||
scope[iterator + 'ShowStartBtn'] = false;
|
||||
}
|
||||
if (f.searchType !== undefined && (f.searchType === 'boolean' || f.searchType === 'select')) {
|
||||
scope[iterator + 'SelectShow'] = true;
|
||||
scope[iterator + 'SearchSelectOpts'] = f.searchOptions;
|
||||
}
|
||||
|
||||
if (f.searchType !== undefined && f.searchType === 'int') {
|
||||
//scope[iterator + 'HideSearchType'] = true;
|
||||
scope[iterator + 'SearchType'] = 'int';
|
||||
}
|
||||
|
||||
setSelectedItem(iterator, label);
|
||||
|
||||
scope.search(iterator);
|
||||
|
||||
};
|
||||
|
||||
scope.setSearchType = function (model, type, label) {
|
||||
scope[model + 'SearchTypeLabel'] = label;
|
||||
scope[model + 'SearchType'] = type;
|
||||
scope.search(model);
|
||||
};
|
||||
|
||||
scope.startSearch = function (e, iterator) {
|
||||
// If use clicks enter while on input field, start the search
|
||||
if (e.keyCode === 13) {
|
||||
scope.search(iterator);
|
||||
}
|
||||
};
|
||||
|
||||
scope.search = function (iterator) {
|
||||
//scope[iterator + 'SearchSpin'] = true;
|
||||
Wait('start');
|
||||
scope[iterator + 'Loading'] = false;
|
||||
scope[iterator + 'HoldInput'] = true;
|
||||
|
||||
if (scope[iterator + 'SearchValue']) {
|
||||
// User typed a value in input field
|
||||
scope[iterator + 'ShowStartBtn'] = false;
|
||||
}
|
||||
|
||||
if(scope[iterator + 'SearchValue'] && scope[iterator + 'SearchValue'] !== '') {
|
||||
scope[iterator + '_active_search'] = true;
|
||||
}
|
||||
else {
|
||||
scope[iterator + '_active_search'] = false;
|
||||
}
|
||||
|
||||
if (iterator === 'host') {
|
||||
if (scope.hostSearchField === 'has_active_failures') {
|
||||
if (scope.hostSearchSelectValue && scope.hostSearchSelectValue.value === 1) {
|
||||
scope.hostFailureFilter = true;
|
||||
} else {
|
||||
scope.hostFailureFilter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fld, key, set, url, sort_order;
|
||||
for (key in relatedSets) {
|
||||
if (relatedSets[key].iterator === iterator) {
|
||||
set = key;
|
||||
url = relatedSets[key].url;
|
||||
for (fld in form.related[key].fields) {
|
||||
if (form.related[key].fields[fld].key) {
|
||||
if (form.related[key].fields[fld].desc) {
|
||||
sort_order = '-' + fld;
|
||||
} else {
|
||||
sort_order = fld;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sort_order = (scope[iterator + 'SortOrder'] === null) ? sort_order : scope[iterator + 'SortOrder'];
|
||||
f = form.related[set].fields[scope[iterator + 'SearchField']];
|
||||
if ((scope[iterator + 'SelectShow'] === false && !Empty(scope[iterator + 'SearchValue'])) ||
|
||||
(scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) ||
|
||||
(f.searchType && f.searchType === 'gtzero')) {
|
||||
if (f.sourceModel) {
|
||||
// handle fields whose source is a related model e.g. inventories.organization
|
||||
scope[iterator + 'SearchParams'] = f.sourceModel + '__' + f.sourceField + '__';
|
||||
} else if (f.searchField) {
|
||||
scope[iterator + 'SearchParams'] = f.searchField + '__';
|
||||
} else {
|
||||
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__';
|
||||
}
|
||||
|
||||
if (f.searchType && (f.searchType === 'int' || f.searchType === 'boolean')) {
|
||||
scope[iterator + 'SearchParams'] += 'int=';
|
||||
} else if (f.searchType && f.searchType === 'gtzero') {
|
||||
scope[iterator + 'SearchParams'] += 'gt=0';
|
||||
} else {
|
||||
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '=';
|
||||
}
|
||||
|
||||
if (f.searchType && (f.searchType === 'boolean' || f.searchType === 'select')) {
|
||||
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue'].value;
|
||||
} else if (f.searchType === undefined || f.searchType === 'gtzero') {
|
||||
scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue']);
|
||||
}
|
||||
scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + encodeURI(sort_order) : '';
|
||||
} else {
|
||||
scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + encodeURI(sort_order) : '';
|
||||
}
|
||||
scope[iterator + '_page'] = 1;
|
||||
url += (url.match(/\/$/)) ? '?' : '&';
|
||||
url += scope[iterator + 'SearchParams'];
|
||||
url += (scope[iterator + '_page_size']) ? '&page_size=' + scope[iterator + '_page_size'] : "";
|
||||
if (scope[iterator + 'SearchFilters']){
|
||||
url += _.reduce(scope[iterator+'SearchFilters'], (result, filter) => result + '&' + filter.url, '');
|
||||
}
|
||||
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
|
||||
};
|
||||
|
||||
|
||||
scope.$on("refreshList", function(e, iterator) {
|
||||
scope.search(iterator);
|
||||
});
|
||||
|
||||
scope.sort = function (iterator, fld) {
|
||||
var sort_order, icon, direction, set;
|
||||
|
||||
// reset sort icons back to 'icon-sort' on all columns
|
||||
// except the one clicked
|
||||
$('.' + iterator + ' .list-header').each(function () {
|
||||
if ($(this).attr('id') !== iterator + '-' + fld + '-header') {
|
||||
var icon = $(this).find('i');
|
||||
icon.attr('class', 'fa fa-sort');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle the icon for the clicked column
|
||||
// and set the sort direction
|
||||
icon = $('#' + iterator + '-' + fld + '-header i');
|
||||
direction = '';
|
||||
if (icon.hasClass('fa-sort')) {
|
||||
icon.removeClass('fa-sort');
|
||||
icon.addClass('fa-sort-up');
|
||||
} else if (icon.hasClass('fa-sort-up')) {
|
||||
icon.removeClass('fa-sort-up');
|
||||
icon.addClass('fa-sort-down');
|
||||
direction = '-';
|
||||
} else if (icon.hasClass('fa-sort-down')) {
|
||||
icon.removeClass('fa-sort-down');
|
||||
icon.addClass('fa-sort-up');
|
||||
}
|
||||
|
||||
// Set the sorder order value and call the API to refresh the list with the new order
|
||||
for (set in form.related) {
|
||||
if (form.related[set].iterator === iterator) {
|
||||
if (form.related[set].fields[fld].sourceModel) {
|
||||
sort_order = direction + form.related[set].fields[fld].sourceModel + '__' +
|
||||
form.related[set].fields[fld].sourceField;
|
||||
} else {
|
||||
sort_order = direction + fld;
|
||||
}
|
||||
}
|
||||
}
|
||||
scope[iterator + 'SortOrder'] = sort_order;
|
||||
scope.search(iterator);
|
||||
};
|
||||
};
|
||||
}
|
||||
]);
|
||||
@ -1,536 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:search
|
||||
* @description
|
||||
* SearchHelper
|
||||
*
|
||||
* All the parts for controlling the search widget on
|
||||
* related collections.
|
||||
*
|
||||
* SearchInit({
|
||||
* scope: <scope>,
|
||||
* set: <model name (i.e. organizations) used in ng-repeat>
|
||||
* url: <default api url used to load data>
|
||||
* list: <list object used by ListGenerator>
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export default
|
||||
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
|
||||
|
||||
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store',
|
||||
function (Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
set = params.set,
|
||||
defaultUrl = params.url,
|
||||
list = params.list,
|
||||
iterator = (params.iterator) ? params.iterator : list.iterator,
|
||||
setWidgets = (params.setWidgets === false) ? false : true,
|
||||
sort_order = params.sort_order || '',
|
||||
widgets, i, modifier;
|
||||
|
||||
|
||||
// add 'selected' class to the selected li element
|
||||
function setSelectedItem(iterator, label, modifier) {
|
||||
// add 'selected' class to the selected li element
|
||||
$('#' + iterator + 'SearchDropdown' + modifier + ' li').each(function() {
|
||||
$(this).removeClass('selected');
|
||||
var link = $(this).find('a');
|
||||
if (label === link.text()) {
|
||||
$(this).addClass('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setDefaults(widget) {
|
||||
// Set default values
|
||||
var f, fld, fka, modifier;
|
||||
modifier = (widget === undefined || widget === 1) ? '' : widget;
|
||||
scope[iterator + 'SearchField' + modifier] = '';
|
||||
scope[iterator + 'SearchFieldLabel' + modifier] = '';
|
||||
for (fld in list.fields) {
|
||||
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
|
||||
list.fields[fld].searchWidget === widget) {
|
||||
if (list.fields[fld].key) {
|
||||
if (list.fields[fld].sourceModel) {
|
||||
fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
|
||||
sort_order = (list.fields[fld].desc) ? '-' + fka : fka;
|
||||
} else {
|
||||
sort_order = (list.fields[fld].desc) ? '-' + fld : fld;
|
||||
}
|
||||
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
|
||||
scope[iterator + 'SearchField' + modifier] = fld;
|
||||
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default the search field to 'defaultSearchField', if one exists
|
||||
for (fld in list.fields) {
|
||||
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
|
||||
list.fields[fld].searchWidget === widget) {
|
||||
if (list.fields[fld].defaultSearchField) {
|
||||
scope[iterator + 'SearchField' + modifier] = fld;
|
||||
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A field marked as key may not be 'searchable', and there might not be a 'defaultSearchField',
|
||||
// so find the first searchable field.
|
||||
if (Empty(scope[iterator + 'SearchField' + modifier])) {
|
||||
for (fld in list.fields) {
|
||||
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
|
||||
list.fields[fld].searchWidget === widget) {
|
||||
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
|
||||
scope[iterator + 'SearchField' + modifier] = fld;
|
||||
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
scope[iterator + 'SearchType' + modifier] = 'icontains';
|
||||
scope[iterator + 'SearchTypeLabel' + modifier] = 'Contains';
|
||||
scope[iterator + 'SearchParams' + modifier] = '';
|
||||
scope[iterator + 'SearchValue' + modifier] = '';
|
||||
scope[iterator + 'SelectShow' + modifier] = false; // show/hide the Select
|
||||
scope[iterator + 'HideSearchType' + modifier] = false;
|
||||
scope[iterator + 'InputDisable' + modifier] = false;
|
||||
scope[iterator + 'ExtraParms' + modifier] = '';
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = true;
|
||||
scope[iterator + 'HideAllStartBtn' + modifier] = false;
|
||||
|
||||
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
|
||||
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
|
||||
// if set to a scope variable
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder];
|
||||
} else {
|
||||
// Set to a string value in the list definition
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder;
|
||||
}
|
||||
} else {
|
||||
// Default value
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
|
||||
}
|
||||
|
||||
scope[iterator + 'InputDisable' + modifier] =
|
||||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject === 'all') ? true : false;
|
||||
|
||||
f = scope[iterator + 'SearchField' + modifier];
|
||||
if (list.fields[f]) {
|
||||
if (list.fields[f].searchType && (list.fields[f].searchType === 'boolean' ||
|
||||
list.fields[f].searchType === 'select')) {
|
||||
scope[iterator + 'SelectShow' + modifier] = true;
|
||||
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[f].searchOptions;
|
||||
}
|
||||
if (list.fields[f].searchType && list.fields[f].searchType === 'int') {
|
||||
scope[iterator + 'HideSearchType' + modifier] = true;
|
||||
}
|
||||
if (list.fields[f].searchType && list.fields[f].searchType === 'gtzero') {
|
||||
scope[iterator + 'InputHide' + modifier] = true;
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedItem(iterator, scope[iterator + 'SearchFieldLabel' + modifier], modifier);
|
||||
}
|
||||
|
||||
if (setWidgets) {
|
||||
// Set default values for each search widget on the page
|
||||
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
|
||||
for (i = 1; i <= widgets; i++) {
|
||||
modifier = (i === 1) ? '' : i;
|
||||
if ($('#search-widget-container' + modifier)) {
|
||||
setDefaults(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope[iterator + '_current_search_params'] = {
|
||||
set: set,
|
||||
defaultUrl: defaultUrl,
|
||||
list: list,
|
||||
iterator: iterator,
|
||||
sort_order: sort_order
|
||||
};
|
||||
|
||||
Store(iterator + '_current_search_params', scope[iterator + '_current_search_params']);
|
||||
Store('CurrentSearchParams', scope[iterator + '_current_search_params']); // Keeping this around for activity stream
|
||||
|
||||
|
||||
// Functions to handle search widget changes
|
||||
scope.setSearchField = function (iterator, fld, label, widget) {
|
||||
|
||||
var modifier = (widget === undefined || widget === 1) ? '' : widget;
|
||||
scope[iterator + 'SearchFieldLabel' + modifier] = label;
|
||||
scope[iterator + 'SearchField' + modifier] = fld;
|
||||
scope[iterator + 'SearchValue' + modifier] = '';
|
||||
scope[iterator + 'SelectShow' + modifier] = false;
|
||||
scope[iterator + 'HideSearchType' + modifier] = false;
|
||||
scope[iterator + 'InputHide' + modifier] = false;
|
||||
scope[iterator + 'SearchType' + modifier] = 'icontains';
|
||||
scope[iterator + 'InputDisable' + modifier] = (list.fields[fld].searchObject === 'all') ? true : false;
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = true;
|
||||
|
||||
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
|
||||
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
|
||||
// if set to a scope variable
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' +
|
||||
modifier]].searchPlaceholder];
|
||||
} else {
|
||||
// Set to a string value in the list definition
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' +
|
||||
modifier]].searchPlaceholder;
|
||||
}
|
||||
} else {
|
||||
// Default value
|
||||
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
|
||||
}
|
||||
|
||||
if (list.fields[fld].searchType && list.fields[fld].searchType === 'gtzero') {
|
||||
scope[iterator + "InputDisable" + modifier] = true;
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = false;
|
||||
scope.search(iterator);
|
||||
} else if (list.fields[fld].searchSingleValue) {
|
||||
// Query a specific attribute for one specific value
|
||||
// searchSingleValue: true
|
||||
// searchType: 'boolean|int|etc.'
|
||||
// searchValue: < value to match for boolean use 'true'|'false' >
|
||||
scope[iterator + 'InputDisable' + modifier] = true;
|
||||
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
|
||||
// For boolean type, SearchValue must be an object
|
||||
if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'true') {
|
||||
scope[iterator + "SearchSelectValue" + modifier] = {
|
||||
value: 1
|
||||
};
|
||||
} else if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'false') {
|
||||
scope[iterator + "SearchSelectValue" + modifier] = {
|
||||
value: 0
|
||||
};
|
||||
} else {
|
||||
scope[iterator + "SearchSelectValue" + modifier] = {
|
||||
value: list.fields[fld].searchValue
|
||||
};
|
||||
}
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = false;
|
||||
} else if (list.fields[fld].searchType === 'in') {
|
||||
scope[iterator + "SearchType" + modifier] = 'in';
|
||||
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
|
||||
scope[iterator + "InputDisable" + modifier] = true;
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = false;
|
||||
} else if (list.fields[fld].searchType && (list.fields[fld].searchType === 'boolean' ||
|
||||
list.fields[fld].searchType === 'select' || list.fields[fld].searchType === 'select_or')) {
|
||||
scope[iterator + 'SelectShow' + modifier] = true;
|
||||
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions;
|
||||
scope[iterator + 'SearchType' + modifier] = '';
|
||||
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'int') {
|
||||
//scope[iterator + 'HideSearchType' + modifier] = true;
|
||||
scope[iterator + 'SearchType' + modifier] = 'int';
|
||||
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'isnull') {
|
||||
scope[iterator + 'SearchType' + modifier] = 'isnull';
|
||||
scope[iterator + 'InputDisable' + modifier] = true;
|
||||
scope[iterator + 'SearchValue' + modifier] = 'true';
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = false;
|
||||
}
|
||||
|
||||
setSelectedItem(iterator, label, modifier);
|
||||
|
||||
scope.search(iterator);
|
||||
};
|
||||
|
||||
scope.resetSearch = function (iterator) {
|
||||
// Respdond to click of reset button
|
||||
var i,
|
||||
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
|
||||
|
||||
for (i = 1; i <= widgets; i++) {
|
||||
// Clear each search widget
|
||||
setDefaults(i);
|
||||
}
|
||||
// Force removal of search keys from the URL
|
||||
window.location = '/#' + $location.path();
|
||||
scope.search(iterator);
|
||||
};
|
||||
|
||||
if (scope.removeDoSearch) {
|
||||
scope.removeDoSearch();
|
||||
}
|
||||
scope.removeDoSearch = scope.$on('doSearch', function (e, iterator, page, load, calcOnly, deferWaitStop) {
|
||||
//
|
||||
// Execute the search
|
||||
//
|
||||
var url = (calcOnly) ? '' : defaultUrl,
|
||||
connect;
|
||||
|
||||
if (!calcOnly) {
|
||||
scope[iterator + 'Loading'] = (load === undefined || load === true) ? true : false;
|
||||
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
|
||||
}
|
||||
|
||||
//finalize and execute the query
|
||||
if (scope[iterator + 'SearchParams']) {
|
||||
if (/\/$/.test(url)) {
|
||||
url += '?' + scope[iterator + 'SearchParams'];
|
||||
} else {
|
||||
url += '&' + scope[iterator + 'SearchParams'];
|
||||
}
|
||||
}
|
||||
connect = (/\/$/.test(url)) ? '?' : '&';
|
||||
url += (scope[iterator + '_page_size']) ? connect + 'page_size=' + scope[iterator + '_page_size'] : "";
|
||||
if (page) {
|
||||
connect = (/\/$/.test(url)) ? '?' : '&';
|
||||
url += connect + 'page=' + page;
|
||||
}
|
||||
if (scope[iterator + 'ExtraParms']) {
|
||||
connect = (/\/$/.test(url)) ? '?' : '&';
|
||||
url += connect + scope[iterator + 'ExtraParms'];
|
||||
}
|
||||
url = url.replace(/\&\&/g, '&').replace(/\?\&/,'?');
|
||||
if (scope[iterator + 'SearchFilters']){
|
||||
url += _.reduce(scope[iterator+'SearchFilters'], (result, filter) => result + '&' + filter.url, '');
|
||||
}
|
||||
if (calcOnly) {
|
||||
scope.$emit('searchParamsReady', url);
|
||||
}
|
||||
else if (defaultUrl && !/undefined/.test(url)) {
|
||||
Refresh({
|
||||
scope: scope,
|
||||
set: set,
|
||||
iterator: iterator,
|
||||
url: url,
|
||||
deferWaitStop: deferWaitStop
|
||||
});
|
||||
}
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
if (scope.removePrepareSearch) {
|
||||
scope.removePrepareSearch();
|
||||
}
|
||||
scope.removePrepareSearch = scope.$on('prepareSearch', function (e, iterator, page, load, calcOnly, deferWaitStop, spinner) {
|
||||
// Start the search spinner
|
||||
if (spinner) {
|
||||
Wait('start');
|
||||
}
|
||||
e.stopPropagation();
|
||||
scope.$emit('prepareSearch2', iterator, page, load, calcOnly, deferWaitStop);
|
||||
|
||||
});
|
||||
|
||||
if (scope.removePrepareSearch2) {
|
||||
scope.removePrepareSearch2();
|
||||
}
|
||||
scope.removePrepareSearch2 = scope.$on('prepareSearch2', function (e, iterator, page, load, calcOnly, deferWaitStop) {
|
||||
// Continue building the search by examining the remaining search widgets. If we're looking at activity_stream,
|
||||
// there's more than one.
|
||||
var i, modifier,
|
||||
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
|
||||
|
||||
// Initialize SearchParams as an empty string if it's not defined. If we don't do this and SearchParams === undefined
|
||||
// then 'undefined' will sneak into the string as we are concatenating and the request will never get sent since we
|
||||
// regex search for 'undefined' in the doSearch section of this process.
|
||||
scope[iterator + 'SearchParams'] = (!scope[iterator + 'SearchParams'] || scope[iterator + 'SearchParams'] === undefined) ? '' : scope[iterator + 'SearchParams'];
|
||||
|
||||
for (i = 1; i <= widgets; i++) {
|
||||
modifier = (i === 1) ? '' : i;
|
||||
scope[iterator + 'HoldInput' + modifier] = true;
|
||||
if ($('#search-widget-container' + modifier) &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]] && !list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
|
||||
if (scope[iterator + 'SearchValue' + modifier]) {
|
||||
// if user typed a value in the input box, show the reset link
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = false;
|
||||
} else {
|
||||
scope[iterator + 'ShowStartBtn' + modifier] = true;
|
||||
}
|
||||
if ((!scope[iterator + 'SelectShow' + modifier] && !Empty(scope[iterator + 'SearchValue' + modifier])) ||
|
||||
(scope[iterator + 'SelectShow' + modifier] && scope[iterator + 'SearchSelectValue' + modifier]) ||
|
||||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero')) {
|
||||
scope[iterator + '_active_search'] = true;
|
||||
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchField) {
|
||||
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '__';
|
||||
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel) {
|
||||
// handle fields whose source is a related model e.g. inventories.organization
|
||||
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' +
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__';
|
||||
} else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' &&
|
||||
Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) {
|
||||
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
|
||||
} else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' &&
|
||||
!Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) {
|
||||
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier];
|
||||
} else {
|
||||
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
|
||||
}
|
||||
|
||||
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
|
||||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'int' ||
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean')) {
|
||||
scope[iterator + 'SearchParams'] += 'int=';
|
||||
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero') {
|
||||
scope[iterator + 'SearchParams'] += 'gt=0';
|
||||
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'isnull') {
|
||||
scope[iterator + 'SearchParams'] += 'isnull=';
|
||||
} else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
|
||||
Empty(scope[iterator + 'SearchSelectValue' + modifier].value) && !/\_\_$/.test(scope[iterator + 'SearchParams']) ) {
|
||||
scope[iterator + 'SearchParams'] += '=iexact=';
|
||||
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'in') {
|
||||
if (!/\_\_$/.test(scope[iterator + 'SearchParams'])) {
|
||||
scope[iterator + 'SearchParams'] += '__';
|
||||
}
|
||||
scope[iterator + 'SearchParams'] += 'in=';
|
||||
} else if (/\_\_$/.test(scope[iterator + 'SearchParams'])) {
|
||||
scope[iterator + 'SearchParams'] += 'icontains=';
|
||||
} else {
|
||||
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '=';
|
||||
}
|
||||
|
||||
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
|
||||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean' ||
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select')) {
|
||||
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue' + modifier].value;
|
||||
} else {
|
||||
if ((!list.fields[scope[iterator + 'SearchField' + modifier]].searchType) ||
|
||||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'or' &&
|
||||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'gtzero')) {
|
||||
scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue' + modifier]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((iterator === 'inventory' && scope.inventoryFailureFilter) ||
|
||||
(iterator === 'host' && scope.hostFailureFilter)) {
|
||||
//Things that bypass the search widget. Should go back and add a second widget possibly on
|
||||
//inventory pages and eliminate this
|
||||
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
|
||||
}
|
||||
|
||||
if (sort_order) {
|
||||
scope[iterator + 'SearchParams'] = 'order_by=' + encodeURI(sort_order);
|
||||
}
|
||||
e.stopPropagation();
|
||||
scope.$emit('doSearch', iterator, page, load, calcOnly, deferWaitStop);
|
||||
});
|
||||
|
||||
scope.startSearch = function (e, iterator) {
|
||||
// If use clicks enter while on input field, start the search
|
||||
if (e.keyCode === 13) {
|
||||
scope.search(iterator);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiate a searh.
|
||||
*
|
||||
* @iterator: required, list.iterator value
|
||||
* @Page: optional. Added to accomodate back function on Job Events detail.
|
||||
* @Load: optional, set to false if 'Loading' message not desired
|
||||
* @calcOnly: optional, set to true when you want to calc or figure out search params without executing the search
|
||||
* @deferWaitStop: optional, when true refresh.js will NOT issue Wait('stop'), thus leaving the spinner. Caller is then
|
||||
* responsible for stopping the spinner post refresh.
|
||||
* @spinner: optional, if false, don't show the spinner.
|
||||
*/
|
||||
scope.search = function (iterator, page, load, calcOnly, deferWaitStop, spinner) {
|
||||
page = page || null;
|
||||
load = (load || !scope[set] || scope[set].length === 0) ? true : false;
|
||||
calcOnly = (calcOnly) ? true : false;
|
||||
deferWaitStop = (deferWaitStop) ? true : false;
|
||||
spinner = (spinner === undefined) ? true : spinner;
|
||||
if (load) {
|
||||
scope[set] = []; //clear the list array to make sure 'Loading' is the only thing visible on the list
|
||||
}
|
||||
|
||||
if(scope[iterator + 'SearchFilters'] && scope[iterator + 'SearchFilters'].length > 0) {
|
||||
scope[iterator + '_active_search'] = true;
|
||||
}
|
||||
else {
|
||||
scope[iterator + '_active_search'] = false;
|
||||
}
|
||||
|
||||
scope.$emit('prepareSearch', iterator, page, load, calcOnly, deferWaitStop, spinner);
|
||||
};
|
||||
|
||||
|
||||
scope.sort = function (iterator, fld) {
|
||||
// resets any existing order_by parameters in $scope.current_url;
|
||||
var resetOrderBy = function(){
|
||||
var url = _.filter(scope.current_url.split('&'), (o) => !o.includes('order_by'));
|
||||
scope.current_url = url.join('&');
|
||||
};
|
||||
resetOrderBy();
|
||||
// Reset sort icons back to 'icon-sort' on all columns
|
||||
// except the one clicked.
|
||||
$('.list-header').each(function () {
|
||||
if ($(this).attr('id') !== iterator + '-' + fld + '-header') {
|
||||
var icon = $(this).find('i');
|
||||
icon.attr('class', 'fa fa-sort');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle the icon for the clicked column
|
||||
// and set the sort direction
|
||||
var icon = $('#' + iterator + '-' + fld + '-header i'),
|
||||
direction = '';
|
||||
if (icon.hasClass('fa-sort')) {
|
||||
icon.removeClass('fa-sort');
|
||||
icon.addClass('fa-sort-up');
|
||||
} else if (icon.hasClass('fa-sort-up')) {
|
||||
icon.removeClass('fa-sort-up');
|
||||
icon.addClass('fa-sort-down');
|
||||
direction = '-';
|
||||
} else if (icon.hasClass('fa-sort-down')) {
|
||||
icon.removeClass('fa-sort-down');
|
||||
icon.addClass('fa-sort-up');
|
||||
}
|
||||
|
||||
// Set the sorder order value and call the API to refresh the list with the new order
|
||||
if (list.fields[fld].searchField) {
|
||||
sort_order = direction + list.fields[fld].searchField;
|
||||
} else if (list.fields[fld].sortField) {
|
||||
sort_order = direction + list.fields[fld].sortField;
|
||||
} else {
|
||||
if (list.fields[fld].sourceModel) {
|
||||
sort_order = direction + list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
|
||||
} else {
|
||||
sort_order = direction + fld;
|
||||
}
|
||||
}
|
||||
|
||||
scope[list.iterator + '_current_search_params'].sort_order = sort_order;
|
||||
Store(iterator + '_current_search_params', scope[iterator + '_current_search_params']);
|
||||
scope.search(list.iterator);
|
||||
};
|
||||
|
||||
// Call after modal dialogs to remove any lingering callbacks
|
||||
scope.searchCleanup = function () {
|
||||
scope.removeDoSearch();
|
||||
scope.removePrepareSearch();
|
||||
scope.removePrepareSearch2();
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
]);
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:teams
|
||||
@ -15,8 +15,7 @@
|
||||
import listGenerator from '../shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('TeamHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'SearchHelper',
|
||||
'PaginationHelpers', listGenerator.name
|
||||
angular.module('TeamHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', listGenerator.name
|
||||
])
|
||||
.factory('SetTeamListeners', ['Alert', 'Rest',
|
||||
function (Alert, Rest) {
|
||||
@ -40,7 +39,10 @@ export default
|
||||
}
|
||||
}
|
||||
}
|
||||
scope[iterator + 'SearchSpin'] = false;
|
||||
|
||||
// @issue: OLD SEARCH
|
||||
// scope[iterator + 'SearchSpin'] = false;
|
||||
|
||||
scope[set] = results;
|
||||
}
|
||||
});
|
||||
@ -74,8 +76,8 @@ export default
|
||||
}
|
||||
])
|
||||
|
||||
.factory('TeamLookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'generateList', 'SearchInit', 'PaginateInit',
|
||||
function (Alert, Rest, OrganizationList, GenerateList, SearchInit, PaginateInit) {
|
||||
.factory('TeamLookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'generateList',
|
||||
function (Alert, Rest, OrganizationList, GenerateList) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope;
|
||||
@ -115,19 +117,21 @@ export default
|
||||
}
|
||||
};
|
||||
|
||||
SearchInit({
|
||||
scope: listScope,
|
||||
set: list.name,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: listScope,
|
||||
list: list,
|
||||
url: defaultUrl,
|
||||
mode: 'lookup'
|
||||
});
|
||||
scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// SearchInit({
|
||||
// scope: listScope,
|
||||
// set: list.name,
|
||||
// list: list,
|
||||
// url: defaultUrl
|
||||
// });
|
||||
// PaginateInit({
|
||||
// scope: listScope,
|
||||
// list: list,
|
||||
// url: defaultUrl,
|
||||
// mode: 'lookup'
|
||||
// });
|
||||
// scope.search(list.iterator);
|
||||
|
||||
listScope.toggle_organization(scope.organization);
|
||||
};
|
||||
};
|
||||
|
||||
@ -11,9 +11,8 @@
|
||||
*/
|
||||
|
||||
function InventoriesAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, generateList, OrganizationList, SearchInit,
|
||||
PaginateInit, LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
$stateParams, GenerateForm, InventoryForm, Rest, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
$state) {
|
||||
|
||||
Rest.setUrl(GetBasePath('inventory'));
|
||||
@ -29,36 +28,28 @@ function InventoriesAdd($scope, $rootScope, $compile, $location, $log,
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('inventory'),
|
||||
form = InventoryForm(),
|
||||
generator = GenerateForm;
|
||||
form = InventoryForm();
|
||||
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
init();
|
||||
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
function init() {
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
|
||||
generator.reset();
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'inventory_variables'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: ($stateParams.organization_id) ? $stateParams.organization_id : null,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'inventory_variables'
|
||||
});
|
||||
}
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
generator.clearApiErrors();
|
||||
$scope.formSave = function() {
|
||||
Wait('start');
|
||||
try {
|
||||
var fld, json_data, data;
|
||||
@ -68,22 +59,24 @@ function InventoriesAdd($scope, $rootScope, $compile, $location, $log,
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].realName) {
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
var inventory_id = data.id;
|
||||
Wait('stop');
|
||||
$location.path('/inventories/' + inventory_id + '/manage');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors( $scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to add new inventory. Post returned status: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add new inventory. Post returned status: ' + status
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
Wait('stop');
|
||||
@ -92,13 +85,13 @@ function InventoriesAdd($scope, $rootScope, $compile, $location, $log,
|
||||
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('inventories');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('inventories');
|
||||
};
|
||||
}
|
||||
|
||||
export default['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit',
|
||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', '$state', InventoriesAdd];
|
||||
export default ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'GenerateForm', 'InventoryForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange',
|
||||
'Wait', 'ToJSON', '$state', InventoriesAdd
|
||||
];
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesAdd from './inventory-add.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories.add',
|
||||
route: '/add',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "inventories",
|
||||
label: "CREATE INVENTORY"
|
||||
}
|
||||
};
|
||||
@ -4,10 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-add.route';
|
||||
import controller from './inventory-add.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryAdd', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
angular.module('inventoryAdd', [])
|
||||
.controller('InventoryAddController', controller);
|
||||
|
||||
@ -11,88 +11,62 @@
|
||||
*/
|
||||
|
||||
function InventoriesEdit($scope, $rootScope, $compile, $location,
|
||||
$log, $stateParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, generateList, OrganizationList, SearchInit,
|
||||
PaginateInit, LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
ParseVariableString, RelatedSearchInit, RelatedPaginateInit,
|
||||
Prompt, InitiatePlaybookRun, CreateDialog, deleteJobTemplate, $state,
|
||||
$filter) {
|
||||
ClearScope();
|
||||
$log, $stateParams, InventoryForm, Rest, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
ParseVariableString, Prompt, InitiatePlaybookRun,
|
||||
deleteJobTemplate, $state, $filter) {
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('inventory'),
|
||||
form = InventoryForm(),
|
||||
generator = GenerateForm,
|
||||
inventory_id = $stateParams.inventory_id,
|
||||
master = {},
|
||||
fld, json_data, data,
|
||||
relatedSets = {};
|
||||
fld, json_data, data;
|
||||
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
$scope.inventory_id = inventory_id;
|
||||
ClearScope();
|
||||
init();
|
||||
|
||||
$scope.$watch('invnentory_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
function init() {
|
||||
ClearScope();
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
$scope.inventory_id = inventory_id;
|
||||
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
|
||||
generator.reset();
|
||||
|
||||
|
||||
// After the project is loaded, retrieve each related set
|
||||
if ($scope.inventoryLoadedRemove) {
|
||||
$scope.inventoryLoadedRemove();
|
||||
$scope.$watch('invnentory_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
$scope.canAdd = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
$scope.projectLoadedRemove = $scope.$on('inventoryLoaded', function () {
|
||||
var set;
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
.success(function(data) {
|
||||
var fld;
|
||||
for (fld in form.fields) {
|
||||
if (fld === 'variables') {
|
||||
$scope.variables = ParseVariableString(data.variables);
|
||||
master.variables = $scope.variables;
|
||||
} else if (fld === 'inventory_name') {
|
||||
$scope[fld] = data.name;
|
||||
$scope[fld] = data.name;
|
||||
master[fld] = $scope[fld];
|
||||
} else if (fld === 'inventory_description') {
|
||||
$scope[fld] = data.description;
|
||||
$scope[fld] = data.description;
|
||||
master[fld] = $scope[fld];
|
||||
} else if (data[fld]) {
|
||||
$scope[fld] = data[fld];
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = $scope[fld];
|
||||
}
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
relatedSets = form.relatedSets(data.related);
|
||||
|
||||
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
Wait('stop');
|
||||
$scope.parseType = 'yaml';
|
||||
@ -102,224 +76,94 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'inventory_variables'
|
||||
});
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: $scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
$scope.inventory_obj = data;
|
||||
|
||||
$scope.$emit('inventoryLoaded');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
Wait('start');
|
||||
$scope.formSave = function() {
|
||||
Wait('start');
|
||||
|
||||
// Make sure we have valid variable data
|
||||
json_data = ToJSON($scope.parseType, $scope.variables);
|
||||
// Make sure we have valid variable data
|
||||
json_data = ToJSON($scope.parseType, $scope.variables);
|
||||
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].realName) {
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].realName) {
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
Rest.setUrl(defaultUrl + inventory_id + '/');
|
||||
Rest.put(data)
|
||||
.success(function () {
|
||||
Wait('stop');
|
||||
$state.go($state.current, {}, {reload: true});
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to update inventory. PUT returned status: ' + status });
|
||||
});
|
||||
Rest.setUrl(defaultUrl + inventory_id + '/');
|
||||
Rest.put(data)
|
||||
.success(function() {
|
||||
Wait('stop');
|
||||
$state.go($state.current, {}, { reload: true });
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to update inventory. PUT returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.manageInventory = function(){
|
||||
$location.path($location.path() + '/manage');
|
||||
$scope.manageInventory = function() {
|
||||
$location.path($location.path() + '/manage');
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('inventories');
|
||||
$scope.formCancel = function() {
|
||||
$state.go('inventories');
|
||||
};
|
||||
|
||||
$scope.addScanJob = function(){
|
||||
$location.path($location.path()+'/job_templates/add');
|
||||
$scope.addScanJob = function() {
|
||||
$location.path($location.path() + '/job_templates/add');
|
||||
};
|
||||
|
||||
$scope.launchScanJob = function(){
|
||||
$scope.launchScanJob = function() {
|
||||
InitiatePlaybookRun({ scope: $scope, id: this.scan_job_template.id });
|
||||
};
|
||||
|
||||
$scope.scheduleScanJob = function(){
|
||||
$location.path('/job_templates/'+this.scan_job_template.id+'/schedules');
|
||||
$scope.scheduleScanJob = function() {
|
||||
$location.path('/job_templates/' + this.scan_job_template.id + '/schedules');
|
||||
};
|
||||
|
||||
$scope.editScanJob = function(){
|
||||
$location.path($location.path()+'/job_templates/'+this.scan_job_template.id);
|
||||
$scope.editScanJob = function() {
|
||||
$location.path($location.path() + '/job_templates/' + this.scan_job_template.id);
|
||||
};
|
||||
|
||||
$scope.copyScanJobTemplate = function(){
|
||||
var id = this.scan_job_template.id,
|
||||
name = this.scan_job_template.name,
|
||||
element,
|
||||
buttons = [{
|
||||
"label": "Cancel",
|
||||
"onClick": function() {
|
||||
$(this).dialog('close');
|
||||
},
|
||||
"icon": "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "copy-close-button"
|
||||
},{
|
||||
"label": "Copy",
|
||||
"onClick": function() {
|
||||
copyAction();
|
||||
},
|
||||
"icon": "fa-copy",
|
||||
"class": "btn btn-primary",
|
||||
"id": "job-copy-button"
|
||||
}],
|
||||
copyAction = function () {
|
||||
// retrieve the copy of the job template object from the api, then overwrite the name and throw away the id
|
||||
Wait('start');
|
||||
var url = GetBasePath('job_templates')+id;
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
data.name = $scope.new_copy_name;
|
||||
delete data.id;
|
||||
$scope.$emit('GoToCopy', data);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
CreateDialog({
|
||||
id: 'copy-job-modal' ,
|
||||
title: "Copy",
|
||||
scope: $scope,
|
||||
buttons: buttons,
|
||||
width: 500,
|
||||
height: 300,
|
||||
minWidth: 200,
|
||||
callback: 'CopyDialogReady'
|
||||
});
|
||||
|
||||
$('#job_name').text(name);
|
||||
$('#copy-job-modal').show();
|
||||
|
||||
|
||||
if ($scope.removeCopyDialogReady) {
|
||||
$scope.removeCopyDialogReady();
|
||||
}
|
||||
$scope.removeCopyDialogReady = $scope.$on('CopyDialogReady', function() {
|
||||
//clear any old remaining text
|
||||
$scope.new_copy_name = "" ;
|
||||
$scope.copy_form.$setPristine();
|
||||
$('#copy-job-modal').dialog('open');
|
||||
$('#job-copy-button').attr('ng-disabled', "!copy_form.$valid");
|
||||
element = angular.element(document.getElementById('job-copy-button'));
|
||||
$compile(element)($scope);
|
||||
|
||||
});
|
||||
|
||||
if ($scope.removeGoToCopy) {
|
||||
$scope.removeGoToCopy();
|
||||
}
|
||||
$scope.removeGoToCopy = $scope.$on('GoToCopy', function(e, data) {
|
||||
var url = GetBasePath('job_templates'),
|
||||
old_survey_url = (data.related.survey_spec) ? data.related.survey_spec : "" ;
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
if(data.survey_enabled===true){
|
||||
$scope.$emit("CopySurvey", data, old_survey_url);
|
||||
}
|
||||
else {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/job_templates/' + data.id);
|
||||
}
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeCopySurvey) {
|
||||
$scope.removeCopySurvey();
|
||||
}
|
||||
$scope.removeCopySurvey = $scope.$on('CopySurvey', function(e, new_data, old_url) {
|
||||
// var url = data.related.survey_spec;
|
||||
Rest.setUrl(old_url);
|
||||
Rest.get()
|
||||
.success(function (survey_data) {
|
||||
|
||||
Rest.setUrl(new_data.related.survey_spec);
|
||||
Rest.post(survey_data)
|
||||
.success(function () {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/job_templates/' + new_data.id);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + new_data.related.survey_spec + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + old_url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.deleteScanJob = function () {
|
||||
var id = this.scan_job_template.id ,
|
||||
action = function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
deleteJobTemplate(id)
|
||||
.success(function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.search(form.related.scan_job_templates.iterator);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
$scope.deleteScanJob = function() {
|
||||
var id = this.scan_job_template.id,
|
||||
action = function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
deleteJobTemplate(id)
|
||||
.success(function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(form.related.scan_job_templates.iterator);
|
||||
})
|
||||
.error(function(data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the job template below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(this.scan_job_template.name) + '</div>',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the job template below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(this.scan_job_template.name) + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
@ -329,11 +173,8 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
|
||||
}
|
||||
|
||||
export default ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit',
|
||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString',
|
||||
'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
|
||||
'InitiatePlaybookRun', 'CreateDialog', 'deleteJobTemplate', '$state',
|
||||
'$filter', InventoriesEdit,
|
||||
'$log', '$stateParams', 'InventoryForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait',
|
||||
'ToJSON', 'ParseVariableString', 'Prompt', 'InitiatePlaybookRun',
|
||||
'deleteJobTemplate', '$state', '$filter', InventoriesEdit,
|
||||
];
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesEdit from './inventory-edit.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories.edit',
|
||||
route: '/:inventory_id',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesEdit,
|
||||
data: {
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'inventories',
|
||||
label: "{{inventory_obj.name}}"
|
||||
}
|
||||
};
|
||||
@ -4,10 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-edit.route';
|
||||
import controller from './inventory-edit.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryEdit', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
.controller('InventoryEditController', controller);
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
<div class="tab-pane" id="inventories">
|
||||
<div ui-view=""></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
</div>
|
||||
@ -11,11 +11,15 @@
|
||||
*/
|
||||
|
||||
function InventoriesList($scope, $rootScope, $location, $log,
|
||||
$stateParams, $compile, $filter, sanitizeFilter, Rest, Alert, InventoryList,
|
||||
generateList, Prompt, SearchInit, PaginateInit, ReturnToCaller,
|
||||
ClearScope, ProcessErrors, GetBasePath, Wait,
|
||||
Find, Empty, $state, rbacUiControlService) {
|
||||
$stateParams, $compile, $filter, sanitizeFilter, Rest, Alert, InventoryList, Prompt,
|
||||
ClearScope, ProcessErrors, GetBasePath, Wait, Find, Empty, $state, rbacUiControlService, Dataset) {
|
||||
|
||||
let list = InventoryList,
|
||||
defaultUrl = GetBasePath('inventory');
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
$scope.canAdd = false;
|
||||
|
||||
rbacUiControlService.canAdd('inventory')
|
||||
@ -23,11 +27,49 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
$scope.canAdd = canAdd;
|
||||
});
|
||||
|
||||
var list = InventoryList,
|
||||
defaultUrl = GetBasePath('inventory') + ($stateParams.status === 'sync-failed' ? '?not__inventory_sources_with_failures=0' : ''),
|
||||
view = generateList,
|
||||
paths = $location.path().replace(/^\//, '').split('/'),
|
||||
mode = (paths[0] === 'inventories') ? 'edit' : 'select';
|
||||
$scope.$watchCollection(list.name, function(){
|
||||
_.forEach($scope[list.name], buildStatusIndicators);
|
||||
});
|
||||
|
||||
// Search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
}
|
||||
|
||||
function buildStatusIndicators(inventory){
|
||||
inventory.launch_class = "";
|
||||
if (inventory.has_inventory_sources) {
|
||||
if (inventory.inventory_sources_with_failures > 0) {
|
||||
inventory.syncStatus = 'error';
|
||||
inventory.syncTip = inventory.inventory_sources_with_failures + ' groups with sync failures. Click for details';
|
||||
}
|
||||
else {
|
||||
inventory.syncStatus = 'successful';
|
||||
inventory.syncTip = 'No inventory sync failures. Click for details.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
inventory.syncStatus = 'na';
|
||||
inventory.syncTip = 'Not configured for inventory sync.';
|
||||
inventory.launch_class = "btn-disabled";
|
||||
}
|
||||
if (inventory.has_active_failures) {
|
||||
inventory.hostsStatus = 'error';
|
||||
inventory.hostsTip = inventory.hosts_with_active_failures + ' hosts with failures. Click for details.';
|
||||
}
|
||||
else if (inventory.total_hosts) {
|
||||
inventory.hostsStatus = 'successful';
|
||||
inventory.hostsTip = 'No hosts with failures. Click for details.';
|
||||
}
|
||||
else {
|
||||
inventory.hostsStatus = 'none';
|
||||
inventory.hostsTip = 'Inventory contains 0 hosts.';
|
||||
}
|
||||
}
|
||||
|
||||
function ellipsis(a) {
|
||||
if (a.length > 20) {
|
||||
@ -62,117 +104,6 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
$scope.triggerPopover(event);
|
||||
}
|
||||
|
||||
view.inject(InventoryList, { mode: mode, scope: $scope });
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'inventories',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
if ($stateParams.name) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = false;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.name;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'name';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.name.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = null;
|
||||
}
|
||||
|
||||
if ($stateParams.has_active_failures) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.has_active_failures;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'has_active_failures';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.has_active_failures.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = ($stateParams.has_active_failures === 'true') ? {
|
||||
value: 1
|
||||
} : {
|
||||
value: 0
|
||||
};
|
||||
}
|
||||
|
||||
if ($stateParams.has_inventory_sources) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.has_inventory_sources;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'has_inventory_sources';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.has_inventory_sources.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = ($stateParams.has_inventory_sources === 'true') ? {
|
||||
value: 1
|
||||
} : {
|
||||
value: 0
|
||||
};
|
||||
}
|
||||
|
||||
if ($stateParams.inventory_sources_with_failures) {
|
||||
// pass a value of true, however this field actually contains an integer value
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.inventory_sources_with_failures;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'inventory_sources_with_failures';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.inventory_sources_with_failures.label;
|
||||
$scope[InventoryList.iterator + 'SearchType'] = 'gtzero';
|
||||
}
|
||||
|
||||
$scope.search(list.iterator);
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
//If we got here by deleting an inventory, stop the spinner and cleanup events
|
||||
Wait('stop');
|
||||
try {
|
||||
$('#prompt-modal').modal('hide');
|
||||
}
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
$scope.inventories.forEach(function(inventory, idx) {
|
||||
$scope.inventories[idx].launch_class = "";
|
||||
if (inventory.has_inventory_sources) {
|
||||
if (inventory.inventory_sources_with_failures > 0) {
|
||||
$scope.inventories[idx].syncStatus = 'error';
|
||||
$scope.inventories[idx].syncTip = inventory.inventory_sources_with_failures + ' groups with sync failures. Click for details';
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].syncStatus = 'successful';
|
||||
$scope.inventories[idx].syncTip = 'No inventory sync failures. Click for details.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].syncStatus = 'na';
|
||||
$scope.inventories[idx].syncTip = 'Not configured for inventory sync.';
|
||||
$scope.inventories[idx].launch_class = "btn-disabled";
|
||||
}
|
||||
if (inventory.has_active_failures) {
|
||||
$scope.inventories[idx].hostsStatus = 'error';
|
||||
$scope.inventories[idx].hostsTip = inventory.hosts_with_active_failures + ' hosts with failures. Click for details.';
|
||||
}
|
||||
else if (inventory.total_hosts) {
|
||||
$scope.inventories[idx].hostsStatus = 'successful';
|
||||
$scope.inventories[idx].hostsTip = 'No hosts with failures. Click for details.';
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].hostsStatus = 'none';
|
||||
$scope.inventories[idx].hostsTip = 'Inventory contains 0 hosts.';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeRefreshInventories) {
|
||||
$scope.removeRefreshInventories();
|
||||
}
|
||||
$scope.removeRefreshInventories = $scope.$on('RefreshInventories', function () {
|
||||
// Reflect changes after inventory properties edit completes
|
||||
$scope.search(list.iterator);
|
||||
});
|
||||
|
||||
if ($scope.removeHostSummaryReady) {
|
||||
$scope.removeHostSummaryReady();
|
||||
}
|
||||
@ -343,7 +274,8 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
if (parseInt($state.params.inventory_id) === id) {
|
||||
$state.go("^", null, {reload: true});
|
||||
} else {
|
||||
$scope.search(list.iterator);
|
||||
// @issue: OLD SEARCH
|
||||
// $scope.search(list.iterator);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
@ -361,15 +293,6 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
});
|
||||
};
|
||||
|
||||
$scope.lookupOrganization = function (organization_id) {
|
||||
Rest.setUrl(GetBasePath('organizations') + organization_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
return data.name;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Failed jobs link. Go to the jobs tabs, find all jobs for the inventory and sort by status
|
||||
$scope.viewJobs = function (id) {
|
||||
$location.url('/jobs/?inventory__int=' + id);
|
||||
@ -382,5 +305,5 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
|
||||
export default ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', '$compile', '$filter', 'sanitizeFilter', 'Rest', 'Alert', 'InventoryList',
|
||||
'generateList', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller',
|
||||
'ClearScope', 'ProcessErrors', 'GetBasePath', 'Wait', 'Find', 'Empty', '$state', 'rbacUiControlService', InventoriesList];
|
||||
'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', 'Wait', 'Find', 'Empty', '$state', 'rbacUiControlService', 'Dataset', InventoriesList
|
||||
];
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesList from './inventory-list.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories',
|
||||
route: '/inventories?{status}',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORIES"
|
||||
}
|
||||
};
|
||||
@ -4,10 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-list.route';
|
||||
import controller from './inventory-list.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryList', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
angular.module('inventoryList', [])
|
||||
.controller('InventoryListController', controller);
|
||||
|
||||
@ -8,11 +8,218 @@ import inventoryAdd from './add/main';
|
||||
import inventoryEdit from './edit/main';
|
||||
import inventoryList from './list/main';
|
||||
import inventoryManage from './manage/main';
|
||||
|
||||
import inventoryManageListRoute from './manage/inventory-manage.route';
|
||||
import { copyMoveGroupRoute, copyMoveHostRoute } from './manage/copy-move/copy-move.route';
|
||||
import adHocRoute from './manage/adhoc/adhoc.route';
|
||||
import { templateUrl } from '../shared/template-url/template-url.factory';
|
||||
export default
|
||||
angular.module('inventory', [
|
||||
inventoryAdd.name,
|
||||
inventoryEdit.name,
|
||||
inventoryList.name,
|
||||
inventoryManage.name,
|
||||
]);
|
||||
inventoryAdd.name,
|
||||
inventoryEdit.name,
|
||||
inventoryList.name,
|
||||
inventoryManage.name,
|
||||
])
|
||||
.config(['$stateProvider', '$stateExtenderProvider', 'stateDefinitionsProvider',
|
||||
function($stateProvider, $stateExtenderProvider, stateDefinitionsProvider) {
|
||||
// When stateDefinition.lazyLoad() resolves, states matching name.** or /url** will be de-registered and replaced with resolved states
|
||||
// This means inventoryManage states will not be registered correctly on page refresh, unless they're registered at the same time as the inventories state tree
|
||||
let stateTree, inventories,
|
||||
addGroup, editGroup, addHost, editHost,
|
||||
listSchedules, addSchedule, editSchedule,
|
||||
stateDefinitions = stateDefinitionsProvider.$get(),
|
||||
stateExtender = $stateExtenderProvider.$get();
|
||||
|
||||
function generateStateTree() {
|
||||
|
||||
// inventories state node
|
||||
inventories = stateDefinitions.generateTree({
|
||||
parent: 'inventories', // top-most node in the generated tree (will replace this state definition)
|
||||
modes: ['add', 'edit'],
|
||||
list: 'InventoryList',
|
||||
form: 'InventoryForm',
|
||||
controllers: {
|
||||
list: 'InventoryListController',
|
||||
add: 'InventoryAddController',
|
||||
edit: 'InventoryEditController'
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory'
|
||||
}
|
||||
});
|
||||
|
||||
// scheduler state nodes
|
||||
listSchedules = {
|
||||
name: 'inventoryManage.editGroup.schedules',
|
||||
url: '/schedules',
|
||||
searchPrefix: 'schedule',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'inventoryManage.editGroup({group_id: parentObject.id})',
|
||||
label: 'SCHEDULES'
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', 'groupData',
|
||||
function(list, qs, $stateParams, GetBasePath, groupData) {
|
||||
let path = `${groupData.related.inventory_source}schedules`;
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
],
|
||||
ParentObject: ['groupData', function(groupData) {
|
||||
return groupData;
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
// clear form template when views render in this substate
|
||||
'form@': {
|
||||
templateProvider: () => ''
|
||||
},
|
||||
'list@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject) {
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
});
|
||||
html = generateList.wrapPanel(html);
|
||||
return html;
|
||||
},
|
||||
controller: 'schedulerListController'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addSchedule = {
|
||||
name: 'inventoryManage.editGroup.schedules.add',
|
||||
url: '/add',
|
||||
ncyBreadcrumb: {
|
||||
label: "CREATE SCHEDULE"
|
||||
},
|
||||
views: {
|
||||
'form@': {
|
||||
controller: 'schedulerAddController',
|
||||
templateUrl: templateUrl("scheduler/schedulerForm")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
editSchedule = {
|
||||
name: 'inventoryManage.editGroup.schedules.edit',
|
||||
url: '/:schedule_id',
|
||||
ncyBreadcrumb: {
|
||||
label: "{{schedule_obj.name}}"
|
||||
},
|
||||
views: {
|
||||
'form@': {
|
||||
templateUrl: templateUrl("scheduler/schedulerForm"),
|
||||
controller: 'schedulerEditController',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// group state nodes
|
||||
addGroup = stateDefinitions.generateTree({
|
||||
url: '/add-group',
|
||||
name: 'inventoryManage.addGroup',
|
||||
modes: ['add'],
|
||||
form: 'GroupForm',
|
||||
controllers: {
|
||||
add: 'GroupAddController'
|
||||
}
|
||||
});
|
||||
|
||||
editGroup = stateDefinitions.generateTree({
|
||||
//parent: 'inventoryManage', // top-most node in the generated tree (tree will replace this node)
|
||||
url: '/edit-group/:group_id',
|
||||
name: 'inventoryManage.editGroup',
|
||||
modes: ['edit'],
|
||||
form: 'GroupForm',
|
||||
controllers: {
|
||||
edit: 'GroupEditController'
|
||||
},
|
||||
resolve: {
|
||||
edit: {
|
||||
groupData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService) {
|
||||
return GroupManageService.get({ id: $stateParams.group_id }).then(res => res.data.results[0]);
|
||||
}],
|
||||
inventorySourceData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService) {
|
||||
return GroupManageService.getInventorySource({ group: $stateParams.group_id }).then(res => res.data.results[0]);
|
||||
}]
|
||||
}
|
||||
},
|
||||
// concat boilerplate schedule state definitions with generated editGroup state definitions
|
||||
}).then((generated) => {
|
||||
let schedulerDefinitions = _.map([
|
||||
stateExtender.buildDefinition(listSchedules),
|
||||
stateExtender.buildDefinition(addSchedule),
|
||||
stateExtender.buildDefinition(editSchedule)
|
||||
],
|
||||
(state) => stateExtender.buildDefinition(state));
|
||||
return {
|
||||
states: _(generated.states)
|
||||
.concat(schedulerDefinitions)
|
||||
.value()
|
||||
};
|
||||
});
|
||||
|
||||
// host state nodes
|
||||
addHost = stateDefinitions.generateTree({
|
||||
url: '/add-host',
|
||||
name: 'inventoryManage.addHost',
|
||||
modes: ['add'],
|
||||
form: 'HostForm',
|
||||
controllers: {
|
||||
add: 'HostsAddController'
|
||||
}
|
||||
});
|
||||
|
||||
editHost = stateDefinitions.generateTree({
|
||||
url: '/edit-host/:host_id',
|
||||
name: 'inventoryManage.editHost',
|
||||
modes: ['edit'],
|
||||
form: 'HostForm',
|
||||
controllers: {
|
||||
edit: 'HostEditController'
|
||||
},
|
||||
resolve: {
|
||||
host: ['$stateParams', 'HostManageService', function($stateParams, HostManageService) {
|
||||
return HostManageService.get({ id: $stateParams.host_id }).then(function(res) {
|
||||
return res.data.results[0];
|
||||
});
|
||||
}]
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "{{host.name}}",
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
inventories,
|
||||
addGroup,
|
||||
editGroup,
|
||||
addHost,
|
||||
editHost,
|
||||
]).then((generated) => {
|
||||
return {
|
||||
states: _.reduce(generated, (result, definition) => {
|
||||
return result.concat(definition.states);
|
||||
}, [
|
||||
stateExtender.buildDefinition(inventoryManageListRoute),
|
||||
stateExtender.buildDefinition(copyMoveGroupRoute),
|
||||
stateExtender.buildDefinition(copyMoveHostRoute),
|
||||
stateExtender.buildDefinition(adHocRoute),
|
||||
|
||||
])
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
stateTree = {
|
||||
name: 'inventories',
|
||||
url: '/inventories',
|
||||
lazyLoad: () => generateStateTree()
|
||||
};
|
||||
|
||||
$stateProvider.state(stateTree);
|
||||
}
|
||||
]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user