From c3842b6bf98f2b51bf9bf7855c1944fb9dd00528 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Wed, 21 Mar 2018 12:07:18 -0400 Subject: [PATCH 001/379] upgrade python-saml for CVE fix --- requirements/requirements.in | 2 +- requirements/requirements.txt | 2 +- tools/docker-compose/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/requirements.in b/requirements/requirements.in index e531173499..cf00960cbe 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -40,7 +40,7 @@ pyparsing==2.2.0 python-logstash==0.4.6 python-memcached==1.58 python-radius==1.0 -python-saml==2.2.1 +python-saml==2.2.4 python-social-auth==0.2.21 pyvmomi==6.5 redbaron==0.6.3 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index fbd0b0b6b6..7bc1abb2e3 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -151,7 +151,7 @@ python-novaclient==9.0.1 # via python-openstackclient, shade python-openid==2.2.5 # via python-social-auth python-openstackclient==3.11.0 # via python-ironicclient python-radius==1.0 -python-saml==2.2.1 +python-saml==2.2.4 python-social-auth==0.2.21 pytz==2017.2 # via babel, celery, irc, oslo.serialization, oslo.utils, tempora, twilio pyvmomi==6.5 diff --git a/tools/docker-compose/Dockerfile b/tools/docker-compose/Dockerfile index 712c7cd607..79b133ac73 100644 --- a/tools/docker-compose/Dockerfile +++ b/tools/docker-compose/Dockerfile @@ -30,7 +30,7 @@ RUN ln -s /awx_devel/tools/docker-compose/bootstrap_development.sh /bootstrap_de RUN openssl req -nodes -newkey rsa:2048 -keyout /etc/nginx/nginx.key -out /etc/nginx/nginx.csr -subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/OU=AWX Development/CN=awx.localhost" RUN openssl x509 -req -days 365 -in /etc/nginx/nginx.csr -signkey /etc/nginx/nginx.key -out /etc/nginx/nginx.crt WORKDIR /tmp -RUN SWIG_FEATURES="-cpperraswarn -includeall -D__`uname -m`__ -I/usr/include/openssl" VENV_BASE="/venv" make requirements_dev +RUN CFLAGS="-DXMLSEC_NO_SIZE_T" SWIG_FEATURES="-cpperraswarn -includeall -D__`uname -m`__ -I/usr/include/openssl" VENV_BASE="/venv" make requirements_dev RUN localedef -c -i en_US -f UTF-8 en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en From ad37f71af42bd0eb6947755509bb136d41af317f Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Thu, 22 Mar 2018 11:26:17 -0400 Subject: [PATCH 002/379] fix_python_saml24_update --- requirements/requirements.in | 2 +- requirements/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements.in b/requirements/requirements.in index cf00960cbe..3a916cc9c7 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -40,7 +40,7 @@ pyparsing==2.2.0 python-logstash==0.4.6 python-memcached==1.58 python-radius==1.0 -python-saml==2.2.4 +python-saml==2.4.0 python-social-auth==0.2.21 pyvmomi==6.5 redbaron==0.6.3 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 7bc1abb2e3..9970dafab0 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -151,7 +151,7 @@ python-novaclient==9.0.1 # via python-openstackclient, shade python-openid==2.2.5 # via python-social-auth python-openstackclient==3.11.0 # via python-ironicclient python-radius==1.0 -python-saml==2.2.4 +python-saml==2.4.0 python-social-auth==0.2.21 pytz==2017.2 # via babel, celery, irc, oslo.serialization, oslo.utils, tempora, twilio pyvmomi==6.5 From 61aafe15d6f58133323392efce2fc0fb27de1df4 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 5 Mar 2018 14:13:57 -0500 Subject: [PATCH 003/379] fix busted shippable builds --- Makefile | 1 - requirements/requirements_dev_uninstall.txt | 1 - tools/docker-compose/Dockerfile | 1 - 3 files changed, 3 deletions(-) delete mode 100644 requirements/requirements_dev_uninstall.txt diff --git a/Makefile b/Makefile index 0ba149c123..dec65947ab 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,6 @@ requirements_awx: virtualenv_awx requirements_awx_dev: $(VENV_BASE)/awx/bin/pip install -r requirements/requirements_dev.txt - $(VENV_BASE)/awx/bin/pip uninstall --yes -r requirements/requirements_dev_uninstall.txt requirements: requirements_ansible requirements_awx diff --git a/requirements/requirements_dev_uninstall.txt b/requirements/requirements_dev_uninstall.txt deleted file mode 100644 index 963eac530b..0000000000 --- a/requirements/requirements_dev_uninstall.txt +++ /dev/null @@ -1 +0,0 @@ -certifi diff --git a/tools/docker-compose/Dockerfile b/tools/docker-compose/Dockerfile index 79b133ac73..d99d57bfde 100644 --- a/tools/docker-compose/Dockerfile +++ b/tools/docker-compose/Dockerfile @@ -9,7 +9,6 @@ requirements/requirements_ansible_git.txt \ requirements/requirements_dev.txt \ requirements/requirements_ansible_uninstall.txt \ requirements/requirements_tower_uninstall.txt \ -requirements/requirements_dev_uninstall.txt \ /tmp/requirements/ RUN yum -y update && yum -y install curl epel-release RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - From 35e38760aa507dfea5795ed6fdd182b2e484301d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 22 Mar 2018 16:11:46 -0400 Subject: [PATCH 004/379] properly sanitize module arguments with no_log (like uri:password) this will _not_ sanitize playbooks that have secrets hard-coded *in* the playbook - for that, people will need to use Vault or a variable/lookup see: https://github.com/ansible/tower/issues/1101 see: https://github.com/ansible/awx/issues/1633 --- awx/lib/awx_display_callback/module.py | 21 ++++++++++++++++++--- awx/lib/tests/test_display_callback.py | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/awx/lib/awx_display_callback/module.py b/awx/lib/awx_display_callback/module.py index 368063d0d1..8aef42301f 100644 --- a/awx/lib/awx_display_callback/module.py +++ b/awx/lib/awx_display_callback/module.py @@ -36,6 +36,8 @@ from .events import event_context from .minimal import CallbackModule as MinimalCallbackModule CENSORED = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" # noqa +OMIT = "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER" + class BaseCallbackModule(CallbackBase): @@ -68,6 +70,7 @@ class BaseCallbackModule(CallbackBase): @contextlib.contextmanager def capture_event_data(self, event, **event_data): + censored_task_args = [] event_data.setdefault('uuid', str(uuid.uuid4())) if event not in self.EVENTS_WITHOUT_TASK: @@ -76,6 +79,14 @@ class BaseCallbackModule(CallbackBase): task = None if event_data.get('res'): + invocation = event_data['res'].get('invocation') + if invocation: + module_args = invocation.get('module_args') + sensitive_module_args = set([ + k for k, v in module_args.items() + if v == OMIT + ]) + censored_task_args = sensitive_module_args.intersection(task.args.keys()) if event_data['res'].get('_ansible_no_log', False): event_data['res'] = {'censored': CENSORED} if event_data['res'].get('results', []): @@ -88,7 +99,7 @@ class BaseCallbackModule(CallbackBase): try: event_context.add_local(event=event, **event_data) if task: - self.set_task(task, local=True) + self.set_task(task, local=True, censored_task_args=censored_task_args) event_context.dump_begin(sys.stdout) yield finally: @@ -120,7 +131,8 @@ class BaseCallbackModule(CallbackBase): event_context.remove_global(play=None, play_uuid=None, play_pattern=None) self.clear_task() - def set_task(self, task, local=False): + def set_task(self, task, local=False, censored_task_args=None): + censored_task_args = censored_task_args or [] # FIXME: Task is "global" unless using free strategy! task_ctx = dict( task=(task.name or task.action), @@ -134,7 +146,10 @@ class BaseCallbackModule(CallbackBase): if task.no_log: task_ctx['task_args'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" else: - task_args = ', '.join(('%s=%s' % a for a in task.args.items())) + task_args = ', '.join(( + '%s=%s' % (k, CENSORED if k in censored_task_args else v) + for k, v in task.args.items() + )) task_ctx['task_args'] = task_args if getattr(task, '_role', None): task_role = task._role._role_name diff --git a/awx/lib/tests/test_display_callback.py b/awx/lib/tests/test_display_callback.py index e87f3ec306..46a7681694 100644 --- a/awx/lib/tests/test_display_callback.py +++ b/awx/lib/tests/test_display_callback.py @@ -279,3 +279,28 @@ def test_callback_plugin_saves_custom_stats(executor, cache, playbook): assert json.load(f) == {'foo': 'bar'} finally: shutil.rmtree(os.path.join(private_data_dir)) + + +@pytest.mark.parametrize('playbook', [ +{'no_log_module_with_var.yml': ''' +- name: ensure that module-level secrets are redacted + connection: local + hosts: all + vars: + - pw: SENSITIVE + tasks: + - uri: + url: https://example.org + user: john-jacob-jingleheimer-schmidt + password: "{{ pw }}" +'''}, # noqa +]) +def test_module_level_no_log(executor, cache, playbook): + # https://github.com/ansible/tower/issues/1101 + # It's possible for `no_log=True` to be defined at the _module_ level, + # e.g., for the URI module password parameter + # This test ensures that we properly redact those + executor.run() + assert len(cache) + assert 'john-jacob-jingleheimer-schmidt' in json.dumps(cache.items()) + assert 'SENSITIVE' not in json.dumps(cache.items()) From f6e507ad1289267d0951ae190ca457d19b36d14a Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 23 Mar 2018 13:36:01 -0400 Subject: [PATCH 005/379] add API setting for UI live updates include context data update help text --- awx/settings/defaults.py | 4 ++++ awx/ui/conf.py | 10 ++++++++++ awx/ui/templates/ui/index.html | 1 + awx/ui/views.py | 2 ++ 4 files changed, 17 insertions(+) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index b381548668..89d1727188 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -168,6 +168,10 @@ STDOUT_MAX_BYTES_DISPLAY = 1048576 # on how many events to display before truncating/hiding MAX_UI_JOB_EVENTS = 4000 +# Returned in index.html, tells the UI if it should make requests +# to update job data in response to status changes websocket events +UI_LIVE_UPDATES_ENABLED = True + # The maximum size of the ansible callback event's res data structure # beyond this limit and the value will be removed MAX_EVENT_RES_DATA = 700000 diff --git a/awx/ui/conf.py b/awx/ui/conf.py index 0d626c28f0..df88890faf 100644 --- a/awx/ui/conf.py +++ b/awx/ui/conf.py @@ -63,3 +63,13 @@ register( category=_('UI'), category_slug='ui', ) + +register( + 'UI_LIVE_UPDATES_ENABLED', + field_class=fields.BooleanField, + label=_('Enable Live Updates in the UI'), + help_text=_('If disabled, the page will not refresh when events are received. ' + 'Reloading the page will be required to get the latest details.'), + category=_('UI'), + category_slug='ui', +) diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 38bbad9de3..ce8af6b086 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -12,6 +12,7 @@ diff --git a/awx/ui/views.py b/awx/ui/views.py index 0a47154615..994da49732 100644 --- a/awx/ui/views.py +++ b/awx/ui/views.py @@ -2,6 +2,7 @@ # All Rights Reserved. from django.views.generic.base import TemplateView, RedirectView +from django.conf import settings class IndexView(TemplateView): @@ -9,6 +10,7 @@ class IndexView(TemplateView): def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) + context['UI_LIVE_UPDATES_ENABLED'] = settings.UI_LIVE_UPDATES_ENABLED # Add any additional context info here. return context From 8643972064e0a6e525f6a26b39d5a8027d8f1254 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Thu, 22 Mar 2018 16:10:07 -0700 Subject: [PATCH 006/379] Fixes issue with sockets and XHR requests for backgrounded tabs adjusts toggling based on API setting and doesn't toggle for job stdout page --- .../src/shared/socket/socket.service.js | 30 ++++++++++++++++++- awx/ui/templates/ui/index.html | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js index f520fbcbe9..7c51298a72 100644 --- a/awx/ui/client/src/shared/socket/socket.service.js +++ b/awx/ui/client/src/shared/socket/socket.service.js @@ -8,7 +8,8 @@ export default ['$rootScope', '$location', '$log','$state', '$q', 'i18n', function ($rootScope, $location, $log, $state, $q, i18n) { var needsResubscribing = false, - socketPromise = $q.defer(); + socketPromise = $q.defer(), + needsRefreshAfterBlur; return { init: function() { var self = this, @@ -24,6 +25,26 @@ export default } url = `${protocol}://${host}/websocket/`; + // only toggle background tabbed sockets if the + // UI_LIVE_UPDATES_ENABLED flag is true in the settings file + if(window.liveUpdates){ + document.addEventListener('visibilitychange', function() { + $log.debug(document.visibilityState); + if(document.visibilityState === 'hidden'){ + window.liveUpdates = false; + } + else if(document.visibilityState === 'visible'){ + window.liveUpdates = true; + if(needsRefreshAfterBlur){ + $state.go('.', null, {reload: true}); + needsRefreshAfterBlur = false; + } + + } + }); + } + + if (!$rootScope.sessionTimer || ($rootScope.sessionTimer && !$rootScope.sessionTimer.isExpired())) { $log.debug('Socket connecting to: ' + url); @@ -75,6 +96,13 @@ export default $log.debug('Received From Server: ' + e.data); var data = JSON.parse(e.data), str = ""; + + if(!window.liveUpdates && data.group_name !== "control" && $state.current.name !== "jobResult"){ + $log.debug('Message from server dropped: ' + e.data); + needsRefreshAfterBlur = true; + return; + } + if(data.group_name==="jobs" && !('status' in data)){ // we know that this must have been a // summary complete message b/c status is missing. diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index ce8af6b086..3dae008dc7 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -12,7 +12,7 @@ From df60876bf3db8ecd60bb9c149e309728fe43eb88 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Fri, 23 Mar 2018 16:17:24 -0700 Subject: [PATCH 007/379] Adds a debug function to turn on $log.debug --- awx/ui/client/src/app.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 529e5d7abe..fc79e6a015 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -146,7 +146,11 @@ var awApp = angular.module('awApp', [ .constant('AngularScheduler.showUTCField', true) .constant('$timezones.definitions.location', urlPrefix + 'lib/angular-tz-extensions/tz/data') .config(['$logProvider', function($logProvider) { - $logProvider.debugEnabled($ENV['ng-debug'] || false); + window.debug = function(){ + $logProvider.debugEnabled(!$logProvider.debugEnabled()); + return $logProvider.debugEnabled(); + }; + window.debug(false); }]) .config(['ngToastProvider', function(ngToastProvider) { ngToastProvider.configure({ From a2a246a834ab34d36e53b38b34e8363f5c6b4167 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 26 Mar 2018 17:12:01 -0400 Subject: [PATCH 008/379] update to a newer python-dateutil with more bug fixes we were vendoring related: dateutil/dateutil#649 --- requirements/requirements.in | 2 +- requirements/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements.in b/requirements/requirements.in index 9711c1e28e..cf1c314d9d 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -39,7 +39,7 @@ pycrypto==2.6.1 pygerduty==0.37.0 pyOpenSSL==17.5.0 pyparsing==2.2.0 -python-dateutil==2.7.0 # contains support for TZINFO= parsing +python-dateutil==2.7.2 # contains support for TZINFO= parsing python-logstash==0.4.6 python-memcached==1.59 python-radius==1.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index fca17117f8..a13c7105ea 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -184,7 +184,7 @@ pyjwt==1.6.0 # via adal, social-auth-core, twilio pyopenssl==17.5.0 pyparsing==2.2.0 pyrad==2.1 # via django-radius -python-dateutil==2.7.0 +python-dateutil==2.7.2 python-ldap==2.5.2 # via django-auth-ldap python-logstash==0.4.6 python-memcached==1.59 From 21d629531fab88fa912dc1eff979ea9af590a495 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 26 Mar 2018 17:21:59 -0400 Subject: [PATCH 009/379] remove an RRULE parsing bug fix that landed upstream in python-dateutil related: a2a246a834ab34d36e53b38b34e8363f5c6b4167 --- awx/main/models/schedules.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 8736647a65..71efa702c6 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -97,7 +97,7 @@ class Schedule(CommonModel, LaunchTimeConfig): @classmethod def rrulestr(cls, rrule, **kwargs): """ - Apply our own custom rrule parsing logic to support TZID= + Apply our own custom rrule parsing requirements """ kwargs['forceset'] = True x = dateutil.rrule.rrulestr(rrule, **kwargs) @@ -108,15 +108,6 @@ class Schedule(CommonModel, LaunchTimeConfig): 'A valid TZID must be provided (e.g., America/New_York)' ) - if r._dtstart and r._until: - # If https://github.com/dateutil/dateutil/pull/634 ever makes - # it into a python-dateutil release, we could remove this block. - if all(( - r._dtstart.tzinfo != dateutil.tz.tzlocal(), - r._until.tzinfo != dateutil.tz.tzutc(), - )): - raise ValueError('RRULE UNTIL values must be specified in UTC') - if 'MINUTELY' in rrule or 'HOURLY' in rrule: try: first_event = x[0] From d744679d22793c0d370a24a0c96b0560025f024f Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 27 Mar 2018 11:21:15 -0400 Subject: [PATCH 010/379] Fixed bug where the machine credential was being stripped from a workflow node if the edge type was changed. --- .../workflow-maker.controller.js | 168 ++++++++---------- 1 file changed, 78 insertions(+), 90 deletions(-) diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index a86b38dc2e..5037f15644 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -74,12 +74,13 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let buildSendableNodeData = function() { // Create the node let sendableNodeData = { - unified_job_template: params.node.unifiedJobTemplate.id + unified_job_template: params.node.unifiedJobTemplate.id, + credential: _.get(params, 'node.originalNodeObj.credential') || null }; - if(_.has(params, 'node.promptData.extraVars')) { - if(_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) { - if(!sendableNodeData.extra_data) { + if (_.has(params, 'node.promptData.extraVars')) { + if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) { + if (!sendableNodeData.extra_data) { sendableNodeData.extra_data = {}; } @@ -87,15 +88,15 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Only include extra vars that differ from the template default vars _.forOwn(params.node.promptData.extraVars, (value, key) => { - if(!defaultVars[key] || defaultVars[key] !== value) { + if (!defaultVars[key] || defaultVars[key] !== value) { sendableNodeData.extra_data[key] = value; } }); - if(_.isEmpty(sendableNodeData.extra_data)) { + if (_.isEmpty(sendableNodeData.extra_data)) { delete sendableNodeData.extra_data; } } else { - if(_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) { + if (_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) { sendableNodeData.extra_data = params.node.promptData.extraVars; } } @@ -104,7 +105,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Check to see if the user has provided any prompt values that are different // from the defaults in the job template - if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) { + if (params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) { sendableNodeData = PromptService.bundlePromptDataForSaving({ promptData: params.node.promptData, dataToSave: sendableNodeData @@ -117,26 +118,23 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let continueRecursing = function(parentId) { $scope.totalIteratedNodes++; - if($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) { + if ($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) { // We're done recursing, lets move on completionCallback(); - } - else { - if(params.node.children && params.node.children.length > 0) { + } else { + if (params.node.children && params.node.children.length > 0) { _.forEach(params.node.children, function(child) { - if(child.edgeType === "success") { + if (child.edgeType === "success") { recursiveNodeUpdates({ parentId: parentId, node: child }, completionCallback); - } - else if(child.edgeType === "failure") { + } else if (child.edgeType === "failure") { recursiveNodeUpdates({ parentId: parentId, node: child }, completionCallback); - } - else if(child.edgeType === "always") { + } else if (child.edgeType === "always") { recursiveNodeUpdates({ parentId: parentId, node: child @@ -147,7 +145,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } }; - if(params.node.isNew) { + if (params.node.isNew) { TemplatesService.addWorkflowNode({ url: $scope.treeData.workflow_job_template_obj.related.workflow_nodes, @@ -155,7 +153,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }) .then(function(data) { - if(!params.node.isRoot) { + if (!params.node.isRoot) { associateRequests.push({ parentId: params.parentId, nodeId: data.data.id, @@ -163,7 +161,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); } - if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ // This finds the credentials that were selected in the prompt but don't occur // in the template defaults let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { @@ -193,18 +191,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', error.status }); }); - } - else { - if(params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) { + } else { + if (params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) { - if(params.node.edited) { + if (params.node.edited) { editRequests.push({ id: params.node.nodeId, data: buildSendableNodeData() }); - if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ let credentialsNotInPriorCredentials = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : []; return !defaultCreds.some(function(defaultCred) { @@ -243,20 +240,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); } - } - if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep + if ((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep let parentIsDeleted = false; _.forEach($scope.treeData.data.deletedNodes, function(deletedNode) { - if(deletedNode === params.node.originalParentId) { + if (deletedNode === params.node.originalParentId) { parentIsDeleted = true; } }); - if(!parentIsDeleted) { + if (!parentIsDeleted) { disassociateRequests.push({ parentId: params.node.originalParentId, nodeId: params.node.nodeId, @@ -267,7 +263,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Can only associate if we have a parent. // If we don't have a parent then this is a root node // and the act of disassociating will make it a root node - if(params.parentId) { + if (params.parentId) { associateRequests.push({ parentId: params.parentId, nodeId: params.node.nodeId, @@ -275,8 +271,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); } - } - else if(!params.node.originalParentId && params.parentId) { + } else if (!params.node.originalParentId && params.parentId) { // This used to be a root node but is now not a root node associateRequests.push({ parentId: params.parentId, @@ -293,7 +288,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let updateEdgeDropdownOptions = (optionsToInclude) => { // Not passing optionsToInclude will include all by default - if(!optionsToInclude) { + if (!optionsToInclude) { $scope.edgeTypeOptions = [ { label: 'Always', @@ -312,17 +307,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.edgeTypeOptions = []; optionsToInclude.forEach((optionToInclude) => { - if(optionToInclude === "always") { + if (optionToInclude === "always") { $scope.edgeTypeOptions.push({ label: 'Always', value: 'always' }); - } else if(optionToInclude === "success") { + } else if (optionToInclude === "success") { $scope.edgeTypeOptions.push({ label: 'On Success', value: 'success' }); - } else if(optionToInclude === "failure") { + } else if (optionToInclude === "failure") { $scope.edgeTypeOptions.push({ label: 'On Failure', value: 'failure' @@ -346,9 +341,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', promptWatcher = $scope.$watchGroup(promptDataToWatch, function() { let missingPromptValue = false; - if($scope.missingSurveyValue) { + if ($scope.missingSurveyValue) { missingPromptValue = true; - } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { missingPromptValue = true; } $scope.promptModalMissingReqFields = missingPromptValue; @@ -365,7 +360,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.totalIteratedNodes = 0; - if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) { + if ($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) { let completionCallback = function() { let disassociatePromises = disassociateRequests.map(function(request) { @@ -376,13 +371,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); - let credentialPromises = credentialRequests.map(function(request) { - return TemplatesService.postWorkflowNodeCredential({ - id: request.id, - data: request.data - }); - }); - let editNodePromises = editRequests.map(function(request) { return TemplatesService.editWorkflowNode({ id: request.id, @@ -394,9 +382,16 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', return TemplatesService.deleteWorkflowJobTemplateNode(nodeId); }); - $q.all(disassociatePromises.concat(editNodePromises, deletePromises, credentialPromises)) + $q.all(disassociatePromises.concat(editNodePromises, deletePromises)) .then(function() { + let credentialPromises = credentialRequests.map(function(request) { + return TemplatesService.postWorkflowNodeCredential({ + id: request.id, + data: request.data + }); + }); + let associatePromises = associateRequests.map(function(request) { return TemplatesService.associateWorkflowNode({ parentId: request.parentId, @@ -405,7 +400,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); - $q.all(associatePromises) + $q.all(associatePromises.concat(credentialPromises)) .then(function() { $scope.closeDialog(); }); @@ -417,8 +412,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', node: child }, completionCallback); }); - } - else { + } else { let deletePromises = $scope.treeData.data.deletedNodes.map(function(nodeId) { return TemplatesService.deleteWorkflowJobTemplateNode(nodeId); @@ -522,11 +516,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } } - if(promptWatcher) { + if (promptWatcher) { promptWatcher(); } - if(surveyQuestionWatcher) { + if (surveyQuestionWatcher) { surveyQuestionWatcher(); } @@ -549,11 +543,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.nodeBeingEdited.isActiveEdit = false; } - if(promptWatcher) { + if (promptWatcher) { promptWatcher(); } - if(surveyQuestionWatcher) { + if (surveyQuestionWatcher) { surveyQuestionWatcher(); } @@ -601,12 +595,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let jobTemplate = new JobTemplate(); - if(!_.isEmpty($scope.nodeBeingEdited.promptData)) { + if (!_.isEmpty($scope.nodeBeingEdited.promptData)) { $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData); - } else if($scope.nodeBeingEdited.unifiedJobTemplate){ + } else if ($scope.nodeBeingEdited.unifiedJobTemplate){ let promises = [jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id)]; - if(_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) { + if (_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) { Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials); promises.push(Rest.get()); } @@ -630,8 +624,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', const credentialHasScheduleOverride = (templateDefaultCred) => { let credentialHasOverride = false; workflowNodeCredentials.forEach((scheduleCred) => { - if(templateDefaultCred.credential_type === scheduleCred.credential_type) { - if( + if (templateDefaultCred.credential_type === scheduleCred.credential_type) { + if ( (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) || (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id) ) { @@ -643,9 +637,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', return credentialHasOverride; }; - if(_.has(launchConf, 'defaults.credentials')) { + if (_.has(launchConf, 'defaults.credentials')) { launchConf.defaults.credentials.forEach((defaultCred) => { - if(!credentialHasScheduleOverride(defaultCred)) { + if (!credentialHasScheduleOverride(defaultCred)) { defaultCredsWithoutOverrides.push(defaultCred); } }); @@ -653,7 +647,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides); - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -671,11 +665,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } else { $scope.showPromptButton = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { $scope.promptModalMissingReqFields = true; } - if(responses[1].data.survey_enabled) { + if (responses[1].data.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id) .then((surveyQuestionRes) => { @@ -700,7 +694,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -709,8 +703,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', watchForPromptChanges(); }); - } - else { + } else { $scope.nodeBeingEdited.promptData = $scope.promptData = { launchConf: launchConf, launchOptions: launchOptions, @@ -729,7 +722,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate; - if($scope.selectedTemplate.unified_job_type) { + if ($scope.selectedTemplate.unified_job_type) { switch ($scope.selectedTemplate.unified_job_type) { case "job": $scope.workflowMakerFormConfig.activeTab = "jobs"; @@ -741,8 +734,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; break; } - } - else if($scope.selectedTemplate.type) { + } else if ($scope.selectedTemplate.type) { switch ($scope.selectedTemplate.type) { case "job_template": $scope.workflowMakerFormConfig.activeTab = "jobs"; @@ -767,19 +759,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', switch($scope.nodeBeingEdited.edgeType) { case "always": $scope.edgeType = {label: "Always", value: "always"}; - if(siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) { + if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) { edgeDropdownOptions = ["always"]; } break; case "success": $scope.edgeType = {label: "On Success", value: "success"}; - if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { + if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { edgeDropdownOptions = ["success", "failure"]; } break; case "failure": $scope.edgeType = {label: "On Failure", value: "failure"}; - if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { + if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { edgeDropdownOptions = ["success", "failure"]; } break; @@ -857,13 +849,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.$broadcast("refreshWorkflowChart"); - if($scope.placeholderNode) { + if ($scope.placeholderNode) { let edgeType = {label: "On Success", value: "success"}; - if($scope.placeholderNode.isRoot) { + if ($scope.placeholderNode.isRoot) { updateEdgeDropdownOptions(["always"]); edgeType = {label: "Always", value: "always"}; - } - else { + } else { // we need to update the possible edges based on any new siblings let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ tree: $scope.treeData.data, @@ -889,8 +880,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } $scope.edgeType = edgeType; - } - else if($scope.nodeBeingEdited) { + } else if ($scope.nodeBeingEdited) { let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ tree: $scope.treeData.data, parentId: $scope.nodeBeingEdited.parent.id, @@ -958,14 +948,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplate = angular.copy(selectedTemplate); - if(selectedTemplate.type === "job_template") { + if (selectedTemplate.type === "job_template") { let jobTemplate = new JobTemplate(); $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) .then((responses) => { let launchConf = responses[1].data; - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -983,11 +973,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } else { $scope.showPromptButton = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { $scope.promptModalMissingReqFields = true; } - if(launchConf.survey_enabled) { + if (launchConf.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions(selectedTemplate.id) .then((surveyQuestionRes) => { @@ -1012,7 +1002,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -1021,8 +1011,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', watchForPromptChanges(); }); - } - else { + } else { $scope.promptData = { launchConf: responses[1].data, launchOptions: responses[0].data, @@ -1098,7 +1087,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // TODO: I think that the workflow chart directive (and eventually d3) is meddling with // this treeData object and removing the children object for some reason (?) // This happens on occasion and I think is a race condition (?) - if(!$scope.treeData.data.children) { + if (!$scope.treeData.data.children) { $scope.treeData.data.children = []; } @@ -1116,12 +1105,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', for(var i=0; i Date: Fri, 23 Mar 2018 17:04:24 -0400 Subject: [PATCH 011/379] Add warning banner when Org Admin doesn't have manage auth --- .../src/configuration/configuration.block.less | 10 +++++++++- awx/ui/client/src/shared/form-generator.js | 5 +++-- awx/ui/client/src/shared/generator-helpers.js | 14 ++++++++++++++ .../client/src/teams/edit/teams-edit.controller.js | 4 +--- .../client/src/teams/list/teams-list.controller.js | 8 +++++--- awx/ui/client/src/teams/main.js | 11 +++++++++++ awx/ui/client/src/teams/teams.form.js | 5 ++++- .../client/src/users/edit/users-edit.controller.js | 5 ++--- .../client/src/users/list/users-list.controller.js | 10 ++++++---- awx/ui/client/src/users/main.js | 11 +++++++++++ awx/ui/client/src/users/users.form.js | 5 ++++- 11 files changed, 70 insertions(+), 18 deletions(-) diff --git a/awx/ui/client/src/configuration/configuration.block.less b/awx/ui/client/src/configuration/configuration.block.less index 7cba028ef9..693fd9a9d5 100644 --- a/awx/ui/client/src/configuration/configuration.block.less +++ b/awx/ui/client/src/configuration/configuration.block.less @@ -97,11 +97,19 @@ input#filePickerText { .Section-messageBar { width: 120%; margin-left: -20px; - padding: 10px; + padding: 10px 10px 10px 20px; color: @white; background-color: @default-link; } +.Section-messageBar-text { + margin-left: @at-space-2x; +} + +.Section-messageBar-warning { + color: @at-color-warning; +} + .Section-messageBar--close { position: absolute; right: 0; diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 892b156d5c..eee1106922 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -142,10 +142,10 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat .factory('GenerateForm', ['$rootScope', '$compile', 'generateList', 'Attr', 'Icon', 'Column', 'NavigationLink', 'HelpCollapse', 'Empty', 'SelectIcon', - 'ActionButton', '$log', 'i18n', + 'ActionButton', 'MessageBar', '$log', 'i18n', function ($rootScope, $compile, GenerateList, Attr, Icon, Column, NavigationLink, HelpCollapse, - Empty, SelectIcon, ActionButton, $log, i18n) { + Empty, SelectIcon, ActionButton, MessageBar, $log, i18n) { return { setForm: function (form) { this.form = form; }, @@ -177,6 +177,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat else { return `
+ ${MessageBar(this.form)}
${html}
diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js index f01b7110af..f365aeb042 100644 --- a/awx/ui/client/src/shared/generator-helpers.js +++ b/awx/ui/client/src/shared/generator-helpers.js @@ -766,4 +766,18 @@ angular.module('GeneratorHelpers', [systemStatus.name]) return html; }; +}) + +.factory('MessageBar', function() { + return function(options) { + let html = ''; + if (_.has(options, 'messageBar')) { + let { messageBar } = options; + html += `
+ + ${messageBar.message} +
`; + } + return html; + }; }); diff --git a/awx/ui/client/src/teams/edit/teams-edit.controller.js b/awx/ui/client/src/teams/edit/teams-edit.controller.js index 49c90c1316..7ecd631182 100644 --- a/awx/ui/client/src/teams/edit/teams-edit.controller.js +++ b/awx/ui/client/src/teams/edit/teams-edit.controller.js @@ -33,9 +33,7 @@ export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', }); $scope.$watch('team_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } + $scope.canAdd = (val === false) ? false : true; }); diff --git a/awx/ui/client/src/teams/list/teams-list.controller.js b/awx/ui/client/src/teams/list/teams-list.controller.js index 32b90528f8..c495416ae2 100644 --- a/awx/ui/client/src/teams/list/teams-list.controller.js +++ b/awx/ui/client/src/teams/list/teams-list.controller.js @@ -6,21 +6,23 @@ export default ['$scope', 'Rest', 'TeamList', 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', - 'rbacUiControlService', 'Dataset', 'i18n', + 'rbacUiControlService', 'Dataset', 'resolvedModels', 'i18n', function($scope, Rest, TeamList, Prompt, ProcessErrors, - GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset, i18n) { + GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset, models, i18n) { + const { me } = models; var list = TeamList, defaultUrl = GetBasePath('teams'); init(); function init() { + const canEdit = me.get('summary_fields.user_capabilities.edit'); $scope.canAdd = false; rbacUiControlService.canAdd('teams') .then(function(params) { - $scope.canAdd = params.canAdd; + $scope.canAdd = params.canAdd && canEdit; }); // search init $scope.list = list; diff --git a/awx/ui/client/src/teams/main.js b/awx/ui/client/src/teams/main.js index f0ac982c7f..df9e15894c 100644 --- a/awx/ui/client/src/teams/main.js +++ b/awx/ui/client/src/teams/main.js @@ -41,6 +41,17 @@ angular.module('Teams', []) activityStream: true, activityStreamTarget: 'team' }, + resolve: { + list: { + resolvedModels: ['MeModel', '$q', function(Me, $q) { + const promises = { + me: new Me('get') + }; + + return $q.all(promises); + }] + } + }, ncyBreadcrumb: { label: N_('TEAMS') } diff --git a/awx/ui/client/src/teams/teams.form.js b/awx/ui/client/src/teams/teams.form.js index 1314cbd183..0b76cca634 100644 --- a/awx/ui/client/src/teams/teams.form.js +++ b/awx/ui/client/src/teams/teams.form.js @@ -19,7 +19,10 @@ export default ['i18n', function(i18n) { // the top-most node of generated state tree stateTree: 'teams', tabs: true, - + messageBar: { + ngShow: '!user_is_system_auditor && !canAdd', + message: i18n._("Contact your System Administrator to grant you the appropriate permissions to add and edit Users and Teams.") + }, fields: { name: { label: i18n._('Name'), diff --git a/awx/ui/client/src/users/edit/users-edit.controller.js b/awx/ui/client/src/users/edit/users-edit.controller.js index 0accb1bdcf..b268e619f3 100644 --- a/awx/ui/client/src/users/edit/users-edit.controller.js +++ b/awx/ui/client/src/users/edit/users-edit.controller.js @@ -30,6 +30,7 @@ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest', init(); function init() { + $scope.canAdd = true; $scope.isCurrentlyLoggedInUser = (parseInt(id) === $rootScope.current_user.id); $scope.hidePagination = false; $scope.hideSmartSearch = false; @@ -68,9 +69,7 @@ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest', }); $scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } + $scope.canAdd = (val === false) ? false : true; }); setScopeFields(data); diff --git a/awx/ui/client/src/users/list/users-list.controller.js b/awx/ui/client/src/users/list/users-list.controller.js index f0c9022581..9b95081a49 100644 --- a/awx/ui/client/src/users/list/users-list.controller.js +++ b/awx/ui/client/src/users/list/users-list.controller.js @@ -14,26 +14,28 @@ const user_type_options = [ export default ['$scope', '$rootScope', 'Rest', 'UserList', 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', - 'rbacUiControlService', 'Dataset', 'i18n', + 'rbacUiControlService', 'Dataset', 'i18n', 'resolvedModels', function($scope, $rootScope, Rest, UserList, Prompt, ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, - Dataset, i18n) { + Dataset, i18n, models) { for (var i = 0; i < user_type_options.length; i++) { user_type_options[i].label = i18n._(user_type_options[i].label); } + const { me } = models; var list = UserList, - defaultUrl = GetBasePath('users'); + defaultUrl = GetBasePath('users'); init(); function init() { + const canEdit = me.get('summary_fields.user_capabilities.edit'); $scope.canAdd = false; rbacUiControlService.canAdd('users') .then(function(params) { - $scope.canAdd = params.canAdd; + $scope.canAdd = params.canAdd && canEdit; }); // search init diff --git a/awx/ui/client/src/users/main.js b/awx/ui/client/src/users/main.js index 5168262f3c..9f4937bf4b 100644 --- a/awx/ui/client/src/users/main.js +++ b/awx/ui/client/src/users/main.js @@ -43,6 +43,17 @@ angular.module('Users', []) activityStream: true, activityStreamTarget: 'user' }, + resolve: { + list: { + resolvedModels: ['MeModel', '$q', function(Me, $q) { + const promises= { + me: new Me('get') + }; + + return $q.all(promises); + }] + } + }, ncyBreadcrumb: { label: N_('USERS') } diff --git a/awx/ui/client/src/users/users.form.js b/awx/ui/client/src/users/users.form.js index 78ea4f33e4..925ec999b6 100644 --- a/awx/ui/client/src/users/users.form.js +++ b/awx/ui/client/src/users/users.form.js @@ -20,7 +20,10 @@ export default ['i18n', function(i18n) { stateTree: 'users', forceListeners: true, tabs: true, - + messageBar: { + ngShow: '!user_is_system_auditor && !canAdd', + message: i18n._("Contact your System Administrator to grant you the appropriate permissions to add and edit Users and Teams.") + }, fields: { first_name: { label: i18n._('First Name'), From 7ce8907b7b49df5fe9375a81de166bc9f0ce45b2 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 27 Mar 2018 13:51:35 -0400 Subject: [PATCH 012/379] reregister node when they come back online * Nodes are marked offline, then deleted; given enough time. Nodes can come back for various reasions (i.e. netsplit). When they come back, have them recreate the node Instance if AWX_AUTO_DEPROVISION_INSTANCES is True. Otherwise, do nothing. The do nothing case will show up in the logs as celery job tracebacks as they fail to be self aware. --- .../management/commands/provision_instance.py | 16 ++++++---------- awx/main/managers.py | 15 +++++++++++++++ awx/main/tasks.py | 16 +++++++++++++--- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/awx/main/management/commands/provision_instance.py b/awx/main/management/commands/provision_instance.py index 4b2ef8f220..e9458ac120 100644 --- a/awx/main/management/commands/provision_instance.py +++ b/awx/main/management/commands/provision_instance.py @@ -2,7 +2,6 @@ # All Rights Reserved from awx.main.models import Instance -from awx.main.utils.pglock import advisory_lock from django.conf import settings from django.db import transaction @@ -27,15 +26,12 @@ class Command(BaseCommand): def _register_hostname(self, hostname): if not hostname: return - with advisory_lock('instance_registration_%s' % hostname): - instance = Instance.objects.filter(hostname=hostname) - if instance.exists(): - print("Instance already registered {}".format(instance[0].hostname)) - return - instance = Instance(uuid=self.uuid, hostname=hostname) - instance.save() - print('Successfully registered instance {}'.format(hostname)) - self.changed = True + (changed, instance) = Instance.objects.register(uuid=self.uuid, hostname=hostname) + if changed: + print('Successfully registered instance {}'.format(hostname)) + else: + print("Instance already registered {}".format(instance.hostname)) + self.changed = changed @transaction.atomic def handle(self, **options): diff --git a/awx/main/managers.py b/awx/main/managers.py index 70c402f672..1683b38c4e 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -8,6 +8,7 @@ from django.db import models from django.conf import settings from awx.main.utils.filters import SmartFilter +from awx.main.utils.pglock import advisory_lock ___all__ = ['HostManager', 'InstanceManager', 'InstanceGroupManager'] @@ -86,6 +87,20 @@ class InstanceManager(models.Manager): return node[0] raise RuntimeError("No instance found with the current cluster host id") + def register(self, uuid=settings.SYSTEM_UUID, hostname=settings.CLUSTER_HOST_ID): + with advisory_lock('instance_registration_%s' % hostname): + instance = self.filter(hostname=hostname) + if instance.exists(): + return (False, instance[0]) + instance = self.create(uuid=uuid, hostname=hostname) + return (True, instance) + + def get_or_register(self): + if settings.AWX_AUTO_DEPROVISION_INSTANCES: + return self.register() + else: + return (False, self.me()) + def active_count(self): """Return count of active Tower nodes for licensing.""" return self.all().count() diff --git a/awx/main/tasks.py b/awx/main/tasks.py index c47101b988..ccef351626 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -204,7 +204,9 @@ def handle_setting_changes(self, setting_keys): @shared_task(bind=True, queue='tower_broadcast_all', base=LogErrorsTask) def handle_ha_toplogy_changes(self): - instance = Instance.objects.me() + (changed, instance) = Instance.objects.get_or_register() + if changed: + logger.info(six.text_type("Registered tower node '{}'").format(instance.hostname)) logger.debug(six.text_type("Reconfigure celeryd queues task on host {}").format(self.request.hostname)) awx_app = Celery('awx') awx_app.config_from_object('django.conf:settings') @@ -234,7 +236,9 @@ def handle_ha_toplogy_worker_ready(sender, **kwargs): def handle_update_celery_routes(sender=None, conf=None, **kwargs): conf = conf if conf else sender.app.conf logger.debug(six.text_type("Registering celery routes for {}").format(sender)) - instance = Instance.objects.me() + (changed, instance) = Instance.objects.get_or_register() + if changed: + logger.info(six.text_type("Registered tower node '{}'").format(instance.hostname)) added_routes = update_celery_worker_routes(instance, conf) logger.info(six.text_type("Workers on tower node '{}' added routes {} all routes are now {}") .format(instance.hostname, added_routes, conf.CELERY_ROUTES)) @@ -242,7 +246,9 @@ def handle_update_celery_routes(sender=None, conf=None, **kwargs): @celeryd_after_setup.connect def handle_update_celery_hostname(sender, instance, **kwargs): - tower_instance = Instance.objects.me() + (changed, tower_instance) = Instance.objects.get_or_register() + if changed: + logger.info(six.text_type("Registered tower node '{}'").format(tower_instance.hostname)) instance.hostname = 'celery@{}'.format(tower_instance.hostname) logger.warn(six.text_type("Set hostname to {}").format(instance.hostname)) @@ -310,6 +316,10 @@ def cluster_node_heartbeat(self): this_inst = None lost_instances = [] + (changed, instance) = Instance.objects.get_or_register() + if changed: + logger.info(six.text_type("Registered tower node '{}'").format(instance.hostname)) + for inst in list(instance_list): if inst.hostname == settings.CLUSTER_HOST_ID: this_inst = inst From 25c8bf93ec29e278dda2700003aa9037c4cad9ca Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Tue, 27 Mar 2018 15:48:58 -0400 Subject: [PATCH 013/379] Add AUTH_LDAP_GROUP_TYPE_PARAMS field to LDAP form --- .../configuration/auth-form/sub-forms/auth-ldap.form.js | 7 +++++++ .../configuration/auth-form/sub-forms/auth-ldap1.form.js | 7 +++++++ .../configuration/auth-form/sub-forms/auth-ldap2.form.js | 7 +++++++ .../configuration/auth-form/sub-forms/auth-ldap3.form.js | 7 +++++++ .../configuration/auth-form/sub-forms/auth-ldap4.form.js | 7 +++++++ .../configuration/auth-form/sub-forms/auth-ldap5.form.js | 7 +++++++ 6 files changed, 42 insertions(+) diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap.form.js index f810729baa..598aa1d3e5 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_REQUIRE_GROUP' diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap1.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap1.form.js index 8430fecee3..c8869c6ce4 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap1.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap1.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_1_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_1_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_1_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_1_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_1_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_1_REQUIRE_GROUP' diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap2.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap2.form.js index 09230cb802..a7d4a8769f 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap2.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap2.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_2_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_2_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_2_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_2_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_2_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_2_REQUIRE_GROUP' diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap3.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap3.form.js index 9c26c22829..029974500f 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap3.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap3.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_3_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_3_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_3_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_3_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_3_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_3_REQUIRE_GROUP' diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap4.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap4.form.js index 95c9f6d175..fa8195c5dc 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap4.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap4.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_4_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_4_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_4_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_4_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_4_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_4_REQUIRE_GROUP' diff --git a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap5.form.js b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap5.form.js index fa93437367..9fe6dbb796 100644 --- a/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap5.form.js +++ b/awx/ui/client/src/configuration/auth-form/sub-forms/auth-ldap5.form.js @@ -54,6 +54,13 @@ export default ['i18n', function(i18n) { reset: 'AUTH_LDAP_5_GROUP_TYPE', ngOptions: 'group.label for group in AUTH_LDAP_5_GROUP_TYPE_options track by group.value', }, + AUTH_LDAP_5_GROUP_TYPE_PARAMS: { + type: 'textarea', + reset: 'AUTH_LDAP_5_GROUP_TYPE_PARAMS', + codeMirror: true, + rows: 6, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + }, AUTH_LDAP_5_REQUIRE_GROUP: { type: 'text', reset: 'AUTH_LDAP_5_REQUIRE_GROUP' From 9b30b02acbde41beda247b5f4b81f99f54759a02 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Mon, 26 Mar 2018 12:20:36 -0400 Subject: [PATCH 014/379] Hide Ansible Environment form fields when there are no custom venvs --- .../src/organizations/organizations.form.js | 3 +- awx/ui/client/src/projects/projects.form.js | 28 ++++++++++--------- .../src/shared/upgrade/upgrade.block.less | 6 ++-- .../job_templates/job-template.form.js | 3 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/awx/ui/client/src/organizations/organizations.form.js b/awx/ui/client/src/organizations/organizations.form.js index 14883a3428..f33dd00526 100644 --- a/awx/ui/client/src/organizations/organizations.form.js +++ b/awx/ui/client/src/organizations/organizations.form.js @@ -52,7 +52,8 @@ export default ['NotificationsList', 'i18n', dataTitle: i18n._('Ansible Environment'), dataContainer: 'body', dataPlacement: 'right', - ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' } }, diff --git a/awx/ui/client/src/projects/projects.form.js b/awx/ui/client/src/projects/projects.form.js index 38c151215d..baa204bcee 100644 --- a/awx/ui/client/src/projects/projects.form.js +++ b/awx/ui/client/src/projects/projects.form.js @@ -52,17 +52,6 @@ export default ['i18n', 'NotificationsList', 'TemplateList', ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', awLookupWhen: '(project_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, - custom_virtualenv: { - label: i18n._('Ansible Environment'), - type: 'select', - defaultText: i18n._('Select Ansible Environment'), - ngOptions: 'venv for venv in custom_virtualenvs_options track by venv', - awPopOver: "

" + i18n._("Select the custom Python virtual environment for this project to run on.") + "

", - dataTitle: i18n._('Ansible Environment'), - dataContainer: 'body', - dataPlacement: 'right', - ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' - }, scm_type: { label: i18n._('SCM Type'), type: 'select', @@ -211,8 +200,21 @@ export default ['i18n', 'NotificationsList', 'TemplateList', dataTitle: i18n._('Cache Timeout'), dataPlacement: 'right', dataContainer: "body", - ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' - } + ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)', + subForm: 'sourceSubForm' + }, + custom_virtualenv: { + label: i18n._('Ansible Environment'), + type: 'select', + defaultText: i18n._('Select Ansible Environment'), + ngOptions: 'venv for venv in custom_virtualenvs_options track by venv', + awPopOver: "

" + i18n._("Select the custom Python virtual environment for this project to run on.") + "

", + dataTitle: i18n._('Ansible Environment'), + dataContainer: 'body', + dataPlacement: 'right', + ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' + }, }, buttons: { diff --git a/awx/ui/client/src/shared/upgrade/upgrade.block.less b/awx/ui/client/src/shared/upgrade/upgrade.block.less index 1f7ecbe25f..4d0203fa32 100644 --- a/awx/ui/client/src/shared/upgrade/upgrade.block.less +++ b/awx/ui/client/src/shared/upgrade/upgrade.block.less @@ -1,15 +1,17 @@ .at-Upgrade--panel { align-items: center; background-color: @at-color-body-background-light; + border-radius: 10px; color: @at-color-body-text; display: flex; flex-direction: column; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: @at-font-size-jumbotron-text; + height: ~"calc(100vh - 40px)"; justify-content: center; - margin-top: @at-space-10x; + margin: @at-space-4x; padding: @at-space-10x; - } +} .at-Upgrade--header { display: flex; diff --git a/awx/ui/client/src/templates/job_templates/job-template.form.js b/awx/ui/client/src/templates/job_templates/job-template.form.js index fc24852e2d..cd2824b8a3 100644 --- a/awx/ui/client/src/templates/job_templates/job-template.form.js +++ b/awx/ui/client/src/templates/job_templates/job-template.form.js @@ -240,7 +240,8 @@ function(NotificationsList, i18n) { dataTitle: i18n._('Ansible Environment'), dataContainer: 'body', dataPlacement: 'right', - ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' }, instance_groups: { label: i18n._('Instance Groups'), From 492e74a345f79d655137216ef1d59ec39834cc3c Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Tue, 27 Mar 2018 14:26:20 -0400 Subject: [PATCH 015/379] Remove pending_deletion button bug from inventory list --- .../inventories/inventory.list.js | 2 +- .../list-generator/list-generator.factory.js | 109 +++++++++--------- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js index c075662883..a8edfa27eb 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js @@ -113,7 +113,7 @@ export default ['i18n', function(i18n) { "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy inventory'), dataPlacement: 'top', - ngShow: 'inventory.summary_fields.user_capabilities.edit' + ngShow: '!inventory.pending_deletion && inventory.summary_fields.user_capabilities.edit' }, view: { label: i18n._('View'), diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index eeeb80d2f4..01ca45edfa 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -395,63 +395,64 @@ export default ['$compile', 'Attr', 'Icon', } if (field_action === 'pending_deletion') { innerTable += `Pending Delete`; - } - // Plug in Dropdown Component - if (field_action === 'submit' && list.fieldActions[field_action].relaunch === true) { - innerTable += ``; - } else if (field_action === 'submit' && list.fieldActions[field_action].launch === true) { - innerTable += ``; } else { - fAction = list.fieldActions[field_action]; - innerTable += ""; } - //html += (fAction.label) ? " " + list.fieldActions[field_action].label + - // "" : ""; - innerTable += ""; } } } From dc46a732bcb9fd3c58fb0208c64cdd0d73774288 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Wed, 28 Mar 2018 09:12:06 -0400 Subject: [PATCH 016/379] fix ldap group type params label * copy pasted ldap group type label and did no previously update. This updates. --- awx/sso/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/sso/conf.py b/awx/sso/conf.py index 504b7724d4..fef093e62d 100644 --- a/awx/sso/conf.py +++ b/awx/sso/conf.py @@ -301,8 +301,8 @@ def _register_ldap(append=None): register( 'AUTH_LDAP{}_GROUP_TYPE_PARAMS'.format(append_str), field_class=fields.LDAPGroupTypeParamsField, - label=_('LDAP Group Type'), - help_text=_('Parameters to send the chosen group type.'), + label=_('LDAP Group Type Parameters'), + help_text=_('Key value parameters to send the chosen group type init method.'), category=_('LDAP'), category_slug='ldap', default=collections.OrderedDict([ From eef6f7ecb001f36197cf1770291417514f8081b8 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Wed, 28 Mar 2018 09:50:53 -0400 Subject: [PATCH 017/379] delay looking up settings SYSTEM_UUID --- awx/main/managers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/awx/main/managers.py b/awx/main/managers.py index 1683b38c4e..8748a71c5b 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -87,7 +87,11 @@ class InstanceManager(models.Manager): return node[0] raise RuntimeError("No instance found with the current cluster host id") - def register(self, uuid=settings.SYSTEM_UUID, hostname=settings.CLUSTER_HOST_ID): + def register(self, uuid=None, hostname=None): + if not uuid: + uuid = settings.SYSTEM_UUID + if not hostname: + hostname = settings.CLUSTER_HOST_ID with advisory_lock('instance_registration_%s' % hostname): instance = self.filter(hostname=hostname) if instance.exists(): From 8c167e50c95173f1717c51705d9170cd3c537d38 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 27 Mar 2018 13:40:51 -0400 Subject: [PATCH 018/379] Continuously stream data from verbose jobs In verbose unified job models (inventory updates, system jobs, etc.), do not delay dispatch just because the encoded event data is not part of the data written to the buffer. This allows output from these commands to be submitted to the callback queue as they are produced, instead of waiting until the buffer is closed. --- awx/main/tasks.py | 27 ++++++---- .../tests/unit/utils/test_event_filter.py | 54 ++++++++++++++++++- awx/main/utils/common.py | 28 +++++++++- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index c47101b988..ca6f7dbede 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -55,7 +55,7 @@ from awx.main.queue import CallbackQueueDispatcher from awx.main.expect import run, isolated_manager from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url, check_proot_installed, build_proot_temp_dir, get_licenser, - wrap_args_with_proot, OutputEventFilter, ignore_inventory_computed_fields, + wrap_args_with_proot, OutputEventFilter, OutputVerboseFilter, ignore_inventory_computed_fields, ignore_inventory_group_removal, get_type_for_model, extract_ansible_vars) from awx.main.utils.reload import restart_local_services, stop_local_services from awx.main.utils.pglock import advisory_lock @@ -811,19 +811,26 @@ class BaseTask(LogErrorsTask): def get_stdout_handle(self, instance): ''' - Return an virtual file object for capturing stdout and events. + Return an virtual file object for capturing stdout and/or events. ''' dispatcher = CallbackQueueDispatcher() - def event_callback(event_data): - event_data.setdefault(self.event_data_key, instance.id) - if 'uuid' in event_data: - cache_event = cache.get('ev-{}'.format(event_data['uuid']), None) - if cache_event is not None: - event_data.update(cache_event) - dispatcher.dispatch(event_data) + if isinstance(instance, (Job, AdHocCommand, ProjectUpdate)): + def event_callback(event_data): + event_data.setdefault(self.event_data_key, instance.id) + if 'uuid' in event_data: + cache_event = cache.get('ev-{}'.format(event_data['uuid']), None) + if cache_event is not None: + event_data.update(cache_event) + dispatcher.dispatch(event_data) - return OutputEventFilter(event_callback) + return OutputEventFilter(event_callback) + else: + def event_callback(event_data): + event_data.setdefault(self.event_data_key, instance.id) + dispatcher.dispatch(event_data) + + return OutputVerboseFilter(event_callback) def pre_run_hook(self, instance, **kwargs): ''' diff --git a/awx/main/tests/unit/utils/test_event_filter.py b/awx/main/tests/unit/utils/test_event_filter.py index 85ecc609d0..fb8f4fa144 100644 --- a/awx/main/tests/unit/utils/test_event_filter.py +++ b/awx/main/tests/unit/utils/test_event_filter.py @@ -5,7 +5,7 @@ from StringIO import StringIO from six.moves import xrange -from awx.main.utils import OutputEventFilter +from awx.main.utils import OutputEventFilter, OutputVerboseFilter MAX_WIDTH = 78 EXAMPLE_UUID = '890773f5-fe6d-4091-8faf-bdc8021d65dd' @@ -145,3 +145,55 @@ def test_large_stdout_blob(): f = OutputEventFilter(_callback) for x in range(1024 * 10): f.write('x' * 1024) + + +def test_verbose_line_buffering(): + events = [] + + def _callback(event_data): + events.append(event_data) + + f = OutputVerboseFilter(_callback) + f.write('one two\r\n\r\n') + + assert len(events) == 2 + assert events[0]['start_line'] == 0 + assert events[0]['end_line'] == 1 + assert events[0]['stdout'] == 'one two' + + assert events[1]['start_line'] == 1 + assert events[1]['end_line'] == 2 + assert events[1]['stdout'] == '' + + f.write('three') + assert len(events) == 2 + f.write('\r\nfou') + + # three is not pushed to buffer until its line completes + assert len(events) == 3 + assert events[2]['start_line'] == 2 + assert events[2]['end_line'] == 3 + assert events[2]['stdout'] == 'three' + + f.write('r\r') + f.write('\nfi') + + assert events[3]['start_line'] == 3 + assert events[3]['end_line'] == 4 + assert events[3]['stdout'] == 'four' + + f.write('ve') + f.write('\r\n') + + assert len(events) == 5 + assert events[4]['start_line'] == 4 + assert events[4]['end_line'] == 5 + assert events[4]['stdout'] == 'five' + + f.close() + + from pprint import pprint + pprint(events) + assert len(events) == 6 + + assert events[5]['event'] == 'EOF' diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index ba3413a133..be531d7e17 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -48,7 +48,7 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'copy_m2m_relationships', 'prefetch_page_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', 'OutputEventFilter', + 'get_current_apps', 'set_current_apps', 'OutputEventFilter', 'OutputVerboseFilter', 'extract_ansible_vars', 'get_search_fields', 'get_system_task_capacity', 'get_cpu_capacity', 'get_mem_capacity', 'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict', 'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest', @@ -1009,6 +1009,32 @@ class OutputEventFilter(object): self._current_event_data = None +class OutputVerboseFilter(OutputEventFilter): + ''' + File-like object that dispatches stdout data. + Does not search for encoded job event data. + Use for unified job types that do not encode job event data. + ''' + def write(self, data): + self._buffer.write(data) + + # if the current chunk contains a line break + if data and '\n' in data: + # emit events for all complete lines we know about + lines = self._buffer.getvalue().splitlines(True) # keep ends + remainder = None + # if last line is not a complete line, then exclude it + if '\n' not in lines[-1]: + remainder = lines.pop() + # emit all complete lines + for line in lines: + self._emit_event(line) + self._buffer = StringIO() + # put final partial line back on buffer + if remainder: + self._buffer.write(remainder) + + def is_ansible_variable(key): return key.startswith('ansible_') From 767991fc2b5c0696859adf4156de1f617ddf58fe Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 28 Mar 2018 11:18:00 -0400 Subject: [PATCH 019/379] Complete removal of InitialPlaybookRun --- .../lib/components/components.strings.js | 4 +++ .../launchTemplateButton.component.js | 12 +++++---- .../launchTemplateButton.partial.html | 4 ++- .../initiateplaybookrun.factory.js | 26 ------------------- awx/ui/client/src/job-submission/main.js | 2 -- .../organizations-job-templates.controller.js | 8 ++---- .../linkout/organizations-linkout.route.js | 1 - .../list-generator/list-generator.factory.js | 4 +-- awx/ui/client/src/templates/templates.list.js | 8 ++---- 9 files changed, 19 insertions(+), 50 deletions(-) delete mode 100644 awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js diff --git a/awx/ui/client/lib/components/components.strings.js b/awx/ui/client/lib/components/components.strings.js index 513098dc76..015fd5d387 100644 --- a/awx/ui/client/lib/components/components.strings.js +++ b/awx/ui/client/lib/components/components.strings.js @@ -87,6 +87,10 @@ function ComponentsStrings (BaseString) { FAILED: t.s('Failed') }; + ns.launchTemplate = { + DEFAULT: t.s('Start a job using this template') + }; + ns.list = { DEFAULT_EMPTY_LIST: t.s('Please add items to this list.') }; diff --git a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js index 4081f29bff..d4c9403823 100644 --- a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js +++ b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js @@ -6,22 +6,24 @@ const atLaunchTemplate = { template: '<' }, controller: ['JobTemplateModel', 'WorkflowJobTemplateModel', 'PromptService', '$state', - 'ProcessErrors', '$scope', 'TemplatesStrings', 'Alert', atLaunchTemplateCtrl], + 'ComponentsStrings', 'ProcessErrors', '$scope', 'TemplatesStrings', 'Alert', + atLaunchTemplateCtrl], controllerAs: 'vm' }; function atLaunchTemplateCtrl ( JobTemplate, WorkflowTemplate, PromptService, $state, - ProcessErrors, $scope, strings, Alert + componentsStrings, ProcessErrors, $scope, templatesStrings, Alert ) { const vm = this; const jobTemplate = new JobTemplate(); const workflowTemplate = new WorkflowTemplate(); + vm.strings = componentsStrings; const createErrorHandler = (path, action) => ({ data, status }) => { - const hdr = strings.get('error.HEADER'); - const msg = strings.get('error.CALL', { path, action, status }); + const hdr = templatesStrings.get('error.HEADER'); + const msg = templatesStrings.get('error.CALL', { path, action, status }); ProcessErrors($scope, data, status, null, { hdr, msg }); }; @@ -111,7 +113,7 @@ function atLaunchTemplateCtrl ( } }); } else { - Alert(strings.get('error.UNKNOWN'), strings.get('alert.UNKNOWN_LAUNCH')); + Alert(templatesStrings.get('error.UNKNOWN'), templatesStrings.get('alert.UNKNOWN_LAUNCH')); } }; diff --git a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html index 5be2373264..9ac0fc2e1b 100644 --- a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html +++ b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html @@ -1,6 +1,8 @@
diff --git a/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js deleted file mode 100644 index 01e04a25bc..0000000000 --- a/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js +++ /dev/null @@ -1,26 +0,0 @@ -export default - function InitiatePlaybookRun($compile) { - - // This factory drops the submit-job directive into the dom which - // either launches the job (when no user input is needed) or shows - // the user a job sumbission modal with varying steps based on what - // is being prompted/what passwords are needed. - - return function (params) { - var scope = params.scope.$new(), - id = params.id, - relaunch = params.relaunch || false, - job_type = params.job_type, - host_type = params.host_type || ""; - scope.job_template_id = id; - - var el = $compile( "" )( scope ); - $('#content-container').remove('submit-job').append( el ); - }; - } - -InitiatePlaybookRun.$inject = - [ '$compile' - ]; diff --git a/awx/ui/client/src/job-submission/main.js b/awx/ui/client/src/job-submission/main.js index f0a38c6874..563942818b 100644 --- a/awx/ui/client/src/job-submission/main.js +++ b/awx/ui/client/src/job-submission/main.js @@ -4,7 +4,6 @@ * All Rights Reserved *************************************************/ -import InitiatePlaybookRun from './job-submission-factories/initiateplaybookrun.factory'; import LaunchJob from './job-submission-factories/launchjob.factory'; import GetSurveyQuestions from './job-submission-factories/getsurveyquestions.factory'; import AdhocRun from './job-submission-factories/adhoc-run.factory.js'; @@ -21,7 +20,6 @@ import awPasswordMax from './job-submission-directives/aw-password-max.directive export default angular.module('jobSubmission', []) - .factory('InitiatePlaybookRun', InitiatePlaybookRun) .factory('LaunchJob', LaunchJob) .factory('GetSurveyQuestions', GetSurveyQuestions) .factory('AdhocRun', AdhocRun) diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js index 3caeb4425b..2d5e0eab40 100644 --- a/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js +++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js @@ -6,11 +6,11 @@ export default ['$scope', '$rootScope', '$stateParams', 'Rest', 'ProcessErrors', - 'GetBasePath', 'InitiatePlaybookRun', 'Wait', + 'GetBasePath', 'Wait', '$state', 'OrgJobTemplateList', 'OrgJobTemplateDataset', 'QuerySet', function($scope, $rootScope, $stateParams, Rest, ProcessErrors, - GetBasePath, InitiatePlaybookRun, Wait, + GetBasePath, Wait, $state, OrgJobTemplateList, Dataset, qs) { var list = OrgJobTemplateList, @@ -73,10 +73,6 @@ export default ['$scope', '$rootScope', $state.go('templates.editJobTemplate', { job_template_id: id }); }; - $scope.submitJob = function(id) { - InitiatePlaybookRun({ scope: $scope, id: id, job_type: 'job_template' }); - }; - $scope.scheduleJob = function(id) { $state.go('jobTemplateSchedules', { id: id }); }; diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index 14ece5f287..43165dbf9d 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -272,7 +272,6 @@ export default [{ list.basePath = "job_templates"; list.fields.smart_status.ngInclude = "'/static/partials/organizations-job-template-smart-status.html'"; list.fields.name.ngHref = '#/templates/job_template/{{template.id}}'; - list.fieldActions.submit.ngClick = 'submitJob(template.id)'; list.fieldActions.schedule.ngClick = 'scheduleJob(template.id)'; list.fieldActions.copy.ngClick = 'copyTemplate(template.id)'; list.fieldActions.edit.ngClick = "editJobTemplate(template.id)"; diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index eeeb80d2f4..2df8eec05a 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -397,9 +397,7 @@ export default ['$compile', 'Attr', 'Icon', innerTable += `Pending Delete`; } // Plug in Dropdown Component - if (field_action === 'submit' && list.fieldActions[field_action].relaunch === true) { - innerTable += ``; - } else if (field_action === 'submit' && list.fieldActions[field_action].launch === true) { + if (field_action === 'submit') { innerTable += ``; } else { fAction = list.fieldActions[field_action]; diff --git a/awx/ui/client/src/templates/templates.list.js b/awx/ui/client/src/templates/templates.list.js index fba0d21ad3..17534209f2 100644 --- a/awx/ui/client/src/templates/templates.list.js +++ b/awx/ui/client/src/templates/templates.list.js @@ -77,12 +77,8 @@ export default ['i18n', function(i18n) { columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-3', submit: { - label: i18n._('Launch'), - mode: 'all', - ngClick: 'submitJob(template)', - awToolTip: i18n._('Start a job using this template'), - dataPlacement: 'top', - ngShow: 'template.summary_fields.user_capabilities.start' + // The submit key lets the list generator know that we want to use the + // at-launch-template directive }, schedule: { label: i18n._('Schedule'), From 98dc59765e667ba587be071999daed9ed223b5c0 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 28 Mar 2018 12:53:11 -0400 Subject: [PATCH 020/379] exclude last_used from activity stream --- awx/main/utils/common.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index be531d7e17..89511ab4b9 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -350,11 +350,14 @@ def get_allowed_fields(obj, serializer_mapping): allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id'] else: allowed_fields = [x.name for x in obj._meta.fields] - if obj._meta.model_name == 'user': - field_blacklist = ['last_login'] - allowed_fields = [f for f in allowed_fields if f not in field_blacklist] - if obj._meta.model_name == 'oauth2application': - field_blacklist = ['client_secret'] + + ACTIVITY_STREAM_FIELD_EXCLUSIONS = { + 'user': ['last_login'], + 'oauth2accesstoken': ['last_used'], + 'oauth2application': ['client_secret'] + } + field_blacklist = ACTIVITY_STREAM_FIELD_EXCLUSIONS.get(obj._meta.model_name, []) + if field_blacklist: allowed_fields = [f for f in allowed_fields if f not in field_blacklist] return allowed_fields From 2a45f352bb33c61ccc42d6076378500e365ba7a6 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 28 Mar 2018 14:40:34 -0400 Subject: [PATCH 021/379] make example cred type work --- docs/custom_credential_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/custom_credential_types.md b/docs/custom_credential_types.md index 33426c2011..409fbcfeae 100644 --- a/docs/custom_credential_types.md +++ b/docs/custom_credential_types.md @@ -67,7 +67,7 @@ new "custom" ``Credential Types``: "id": "api_token", "label": "API Token", "type": "string", - "secret": True, + "secret": true }] }, "injectors": { From 23267bce387602514ce351b4f5b21748a81d1dc1 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 28 Mar 2018 15:16:26 -0400 Subject: [PATCH 022/379] properly filter disabled hosts on smart inventory composition see: https://github.com/ansible/tower/issues/1053 --- awx/main/models/inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index b63e8a48c0..83f5d65fd7 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -233,7 +233,7 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin): return {} else: all_group = data.setdefault('all', dict()) - smart_hosts_qs = self.hosts.all() + smart_hosts_qs = self.hosts.filter(**hosts_q).all() smart_hosts = list(smart_hosts_qs.values_list('name', flat=True)) all_group['hosts'] = smart_hosts else: From 4e0b890a033f3d525eab1a3e07d4d9db702761c0 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 28 Mar 2018 15:27:12 -0400 Subject: [PATCH 023/379] Fixed host filter tag removal --- .../smart-search/smart-search.controller.js | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index 87a3b7ad18..e7dd435307 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -324,12 +324,11 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', ' if (Array.isArray(set[key])){ _.remove(set[key], (item) => item === value); // If the array is now empty, remove that key - if(set[key].length === 0) { + if (set[key].length === 0) { delete set[key]; } - } - else { - if($scope.singleSearchParam && set[$scope.singleSearchParam] && set[$scope.singleSearchParam].includes("%20and%20")) { + } else { + if ($scope.singleSearchParam && set[$scope.singleSearchParam] && set[$scope.singleSearchParam].includes("%20and%20")) { let searchParamParts = set[$scope.singleSearchParam].split("%20and%20"); // The value side of each paramPart might have been encoded in SmartSearch.splitFilterIntoTerms _.each(searchParamParts, (paramPart, paramPartIndex) => { @@ -340,8 +339,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', ' searchParamParts.splice(index, 1); } set[$scope.singleSearchParam] = searchParamParts.join("%20and%20"); - } - else { + } else { delete set[key]; } } @@ -350,8 +348,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', ' if (termParts.length === 1) { removed = searchWithoutKey(tagToRemove); - } - else { + } else { let root = termParts[0].split(".")[0].replace(/^-/, ''); let encodeParams = { term: tagToRemove, @@ -382,11 +379,10 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', ' removed = searchWithoutKey(termParts[termParts.length-1]); } } - let cleared = _.cloneDeep(queryset); - removeFromQuerySet(cleared); - if(!$scope.querySet) { + removeFromQuerySet(queryset); + if (!$scope.querySet) { $state.go('.', { - [$scope.iterator + '_search']: cleared }).then(function(){ + [$scope.iterator + '_search']: queryset }).then(function(){ // ISSUE: for some reason deleting a tag from a list in a modal does not // remove the param from $stateParams. Here we'll manually check to make sure // that that happened and remove it if it didn't. @@ -398,11 +394,12 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', ' if($scope.querySet) { $scope.querySet = queryset; } + $scope.dataset = res.data; $scope.collection = res.data.results; - }); - generateSearchTags(); + generateSearchTags(); + }); }; $scope.clearAllTerms = function(){ From 33e24577217113c849ed4d64fa2a1001fcf74f5e Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 28 Mar 2018 15:38:58 -0400 Subject: [PATCH 024/379] remove access_token from ActStr serializer --- awx/api/serializers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index c3b6295c5d..3a6ebf85e8 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4689,7 +4689,6 @@ class ActivityStreamSerializer(BaseSerializer): 'api:setting_singleton_detail', kwargs={'category_slug': obj.setting['category']} ) - rel['access_token'] = '*************' return rel def _get_rel(self, obj, fk): @@ -4743,7 +4742,6 @@ class ActivityStreamSerializer(BaseSerializer): last_name = obj.actor.last_name) if obj.setting: summary_fields['setting'] = [obj.setting] - summary_fields['access_token'] = '*************' return summary_fields From 843833156392b5d92d3e74ad1488f455be799933 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Wed, 28 Mar 2018 15:57:14 -0400 Subject: [PATCH 025/379] make jobs_running more rich in OPTIONS * Expose jobs_running as an IntegerField --- awx/api/serializers.py | 9 ++++++--- awx/main/models/ha.py | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 3a6ebf85e8..41b97c19a9 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4506,6 +4506,12 @@ class InstanceSerializer(BaseSerializer): consumed_capacity = serializers.SerializerMethodField() percent_capacity_remaining = serializers.SerializerMethodField() jobs_running = serializers.SerializerMethodField() + jobs_running = serializers.IntegerField( + help_text=_('Count of jobs in the running or waiting state that ' + 'are targeted for this instance'), + read_only=True + ) + class Meta: model = Instance @@ -4529,9 +4535,6 @@ class InstanceSerializer(BaseSerializer): else: return float("{0:.2f}".format(((float(obj.capacity) - float(obj.consumed_capacity)) / (float(obj.capacity))) * 100)) - def get_jobs_running(self, obj): - return UnifiedJob.objects.filter(execution_node=obj.hostname, status__in=('running', 'waiting',)).count() - class InstanceGroupSerializer(BaseSerializer): diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index 3356dc3a5d..b7e50ec2b4 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -85,6 +85,10 @@ class Instance(models.Model): # NOTE: TODO: Likely to repurpose this once standalone ramparts are a thing return "awx" + @property + def jobs_running(self): + return UnifiedJob.objects.filter(execution_node=self.hostname, status__in=('running', 'waiting',)).count() + def is_lost(self, ref_time=None, isolated=False): if ref_time is None: ref_time = now() From bf6412ea063b53622ce5e870743847be01ba4936 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Wed, 28 Mar 2018 15:58:14 -0400 Subject: [PATCH 026/379] fix api browser endpoint name * Endpoint exposes all jobs associated with an Instance. This is what we want. Align the endpoint description with this behavior by removing the word running. --- awx/api/serializers.py | 1 - awx/api/views.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 41b97c19a9..685331d4ce 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4505,7 +4505,6 @@ class InstanceSerializer(BaseSerializer): consumed_capacity = serializers.SerializerMethodField() percent_capacity_remaining = serializers.SerializerMethodField() - jobs_running = serializers.SerializerMethodField() jobs_running = serializers.IntegerField( help_text=_('Count of jobs in the running or waiting state that ' 'are targeted for this instance'), diff --git a/awx/api/views.py b/awx/api/views.py index d5ae7782c6..d3378c127d 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -631,7 +631,7 @@ class InstanceDetail(RetrieveUpdateAPIView): class InstanceUnifiedJobsList(SubListAPIView): - view_name = _("Instance Running Jobs") + view_name = _("Instance Jobs") model = UnifiedJob serializer_class = UnifiedJobSerializer parent_model = Instance From c1cc92afa06e14ee9c399eb3b48b0ad6d481e00d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 28 Mar 2018 17:02:32 -0400 Subject: [PATCH 027/379] properly filter disabled hosts on smart inventory composition see: #1053 related: https://github.com/ansible/tower/pull/1155 --- awx/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/api/views.py b/awx/api/views.py index f5fdcf6eb2..11a364f587 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -2408,7 +2408,7 @@ class InventoryScriptView(RetrieveAPIView): return Response({}) else: all_group = data.setdefault('all', dict()) - smart_hosts_qs = obj.hosts.all() + smart_hosts_qs = obj.hosts.filter(**hosts_q).all() smart_hosts = list(smart_hosts_qs.values_list('name', flat=True)) all_group['hosts'] = smart_hosts else: From 27e46304265958c3722a00c1571c74a6eb4e8569 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 07:53:10 -0400 Subject: [PATCH 028/379] add verbosity to local_path error --- awx/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 685331d4ce..1cf136cc98 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1345,7 +1345,7 @@ class ProjectOptionsSerializer(BaseSerializer): if scm_type: attrs.pop('local_path', None) if 'local_path' in attrs and attrs['local_path'] not in valid_local_paths: - errors['local_path'] = 'Invalid path choice.' + errors['local_path'] = _('This path is already being used by another manual project.') if errors: raise serializers.ValidationError(errors) From 48112f2c5689f8f83d1f69a1b138a7e8f124fb36 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 08:37:20 -0400 Subject: [PATCH 029/379] prevent modified from showing up in activity stream --- awx/main/signals.py | 6 ++++++ .../functional/models/test_activity_stream.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/awx/main/signals.py b/awx/main/signals.py index 52bd5e55c0..5021f53290 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -400,6 +400,12 @@ model_serializer_mapping = { AdHocCommand: AdHocCommandSerializer, NotificationTemplate: NotificationTemplateSerializer, Notification: NotificationSerializer, + CredentialType: CredentialTypeSerializer, + Schedule: ScheduleSerializer, + Label: LabelSerializer, + WorkflowJobTemplate: WorkflowJobTemplateSerializer, + WorkflowJobTemplateNode: WorkflowJobTemplateNodeSerializer, + WorkflowJob: WorkflowJobSerializer, } diff --git a/awx/main/tests/functional/models/test_activity_stream.py b/awx/main/tests/functional/models/test_activity_stream.py index 7253999fb7..f13ce48f20 100644 --- a/awx/main/tests/functional/models/test_activity_stream.py +++ b/awx/main/tests/functional/models/test_activity_stream.py @@ -16,6 +16,7 @@ from awx.main.models import ( # other AWX from awx.main.utils import model_to_dict +from awx.main.utils.common import get_allowed_fields from awx.api.serializers import InventorySourceSerializer # Django @@ -181,3 +182,20 @@ def test_annon_user_action(): inv = Inventory.objects.create(name='ainventory') entry = inv.activitystream_set.filter(operation='create').first() assert not entry.actor + + +@pytest.mark.django_db +def test_modified_not_allowed_field(somecloud_type): + ''' + If this test fails, that means that read-only fields are showing + up in the activity stream serialization of an instance. + + That _probably_ means that you just connected a new model to the + activity_stream_registrar, but did not add its serializer to + the model->serializer mapping. + ''' + from awx.main.signals import model_serializer_mapping + from awx.main.registrar import activity_stream_registrar + + for Model in activity_stream_registrar.models: + assert 'modified' not in get_allowed_fields(Model(), model_serializer_mapping), Model From 19c3e26cf2dad8d385bc6d002245764e544f97d9 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 08:59:03 -0400 Subject: [PATCH 030/379] exclude created/modified from RoleSerializer --- awx/api/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 685331d4ce..c9af3a3582 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2261,6 +2261,7 @@ class RoleSerializer(BaseSerializer): class Meta: model = Role + fields = ('*', '-created', '-modified') read_only_fields = ('id', 'role_field', 'description', 'name') def to_representation(self, obj): @@ -2276,8 +2277,6 @@ class RoleSerializer(BaseSerializer): ret['summary_fields']['resource_type'] = get_type_for_model(content_model) ret['summary_fields']['resource_type_display_name'] = content_model._meta.verbose_name.title() - ret.pop('created') - ret.pop('modified') return ret def get_related(self, obj): From 5ffe0f40d230a93111f1492bdd00d34e367a7161 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Thu, 29 Mar 2018 10:53:09 -0400 Subject: [PATCH 031/379] Only show banner if user is an org admin without edit capabilities --- awx/ui/client/src/teams/edit/teams-edit.controller.js | 11 +++++++---- awx/ui/client/src/teams/list/teams-list.controller.js | 4 ++-- awx/ui/client/src/teams/main.js | 9 +++++++++ awx/ui/client/src/teams/teams.form.js | 2 +- awx/ui/client/src/teams/teams.list.js | 2 +- awx/ui/client/src/users/edit/users-edit.controller.js | 8 +++++--- awx/ui/client/src/users/list/users-list.controller.js | 4 ++-- awx/ui/client/src/users/main.js | 9 +++++++++ awx/ui/client/src/users/users.form.js | 2 +- awx/ui/client/src/users/users.list.js | 2 +- 10 files changed, 38 insertions(+), 15 deletions(-) diff --git a/awx/ui/client/src/teams/edit/teams-edit.controller.js b/awx/ui/client/src/teams/edit/teams-edit.controller.js index 7ecd631182..41a37b19cb 100644 --- a/awx/ui/client/src/teams/edit/teams-edit.controller.js +++ b/awx/ui/client/src/teams/edit/teams-edit.controller.js @@ -5,17 +5,20 @@ *************************************************/ export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', - 'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'OrgAdminLookup', + 'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'OrgAdminLookup', 'resolvedModels', function($scope, $rootScope, $stateParams, TeamForm, Rest, ProcessErrors, - GetBasePath, Wait, $state, OrgAdminLookup) { + GetBasePath, Wait, $state, OrgAdminLookup, models) { + const { me } = models; var form = TeamForm, - id = $stateParams.team_id, - defaultUrl = GetBasePath('teams') + id; + id = $stateParams.team_id, + defaultUrl = GetBasePath('teams') + id; init(); function init() { + $scope.canEdit = me.get('summary_fields.user_capabilities.edit'); + $scope.isOrgAdmin = me.get('related.admin_of_organizations.count') > 0; $scope.team_id = id; Rest.setUrl(defaultUrl); Wait('start'); diff --git a/awx/ui/client/src/teams/list/teams-list.controller.js b/awx/ui/client/src/teams/list/teams-list.controller.js index c495416ae2..254629d30b 100644 --- a/awx/ui/client/src/teams/list/teams-list.controller.js +++ b/awx/ui/client/src/teams/list/teams-list.controller.js @@ -17,12 +17,12 @@ export default ['$scope', 'Rest', 'TeamList', 'Prompt', init(); function init() { - const canEdit = me.get('summary_fields.user_capabilities.edit'); + $scope.canEdit = me.get('summary_fields.user_capabilities.edit'); $scope.canAdd = false; rbacUiControlService.canAdd('teams') .then(function(params) { - $scope.canAdd = params.canAdd && canEdit; + $scope.canAdd = params.canAdd; }); // search init $scope.list = list; diff --git a/awx/ui/client/src/teams/main.js b/awx/ui/client/src/teams/main.js index df9e15894c..7140e9aaf9 100644 --- a/awx/ui/client/src/teams/main.js +++ b/awx/ui/client/src/teams/main.js @@ -42,6 +42,15 @@ angular.module('Teams', []) activityStreamTarget: 'team' }, resolve: { + edit: { + resolvedModels: ['MeModel', '$q', function(Me, $q) { + const promises = { + me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations')) + }; + + return $q.all(promises); + }] + }, list: { resolvedModels: ['MeModel', '$q', function(Me, $q) { const promises = { diff --git a/awx/ui/client/src/teams/teams.form.js b/awx/ui/client/src/teams/teams.form.js index 0b76cca634..855d8519d1 100644 --- a/awx/ui/client/src/teams/teams.form.js +++ b/awx/ui/client/src/teams/teams.form.js @@ -20,7 +20,7 @@ export default ['i18n', function(i18n) { stateTree: 'teams', tabs: true, messageBar: { - ngShow: '!user_is_system_auditor && !canAdd', + ngShow: 'isOrgAdmin && !canEdit', message: i18n._("Contact your System Administrator to grant you the appropriate permissions to add and edit Users and Teams.") }, fields: { diff --git a/awx/ui/client/src/teams/teams.list.js b/awx/ui/client/src/teams/teams.list.js index 93aab82848..1af52a8084 100644 --- a/awx/ui/client/src/teams/teams.list.js +++ b/awx/ui/client/src/teams/teams.list.js @@ -43,7 +43,7 @@ export default ['i18n', function(i18n) { awToolTip: i18n._('Create a new team'), actionClass: 'btn List-buttonSubmit', buttonContent: '+ ' + i18n._('ADD'), - ngShow: 'canAdd' + ngShow: 'canAdd && canEdit' } }, diff --git a/awx/ui/client/src/users/edit/users-edit.controller.js b/awx/ui/client/src/users/edit/users-edit.controller.js index b268e619f3..82ae48af1d 100644 --- a/awx/ui/client/src/users/edit/users-edit.controller.js +++ b/awx/ui/client/src/users/edit/users-edit.controller.js @@ -14,14 +14,15 @@ const user_type_options = [ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2', - '$state', 'i18n', + '$state', 'i18n', 'resolvedModels', function($scope, $rootScope, $stateParams, UserForm, Rest, ProcessErrors, - GetBasePath, Wait, CreateSelect2, $state, i18n) { + GetBasePath, Wait, CreateSelect2, $state, i18n, models) { for (var i = 0; i < user_type_options.length; i++) { user_type_options[i].label = i18n._(user_type_options[i].label); } + const { me } = models; var form = UserForm, master = {}, id = $stateParams.user_id, @@ -30,7 +31,8 @@ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest', init(); function init() { - $scope.canAdd = true; + $scope.canEdit = me.get('summary_fields.user_capabilities.edit'); + $scope.isOrgAdmin = me.get('related.admin_of_organizations.count') > 0; $scope.isCurrentlyLoggedInUser = (parseInt(id) === $rootScope.current_user.id); $scope.hidePagination = false; $scope.hideSmartSearch = false; diff --git a/awx/ui/client/src/users/list/users-list.controller.js b/awx/ui/client/src/users/list/users-list.controller.js index 9b95081a49..b684a5bbdb 100644 --- a/awx/ui/client/src/users/list/users-list.controller.js +++ b/awx/ui/client/src/users/list/users-list.controller.js @@ -30,12 +30,12 @@ export default ['$scope', '$rootScope', 'Rest', 'UserList', 'Prompt', init(); function init() { - const canEdit = me.get('summary_fields.user_capabilities.edit'); + $scope.canEdit = me.get('summary_fields.user_capabilities.edit'); $scope.canAdd = false; rbacUiControlService.canAdd('users') .then(function(params) { - $scope.canAdd = params.canAdd && canEdit; + $scope.canAdd = params.canAdd; }); // search init diff --git a/awx/ui/client/src/users/main.js b/awx/ui/client/src/users/main.js index 9f4937bf4b..3701d4f626 100644 --- a/awx/ui/client/src/users/main.js +++ b/awx/ui/client/src/users/main.js @@ -44,6 +44,15 @@ angular.module('Users', []) activityStreamTarget: 'user' }, resolve: { + edit: { + resolvedModels: ['MeModel', '$q', function(Me, $q) { + const promises= { + me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations')) + }; + + return $q.all(promises); + }] + }, list: { resolvedModels: ['MeModel', '$q', function(Me, $q) { const promises= { diff --git a/awx/ui/client/src/users/users.form.js b/awx/ui/client/src/users/users.form.js index 925ec999b6..93a1264edd 100644 --- a/awx/ui/client/src/users/users.form.js +++ b/awx/ui/client/src/users/users.form.js @@ -21,7 +21,7 @@ export default ['i18n', function(i18n) { forceListeners: true, tabs: true, messageBar: { - ngShow: '!user_is_system_auditor && !canAdd', + ngShow: 'isOrgAdmin && !canEdit', message: i18n._("Contact your System Administrator to grant you the appropriate permissions to add and edit Users and Teams.") }, fields: { diff --git a/awx/ui/client/src/users/users.list.js b/awx/ui/client/src/users/users.list.js index c53eb79887..95ff26f5e6 100644 --- a/awx/ui/client/src/users/users.list.js +++ b/awx/ui/client/src/users/users.list.js @@ -48,7 +48,7 @@ export default ['i18n', function(i18n) { awToolTip: i18n._('Create a new user'), actionClass: 'btn List-buttonSubmit', buttonContent: '+ ' + i18n._('ADD'), - ngShow: 'canAdd' + ngShow: 'canAdd && canEdit' } }, From 86579775b2bf5ccddab279761ecf59114d25c5a7 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 10:55:43 -0400 Subject: [PATCH 032/379] field OPTIONS tip for filterability --- awx/api/metadata.py | 20 +++++++++++++------ awx/main/tests/functional/api/test_generic.py | 10 ++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/awx/api/metadata.py b/awx/api/metadata.py index bc44deb6f0..ebeb5f3286 100644 --- a/awx/api/metadata.py +++ b/awx/api/metadata.py @@ -44,9 +44,9 @@ class Metadata(metadata.SimpleMetadata): if placeholder is not serializers.empty: field_info['placeholder'] = placeholder - # Update help text for common fields. serializer = getattr(field, 'parent', None) - if serializer: + if serializer and hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'model'): + # Update help text for common fields. field_help_text = { 'id': _('Database ID for this {}.'), 'name': _('Name of this {}.'), @@ -59,10 +59,18 @@ class Metadata(metadata.SimpleMetadata): 'modified': _('Timestamp when this {} was last modified.'), } if field.field_name in field_help_text: - if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'model'): - opts = serializer.Meta.model._meta.concrete_model._meta - verbose_name = smart_text(opts.verbose_name) - field_info['help_text'] = field_help_text[field.field_name].format(verbose_name) + opts = serializer.Meta.model._meta.concrete_model._meta + verbose_name = smart_text(opts.verbose_name) + field_info['help_text'] = field_help_text[field.field_name].format(verbose_name) + # If field is not part of the model, then show it as non-filterable + else: + is_model_field = False + for model_field in serializer.Meta.model._meta.fields: + if field.field_name == model_field.name: + is_model_field = True + break + if not is_model_field: + field_info['filterable'] = False # Indicate if a field has a default value. # FIXME: Still isn't showing all default values? diff --git a/awx/main/tests/functional/api/test_generic.py b/awx/main/tests/functional/api/test_generic.py index 4d68b43ead..f445ee73f7 100644 --- a/awx/main/tests/functional/api/test_generic.py +++ b/awx/main/tests/functional/api/test_generic.py @@ -91,3 +91,13 @@ class TestDeleteViews: job.get_absolute_url(), user=system_auditor ) assert resp.status_code == 403 + + +@pytest.mark.django_db +def test_non_filterable_field(options, instance, admin_user): + r = options( + url=instance.get_absolute_url(), + user=admin_user + ) + field_info = r.data['actions']['GET']['percent_capacity_remaining'] + assert 'filterable' in field_info From 0295351bf18a468f71dcbcd4d234af6d39e23a48 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 12:07:05 -0400 Subject: [PATCH 033/379] include credential type details in activity stream --- awx/api/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 1cf136cc98..9c9d0634d7 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4616,7 +4616,8 @@ class ActivityStreamSerializer(BaseSerializer): ('workflow_job_template_node', ('id', 'unified_job_template_id')), ('label', ('id', 'name', 'organization_id')), ('notification', ('id', 'status', 'notification_type', 'notification_template_id')), - ('access_token', ('id', 'token')) + ('access_token', ('id', 'token')), + ('credential_type', ('id', 'name', 'description', 'kind', 'managed_by_tower')) ] return field_list From 471248e66c8305abd5dfdd461944696b6d522d6a Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Thu, 29 Mar 2018 13:33:17 -0400 Subject: [PATCH 034/379] Fix LDAP_GROUP_TYPE_PARAMS revert value --- .../client/src/configuration/configuration.controller.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/src/configuration/configuration.controller.js b/awx/ui/client/src/configuration/configuration.controller.js index fec8c23227..90a2b554d8 100644 --- a/awx/ui/client/src/configuration/configuration.controller.js +++ b/awx/ui/client/src/configuration/configuration.controller.js @@ -338,7 +338,6 @@ export default [ Wait('start'); var payload = {}; payload[key] = $scope.configDataResolve[key].default; - ConfigurationService.patchConfiguration(payload) .then(function() { $scope[key] = $scope.configDataResolve[key].default; @@ -361,10 +360,13 @@ export default [ else if($scope[key + '_field'].hasOwnProperty('codeMirror')){ const isLdap = (key.indexOf("AUTH_LDAP") !== -1); + const isLdapGroupTypeParams = isLdap && (key.indexOf("GROUP_TYPE_PARAMS") !== -1); const isLdapUserSearch = isLdap && (key.indexOf("USER_SEARCH") !== -1); const isLdapGroupSearch = isLdap && (key.indexOf("GROUP_SEARCH") !== -1); - if (isLdapUserSearch || isLdapGroupSearch) { + if (isLdapGroupTypeParams) { + $scope[key] = JSON.stringify($scope.configDataResolve[key].default); + } else if (isLdapUserSearch || isLdapGroupSearch) { $scope[key] = '[]'; } else { $scope[key] = '{}'; @@ -456,7 +458,6 @@ export default [ } } }); - return payload; }; From 3f15966d9dbdd20289e2c97b138142b2f1cfe7d1 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 29 Mar 2018 13:54:22 -0400 Subject: [PATCH 035/379] Fixed templates list pagination --- .../templates/list-templates.controller.js | 22 ++++--- .../client/features/templates/list.view.html | 5 +- .../shared/paginate/paginate.controller.js | 64 ++++++++----------- 3 files changed, 40 insertions(+), 51 deletions(-) diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/list-templates.controller.js index 321c991011..a5f5213cbf 100644 --- a/awx/ui/client/features/templates/list-templates.controller.js +++ b/awx/ui/client/features/templates/list-templates.controller.js @@ -46,17 +46,19 @@ function ListTemplatesController( $scope.canAdd = ($scope.canAddJobTemplate || $scope.canAddWorkflowJobTemplate); // smart-search - const name = 'templates'; - const iterator = 'template'; - const key = 'template_dataset'; - - $scope.list = { iterator, name }; - $scope.collection = { iterator, basePath: 'unified_job_templates' }; - $scope[key] = Dataset.data; - $scope[name] = Dataset.data.results; + $scope.list = { + iterator: 'template', + name: 'templates' + }; + $scope.collection = { + iterator: 'template', + basePath: 'unified_job_templates' + }; + $scope.template_dataset = Dataset.data; + $scope.templates = Dataset.data.results; $scope.$on('updateDataset', (e, dataset) => { - $scope[key] = dataset; - $scope[name] = dataset.results; + $scope.template_dataset = dataset; + $scope.templates = dataset.results; }); vm.isInvalid = (template) => { diff --git a/awx/ui/client/features/templates/list.view.html b/awx/ui/client/features/templates/list.view.html index a6557423ce..ba45081856 100644 --- a/awx/ui/client/features/templates/list.view.html +++ b/awx/ui/client/features/templates/list.view.html @@ -114,11 +114,10 @@ + base-path="unified_job_templates"> diff --git a/awx/ui/client/src/shared/paginate/paginate.controller.js b/awx/ui/client/src/shared/paginate/paginate.controller.js index 38d02b279f..34a3031a0e 100644 --- a/awx/ui/client/src/shared/paginate/paginate.controller.js +++ b/awx/ui/client/src/shared/paginate/paginate.controller.js @@ -5,45 +5,26 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q queryset, path; $scope.pageSize = pageSize; - $scope.basePageSize = parseInt(pageSize) === 5 ? 5 : 20; $scope.maxVisiblePages = $scope.maxVisiblePages ? parseInt($scope.maxVisiblePages) : 10; - function init() { - - let updatePaginationVariables = function() { - $scope.pageSize = calcPageSize(); - $scope.current = calcCurrent(); - $scope.last = calcLast(); - $scope.pageRange = calcPageRange($scope.current, $scope.last); - $scope.dataRange = calcDataRange(); - }; - - updatePaginationVariables(); - - $scope.$watch('collection', function(){ - updatePaginationVariables(); - }); - } - $scope.filter = function(id){ let pageSize = Number(id); $('#period-dropdown') .replaceWith(""+id+ "\n"); - if($scope.querySet){ + if ($scope.querySet){ let origQuerySet = _.cloneDeep($scope.querySet); queryset = _.merge(origQuerySet, { page_size: pageSize }); - } - else { + } else { queryset = _.merge($stateParams[`${$scope.iterator}_search`], { page_size: pageSize, page: 1 }); } $scope.toPage(); }; $scope.toPage = function(page) { - if(page === 0 || page > $scope.last) { + if (page === 0 || page > $scope.last) { return; } if (GetBasePath($scope.basePath) || $scope.basePath) { @@ -52,24 +33,23 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q let interpolator = $interpolate($scope.basePath); path = interpolator({ $stateParams: $stateParams }); } - if($scope.querySet) { + if ($scope.querySet) { // merging $scope.querySet seems to destroy our initial reference which // kills the two-way binding here. To fix that, clone the queryset first // and merge with that object. let origQuerySet = _.cloneDeep($scope.querySet); queryset = _.merge(origQuerySet, { page: page }); - } - else { + } else { queryset = _.merge($stateParams[`${$scope.iterator}_search`], { page: page }); } - if(!$scope.querySet) { + if (!$scope.querySet) { $state.go('.', { [$scope.iterator + '_search']: queryset }, {notify: false}); } qs.search(path, queryset).then((res) => { - if($scope.querySet) { + if ($scope.querySet) { // Update the query set $scope.querySet = queryset; } @@ -83,10 +63,9 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q } function calcCurrent() { - if($scope.querySet) { + if ($scope.querySet) { return parseInt($scope.querySet.page || '1'); - } - else { + } else { return parseInt($stateParams[`${$scope.iterator}_search`].page || '1'); } } @@ -96,23 +75,20 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q maxVisiblePages = parseInt($scope.maxVisiblePages), pagesLeft, pagesRight; - if(maxVisiblePages % 2) { + if (maxVisiblePages % 2) { // It's an odd number pagesLeft = (maxVisiblePages - 1) / 2; pagesRight = ((maxVisiblePages - 1) / 2) + 1; - } - else { + } else { // Its an even number pagesLeft = pagesRight = maxVisiblePages / 2; } if (last < maxVisiblePages) { // Don't have enough pages to exceed the max range - just show all of them result = _.range(1, last + 1); - } - else if(current === last) { + } else if (current === last) { result = _.range(last + 1 - maxVisiblePages, last + 1); - } - else { + } else { let topOfRange = current + pagesRight > maxVisiblePages + 1 ? (current + pagesRight < last + 1 ? current + pagesRight : last + 1) : maxVisiblePages + 1; let bottomOfRange = (topOfRange === last + 1) ? last + 1 - maxVisiblePages : (current - pagesLeft > 0 ? current - pagesLeft : 1); result = _.range(bottomOfRange, topOfRange); @@ -137,6 +113,18 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q return Number(pageSize) ; } - init(); + let updatePaginationVariables = function() { + $scope.pageSize = calcPageSize(); + $scope.current = calcCurrent(); + $scope.last = calcLast(); + $scope.pageRange = calcPageRange($scope.current, $scope.last); + $scope.dataRange = calcDataRange(); + }; + + updatePaginationVariables(); + + $scope.$watch('collection', function(){ + updatePaginationVariables(); + }); } ]; From 9244989a140c70cfe6aa46200e75d7a30079abbb Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 14:57:26 -0400 Subject: [PATCH 036/379] test for all models in ActivityStreamSerializer --- awx/api/serializers.py | 25 +++++++-------- .../test_activity_stream_serializer.py | 31 +++++++++++++++++++ 2 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 awx/main/tests/unit/api/serializers/test_activity_stream_serializer.py diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 9c9d0634d7..52f19eff11 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4616,8 +4616,10 @@ class ActivityStreamSerializer(BaseSerializer): ('workflow_job_template_node', ('id', 'unified_job_template_id')), ('label', ('id', 'name', 'organization_id')), ('notification', ('id', 'status', 'notification_type', 'notification_template_id')), - ('access_token', ('id', 'token')), - ('credential_type', ('id', 'name', 'description', 'kind', 'managed_by_tower')) + ('o_auth2_access_token', ('id', 'user_id', 'description', 'application', 'scope')), + ('o_auth2_application', ('id', 'name', 'description')), + ('credential_type', ('id', 'name', 'description', 'kind', 'managed_by_tower')), + ('ad_hoc_command', ('id', 'name', 'status', 'limit')) ] return field_list @@ -4659,6 +4661,10 @@ class ActivityStreamSerializer(BaseSerializer): def get_related(self, obj): rel = {} + VIEW_NAME_EXCEPTIONS = { + 'custom_inventory_script': 'inventory_script_detail', + 'o_auth2_access_token': 'o_auth2_token_detail' + } if obj.actor is not None: rel['actor'] = self.reverse('api:user_detail', kwargs={'pk': obj.actor.pk}) for fk, __ in self._local_summarizable_fk_fields: @@ -4672,18 +4678,11 @@ class ActivityStreamSerializer(BaseSerializer): if getattr(thisItem, 'id', None) in id_list: continue id_list.append(getattr(thisItem, 'id', None)) - if fk == 'custom_inventory_script': - rel[fk].append(self.reverse('api:inventory_script_detail', kwargs={'pk': thisItem.id})) - elif fk == 'application': - rel[fk].append(self.reverse( - 'api:o_auth2_application_detail', kwargs={'pk': thisItem.pk} - )) - elif fk == 'access_token': - rel[fk].append(self.reverse( - 'api:o_auth2_token_detail', kwargs={'pk': thisItem.pk} - )) + if fk in VIEW_NAME_EXCEPTIONS: + view_name = VIEW_NAME_EXCEPTIONS[fk] else: - rel[fk].append(self.reverse('api:' + fk + '_detail', kwargs={'pk': thisItem.id})) + view_name = fk + '_detail' + rel[fk].append(self.reverse('api:' + view_name, kwargs={'pk': thisItem.id})) if fk == 'schedule': rel['unified_job_template'] = thisItem.unified_job_template.get_absolute_url(self.context.get('request')) diff --git a/awx/main/tests/unit/api/serializers/test_activity_stream_serializer.py b/awx/main/tests/unit/api/serializers/test_activity_stream_serializer.py new file mode 100644 index 0000000000..50849b31c5 --- /dev/null +++ b/awx/main/tests/unit/api/serializers/test_activity_stream_serializer.py @@ -0,0 +1,31 @@ +from awx.api.serializers import ActivityStreamSerializer +from awx.main.registrar import activity_stream_registrar +from awx.main.models import ActivityStream + +from awx.conf.models import Setting + + +def test_activity_stream_related(): + ''' + If this test failed with content in `missing_models`, that means that a + model has been connected to the activity stream, but the model has not + been added to the activity stream serializer. + + How to fix this: + Ideally, all models should be in awx.api.serializers.SUMMARIZABLE_FK_FIELDS + + If, for whatever reason, the missing model should not generally be + summarized from related resources, then a special case can be carved out in + ActivityStreamSerializer._local_summarizable_fk_fields + ''' + serializer_related = set( + ActivityStream._meta.get_field(field_name).related_model for field_name, stuff in + ActivityStreamSerializer()._local_summarizable_fk_fields + if hasattr(ActivityStream, field_name) + ) + + models = set(activity_stream_registrar.models) + models.remove(Setting) + + missing_models = models - serializer_related + assert not missing_models From 2ccbf5a8176c28822ccd86d1b7ec5990e66869eb Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 29 Mar 2018 15:09:31 -0400 Subject: [PATCH 037/379] Fixed bug relaunching job on successful/failed hosts --- .../relaunchButton/relaunchButton.component.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js index 045afd3585..8afa1e2057 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js @@ -101,9 +101,19 @@ function atRelaunchCtrl ( }; }); } else { - jobObj.postRelaunch({ - id: vm.job.id - }).then((launchRes) => { + + let launchParams = { + id: vm.job.id, + }; + + if (_.has(option, 'name')) { + launchParams.relaunchData = { + hosts: (option.name).toLowerCase() + }; + } + + jobObj.postRelaunch(launchParams) + .then((launchRes) => { if (!$state.includes('jobs')) { $state.go('jobResult', { id: launchRes.data.id }, { reload: true }); } From a4721dc9e7d3d85c0bd7a23c9080a9cdc7b5ace6 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 15:43:25 -0400 Subject: [PATCH 038/379] validate token scope --- awx/api/serializers.py | 19 +++++++++++++++++++ .../api/serializers/test_token_serializer.py | 14 ++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 awx/main/tests/unit/api/serializers/test_token_serializer.py diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 6f74ac9d02..9e26fb0634 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1095,6 +1095,7 @@ class OAuth2TokenSerializer(BaseSerializer): refresh_token = serializers.SerializerMethodField() token = serializers.SerializerMethodField() + ALLOWED_SCOPES = ['read', 'write'] class Meta: model = OAuth2AccessToken @@ -1142,6 +1143,24 @@ class OAuth2TokenSerializer(BaseSerializer): except ObjectDoesNotExist: return '' + def _is_valid_scope(self, value): + if not value or (not isinstance(value, six.string_types)): + return False + words = value.split() + for word in words: + if words.count(word) > 1: + return False # do not allow duplicates + if word not in self.ALLOWED_SCOPES: + return False + return True + + def validate_scope(self, value): + if not self._is_valid_scope(value): + raise serializers.ValidationError(_( + 'Must be a simple space-separated string with allowed scopes {}.' + ).format(self.ALLOWED_SCOPES)) + return value + def create(self, validated_data): validated_data['user'] = self.context['request'].user validated_data['token'] = generate_token() diff --git a/awx/main/tests/unit/api/serializers/test_token_serializer.py b/awx/main/tests/unit/api/serializers/test_token_serializer.py new file mode 100644 index 0000000000..5ead166664 --- /dev/null +++ b/awx/main/tests/unit/api/serializers/test_token_serializer.py @@ -0,0 +1,14 @@ +import pytest + +from awx.api.serializers import OAuth2TokenSerializer + + +@pytest.mark.parametrize('scope, expect', [ + ('', False), + ('read', True), + ('read read', False), + ('write read', True), + ('read rainbow', False) +]) +def test_invalid_scopes(scope, expect): + assert OAuth2TokenSerializer()._is_valid_scope(scope) is expect From 838b723c73c8b316e1f970069a19ef5928016d8c Mon Sep 17 00:00:00 2001 From: chris meyers Date: Wed, 28 Mar 2018 09:10:39 -0400 Subject: [PATCH 039/379] add all instances to special tower instance group * All instances except isolated instances * Also, prevent any tower attributes from being modified via the API --- awx/api/permissions.py | 5 +--- awx/main/managers.py | 3 +++ awx/main/models/ha.py | 4 +++ awx/main/tasks.py | 8 ++++-- .../functional/api/test_instance_group.py | 18 +++++++++---- .../task_management/test_rampart_groups.py | 3 ++- awx/main/tests/functional/test_instances.py | 27 +++++++++++++++++++ 7 files changed, 56 insertions(+), 12 deletions(-) diff --git a/awx/api/permissions.py b/awx/api/permissions.py index 3224b555e9..6c7fee85bf 100644 --- a/awx/api/permissions.py +++ b/awx/api/permissions.py @@ -231,10 +231,7 @@ class IsSuperUser(permissions.BasePermission): class InstanceGroupTowerPermission(ModelAccessPermission): def has_object_permission(self, request, view, obj): - if request.method == 'DELETE' and obj.name == "tower": - return False - if request.method in ['PATCH', 'PUT'] and obj.name == 'tower' and \ - request and request.data and request.data.get('name', '') != 'tower': + if request.method not in permissions.SAFE_METHODS and obj.name == "tower": return False return super(InstanceGroupTowerPermission, self).has_object_permission(request, view, obj) diff --git a/awx/main/managers.py b/awx/main/managers.py index 8748a71c5b..ce5a3a1971 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -113,6 +113,9 @@ class InstanceManager(models.Manager): # NOTE: TODO: Likely to repurpose this once standalone ramparts are a thing return "tower" + def all_non_isolated(self): + return self.filter(rampart_groups__controller__isnull=True).distinct() + class InstanceGroupManager(models.Manager): """A custom manager class for the Instance model. diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index 3356dc3a5d..181b3f768b 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -159,6 +159,10 @@ class InstanceGroup(models.Model, RelatedJobsMixin): def _get_related_jobs(self): return UnifiedJob.objects.filter(instance_group=self) + def add_all_non_iso_instances(self): + self.instances = Instance.objects.all_non_isolated() + self.save() + class Meta: app_label = 'main' diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2c5c1afa7e..3aeaadd786 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -143,9 +143,13 @@ def apply_cluster_membership_policies(self): actual_instances = [] Group = namedtuple('Group', ['obj', 'instances']) Node = namedtuple('Instance', ['obj', 'groups']) + + # Add every instnace to the special 'tower' group + InstanceGroup.objects.get(name='tower').add_all_non_iso_instances() + # Process policy instance list first, these will represent manually managed instances # that will not go through automatic policy determination - for ig in InstanceGroup.objects.all(): + for ig in InstanceGroup.objects.exclude(name='tower'): logger.info(six.text_type("Considering group {}").format(ig.name)) ig.instances.clear() group_actual = Group(obj=ig, instances=[]) @@ -312,7 +316,7 @@ def purge_old_stdout_files(self): def cluster_node_heartbeat(self): logger.debug("Cluster node heartbeat task.") nowtime = now() - instance_list = list(Instance.objects.filter(rampart_groups__controller__isnull=True).distinct()) + instance_list = list(Instance.objects.all_non_isolated()) this_inst = None lost_instances = [] diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py index 038950927c..f7978ca8da 100644 --- a/awx/main/tests/functional/api/test_instance_group.py +++ b/awx/main/tests/functional/api/test_instance_group.py @@ -80,12 +80,20 @@ def test_delete_instance_group_jobs_running(delete, instance_group_jobs_running, @pytest.mark.django_db -def test_delete_tower_instance_group_prevented(delete, options, tower_instance_group, user): +def test_modify_delete_tower_instance_group_prevented(delete, options, tower_instance_group, user, patch, put): url = reverse("api:instance_group_detail", kwargs={'pk': tower_instance_group.pk}) super_user = user('bob', True) + + # DELETE tower group not allowed delete(url, None, super_user, expect=403) + + # OPTIONS should just be "GET" resp = options(url, None, super_user, expect=200) - actions = ['GET', 'PUT',] - assert 'DELETE' not in resp.data['actions'] - for action in actions: - assert action in resp.data['actions'] + assert len(resp.data['actions'].keys()) == 1 + assert 'GET' in resp.data['actions'] + + # Updating tower group fields not allowed + patch(url, {'name': 'foobar'}, super_user, expect=403) + patch(url, {'policy_instance_percentage': 40}, super_user, expect=403) + put(url, {'name': 'foobar'}, super_user, expect=403) + diff --git a/awx/main/tests/functional/task_management/test_rampart_groups.py b/awx/main/tests/functional/task_management/test_rampart_groups.py index 9b4b3eac44..e71afa09db 100644 --- a/awx/main/tests/functional/task_management/test_rampart_groups.py +++ b/awx/main/tests/functional/task_management/test_rampart_groups.py @@ -162,6 +162,7 @@ def test_instance_group_basic_policies(instance_factory, instance_group_factory) i2 = instance_factory("i2") i3 = instance_factory("i3") i4 = instance_factory("i4") + instance_group_factory("tower") ig0 = instance_group_factory("ig0") ig1 = instance_group_factory("ig1", minimum=2) ig2 = instance_group_factory("ig2", percentage=50) @@ -174,7 +175,7 @@ def test_instance_group_basic_policies(instance_factory, instance_group_factory) ig2 = InstanceGroup.objects.get(id=ig2.id) ig3 = InstanceGroup.objects.get(id=ig3.id) assert len(ig0.instances.all()) == 1 - assert i0 in ig0.instances.all() + assert i0 in ig0.instances.all() assert len(InstanceGroup.objects.get(id=ig1.id).instances.all()) == 2 assert i1 in ig1.instances.all() assert i2 in ig1.instances.all() diff --git a/awx/main/tests/functional/test_instances.py b/awx/main/tests/functional/test_instances.py index 11484dfc6e..e2d02a5df4 100644 --- a/awx/main/tests/functional/test_instances.py +++ b/awx/main/tests/functional/test_instances.py @@ -31,15 +31,32 @@ def test_instance_dup(org_admin, organization, project, instance_factory, instan assert api_num_instances_oa == (actual_num_instances - 1) +@pytest.mark.django_db +@mock.patch('awx.main.tasks.handle_ha_toplogy_changes', return_value=None) +def test_policy_instance_tower_group(mock, instance_factory, instance_group_factory): + instance_factory("i1") + instance_factory("i2") + instance_factory("i3") + ig_t = instance_group_factory("tower") + instance_group_factory("ig1", percentage=25) + instance_group_factory("ig2", percentage=25) + instance_group_factory("ig3", percentage=25) + instance_group_factory("ig4", percentage=25) + apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 3 + + @pytest.mark.django_db @mock.patch('awx.main.tasks.handle_ha_toplogy_changes', return_value=None) def test_policy_instance_few_instances(mock, instance_factory, instance_group_factory): i1 = instance_factory("i1") + ig_t = instance_group_factory("tower") ig_1 = instance_group_factory("ig1", percentage=25) ig_2 = instance_group_factory("ig2", percentage=25) ig_3 = instance_group_factory("ig3", percentage=25) ig_4 = instance_group_factory("ig4", percentage=25) apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 1 assert len(ig_1.instances.all()) == 1 assert i1 in ig_1.instances.all() assert len(ig_2.instances.all()) == 1 @@ -50,6 +67,7 @@ def test_policy_instance_few_instances(mock, instance_factory, instance_group_fa assert i1 in ig_4.instances.all() i2 = instance_factory("i2") apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 2 assert len(ig_1.instances.all()) == 1 assert i1 in ig_1.instances.all() assert len(ig_2.instances.all()) == 1 @@ -66,11 +84,13 @@ def test_policy_instance_distribution_uneven(mock, instance_factory, instance_gr i1 = instance_factory("i1") i2 = instance_factory("i2") i3 = instance_factory("i3") + ig_t = instance_group_factory("tower") ig_1 = instance_group_factory("ig1", percentage=25) ig_2 = instance_group_factory("ig2", percentage=25) ig_3 = instance_group_factory("ig3", percentage=25) ig_4 = instance_group_factory("ig4", percentage=25) apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 3 assert len(ig_1.instances.all()) == 1 assert i1 in ig_1.instances.all() assert len(ig_2.instances.all()) == 1 @@ -88,11 +108,13 @@ def test_policy_instance_distribution_even(mock, instance_factory, instance_grou i2 = instance_factory("i2") i3 = instance_factory("i3") i4 = instance_factory("i4") + ig_t = instance_group_factory("tower") ig_1 = instance_group_factory("ig1", percentage=25) ig_2 = instance_group_factory("ig2", percentage=25) ig_3 = instance_group_factory("ig3", percentage=25) ig_4 = instance_group_factory("ig4", percentage=25) apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 4 assert len(ig_1.instances.all()) == 1 assert i1 in ig_1.instances.all() assert len(ig_2.instances.all()) == 1 @@ -104,6 +126,7 @@ def test_policy_instance_distribution_even(mock, instance_factory, instance_grou ig_1.policy_instance_minimum = 2 ig_1.save() apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 4 assert len(ig_1.instances.all()) == 2 assert i1 in ig_1.instances.all() assert i2 in ig_1.instances.all() @@ -122,11 +145,13 @@ def test_policy_instance_distribution_simultaneous(mock, instance_factory, insta i2 = instance_factory("i2") i3 = instance_factory("i3") i4 = instance_factory("i4") + ig_t = instance_group_factory("tower") ig_1 = instance_group_factory("ig1", percentage=25, minimum=2) ig_2 = instance_group_factory("ig2", percentage=25) ig_3 = instance_group_factory("ig3", percentage=25) ig_4 = instance_group_factory("ig4", percentage=25) apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 4 assert len(ig_1.instances.all()) == 2 assert i1 in ig_1.instances.all() assert i2 in ig_1.instances.all() @@ -143,11 +168,13 @@ def test_policy_instance_distribution_simultaneous(mock, instance_factory, insta def test_policy_instance_list_manually_managed(mock, instance_factory, instance_group_factory): i1 = instance_factory("i1") i2 = instance_factory("i2") + ig_t = instance_group_factory("tower") ig_1 = instance_group_factory("ig1", percentage=100, minimum=2) ig_2 = instance_group_factory("ig2") ig_2.policy_instance_list = [i2.hostname] ig_2.save() apply_cluster_membership_policies() + assert len(ig_t.instances.all()) == 2 assert len(ig_1.instances.all()) == 1 assert i1 in ig_1.instances.all() assert i2 not in ig_1.instances.all() From 92dc45094091857e0cd2a5419c6e2610338984b9 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 26 Mar 2018 17:07:23 -0700 Subject: [PATCH 040/379] Adds acceptance doc for networking UI * Adds gerkin feature file for networking visualization * Adds implementation details to networking.md --- docs/img/network-example-1.png | Bin 0 -> 104251 bytes docs/img/network-example-2.png | Bin 0 -> 203433 bytes docs/networking.feature | 76 ++++ docs/networking.md | 686 ++++++++++++++++++++++++++++++ docs/networking/README.md | 100 +++++ docs/networking/animation.yml | 29 ++ docs/networking/button.png | Bin 0 -> 56313 bytes docs/networking/button.yml | 43 ++ docs/networking/buttons.png | Bin 0 -> 34986 bytes docs/networking/buttons.yml | 28 ++ docs/networking/details.panel.png | Bin 0 -> 64295 bytes docs/networking/details.panel.yml | 26 ++ docs/networking/device.detail.png | Bin 0 -> 41788 bytes docs/networking/device.detail.yml | 19 + docs/networking/group.png | Bin 0 -> 115989 bytes docs/networking/group.yml | 119 ++++++ docs/networking/hotkeys.png | Bin 0 -> 46783 bytes docs/networking/hotkeys.yml | 25 ++ docs/networking/keybindings.png | Bin 0 -> 165217 bytes docs/networking/keybindings.yml | 38 ++ docs/networking/link.png | Bin 0 -> 42686 bytes docs/networking/link.yml | 42 ++ docs/networking/messages.yml | 19 + docs/networking/mode.png | Bin 0 -> 54540 bytes docs/networking/mode.yml | 96 +++++ docs/networking/models.png | Bin 0 -> 211837 bytes docs/networking/models.yml | 130 ++++++ docs/networking/move.png | Bin 0 -> 336443 bytes docs/networking/move.readonly.png | Bin 0 -> 151622 bytes docs/networking/move.readonly.yml | 65 +++ docs/networking/move.yml | 107 +++++ docs/networking/null.png | Bin 0 -> 32591 bytes docs/networking/null.yml | 15 + docs/networking/pipeline.png | Bin 0 -> 143969 bytes docs/networking/pipeline.yml | 233 ++++++++++ docs/networking/rack.png | Bin 0 -> 104796 bytes docs/networking/rack.yml | 83 ++++ docs/networking/site.png | Bin 0 -> 106325 bytes docs/networking/site.yml | 83 ++++ docs/networking/stream.png | Bin 0 -> 76443 bytes docs/networking/stream.yml | 42 ++ docs/networking/test.yml | 50 +++ docs/networking/time.png | Bin 0 -> 69379 bytes docs/networking/time.yml | 37 ++ docs/networking/toolbox.png | Bin 0 -> 59368 bytes docs/networking/toolbox.yml | 98 +++++ docs/networking/view.png | Bin 0 -> 70936 bytes docs/networking/view.yml | 45 ++ 48 files changed, 2334 insertions(+) create mode 100644 docs/img/network-example-1.png create mode 100644 docs/img/network-example-2.png create mode 100644 docs/networking.feature create mode 100644 docs/networking.md create mode 100644 docs/networking/README.md create mode 100644 docs/networking/animation.yml create mode 100644 docs/networking/button.png create mode 100644 docs/networking/button.yml create mode 100644 docs/networking/buttons.png create mode 100644 docs/networking/buttons.yml create mode 100644 docs/networking/details.panel.png create mode 100644 docs/networking/details.panel.yml create mode 100644 docs/networking/device.detail.png create mode 100644 docs/networking/device.detail.yml create mode 100644 docs/networking/group.png create mode 100644 docs/networking/group.yml create mode 100644 docs/networking/hotkeys.png create mode 100644 docs/networking/hotkeys.yml create mode 100644 docs/networking/keybindings.png create mode 100644 docs/networking/keybindings.yml create mode 100644 docs/networking/link.png create mode 100644 docs/networking/link.yml create mode 100644 docs/networking/messages.yml create mode 100644 docs/networking/mode.png create mode 100644 docs/networking/mode.yml create mode 100644 docs/networking/models.png create mode 100644 docs/networking/models.yml create mode 100644 docs/networking/move.png create mode 100644 docs/networking/move.readonly.png create mode 100644 docs/networking/move.readonly.yml create mode 100644 docs/networking/move.yml create mode 100644 docs/networking/null.png create mode 100644 docs/networking/null.yml create mode 100644 docs/networking/pipeline.png create mode 100644 docs/networking/pipeline.yml create mode 100644 docs/networking/rack.png create mode 100644 docs/networking/rack.yml create mode 100644 docs/networking/site.png create mode 100644 docs/networking/site.yml create mode 100644 docs/networking/stream.png create mode 100644 docs/networking/stream.yml create mode 100644 docs/networking/test.yml create mode 100644 docs/networking/time.png create mode 100644 docs/networking/time.yml create mode 100644 docs/networking/toolbox.png create mode 100644 docs/networking/toolbox.yml create mode 100644 docs/networking/view.png create mode 100644 docs/networking/view.yml diff --git a/docs/img/network-example-1.png b/docs/img/network-example-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d602e232af4100b770fd7fd15d478692545d4d82 GIT binary patch literal 104251 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 @bDBV6=5JDjpfT_ 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

llxX} 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 Uvr4lY&%mFIq=H15m|@WW12vPFHUIzs literal 0 HcmV?d00001 diff --git a/docs/networking/buttons.yml b/docs/networking/buttons.yml new file mode 100644 index 0000000000..b1cc56a246 --- /dev/null +++ b/docs/networking/buttons.yml @@ -0,0 +1,28 @@ +app: buttons_fsm +finite_state_machine_id: 7 +panX: 133 +panY: 41 +scaleXY: 1 +states: +- label: Start + size: 100 + x: 392 + y: 88 +- label: Ready + size: 100 + x: 392 + y: 281 +- label: ButtonPressed + size: 100 + x: 394 + y: 491 +transitions: +- from_state: Start + label: start + to_state: Ready +- from_state: Ready + label: onMouseDown + to_state: ButtonPressed +- from_state: ButtonPressed + label: onMouseUp + to_state: Ready diff --git a/docs/networking/details.panel.png b/docs/networking/details.panel.png new file mode 100644 index 0000000000000000000000000000000000000000..d060f3bd2f34eeb0f8b0d8894c3824d0416bdedc GIT binary patch literal 64295 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 VVVk%c~0)l#-=&ti$9o^e$pB45W|(A5qM-OgCe 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�w*?4t6z$`F(wJCd|rf$>ig@ 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%0A2@j8inmcz-g&iuUl8!VJv`K4I_{yolM=NAQ&+B0 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 literal 0 HcmV?d00001 diff --git a/docs/networking/details.panel.yml b/docs/networking/details.panel.yml new file mode 100644 index 0000000000..555d21c8be --- /dev/null +++ b/docs/networking/details.panel.yml @@ -0,0 +1,26 @@ +diagram_id: 70 +finite_state_machine_id: 21 +name: diagram +states: +- id: 1 + label: Start + x: 590 + y: 233 +- id: 2 + label: Collapsed + x: 594 + y: 490 +- id: 3 + label: Expanded + x: 919 + y: 491 +transitions: +- from_state: Start + label: start + to_state: Collapsed +- from_state: Expanded + label: onDetailsPanelClose + to_state: Collapsed +- from_state: Collapsed + label: onDetailsPanel + to_state: Expanded diff --git a/docs/networking/device.detail.png b/docs/networking/device.detail.png new file mode 100644 index 0000000000000000000000000000000000000000..0f11ac8ffe0280a6f99cf6c960695939ad1b878c GIT binary patch literal 41788 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 literal 0 HcmV?d00001 diff --git a/docs/networking/device.detail.yml b/docs/networking/device.detail.yml new file mode 100644 index 0000000000..fcb2f52eb6 --- /dev/null +++ b/docs/networking/device.detail.yml @@ -0,0 +1,19 @@ +finite_state_machine_id: 19 +name: device_detail_fsm +states: +- id: 2 + label: Ready + x: 517 + y: 588 +- id: 3 + label: Disable + x: 770 + y: 455 +- id: 1 + label: Start + x: 507 + y: 336 +transitions: +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/group.png b/docs/networking/group.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca5ead53816821baad00c685860f232d257dd61 GIT binary patch literal 115989 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

ohx9E|;|{KYi! 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 literal 0 HcmV?d00001 diff --git a/docs/networking/link.yml b/docs/networking/link.yml new file mode 100644 index 0000000000..7b3a7ea16c --- /dev/null +++ b/docs/networking/link.yml @@ -0,0 +1,42 @@ +finite_state_machine_id: 4 +name: link_fsm +states: +- id: 5 + label: Selecting + x: -429 + y: 63 +- id: 2 + label: Start + x: 15 + y: -221 +- id: 4 + label: Connecting + x: -429 + y: 466 +- id: 3 + label: Connected + x: 47 + y: 453 +- id: 1 + label: Ready + x: 26 + y: 61 +transitions: +- from_state: Ready + label: onNewLink + to_state: Selecting +- from_state: Selecting + label: onMouseUp + to_state: Connecting +- from_state: Connecting + label: onMouseUp + to_state: Connected +- from_state: Connecting + label: onMouseUp + to_state: Ready +- from_state: Connected + label: start + to_state: Ready +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/messages.yml b/docs/networking/messages.yml new file mode 100644 index 0000000000..06ee3b9b75 --- /dev/null +++ b/docs/networking/messages.yml @@ -0,0 +1,19 @@ +messages: + - {msg_type: DeviceMove, fields: [msg_type, sender, id, x, y, previous_x, previous_y]} + - {msg_type: DeviceCreate, fields: [msg_type, sender, id, x, y, name, type, host_id]} + - {msg_type: DeviceDestroy, fields: [msg_type, sender, id, previous_x, previous_y, previous_name, previous_type, previous_host_id]} + - {msg_type: DeviceLabelEdit, fields: [msg_type, sender, id, name, previous_name]} + - {msg_type: DeviceSelected, fields: [msg_type, sender, id]} + - {msg_type: DeviceUnSelected, fields: [msg_type, sender, id]} + - {msg_type: InterfaceCreate, fields: [msg_type, sender, device_id, id, name]} + - {msg_type: InterfaceLabelEdit, fields: [msg_type, sender, id, device_id, name, previous_name]} + - {msg_type: LinkLabelEdit, fields: [msg_type, sender, id, name, previous_name]} + - {msg_type: LinkCreate, fields: [msg_type, id, sender, name, from_device_id, to_device_id, from_interface_id, to_interface_id]} + - {msg_type: LinkDestroy, fields: [msg_type, id, sender, name, from_device_id, to_device_id, from_interface_id, to_interface_id]} + - {msg_type: LinkSelected, fields: [msg_type, sender, id]} + - {msg_type: LinkUnSelected, fields: [msg_type, sender, id]} + - {msg_type: MultipleMessage, fields: [msg_type, sender, messages]} + - {msg_type: Snapshot, fields: [msg_type, sender, devices, links, order, trace_id]} + - {msg_type: id, type: int} + - {msg_type: topology_id, type: int} + - {msg_type: Topology, fields: [topology_id, name, panX, panY, scale, link_id_seq, device_id_seq]} diff --git a/docs/networking/mode.png b/docs/networking/mode.png new file mode 100644 index 0000000000000000000000000000000000000000..b8dff5610da2b527e5a106c52c2db97d44480631 GIT binary patch literal 54540 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 literal 0 HcmV?d00001 diff --git a/docs/networking/mode.yml b/docs/networking/mode.yml new file mode 100644 index 0000000000..b124a3d32e --- /dev/null +++ b/docs/networking/mode.yml @@ -0,0 +1,96 @@ +diagram_id: 68 +finite_state_machine_id: 9 +name: mode_fsm +states: +- id: 7 + label: Device + x: 558 + y: 821 +- id: 2 + label: Interface + x: 340 + y: 1053 +- id: 5 + label: MultiSite + x: 569 + y: -88 +- id: 4 + label: Process + x: 833 + y: 1051 +- id: 6 + label: Rack + x: 571 + y: 486 +- id: 3 + label: Site + x: 564 + y: 201 +- id: 1 + label: Start + x: 568 + y: -379 +transitions: +- from_state: Device + label: onMouseWheel + to_state: Process +- from_state: Device + label: onMouseWheel + to_state: Rack +- from_state: Device + label: onMouseWheel + to_state: Interface +- from_state: Device + label: onScaleChanged + to_state: Interface +- from_state: Device + label: onScaleChanged + to_state: Rack +- from_state: Device + label: onScaleChanged + to_state: Process +- from_state: Interface + label: onMouseWheel + to_state: Device +- from_state: Interface + label: onScaleChanged + to_state: Device +- from_state: MultiSite + label: onMouseWheel + to_state: Site +- from_state: MultiSite + label: onScaleChanged + to_state: Site +- from_state: Process + label: onMouseWheel + to_state: Device +- from_state: Process + label: onScaleChanged + to_state: Device +- from_state: Rack + label: onMouseWheel + to_state: Site +- from_state: Rack + label: onMouseWheel + to_state: Device +- from_state: Rack + label: onScaleChanged + to_state: Site +- from_state: Rack + label: onScaleChanged + to_state: Device +- from_state: Site + label: onMouseWheel + to_state: MultiSite +- from_state: Site + label: onMouseWheel + to_state: Rack +- from_state: Site + label: onScaleChanged + to_state: MultiSite +- from_state: Site + label: onScaleChanged + to_state: Rack +- from_state: Start + label: start + to_state: MultiSite diff --git a/docs/networking/models.png b/docs/networking/models.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b22910d82d3caf4c2e494f98166d9e9444ed87 GIT binary patch literal 211837 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�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�?nEL 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ª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�rq%{9w+-;|xk(oBi{8rc&C^)Ta4+x7R;ozkN9WOjF?8-ws15 zPL81>bA`^Y^GB?59tNZX3c26o@IOAG5wf#};@N=^N_MlFRQJ7Qk9fivax7AK` zO&w3r{*{t8e5J=~q+=>#DR|zd!*6hkXXLYrI%brJdR{>MJ$dcUUA9#h^9bwSJY2e+DeF=W&!5tN zc6duzEf?p3ZK~VP)s<)|P`)kk1%w@Fsjr`_-8wt*KXqlS z*k(ce7L{1S#04q-@@K39L88smM9eFaKkk;+x^v?(~qBtM^LU`~y1UC11-w~sy zx1C^4`ZtXbg@sUCg> zeiMkEQ5+E_?OiCH-^0N0HrAi;zvt&4_3&Luc#jn@6fFPZEZq}#H#0NN;f-U*{ALb( z$4P?mAv#kt%O&G~@gb4P;SJ15``dpJ6*BZJ4W#lznemr0-!KL!Rt^86Aq;~RmW?u zj9^+!17x8MSri2_L>M3wPGm28-2^SGyk>tX()hmbV3>mIQf}gEnZZLDu?>Od2_jSt z=1JguDn%n$Q6S|~U|lTnj!%%ulVxGtQzc&i)sXlSAm!Ej%$GXnudk9pL)bt=b4khwDG}_B4`F+Ub4fnt_A2k=tQPZRG18Y`hTw{idFcN5c0i<8( zy(TAVHDt{pASKW^VTI=m959Bxu8Wen+%2Ge)XcG#Dx)JXPDh*f>S{=Q49Mu%`;6ih z8F7P*c9uRtgIsRcgS_c_NCt-9069Mvcnu};1-SUJuYy_BB<0}$f`fdSBxD?|LXN_8 zCefX*sh!pckW#Ll-6)Vzl5jg}+Tuw~XB3##U*g8lf+6o&V4=v2;s%?j^)cGSfj7{6 zB9FXBLpo!eKG^<`tDjQ0m<9Zf~(Iy{;R2yAfc#uFG z2lntuEXooY>!2~~ZVsH1s!aCrX3BE86Y)^v?Gvtk_92PsV;7Ll*p`N;I2bJMh%(H_IDWf zsKj9K-|JhE(Vt+kIYN0U)K!)M?ucc&B$fT&KZo+MX21l?5_#w5h#s*N((Ie)(JBfB$-%iP^gq&d?-q> zHI^+qRsR~*vZt0)tIx{{M2XCWM2p9L=BQ~uf+pfCIlms|Flwj?+;KazQ-2RwunXtY zbiq3pZE|-=b+Wftld6o%n6?`<{j$Qn? zX{ZeA7?uh_Gb0s%{P3kshiXPrpirA_z2{IMdDQ`ugM4HM!eo{fK&oEQ)JKW5heUJE zGc^&=azIAo@U$klnq=h43p0?-*oIh?t?S1K{1s@9ZT1=IXoC9QDzuWOF~ODf_+Wh5 zKvR2P=;@-IAHU<^3>_ftTons1gx4RJXH!fCD&n1+rGKjANLzYIm*(S7|t*T3|dM-5yfa} z@11Bx@Lg5RdMCkVJ@>l)xPbOTCJ=7fIJah^Ea#u_SU8QJoj*N}-cc(Wv)}~N-63P} zGhN|iJR#^ybML{Q^o&o<4rm%}iv&^HexsF;#XJO!#+Uoi$sSp~uQs?wWQ`L2PiP>e znlE7V?TRi;tDEItS0Z8G5c73LIkEq<0N+vQtxTtP8-CU{jH1hjOY9r!af2SE?QqLu zFwi7GoAm#%rY8jF0LD7IpH7#t8-gC(RQZVk!JYe|*GmlkgHI~Y>VEi)fq&YX28Z(0 HE&lUg=i>X) literal 0 HcmV?d00001 diff --git a/docs/networking/models.yml b/docs/networking/models.yml new file mode 100644 index 0000000000..683dde4bfd --- /dev/null +++ b/docs/networking/models.yml @@ -0,0 +1,130 @@ +app: awx.network_ui +external_models: [] +models: +- display: name + fields: + - name: device_id + pk: true + type: AutoField + - name: topology + ref: Topology + ref_field: topology_id + type: ForeignKey + - len: 200 + name: name + type: CharField + - name: x + type: IntegerField + - name: y + type: IntegerField + - name: id + type: IntegerField + - len: 200 + name: device_type + type: CharField + - default: 0 + name: interface_id_seq + type: IntegerField + - default: 0 + name: host_id + type: IntegerField + name: Device + x: 348 + y: 124 +- fields: + - name: link_id + pk: true + type: AutoField + - name: from_device + ref: Device + ref_field: device_id + related_name: from_link + type: ForeignKey + - name: to_device + ref: Device + ref_field: device_id + related_name: to_link + type: ForeignKey + - name: from_interface + ref: Interface + ref_field: interface_id + related_name: from_link + type: ForeignKey + - name: to_interface + ref: Interface + ref_field: interface_id + related_name: to_link + type: ForeignKey + - name: id + type: IntegerField + - len: 200 + name: name + type: CharField + name: Link + x: 731 + y: -33 +- display: name + fields: + - name: topology_id + pk: true + type: AutoField + - len: 200 + name: name + type: CharField + - name: scale + type: FloatField + - name: panX + type: FloatField + - name: panY + type: FloatField + - default: 0 + name: device_id_seq + type: IntegerField + - default: 0 + name: link_id_seq + type: IntegerField + name: Topology + x: 111 + y: 127 +- fields: + - name: client_id + pk: true + type: AutoField + name: Client + x: -162 + y: 282 +- display: name + fields: + - name: interface_id + pk: true + type: AutoField + - name: device + ref: Device + ref_field: device_id + type: ForeignKey + - len: 200 + name: name + type: CharField + - name: id + type: IntegerField + name: Interface + x: 977 + y: 312 +- fields: + - name: topology_inventory_id + pk: true + type: AutoField + - name: topology + ref: Topology + ref_field: topology_id + type: ForeignKey + - name: inventory_id + type: IntegerField + name: TopologyInventory + x: -204 + y: 12 +modules: [] +view: + panX: 213.729555519212 + panY: 189.446959094643 + scaleXY: 0.69 diff --git a/docs/networking/move.png b/docs/networking/move.png new file mode 100644 index 0000000000000000000000000000000000000000..e199991c76555b7d9461d03c92136d01e83d9d62 GIT binary patch literal 336443 zcmeFZg;$jA_b#s3A|N0jAfcqxNcVuEbO=btC@DE~4KN@A0@B^m9nw7r(kUq|-8J;U z%={kooX_{1_x<<>{MI_N7AP#3=eh5D?`vPzwf957Yegx%yA*e?T)BcLBmGk4%9UHl zD_3r6-o^pGbJv8b1^DNxy^7R}E5-ef)`2hX*hp*HU%5icaQSgnMumRw$`$b|GB2OM zak;uRb4OQgHF;0~s@;lVMWJ|; zgeK?^zgc!XUt)mC@XO%y54bz$I4tS8mN<8DK6F0O{e_-`{j%9LCJbNZKn#2SnzkQs zjoG!Q+Y=i&6T`2pth`H!bDP%d%76c)>Eo5b%M-!(*b~IRSHhMcKyEim8<{t zQ=z0+Y18OE61-P``GNc5m+$c3fBE;j{=awlv$6kgAO3%}56$Esib)5JAqr26hpJmz z#T>keokLOm^8k#O|DPh*kbmtqdc<+}) zzpq#%qyYcAYH(mo2EMS#l3jmzw2+z_%EJ<<_g}O2cjC!kpCp~;fSepd6j|{Z8AQJa zb(+q(*+yyUA};ov;Sd~FA^-UhQD;$~F7PHMan_Kzeh=f% zx@mnQj^v$-AZA%il;lTIp0lD z^uOZ~05?{5D%iueFiWYko)s0()_cEM>4e6LRD3FrG$Z8If?Ukce1DQLb+hq8EW8a93!dfdY&Dbw?o&e=P0yw6B6CscdHs#6(ley;!tQXT*Sb0 zN(g@H?-E(J0db&tR8H$8Yb3o(T@<=L4(W0J`&Mbw4OVqlEll?{N>zUeC{Iliuoy|j zp6zNestFEY&UVT0x2)|7F}}t54K)HM-Le073)AxeIU>T@4wqM(XOcbFo4 zepOEDbd-#C&XJs{%KdB`gOedavYyA#ok=Yzl_WajA#zJz>!Kw}>kbs{l(pYw7bDT1 z(l~LjdXrpluzO~KPN!sy56o|~0`jcU*MhAtGV^=`yU5Yq)X2ntSgMd zIVVNZv+;I{rN1elAv?EDPx0aeU}xA1bUo%v@nk$4xn&lp5>}8k_;jvn znDKdsui7CZkNdFdfYW- zZ`Jc7U52x<7r*+njL&NPM>^xco{bsu*)78NGQalbTiF}?VE?@)4pQSpGAUVy8n9)3 zcDlWp=-8Ily(Nwdl3;pSpUv>_#xyGk!XukECu$^)OV*G%>{iK3#7#JE-(o}5F_4WDg zW-8*5i-?=sZLjM?QoF4DHiReHFsPG0#4WJ{-HLflW$WSLwLZ|Z;xC7XlC8S%1N#Km zWSZ*Dgn9OpAF5IhV~2as9!dVS8h=T?Q70g@EqXR8CK+1~8}mIXD1q?kPgN}}6lcN~ z{hclH2CTz_(vygZ9P21=WXZnf)^ZuaNrK(mE0G;2e#XK9pX#@0)u|q_YRjq_&O&wI zn0sXR1+$3R59U5}B6Jb(eicM2s(V#8_`k1q1xKgKl2goxxTId75ag#V4>?YD_kl6X z%J^7fRl~geUju^ew0{Ql&P6opB=%}5O9yCkLQU~Ell!9f{gjl>bxMmw?g-es zNrceqDO=0B$vSzyQd5gwn5QD)<;=}f`yhu@i6~x*ntocKqU&~ycb6b^&ustkoyC-x z)hq@^NH0fC0kd^rx~lFp`W){z4blAYv_cH*j7-(;!73vdCX`%kI=FXrwmtQ(o>Q4f z>pS-<#hk-OL}2lrX`|_fukI>|z!^l9##^VPp0cVpyzqbd`O%iC9X{x9#IAtk{gaBx zJ07)mNF!G>E0gEGu9J_((+oW<77i4+FdC*i?u*sqsaOa|%9Ve(qWR}s<7oaX0tdD3 z!wyEM%K*K1eEfT{K1q{zV}6#YtWMXq6nB>e_RV1Gb`dmYke|Xeq%^B z=PPke=-}Lqr}8Mf(G6~LA}i=mCo4^Emla$w2-geYC}+=}eBSzUyffblpR!^2Mr4U7 z-?vlgMRZF6OatDyEvxLfSiK~D@{Lmoe3UWI$mxa-AknCDFMIN}OK$1QVjLOPvLVEF z*_S{}-|WK6cmBo6>Kw{2>deYV_#AO=+4@7iJmRFy>P?bIju>V*Yw=p&>;l7I%G6s& z96h2jC1H=sD)Of%zDkOr*aDfKdT^78HV|g7?t2DAlPi5)aI)`#si$W8?Bh`RZO314 z!@+W%GwR7TN4o3wbbUVGkD#ow#x3pFyY}dG_FA+&C_|jjW*aZ;b3$l@-IyodfAgB$ zp(Ywun#G)iE^_csG#Bi%Vlp1E>$(}(Q+)?(WDLT+K?-k27%$;G5xf5>w5y1w)<1mj+Z5~YJrd!Q zyZFa!pr`Dt%=rkaFFkYEZQ%~3Ui3IbWT?fErakx_&0k1=sl4|w!RGiZh)l5`LuJ-y zD1hg-)wQ-*u-UT4xGE!pf)LyUxz6Wsw`s1Bg=vu+;>kue=!WsR_Z&87yM!BMce7cu`5GY60c5mOM}OmCehVP_6Yj zo6z9T+L+1m$n)`7(bXTzTASF-MxP1C_zruNG(J`Mf_guXScdeUi(WyhJSfFYlH3l-mI0> z=qLLFUImvOG1h#~>c=uB;rx>hT8Usx>+ExGh^woNO+h3gF?vYUPC3f{MSAhOz&eS2 zZz^)Rm?M70^fr)dv-ooKs|G0NYF?q5%@JE!!aM$Zi>?&!dq=3&*pIy}!{Gl|egMWN>!rtOF`jX7sHXV(%> zn@86Gwcu$--IgSBY;@+lJ4a;Jp|^4E#fP+tSyfVee#(<>TzGNNI%^~xb-qkj&p$H6 zy2XS*r!pW=aZh1qLn2JTRL6UWZOnh2ez~)(pfOdzvnv=7UBR zT@~Gb&}T!O&DKI=@zJf5H1wQ`wvVB|A+lyg2igTIb>nYhT2D$%i zV5Mwtv)}r3ud=#HcGx5)QGXW6ijWCvl^?L_oF+^)b(6;Ci&#(HXjNWy#bxC6oO%)B zat0%>{Eq$#=M7q?oOO68^ur~+UiGIK`WqW?Ka)Q(c&UMb1zphSWk^&TIr zbm6opKpS&JdDSDf`7QmMrcAV>8kR-uF(3W7A1n`Ys7;;5ZOsW*fK;1CZz^&l%2nLo z8meT?Tb{VuKk4I8g}6!K^Sk0SKVDu_3(`5L>58$x{}*g}&o+3q{3y941?~|LTil(= zWlBbtl}{_WKG<~1dN#@k<;AuXmYd__E zWI3b1_rdk*Q^yI%5mpCVN~K^45$py|^kj%of%r*$(SCVh)I z=K()9iOLy|YZU1xlTT4p2#jd-=O;&DK4w3hL0i^wC&h=KlSh``pzDStdy_ZZ^B;GF z7zqi*HCh%w&CoHj{CE%$bR=W9!>}V?M`q@J@*JYRg2~82eU`~sQ7B@q!%Usw32#D4 zYM6ZmCm5o$x1$eqb$1_(q^df{7Z*Ops(8q_OZ|m27D{|Ux1xwZ__%ku;%W4h2p{mg zU0U+(O2iIyz(y^Pj-VB%qVC5*yWcZCPP#gzxD%3$lCd*WSnd5Dv3{OlvTDstOdsYe zu7{IncCl|kimt7#g~7VZB8dTk6*#krcm8YbHYkg9CII5`f#F16Pji+{A378$nP$5^ zlR4Zqi9tJ~0s~2ZU6Tca{C4nU+05j zz&0lK1A|QZu=6=jzPX%rzi6o2i5T(bN-V#JB(anp9Zj-P1cPKmqvfPs-hA%@op1?- z$`#koT1GJsn&#;(CO6luk_vM0*g*r*{N1i^CiO|*oFoYdmCi?}Pxo6dI4NSy z4%{9lJ2=jPB`DA+iON_4hPC0X!xRe2*YLs}m@y{UkFNhY+DE(m>=gCU@6fs?MD#C8 zwK@NKodJGb>+7!BlxXFHFRy3k4?It17iCHis#wpzF<_YH?>m z-JwZoS$jXnqB{J2$*rB}cc-(Ocn=99YwYD1CQo5E+MFrfG^YwCc)*h~-w3~TfAjkZ zd*vn@z%?uIsYGm;&TV#Y)FN`j`eA2F?JX{9ou3E}$m{v=MlDpN(bs9ogH5^J_j&Q= zjf{=QlV!!HJe!mqet!WNhXnESfibAUu>WTYHg9bRsCq2P(k*0{4%B!wImAdR<=8nr zJ(5tJt&!M>OsE(X35AVSQN^Rm%|TFM^EfPok`Edvqxi6#YirHe%FNY+$bG$1Bqr_E zXrhG8=0vN&i2mkK-)WZ9=u&NDrN@Ne1Lr!7@mbSMtTBSmCg#kbHc%xz+1x>{-ICKE zg>xG6m;3w~BR>A|=voc}H4B4}m>7+Rk43GTkWKTeurZyaViu;v633Xu3+4K(MS`&4 zR^GE8P>FVw8S<(D~g@?R*}h5PIm&IF!i(yGZUiq53*(I-<@*qvnkrm z2)0K|BVOx>wvO_|HWe5+$z=w3CVt$Xx%n~P^jBVe=IYjjL~wC`s93(zj6Hbg3kJnV z+QTbUZGB>*ZoBC7==on@#8RR8Lkns)4gKmdx>k@(R{5r zSCkV9zNzcvhMqWIapJUby%)?Az*YTY&}@)SHIbvkbti%`; zr*40u^6`OTBPGokjjb7~beo2X>s^g46Kh0BR=mkGE>Df)-PwL#p#^H*vaB}$*418a zib3Sk$9uI^e3=1lcAMRY4~53YYfyzy1Y+(ZTIbd*7W_*21DuETd+xifzx4&w=>{?H z(35-}4SLsZx>1ZN6g8dBlQHcmX(q;-Xew1=50l ze~qEQzTyiL`*ja@GB&QRLzPWlvmd`qZ1cNWKbyzsX$;4bqVj30->GT&FZA-^MeZUy z6jCf}F3^)EG)1|M?uC!pL(U^QI_AcWyVLLBt#+E%DiU%_RXy-Jq2?DWoX0`yw-<+E zw}-d+DLruPi9B5v`=U_<@>XlyJ$~`}-&y~*4D;&=!MfE=w{Ic`uRI$%?mcM_ld&3?Z!bINqu56Rv zBi#08DxsWd0lBxlVm@5irKiyU6@Owq9Ze!l!wkxkaa%ffsr6`FYC zIJ#cO7P~r*K<#ZR_!IRf%L!Cj9BeqlPbP&~33BB)r4V}txylE@fd*uMA!nsH-u`$t zq$${U&?;L``*r)Xs>=G&`x`zu0VUH>reEpytDw`uO3f`td5uMsq$&-RjLvsvGUW zmOBd}f}S?xt{ES8ABj#G86+ik43N*c*AOGbhSylE__3$8emzuy1JvBN>{HN_&}<6h zo~NS66aK*}b4!mfmTf7+k7S!|LM^}3XN!tVy^Uso4n5(kB zx|8=h+rck}612MsoIkQ4P~|we&P`NwU0x-m%l7D1$*&)DEw@6G9&-PzFpeghMa2%> zeZXM!4eRNl2r0)cp?}n1C%W1lAGyx%=(-ek^&PPR(`g09NyfN^5?iLy_-bBorLEeN z$V?rI3+lnOx3^a%*qL@>Tr6HU+sPmW^IAB3frDsFrLr2B`PrA(j(1N?<6F8IybZ~~ zVAD6HC76l`pkcxqus?N@HGb>cW{(36VZF`9+P7~W=ov~>mhi<9&3KA=&DCE^l$_4< zOSxCkBId|YsD3?fnxf}kQYza0xr>K#(W!p?-WZ}VN;wx#>kzY9u5Ig`WM?{j4s9P7 zi~P~curmxfT^=ILrkm}}OLlS@&Q{pp-&dI_9{>xRkK`&A#0LIX)mZB4U2Zy1tX(Nv zM@~su@3L>8vjH7;wb&V#&_fpEog*nbH_L z<+16x@6gsqa=0U8KatKe#RxQK)4DRVSiNK0M3L;F683OfI%ga1T3;Kpnly=uRn@`E z8MVaz0!bZRMYnU$^$hIl!8}X2&X&d4LkInaT#>`jkD(!0c6Fbwmo)TZD=d0d97AS z^h~;R%&ENEHQ+CFOEXlQ(sa}L%`FS-9qoDqpL@Sfwm1^Ab;}mpJ5dLY|Z#2}dV(|F^T>lJS~ACG$>( zT(wU*{iVGv#@)7`n|?~p&?#v zxD`p#9Sey{*Z$f;ykE+6wFPy|GkHkz7lagV?!Iw*Q|Ii;ko#XM$CaxvBHBWzQicNL zuh%Km1$z3?1hjpBQ-qf{VhY<}^Z3*BZ(g{*Gux>1`t@r~P0crN99H|OT(#8I)s>Z% zwY2hv*Uf>Kg#{HARdRB&p04gR@S2&acix@NkRkwqKu{=@h9-5k5vHSC^8Wt6D6K=> zm3L_TjI6iU*(AiBl~$+w_8=%xy;?=v6CPBFK0I@}tUBjupoTT*6XOZ%Te^U4*A7D=RA#6B9qb#=W_q+YwaB?jb%O% zTnpO24Lb_fXq6=fpG|3y9TWg!PZ!z4Z*tS@x%kZ4HmgRP-r%UlWFt0?DHYvi14VBp z&0u$vi^ezSl-m&(IXO@2o>s<+ovtEs$V1!|-It4K2qI^ewo{AVGiR=Tl!Ca}Ny4{Z zINdMh_ukstV$~?tZFJ*hXIEfctcIQxo0a-Ze4NGNt-tY`T@ zGT)lc?WGlW$p`|9O9Fz1V;^Lvmcvbl>!IzY;{rF@8<5jo8y|lEHnMr@fBIuN`Rs8W6>}Z2w-GM$YBy(*4@G$C;@JyYf zy`y8a;3?O-{?KlxFS50+u1?zyDQ%JhJK2ejjqPdl9vT{oh#=~kqMLv%xkN6@DYyFJ zQG(rFjEpj)rk4aCz5K@(H8bAmPZ6y*A7xqBwzS+@XbZB+3+)}N&iauFx;=thBZ=*R z{a8g{wgXkUp8jBW)nt47q+>YR(}&i%Dx?9sv3T?9oTGujl}8s`B`;&AK~^yGne z(L&H>vg!-3Uai*}<$<#yw)2z$>o6tvWeugZoNa=$v)vxg6H4^ienI34+R0@~6}84{ zAkoZny;!fF5g|m+$jBG}wqtH?ZeXB9DHR++3W->(E|ZGTffyVToj{T90lRV^Fc%jW z4GkhPTN$s;mZ`mXB=~C0zkedMyY)30`h;(S0{>rnKwRqX{jptXy>7`bayOFnbV;)0 z_a9;lA7_KC$c1fxZm3EzU@$)yQE^R{o}8*4e3=iJ)r`4PXSpp5lfVH|)nC8*#Wl@; z*8ic@W_fY?K71jpY<0SERZ{j%)9SRKk0MlBty{4Jg7)vY)$?W{(G~_B!FPKiOH(%9 z1{Eo04)Kj$07G2AcVaUhEXS~XJi;q<|Eh2`9t>aj9sqQ7Wslv%X~|I=#5&T;R>1>?ebg-YoH7p*0ox`5f<>6q`0B_R8|4x>d_$4X2T#(wRz1k@8gY zs*FN3Gmlq|8FQY}9TD%L4OVVIPZO`>hVBX+pMn_GAVN0EwsBy1dRlYhx*t78QGZ7^ z8r-3wR@>9L_5&4|bnS^7x#B!74j&V3-a+UiLA%2>(euh4_WHW7!2BbK%{VOerBEpv?A@~6?wMyFCaC;I1zNusVE zP@cC@pS~3a^Qx7Uh=|K-vVzmF-9hFc_rbr4+9a*GCIauh?gy{nyHT-Zly2(kPXdmE ztLp?YGrAO*wuhX~F$Z~lLJaWnYRGKk#)C52Dh`+RLg@{f_(V7-r@iv)Pqt6H8!xsN zcAk5p9GxfbmtOVm?d@%Zog~~iWbgw!tiA54HZ-G(8o3yHHeo&2yu!CN zOT1pTj~8kk%))lfI6b7KI>PCsz(*sh&t+#QWY&tchz&qnFo|2>TtL;{rkuM!fi_utcaYiB$?=5TyfH?}-Ejwnp@DiK=( z%nLqoc<*E5#U>pTRclPF%WJL!=1M|Ux((z$CBAS62SrxOFH%nKZq=FSlAR1G$`=>- zX7KZB1jCBj+8lG=!%9~n|&2ff^! zoSex?;@EdrmcanJ(51K|WxAS&3p8f($$)Bhw%1CaQKGM>TL`W@c*bkP{%<}IB`zTw z9Fp&CM=1Wq;<|%t^!p z@bxbi;<%y79CkGo`;A54uyr;zCmUPW;4@(?O!A(~Ps9=!@%$8C+;kq-e;v960XHnZ89g-1Xzz_%N=kHDHj&xrI!%T-wNur-$vXuj z59ZjYCmWTA>(14w1!9CQ+L)v$l@>J~xhcg_xTAaHbx810a_018q34$^ONJ_V29bc5 zm*ee`krB(ErSH(FBL)VPsfH0H&-=k7L3_V@3eJ~%Z}@n7oMbtBhB-H4%;ogs(Fqh&ORXo9XCLZ!PlT}#)Y-%F zw8YNV>4d;M?Run~i?`Q}yVB+qch(B_t<6$03(3Vi8!leF^;YmWWh5abO%bqtfrBzv z253{L=<&~}r>fLVMza4LU-}YxW9i+YT7kMu*4M9JgUCQTOOwJCbt^uORNCymbY5P@ zxB@3gnxP&9iIpW^HfS7mdgH7j{kG!s=7<|k+mn@6Q+!3fNXy_t{({DeMk*+D_v~O* zt}ZP@DimPL-Hlks&9SVkEK$tqqP4Yk)i}iMpS?IQUZCJsw<~A#{l+3H4%&bf04wZw zrUfgr0S2vwvO%1@^!qMaK zt_K*&H;?|gSy#OKN#9m^ng*%TTwce^7q@fiu%rnks{=EctS}=QtKQ>tetjVM`QeWo zrPOP;@TZ$Rbn;Jk>UZzny-UijJ@R~1neCrsH8nLRKJWa0$-*zs zy-GcOZu}W9J7z2OM56$mQ-H%Ifen|UUI#89>JxY5(em+5+51wrXM%!fyKroMA&aFG zKImU+>9gUPv+q8y>_YK-AkCJxCdx+>R8>{Q zP-_{0s&aZIk^8UJ){_FO9dA`y{e792Z-Ho(3VEdbw4IVqGlBRa&-*Zuj$doJSoC9V z?t@_^F?K+f?HGmpO9qKc-3A!nZME*-bh(tmJD8H68tkrVwevlI*f_swl&3KWNML+6 z&C=$sE}4yi&d$@V@`3zzBvejJh5|En4Ss?o>Hr z*#0>nB&4bd?4TFqd=abVH2TE z%ICBe&58*;U3KTfjzu(FM$+=~pS7_eRKkj>;2B4&G?RbI_N`Zd7H|B_gZ{nYgV&qT z$5X%|cgw-ySgmpsvyKTfpXSbRx@Fs2M=!kl`unXV9{-o2w<0py!Z|nA{y)cd!y7t=KKm7U#~BTsHO=v(=hV)(f$&Ea6cosm%{eZL|G}C6AwI{zKF{e(CV2kV z0-u`mRCDmDMRHYo<2YM;dT?W$2;;yWZYMi4S-G3zB_(?GReIf-_y4)5`|nkNM2Tek zBf3hTd$+W-0BDj&MagZ_>vi>-Woe||2m=8@sM&CKUNjBZJ&yidj|cys@$;4p=i||~ zLEJk1Z-}{l0dNNr5|T3FM~_6Dwlw7@HNF6%J|X{gY*bVQ^epP%jmP{e?>xYFelO4I zF|BGRNVkSo&(Y25WeJ=AA|k6nFDOLkB+IAs#XJZOqQj9!?bZ{ht&0^S_@pz)C1--q!hF zrr0GT0d!A3zV>(Wm=NMmAtA>H2a!nJyLYRgbF;Q2%3S|M*5jwYEK_H)$A7=uFttgk2)zjc!QXza}`z~?z0WrmYg`RD%+VzDWFDJ(3^%zQa!l%Ja$ z#$*8)5e`CnR#rto$`$%8FDxu8LYiNQ)>_4Q+^lfU0g zNc|U(V}O8Bks3%5?FgZo`Wmh5)+lacvp1L~0fIEK-2&?9(c!StIHL9c7}u^R4r}&M zRO|=92#G{m&(tvtIFOQ%sHv-0QNO@JH@Y1~xQ8}1HE|hdLg$*iy}iGqbO<`*f&GR7 zm$!n-uM=Kwh`3L4iYRP^47f#Wfo}Z{+}kx2V`7dQBTHS8x9&Y0+;>#D_MeUO%DTO@ zvH~1Oc1}(YJ>>+v(?m@}qo?o6go=tvMLK;qNY1otHy*HI0G>1)^4rA!=>wb+?4dF0 z6L3mD^O}K5j668VI>2BsT&~lKBz3t{NSBZF6H3z3VW`E$ML23W)k#?*m-Ig<^(FE; zeRzLc5Sg(V>zyVtQqro|7S+}>BBG)U-$D`-6X~bI6;>8|7|C7e36vBRg6WsShm0f< zy@!C$7ON|Y94!>*9fTimX$f+0pR~5L03?Bwn!2COlbc!piIp<=h7$n zHF8dakMf-1!nY>xY+Y79K1Kj>+HXef{4RqdcL4qSGWp4a`}f7gYvsVCTqovBo#DvU z=n+q#HcF<+NqWov@4)m9A+B*d$~V%DnmaKvY1Cqmcq}zE*Hf~9{AF4HaU5_Xm)pH8 zC!x_?;gE=L&4@$+;z8T$orbUQU9(y>=NZZ||@FCQjnS zIR5xlDxpZ2JF!r*BQqD53UyEnQ{M7sgX_UkXkCAPyxafq10h-Q>?Imew?iPjwY0R_ z9i{J5bvb~}c7Xg4eoP!9|0KN5D3Wg)Kz7v8W_)~nL^nj|vqs9}Fa_&p@<{@mjEvcO zI~)&BVm$yX{elaayv~ooYQM3>A<9bx3tMc*d#aaxaPN^oX-0n9^!c5d`g#Hyu&$qk zV$>>A47-jKg4N}JCNi7tZqRo$7D60P_ufr#T4%jHSgy8?mjc;u1cy3LHqmd=SpLkE z)XLFN-)wZ+PDmh>R@s9BHrrH{m1?$pypW@LD%eA)NbSufLv=_JAHT4evPGvE{XzPd z;@S9l_=JRnUJgZG_q-{_VxMbOybliwk{hq`|C8#!_hyoo*7oA`^wi`07|0!qSh;j} z86z>H&ig?Lw?QWljUsPjqFFWQDMRTu!f*B*WJHjv@Wt1P0R>A;Ost6PcYFY&7MgPu zQo($`H=}~y2HUYj1W{s@*H+WGBGJ6jQn4@EM9*{ zTHJjcFw2~G`4qp*l|aPe8y_Q9Ds_0b^};ULY1yL`xOM^GxQFYCIUd*e%POES^~H1N z5OdwAbgpJ50P-`Jn6_|0>gsR)F?QW(f}>K7%dxYot81*z)E9jR7dNuBiAUGZue6B- zpH|_2&*z_$2-q%s2kJjyJrog2mr-do$d0v)hG?FK6i%~Ft^LSYCL98!Mt)KBn-@gW zv2CfqqXO&=Xr9YU{@+#9z?<8&Kes5h!@H=4l5fPJtBi#^4e;d?V`3P~vz7)}C^sJI z{bxACQdM02OLWv>#COTflnWIy5|ZWJ-4d#==~S*C-wYu?KN3_S{Sn=DK4j+_<8ZLj zhpcXG@x^UzZ7s?L@m#ha?gOm|_j|vO(Nb1CrMPckAeN&`ThpkwA}KBUMlTT&QKl#| zy0mHeGcNmoE}QV+XOjm^S2yW6rnfJ4Vkg2yO4uMvSYKa1!c*$QDN&yW4HH0O00k7f z95BQn7t>i+uRaM%0G=gqx;$w7Tge=k2VKQxbE$b?NQYAPli^7eV{4kn1K$Mz@o?al_`38Q^4K$*xQ?&9KV z6lwzI*JmjjkCB)p!vAJ8$IP$w^_xukD7u78oSLLUsR5Ja_?fcF#pyoK(*VBi>*O~f{eCGs5G7T- zDTgi~lgoSc4j~E4F&@2(bM2aN z6{9S)KpgN7N4=WvjRAywO8egUAFFkc^lJQ7S~&w!GP1c^`&EGb0Bq7Ov#d-dXLuCw zQpU!FfR~_P7yU9oU0hvUT-@Ab-S+hx-DCtH|(gayYXcgX>F_{HL~P!x$({=vpXaW}U*?HGEP zS@nyluU`UNdUbdoqur-+Is;p_780(&AvwoSiz8*jOPjPKQM#{~MrC<_AH363+IMbR zCtSk9!c0u7D}9M&53|3oOtL-xiUE6|Iv2f=L?&CZ`|TxJ``oEmknpFo zzx7<)vnDe$Q@7qpVTYy%?HTVgr{5SOgSPV*=PMEi>P^=;=lj7g-+XTM5D07w07k8^ z7k$V$X(yGK=S8k^Mq&Cy@`SY3bB&bAfY8ub*=5(^P%vsOD^`EOQ{NQ-D&V_|_W%q+ z=Xn0lGnoa1HJ<3YiqUW1o-++fO};2A6u0SVXBYCP@i zlb5kWQXtoezk=HY&luEBSXhn}?Ch;`QtO2rzfE*6T$mesIr_F6JB);PcXw~hA>d4( zAN;Wnel|33PgO@oM9>?y13r$W8tScPb4o9!5w4%7? z!%IDd-5)aJT;hx2)vnM>4jgaGHhe(IE&WW9ainM4h=zs+Xhq9}W5W=SrQIPrG@T`n z&d$z=24lq$>Ur;|;m4P$%b_JA$#~}X@#Fj5G9Ry3cM5^wkD&E|I8MX0!8FoQ5jl&` zY=Wh4zkLAgfzDQOWW%~?s>g9C_B6EF24CWT|1ALIKu}g?Gj%GdE?Mdx_djy1g9R=WmrS<7kcOetD5C=UE?ee?2sJ|P!6J2q!Ql`* z+(&}EgF+ffqc^1~%bbkTn12JQFkx808IXAL($dd1OM2@G@bP164v|6e@!3Jl>vSC6TPgIr zC1b8GeBqIZvKO|zwfS{rEi$4i>e?zr4x956Bl78iWfA%2OdZzL3QYy0Gn~ScSIi{F zIFDYlTxycbos{1Op7sQ4A8$QB8@VI>d9cWJM%D@NQq;v*flQ!PS3W3F_sPMDF-3{d z0m4xZUJl`NeL9t`kZ53F063h0#P~wJ%8H}MTx#4C1MEj0UL2nl#lfMD7^z*k4IuIX zG8`#KKQSH)@jUD09;m?B{q|RcUI6BXRIUo+<&B7&;&!GEn@30RRhMsCr-MzMtF^OU z*j;tK?jrZLu_{0{P^B6=5ves<8a4NYTr5SjLZdaFA1dFv#TnQZj!(g-75XMUH=?gK zTPmPmtHR5j=wxdmgHL&fugm;C>5OiVMbb2H62MCVYMp5Yl<{z1A!?69X1@C!@=&vFhUwbbq=UbRzdX{o3E8YS(W# zcuSm=k}C~7O7Q$ukld9O-?hpWc?88@13%04e;g1tKN52OWS!8+ErXsT?C^UTTUy=A zcO7a{UBJ0t!5QPAl^X{TLt|*N^F@_5gxu0-BRm_mlc$X$LJe}%tmH`W9tqe0jUzjsmqL}$Iq;dk zH0h(NEc_ud0MR(S{T_aFtV|sH)1rUNM$2h%WE@H@X+Io1u=WeRffSZi3ym0>ijP&K zk!>g*<9pkFc%_xA&Lnz#iC#_~!bbq|u+T#V<2nnc>dwDbXPkGd{bExO&feuQKPxwM zKlDETVR$pO^M}+Nlx)uak-&VGh9s3{?Th|cI=?>QV&z|GR*7xnxe&L);9!xGJN!ll*xq(gV4vVBW8JMxuUY!oPw5 zq4>dgWN9D`KiB)oZxwI(QXI#Wi+{|;uSvc)^wX!(i_V&g*H!81Pqc1|KgqXa((!va z_B2|7XlEcXWgkh3d+hTV@VE@D-YNbe)9zVYclkZ*m;9bBh`D6 zQ6b=-r5mQ+Q2bPUpeJwCQv!#ND}FvTo38!J@DDq8xpLK21Obp& zjxdq~>R)9}qP#YT4g1eIPVx(qbw%A^XKrzYMpLn-%R878DKphn| z7QuJv^-T(nRW7cqFdk$qr@Vae;)Q`h`s8V{uyc10(3cH)$k|d?Ct*<}_ja*8xNO#^ zLMt0_>15YYK))7|$ehK>Qu~CJ%z2#5;DQ?7epSeN2iQQZI0{)eOXqif%fc&HbsoH4 zrI8s}8dxdbfP{L{Vw=B&c|LxQIRPGUOVHGds#`8Vz{}5UV(h<%dLm!enCdm?&$6-b z_0E(e>S70}R94sI@@eL-NYo&N_M2tUqW52Q0sUN#4X~xOUet_DDRI;LR->dhX~Y{@ zcYhc59x?&<)4eGuqNweVL8)=6K&$kv_fh*Hul?CRKj0=XrMla7hCSZ+2F%%!u&Nho zm1n27EZq^=CUOG)^1{{HVEXvNkr&xYsXNQdM`1VYYFTItBm!~@CxPdbQpG@HhElW|D%XQF((ywZsS6XB+p`Oi$(d-g(OIykK?uS z$TOIpv1pwfh^4zkkL5GfFRMwfvLSPV0h5*W#FUBe1=Msg12wfK5Oamu^#MdT{TaFdqAQ8YSwZJEU z;W`xCm&o4(A1Oon;~fTZ08D}8&NH1q^E?1(-{xVbm2ny27ZUs06p5sLevGm&Nfk&R z051yIq=2(a<~HV(A>>VCMq6Uxria=xq1dgSU8OOlCjN$DT^8!ap3ldyvy*U*oojR* zct@Lr&kS~k;4bg>bNhSqOR8Q`GFtumJlVPfPn@X6Haxa!B#*EpLA^py}S4EQ}fniBN$9O+*3+EkoR$kN*A^ zo6CV!6ZH+3-5nuqphZf>F8@ceQ*pdjQ_C8g*G-jdYAq-%`K9QojJ9U=%v9f8!uWjr zQLqdz8=aPCxBIlErV|O{$a;glS7LB`_?F~7RTZKowb=<8zVY5feorse!&^TWd+fmjMdlW_1A(dYYbn9qxupSWvd?tnJNHXpz5Pa;6VEa zUpM`>Z-3@_84b;qn}$xh%9wMyBA4b{{il4|09Vv_!V9oqmX_C{OC22@K-)%d$F!5} zEo=?!vGw|L$T~&HQMTv>CUIl1ZmZTtix|+NI@F(~<>aOSe;MelY-{;6(Rqn8;Z{7k z`J;azD#62OBcQ8WDoHn*J$)Xc3JvZ6Mo)Rh@m zuM4oK@v9z&-kz>U&M;{lcHkvOMp{6z&kYT8PWWYaAXpPAk8!`Qu-e=V^EBQ6mGfbm zKda|F6QiOs`P7Kk1_0>HP;A7Xd@2^eoN#Wwn zv;Wz>NtV|-I@PmF(}skZwdmkdW?_ZeBt{Kv257 zk?u~(b3OQ-^Xtqo0oV_!SgUTfWBs;_)Xgg=%IFtm2F_5eeFTno@!Qis!Y1P{#rbWs76M>Q@Cadh1&1ipd z2v$zFH#Y|hjmH4S8UIb{fAek{-JsR8Cdxh8!7WDwY|%hqO(B|<`oEeMAG?HE%ScOO zXxAQqAqO3V<==g8JjgNYL7aSrz}J8y^N+@p^8zkoPcyorq$HhxB`C4paI7+m#O;lr zSgL{p%A1E~86thb#SA?`LX%tjs#dzyeS6%}#K5`rm!6(iNhP}Pg5Hroh>C`xW&_d3 zLZtmf;yj!6i%utNtfnpF4^aFa{k__4B9704#fd&wCROXYfAj7f4Kejbzw zrLZM7onLZ;advjL*{sd6HOg7_8m+ViqUpsSJ|5Z(VuuIAnf!;}W*|LHGa$J8C!;~5 zJmZa?TF~|g8ZPxblG<)}N#xXutAFcY@;{WGSJ77LdR0&Rh(8Udm9o|ksL|A;u&`W? z(BRd7{)gc#;KW|A158olj_!@5bnE)|w&jApf6np{phI2kjO|fFh;7#mDk!>K<&{yEUP~{&VS7)pe z51-!J^G^hqjuRdevr=arKmT5upAuoyzPZKzVDY>2I%p3jdvi7}V%6?ge_W^#4y>{B zW6eU6vuV@Yjon>q%OA&UNB!(CZEh*?LjZx)&Dpk?1RC~01XLJzwq92Z2$Vx_ z3{+{cdl4lm;KYV}pdKqwW7vQF-<})ox+*cu6@7=D-yM;MH^g~pHX)E3w9?xMQn@p8DPT(3R+6 z+^NAuv=BJ(LC7w%g@J=oDiiz3$;k^AX!d2A^K6$pV|^nCvq_XcAR~9V+JuXF^Aeyr@3;b#lZEA!aZ1RMA%XM0I-p-l@~mkfDidVTlFpWum7Pi|&9|Vg zC8hN{Hq<^GGeGG>MY-IU6mfXyf{x|v}o409&(Wqz)=S4x|8 z#-9&VyVn8r((~b3I!KW1{p=U0r|E@3rEv_%|5ynJcpnv4PvGUcqZp%P&U6<8BIop| z!8~N!O*ZNt1jyygg#ej+nw_0(NH(w1l;I|^|100u_W`VKeG+q4h7hILJ-lcn{CoiO zVJNGIkeHRMajmtr^;;Iemdi#IU41hr!Iir+fK`yft1JfyuFXyJ=kT!NY6l!|?rAQ< zeFZ=};FDliUjekkZf{AC=dtAr+{Vg1K%{kkq%Q!z+1!7YgzW?V`*2i6{Q$e2JfEpD zWM#~6c+?rLdliI5t5Cd@K&2V?sfVQ5uFo$)St-3JDw4V}sewE&@Dj2kU#`jDe13>1 zFqlkAhS<2cJVfRrXRvk9XP1}tteJzRsHlCC<%VP5Rt=lLkQu8;qjC^Hepf`|8qRpDIwX_(V?cormMY}y?Sv{!i0yQ_wHSh) zNjwZLtAU#1d2f%{UE(Z_j}3F?9$0p0X{F5TFu#qf=ge{ke1O*g%WKU0nOE|BuS1Bi zu&~9)Z@enBDF-~o2+8bwnez4PAegF1jv$H-rG~cD-9z^Khlg20ry|gOcme1vRK5G_>!u^vNsHIuPD+O2+ui4<75au*1Bgp> z6%?Wm)HF0o-#_eWw%h2Y>V-Tyd zz*!SJy;kNNRJ0#rFYP=3yTrEZeL$uhR1DsLY4f?Rtg8T1Z^iwYPq8qyY**geYRA>M4Jjta^|sS zl3-)!xEYdR;RAElkvCrg{o>`U!Et%BBs+zTmSMLQ2r-Q%T!I)Vum=EVlD#KH<9X>N zP#t*H^W{F5kc8y1W9GxV9;DvU;zX-pbB%4#u2Xqeb)Rn9y1O@c6N)HsAgv8xtf#c) z=Tl0P46=DaRsyb{J3xOj<(;3Ozi@A)kzBXf%#hgI*6jTgOblUYq{EIv(_FrXv-2Ap zX7!w@6)VxdRVKTTdYNz(6%|u+DZD&4j8&6R0z6e9G+qu6R~BT?;}Lqb2DI0xx_$Gn zkcxV=hGhpX+TaV9KOI45>O~XQ7`|NLhH7fx2`s%cA1tp>g3M#vs%m;A%;peAzY%}E*6*Xx^dbfWrE$Qm zMUToRvq+n=7`6%c-+Lliza9eDl#T(gPYy9@Nl1S3!QBBVNl7Vo@Hk116|7D$m8v~3 z2KL*(M9&ukeCKesMod>ob8CKvfV-lvo8z=TnEAkUf7%}G9GEjYbf1qWF*6zT!}ssJ z^*AQC#1+h&vI$Cg&5e#LNiAEozDHn0;?Gqnt)>eCqOsz`)jqsP0^Eu>>@46tN<>t* zb$0?ciMr%KQ=ix3Z=Zi3ylayPBp-wVZWoIqG71}3ELq#4^VeWNlLlOg36onF@vV7T zwUvYW>Gp(@hq!>8%k2;UP;W{2gT*FQW{so&`akjzE5n(mAy@^8HUWS}XmEM6Um{k@HLx|c z^F;u2iBorrM~c8B(muc(Ocg=?CXy|7=I{SDG9IbMO{VB8xo5}oZQA#J*;rqvRF}?@ z)?RN`-TMYf4V2JvC$nQl6Y7w1w*^O4&e|>rD!Xhg?q8W`;=TtQf_#7PaB(3Jvw1^u&F5@LUj;R z)-%zE0p+Nwv^0+U?(WV*bfE12?LSLczNqQ@DHf?_WVXD$Jh`|;L5(x7{OIkKQvZwm z`evK(g6IC_pqKY@N5j*Al6GubitD9}j*fH)_T@oqkUSF=*>(nRazbh<5YSn)%DMmPuaH(6;G7ffk>G!`k zoG~ca;zDc7N!@K)0v*et3QY*uqh1kx1BwA!&RJ@s#TcBCGxehc?Bt!NfSEpF*WM2F zClZu3~A)Ow9u#=&cPSJ+Ur-Wp|6u z!?}Y_3Ow5lmL7I=HG)@lV_z584wXsFA@2bNtYHb6W}iioKo11;HKjk4EGy?kV(jvf z=AC-g{3;~bEdxMqn;&R6lv43$KdFm~E&~TO=n8YT9e5q60iwgAy?vDleNiGzXJwYx z?S*v`t_^h>N(K#DQR$DJZ*TuZR|37UrDEkk{*I8@-lDZ?;YdcyMIWw#*HMkL@vRz% z%V!=I-V#W?UqjOBMA~fYCBaZ`B!{x-5Z%y-=u?s^;CL5U9?9k>EI&aYs zdLi8kc&}8{)Fq0&pa&I8=YC{^b2$t~>=nbDuL}hXd;xH191CDT4Oh~wv~WEBX23gR z=~7wgP&`&KvXeSss6}*W#+#7*-OFQdso8_rCptsG=V;KmjRmEs(MyrJ{r5nW&eWP$ z=2fA9B4s40l-t|4C?5QL748h6(_Vlu&==NakDv1$_9)3yyUS*T;ML06EmJ@@HdrjR z07vx6#D^~nbXZsB^KGYJU(F|fDVU!aLUSd5CSSnUMRw$hg-yDA$?ma9j-&r}2}AG* z_*_%s%N6ZO92IJRATzCtrKKfcJId~Sl>PI`e0|@g$1sxr8_;4d*pxEb*5QU`miEWx zEl^X-TW- z&}@lSo3}CKyiZJWjKS z>CSa=^;6v%uzP)w7|~^O5D~MMKUjMEpY6HyAv8>5;OBE(@xP5d>o4Rdr+$&+=ilG^ zdWrd)*B77KmWG7U{`*M0mdS3m=IIoZu@uBGwE!=wwxEg-JQp$Mvl+5rR77m)8B0N0D zggVCr*N1iN*R08;eC=Oa$NrW%`C8_5=Ds5uuL;vHG)Y$FHH=quv*_=CMN27vD<*rt z_r&wz1TkDH)x~nlL+I}>Kz9|~_G+^XP4yc7;6L)hn;B+dA$`iz=Do zcu-a7p(u2;SEcWZ$87oVdzB#)7ER9U#BuA$oH53b`2g>YZ)b6v0bb>f#b7tJ9zj&F@}IMx#?wXlHICb1N*6!d{9gt={-KZ>xQw) z)}g@6ay*WZ$~O1mnVZ1E{E*n|#6}lV?zUvtCjeUXf4=ziUughUWKwh~?GK+TwW5=4 z99&JtU-p|A?Dor@P5*5R^OwipdhagJJ!nRj>QfG{Kh2;zbg2?c4iVx_4iB}Q?-Wt~ zZV0@2(L>^Z5L-1p93&ulR&H%cbR`2rW#j^jH_g*TU(`h<0{(^wD=H@T-T+x`NtqvP zb~OrPdU3?*-y7?Cdfs3~iT`egL&8ZZTfk!NKJNbW$4FOKH(@ms2+jD9ThBX@SV5<; z1?U4$w=?Z+_h$njJ(^iq2-T1pfZq*%MtNZXv_q$wJS4t^{nlaXd!=Wx1ib6Ptnx*0 z$(U5wfp`t9S)%(lUb<8aH-`23 z0hzzXcIMHLar6Foxv6YV4;O_%+XPRFcGpB8`W<)%?6AcZj_j)PlG1^&QXRkGfzyEp zAT2oEf@Ux)D~o39hsq+H@8qjsE@)`z7@UEMDH*93Pg^|gf5BMw?big9f6Ses!$~Tg0|1kvrHE)8urtWJv^DhaJjSSm|t<1bHH|4+> z8<84F@D(J{Q~g^tcbMKd73O}0v*(zo73O-4QXMN3Ig|+<1)tyXW? z^Gn2rLEh&HAvvao#w3HrRALDNLTN#tt3?1&%-7q-n!Uqpbg}0$7yz9BWyU^0E*^e} ztF8UUH54}^m4$JWysCM5V%1pOMwsUiZX13h^zlx8{Vw7G`o5S|KnxNm5!8Xj&MGRH zgI%QRfL`=_-r6Sg@7-M_+KOtCLKIj(PA>8g2PnUj;f=J9lsRkW-Q`18-CgDU&sL+% zc+0_$7L7@(@u_rgu67V>L4)e%(#u^77vo;CZ;`oW{j1XPnAgn*{Q;iJMg1n<)9Naf zl0mszZ}d6GX_zjuazhOR*#>{-O1ifFfLv^$Tt|1eMMog?$Bx~JB6<9fHs4q7r~7bTw@L%nvG~P4_v5mx@1&(QtSX%O zUAUQ_<->-3J&d#g$4h3kQ zA98VUV5?JP{VX5Lx+)(HnPQm0J3j8fuH8^57u;VWLupeaf-_P0ToUvnyjdx%oJUJ9 z1gAvba#bn`yYBMZt#VMpEHSXswR&$PtIl~AQkj#=Y~e`ZEcFZ%E;MFlqD1Tpl_aVg ztgq@XEGP7fPp2od+u-yv4-7O{#BGngkKdNc;?n=p|3)ehRxS{#>z0PC&-E)TpU7}evI9bv!9oXaODzTdhUVsIm_bAJ)`UGwSI!de^O zL~*Xs?stt{-%rbqPhKNm_E<1E9Wn(CnO>9fJ3(LT{6$5%^gM_QY`$Z7Vq{d=KbB>- zSs*gnJfhri947iw(IBdn)$F|4yyKdv^bIvFH5LCV&V^HgH@(e$IMNyl7HVoZG-Z0+ zvg$K061pG}Q29UME9I$v6s&tk2)iL4=JH*SFkfK4D7IfY6Su&TRL_}z^P-zNU1h4~ z17mymDluc^FBxi{xT(K))_tjoGdc;&{K;eua(td)3Q;F2nT_HeI5?$tL?u+~Hqm>A zyVG;xHPr7mO`C|4SVKnJ7AuMQqbnKo#Ke|L-#5y0;X6s2zIKD64k; zdZnrPvWHpwwUDuY^8_oVv7gtp*`3$(E0re~v41EWUH|d#4a?B7x$canq@)}i*hxWA zvbN-^u*rBRmRxOaZ1k4gLc|CWsqlE@wNFbPEBei@B{srz!C!I$rZ`aeBr4!I%WsVc zU*(Zly+S4Gz;6|;*6A3Wc=>bVH~8%|Qh)CMo_E?>6{hCgA$s7)QwW7%VPT!Vms*`3 zZ|%kitjqa+>g*B{wSR@+@p_YpKb5MIzU`~imS~%s_B#!U?n}m*kWj&o4~L>*Xvas) z*Q!bBw0S4j$L7h>Bzx*X7UK`Uvagy1`008@8QNT_U*P({zFMP~T~+;Akc~^bp#2(G z0KKoN)uS&&w0(Q4PSZo3=*PO|-AOcaI*)8*B%f1_RP6}?N1@MV`1{7Q2(>0M>b`A` zfrWbRMpWX-hYN|R$FaDJ3EQNM?mg)KEsEh4^TpWifS7Wka_FaUYtcSBJIo3$-2!O8 zQx!-m_7n*;et1Q7!}>2+zGC7D`SR9Ht}X&!4su>bGp{d`ng!qCCOZ>A%LBj9GsXlT zN73%z7e}p?f}y{SC%Kn7h-CqBkXk3J z<ZnxU;ZI{a z)@GQwM|}P@ZDbe@w>yi21BnoHCV%}0Pr7VXcbj#O9(INTRN1%~@Y#m(@VWRY=}Vjt zTr}o3m+`!Es5kCvJ;YN}2>NcMIPT7e%IZ;zwQF}n4-#^S19Sf&3MRbPb|soz81A;7 z>|pUR8QwI{SZb?LIqaEZb-k%RLbc&{Abv6zJ3ujxQ_E_7T&N0`7>Ydc-)pK#+UL$j z|NK;%(iY{j^2U^(UvuE7s#VtZsW;bGE<-`nq;3m?A7o)A7|=E*vLV$4K@y*oNF*!( z?or<1ZMrRngP4AWy`W zx*Du9*GKkA?|;n60HGC*Y^&~NNXjhD5g99 zxdEPx>;TUpL=Wom7@$D5!_)f~29xM80GLw}#PopTx0&MJIHJ|#x1DC9i>LUrocA7S za2>1C;b4O9Wgxe4C#)T+h%ew#J^*z((s1~VziL7Nfi1={C8P$LV~CJ@D+~Va>JsZ| zUhapxWQQh{DCb$riYoQRVK0|#| z10asa;e5y=aFF?k&8^%hBiohg(l|CYMln5+EA|SzXo@!ipToikE3oxcZ~b9_cui6h z*R6{N4P#}c!Jd#|U)0O+*ERVqtKr}a$Qfm$hcS`G(bI6LSzNpSsM1%PT2U``#yrg0 z(9p=8Y5fs3#)+H0LdWZR9jU* zUh0ly{zS3|q#%MAD?lX9>@PcT>Zf5_91+Y5t*E{pa*ma3_2wigqVwdS(qRWXi2#T5);flmm=Y z;``60!wEm-iBM>uLqNP3u40LwLrzd2ET@DKuo}wtJws>?zTv09Ai%YpNy!bJQwHmF zuytavBL(_djj5!stW`czfPfSrvjhZR%xibTk2jPqSDQCNNIARyD!ddR6b;~k3?bZE zUtj;yM1(kaxux1Ps`-oMhwNA(;-y-Z;cgm(h@lSRX*g=7`{H#CQy;|tf>VTAJAMD& z-V&ty&>Y8Ps4MB|*#R#5nLq+6)C4TnrnYKxQ(X+00Yu;h=;Tevw#MZkuMqrLaY9S& z#Q>e-BG6C%1lBkb9={1msiD4^5PAZcCLldr?7&HUcu5}%WURV?*A94RD2o662N*Ki zXF#JF4 z`JMhi_2;?hHcQYDym|P7tjo!mj$1mSe;qd>hg^HSMlcim|O(ZqgPJuUTrVZ#0+hqF%gz0JD; zg~u5S-gJKr?xIl&iX-=dw)8jKS;DS~T68WW%&a(FXs1kiW%mYe6ct(6Wm8q&S6DAX zgxC&Kc!7J1^Ase{wC7wL2>T_X12T};ahYl51J#BjpnOSeW(j4y^}FR%yZqe?oGCxJ zx=x0@z9qtn5VN8i_`)7X(H4`8gUA(m;}3?rOKYN@=T@yxvIE;^Wmy{zhuxL#Mv<88bj*0EQ8>bbrP z;Qz1E?}DCB)R(Q4ic=3;J%8D#nyvKfX#*jyNDjkd3?2m|_CGoZxgnQjE~){(7t$@( zabezpz+`&^X3g~3PZRq84xaSc^|hqRU!bahdLcZ4VfTJA(TLz3)ZJ}94nnJfAvkU33kpF&?|x>XzTVvZT#D?AUFWLy z)_ilRC0?haC?`kGWsf^xRgDzsav~0!b8pU4;_}S({6OeifRvR-P)>nkxMPozVT?@0 zdB5!IlNhFUmXS1=FL?-Q-ThG(-e#;ilz|0YV~u*JqPAi+rr_mIPym9+M4F?w z>@$)?hK0vvgY9BT))~xkcYQ2*s5i}+emhvV_j zJ@?0ZFREj)R7-H!DZJW0Y!4U8g|>md1&;_CBU_Z_yNRu^_caNBd)Jg8zdQ93m+Crm zi;I<*x6ZF}<98pqf1NYdB&SMil>WBSj$u}K;cu+#G*yupX1 z2i}9F1|91<2xP2w!1#fJAGzf-VCRWV-RQg%^v2}7vjX%`>@@*E#?A!#v+(1VmKI@e z!I9@-avUC>3nAobuF>f%GNX;|aPjdlj3pf1e|1U>I1Ltl5|rDhdO6*CS)I!IN6x_e zzLIiGdMR&S3_W7j&I~P%lvIw;P*FeSEcGk}b0JvH9vyp(ubKtoY z6BWhG%&eYJ!Wjzrou!x}tCG1tLT}Wj&Sl=R^fUTvooSpQ6YvIX!IRc!pfU{#`N@PI z&Dhw_WtQnC1=PEouc{5?^l=oq)oPlb&e*x61q{(*-)d#iG?^5Mq7-OHQH&PK6FO(> zo#rVoozNU&ipUxIw#0ACwyf89fEZPU^pEsI+Rk01N(U%lLY8Rd@*4WNiqISq|zQocQbO*fU z_7*FJ_xZ6}3it>~Vy!QNx0cxb|pQ=hGIS>9`5u8ts@#rr-@I-fvVJW|5_=R%#LNEWeITb(eCB&6$p z4WXSTrWR&{L<)Y~&;v__Fh@3(9>JCb6&ODBT+-ZNbHA}$Y1998q3jOX0Ngw0ohC!O zUXiyvwLjfKGRALI;kcf6=eGu;wKUBH+ia-l?%?3Ei;-@s)qM9drnXF zQg!^d{S}7hzwIr2TQixmk<^gUdG{J2r<7)AAB5rQX$30>Z)FlZFn`c0Hp~MQ936s> zOWc_qpgDd|n^IMH-Aw|i4mk|a)c%BJHl#6-jtu;WSi)KirG>1yP~<=)oRg%$d@igP+ ziY#xTnuu@-+bTfzIw>xJ{x#L(y_-f=ufIQYML(hg$&w$LtoqY*-c0eyUfyuWRo47i=$VD!{r7->t(See?!n0rWg zNN+S`YRWR;wp-Q{rVhe*W2iOU$&Ba=yj7`u8O8MJm=?%tV-TAOcH=XxVxhSE;W0=i zIk#Y~(V1E(pfSvKu`wl@BQ`LCt^7DvU#0a}j!fisSA zyZI=8hrpuWC{K*(tSj$p{j}dHml>E+E_^#&AWDyB^0Oq32^O|5KPWlairzHz3RA?N zn$mXb?st@mnNBxV4(GY{Zq~1=;dm&Gcv4%=9|_p7j-yq`o_4ZhMn3_qkrpYjSWk?y zDKnIVO|rGMbso~7n(E!!10?EC;6W)NebcL2@pJ=CmHfT zlYa9F(af3ZKOfdw7n*+mHovAu3QJ-^s)?!i4A+b6ebryPSKvD zR$KR`46ke3X|YKWGJUJ@4Ag<~o1o-fBPNevc+zM*wU`mj0f6C*7$r92Yj`9ToCjUF3$D(ZiX6N;nFC#yH4q~zZ!wI=Z zxl4k$!qfWgS51iQ8as6H$-l%bnXl@pd#xD|d3NG(HeglMcl6$L2c&orzwrX0Hn zs~rw7TUNsN=CN#UBC39^qB0^kx%D)Qvhvs0Vj{}OnN_XOURY2^dD5MoouXB7+#bb5 z6zaD-TW6Wn9bz{PJR3Je) zLmJ&1q`;&9K`9D3_6rS9=fY3mU!<+~#H=I2s+{`J3kV3Xu~lCYe%WEd;i!&t-k)Y( z#Zr>X)Tv9T6Jpvwwu{YV#iz_<)d zbCj}Zyw>Ch!oKpY0FE7CQFgwSqIn4mi&v8Tau)}YWMgylrz;qSrzO~X>A#Ip<#>V% zWb}`)B6}_efm!f>cuqJ*1cGmNK|Mb0`u_d9R`DRE^$+ubGt@TiyjuP>LD6CWf6ups=i3?v5;6%$NL8<>3`C#1 zSEG4YKqJRt(F8NU7s?%_a$XDivKOz11b?G;5>*=Zit%?cXoNis#fEp*x73V*YgaY z1dkJ%?j)x#(G6fSF!dy`*APqSW;fel_b3RSx$`v;v@$lOV*4)^X1+P+X&5HRM5~!0)*WlJ`+xh^%2Ti#!vjSOSYW;V;JCB7GMC- z%bG*f2XZHYun7uKaa`~*sCaBpuVua-qJF?wl;qN%WDKT3vq|bFfa|~50G2_(5)4%L zTCZ4RVgXNxjT!&Qo{Z zQnWc*_pyGO7a10k?%+k}t>**#W&CN)8=`HHc0}z;0WYT0vUGP#5I8uyXptH!0|L>m zRQmXf&f7Kj!S|}StIz8rB_8OXH(AFTMEzq@^i1V!BQP!Ogaa&B@Uh z4eM=Q=Rx%@O8RE|Tt)fp?GhtcMFieAimC-qsx=-+uEUa*$1uR}Xk@WRq5VWn6oWeR zmO>6=GSk+c1Jn^<5?M&JAUA}0Pth^s=oux`RBmKv^PggXv`7ymoZmR1O8(0R9k3@{ z!~`_4GgYfE^75=auKNL10ys2PG0BSISd5oOyG;0PE^ zy<)`#4d>{`zBYy(UW7?`kwl%XIqnAm+eMa3R5>^Dgs*Zm`B&q2&78))n|bY|xGul13Tg-W!*0LITg?n@;7 z6twWQ>pZV93(cHK7Q1h~f#NQm?+f1dQ>TSNZvp#jd ztO8Cn0_ENkq2Z#IbWUNe*07t-n59DBzl$$9kr`fne^HJSUi+hW0SIzBVW3QJV@!Xa zt0TCo^H8N3!|j8FQ_GH4`&4Ss4FO-UXan_yv}#zGnSV>BSa@Wm@@fq=gCkGR_X#=2 z!pFi9FgBEg+Mw5vXxr|7|NcGoq+4bp$j$@?JLPYCl!lgoDIRJr09rOoHgQO$St}p2 z_*ZH4u|&oNRgSX*9{)-pAN=_~j`df|;{~!H!Ww1_!@bXnayDAENz$(t%lBraZyqFOSl@edQQX}W zym8s+)sbA4#w<>UYBN-CrdF9k12M6CI*e@x8Ob713Sdr3wB1a!f zhRo?F0Jn39b5Enl(DCs9 zB80RI{uG4Mwqd{nm;cX*MB04k_-K&4Cc~iI&%+tY=}3ap4lkQRW_D5niKfHGo1??F zoV;nFQp>}`;f=e&z;odcLyreFEGmrELcO8iRQQM25A^7xtf0@l9LUc9*P_#9c7VM) zuvfJk)lK=T@!9ZFYF3lacD~3H27C@DHuAkwz}8^utaz=-?}&hheM1l!p%Dpd_C-M; zbke8-;|}s#zz`sRHPFWS4$8(q_C`!`7DEa_NFJT5RR{ENR zbs3>7VzAIv699Qcu?TGCtoeVSpSy8zZ=z5ow?N$%+7?F4Comh1vihhautAS$#S6W2 z0&J!KY$h9aM_`zFfwK0WMO5+F%*n9lE0EwJ zxTxnEo%x2ztbn@rAPH9Lk28qv03!S>04{;2#O4Om+xC;k?bijlvds`Lv7$oX22?=8oe&jj=>&mt-(?a zB)iSJar%GB4h1|0VGfQ7c$G^yq)(TpU1F8FMbIJ#A7OzjddN2Dv%z-`c0d)pI6Va| z1Z{$s|KndvhI6%`Ut*YS3^3r#y?z8WpxEp8GQBaH&^5q2Uz-y~jlF?Z`<2RR9fybQ zvuox?L!540-t}%C$XvuaTR>T=7O1aL;C!HfrpEfy(;8t9z&- z1bM<{F!1}77~iX_(a&zd<~dKOh@9Vys$n3qL^SZXRBIk#6!mDCChNhnNGT#8FA!=1 z%k#s-L&638<1z^-0l=4ZM$9tpo9z*EW==$ z-~CGKC5i?MJP=QA5-Rb&?yhR_cBIY!Bt`JDbh;51`z zVBc9JpKcK=G-?IjUKcIE8dgWh1;IUopHWU|Gh#$_2s>T=x?=PgyULQ8D zi01W}1&ummf{Efr3q!jDSP2(&wqb6mp>pNTe#Sh=ML{Hol9u*GL^1`q7cL=T8G0A; z7&229Fldw05&}lyIECE5q`wVO8F7Lm$CFm_jq+c2l|~Nr6&(-}_)ODwm8E~c6K4Uv zEj9joUAd6JmXVB@RnW(-K5ti=QffoQBe$boIDj-3pE9Kf8>#|e0;^@;WwdP2eSDPYo>(ZwRm{!=(t?jqP zx}u^mXG`GQ4>4zxp6~K@bE8)t(%*#n%eOi)Hh%k+Yk%@5D0sfU)+#}_^)swo0@SAl zV0Z9x$N_tJsJGYY!v_v!bVMgz)XC7WFvbp|v>aZTVvFqb^hnXFYGm8J*v)reeST|~ z7zKmvIN%?%7#o4ELAg%kXZJWKh^Tln@Is~KI5I(@0r6E#8w3QCKL&n~~F(s8% zX)8T`2Nn4jMF#rMg%*#hkPx_UbqS6Qm`uNFH$gPyjw6^t*#mm7Omze|II=0c&>yg_ zv;PNO#X^=;Q(;%pHHCXfiU71px&Y^?jBnT!a}*akAm9n&>{mZy0DRR!^LcyS{uG5S zj5Ay3pt!Xx9GT)Da-1RTfAh38;&+Nh`U876^MlKj<(;FIpDQYOv*J)(9Dr{srd2R7 zILJvcg4~w~urBxyZ-_(9ciU~dB7BHd!Xme)~wu=zmFdgiG{huEFQjT_a z0U2AbJRC2Junw%AK*X_?JG!dMgh4tZ0OiY)fzQGf?*U>vPXpSE?`Xz;W@7(2RYJ=p z(x--(rlT(gtVe;W^@fj+AngvQXJzH(rwD>OxeV`BO81srdTpyd2^8nA3cKh8Qt8jt zo#pK%^8?C)$Pai<$7NqY6J8siC{mo+Co%Y@3==*`k%*_y$InmbVe?UE{2 zb4X7$Ine~wg#&pcS?jN0zIBVsk*a?bK?}Ipb0%d4cnukJ1%yhoAO7}vECWdrRF-!6 zbZ&_!AG}B$(Xbw+tq}J#ROovh5@COVk9Xjsb|2GTv0n#(Yza`RA*|jnLjFC-OWa7Q zeBDJh*W9RAuj3L3{K!cyk&ZVKlQjL3AT*$QEIdFXlA4wDeJd#V<0{fq!xk3Q0P5Rr zQpF0C&giI!4&d=LTB1t3ifPlqzOe56ACecNS^)z+{ZF`?R450&ctqa}X#qt@AIA>b zCv$9pe+iV$8V2kI9!vfjl zHrxMp7ftv+XD-=Rxm*5gsj#P(8~(%EWaQscr}omsB9^C}B_6|o!|MS5r*p2bshLRH z(x>*D10GUl!OVQj1#?R1cpy7vHa3ZD0J=hfuJKI|3MmQCdI2~ zA~=;%F{~2(%*+fA6Vsp)u~qnz636xFsj-EISjwr6K2`^+hd2VpVzz)6`($(b?Oqx9 zLI$dEHJ52vK{H|GACj!8TSnO?@P$LRYwy(`Z&6D#9u8!;9n3e(^kfwVG^kHC9j7z9 z7HC)>_6bQ@7P4-4{+p`Wq{L)1fdMSp4^AEY5+9A=xCC9f$G`}dcTX?z3wy^-57mqa zHV1>0K&PoSN|2N;PggmJ+18E$26!FM_A+V#@x>`nE<^#WYwWrD=0S$c;@zA#|FZcl zc_2do5N)uP5b@-8sRETR=^9ZY?4Bf4nlj)vi8%#yf58WHNBBX&-_7K-ez;022S>xD zFDZT$1GC&Y6?>__D$>OhRt}DS#ysA}C<;C9Ja`z~-6Wq+ImR{1`OyEWr4V$OX@5E? zxGII7#7jVv7O{o}ga|;q3sLWhA!TJ;$HCE;!%&I@<1w+%#ZGP{`P)!ujEvcEhdp|MKdH4;ZPPJkuz3X5{? z7)r1A#y`3wsW}3`4NlIsxe^DIrw@LykiK-=VU?4Epu!7Z5YY z%K#V!aOH7^?Z@{+dG>=vz~>Hf>>@w9p`U>r375?t37;!G0X9m*@8qca5ino{@hpHA3xr`ii}C_b z!;NE6OBn(qG-@;~D@h?^rXn*GVgQF5qCq4Vf(lSRN5F&l{8!J)p7 znBx#SA@=hb$_obXO)pw(z}?RTHtq#~Tve2}EfHe;Ca}OS_jTMYEH*%keV=mr7aWshQCgT*-X~+FtJx2uKhn&kmUi`~alb0n+B!{_h2^M+ z+_{fzKj)sMfc?CC2G#}rW%ryL@5`mJm-V2P%F-M~&JbYG%-2MTZz45qdO%mNKRz$3 zs9@#jSb6PlVmKdeC)P03#uA@eeP}VS-Kgz|PKNoA3wb2Q&+~5nK%3l%Ee;kIx3SsD z7U`*sZmr#2e$X!g0%3R}NynCWasm!0E;L3vmq1Da%sJS zX)>{S8>}xG1D5_@{Cy5B$iM+9p|ZFSAPQi;VcTZ*Gfg?fDQ@z=w>6$dQbLc6gK!RQ zl9Lpeyur;EM{B_QiGQ3C{Dn$3>VrIwnxI07gDdIX={=jik^KT`unjc7mKX($gFw+% zGJpaXWJXq2TVyiMTg2kL6&WWD6`>x*2PRFbd8wNlJbv>y-Cqu=1}ovzoq^4NekQ$@nDDIby$p zJx{J8uwM(uvnk8n1}e(lQG}9H=U{G48BZH^{Ys`8Iz&S`~V% z4uCh!Y8W;-sd;~RuWlnvmsljbs7RHdhYJ}>Nu7_FlECbypsx@j+oX@AkCETz8~68; zS=R&E0H1Ll-ZPLn1#SI70;cV^LPVLS|?TKfg^P>W^tV~s`8^U2~# zb(cYJ3AiDKmYs$!wd%Qj4|MCbU+PJmIner@v(-hU5-z&Yt$z6J>wdw3`H;s4gcUfC zzUQm>1hn(3nLp^bYu?CQx+)OayyMSrFNDi^b!UIps|*;C&xdO{t)67*>|H z_ue^&88Q)`0tMNZu2wRWs!z8_CDVayaVHm`s##m{zAX<32w18ND{ON+H6C=@_L|?T zw-`OV!W1H*Y-!niQCo9%7|-{7lRZF8;065jtG!2Wmj@61UR`c#*@y9}xL7B-=-teB z&s*@`y6mRtM_5ipcv;3jIiA;?MZcCqVURKel2*zSpig@U3TZSkd?2lgvC)COGF~B6 zYZnBFTdt25#5>D>F(Mv&C)}@^HI!gMng33$8~l@I2@liLV?q4%Rl9_WvNBSf)wXmb zo2;G>Ky$QlT->{<^fgmsDC^8qXMcW1%82}ai)^iah^39jd!C_lO%{Iubc}5Z$Lh20 z-@hM8BzvO56ldA!d@7G3%!nk*%sLKW``@~$g3X@F%F@)~yS1DT>T~E6>Y$xI_x;_Y=cs(*~C33Jr(1<7eTn{>!&DsIKwtl9`a>6`4WC>e}{H^tI znQAa2jA<^p@^Idg`vbJ`)MF1F8x?PNwTa#k1$F~gOBD~Evvd|c{dkDFzk;8ZVaulPIJgKWM!>IQCjNY~5?9oj$MbMT1=DL0})n8uk3|JA|B%QNUn?XME#8 z=7RDSBLjo5pkU(RX-#&&hp8z8ugp^k2=ut6FEjhBi&8#a92I)Dhi3(O+IF@x%~;%# zFGR}U_RU5NYjZ4z=t@5(F3u51-CWY-zelS&#v$V`Y%GAQ@BQUM9CIeX7Yx&Sv^s!= zNG1_qkBMA3A(L?@ImD6s%MiK`Za*NJ9<=>VuU2D8;occ0LcTPYlOe%DJpyAhB z{11RWAT8zLokhZ==Az>J06pCa>4XEh8h>=n(caNfl08da)?Ai>av%ny2e@$0YAIN` za7lSPr>Av%(1z&|AD1fuGK;PLV9nb)56Y#2_ z5JS6b;%7s(s_Z5OPq~Ny&5yK__+=_#XVxaGBR=yb)cr0y*r^o(`cuhLAVQ|LY_YC@ zGU(x<7{Xn9e##97NCuoXZ#|c!LsT4929c3%V_SbSTD*Dl=%;Mg8{YbkAA99sln<7x zZ%$5*$)Hs1S?;Dt6@_6A_8q8t(moc zAo|@FeV1ZB=w7NiHdm>Nz}9O}^0qa!S2uDS1X!k&D2^7sZ1)VRz#5AMKqqr*zvjE5%iPAw;0M1vpMUR=DGOu2LsBk*fjX#q z;BO5YVoVG?yubtX{v)sm-l+G!AStYp9v=_7v>~9-0b*VibCHFgMz1p0oV(MVWG(neITVfdBa%6#`LfJXH<8s&&}-&&nOeL7lqp}(_&32qx| z!L!Y&;_7NHnPr9ca_qD5nV)K8Y-^);hB^8VG@kDzQ0kw~X(lhf<|%6JP{OwDnk1{d zHXP~VH@cd&3MJ%Bq4m+mNUvexHnX?0!*^~&7Pq3VH0W^@J;6ZE{Y&$j`4!LyrkWgx z&;`(|baa&S`#4sR&fN?(bywF~UBCOxm-6W?)8vQF@=0>-A4IWnsIalIt+bc}e%qml z_u`QYdf)Nr>gf0>ssXS`a-k1>#MmP-TgUZoyE%%1U{SvG=?<&j-gG)fZ$ByeO=&Wa zauxLPIw{h1e>QlE|0K^XwS(TU5H(UZC?Mth9E?=Mf4iH3`k0izM6~6deL~}~{-%hq z#caMo$y@?TKK*Rn#n0zM>b7-|ezR7cJly5+ODii~aonH15EE%l-Px>!y)V^G;c)mg z1yzI4ClRx|?C|+@1V$7{ia%&Ch?2*C{>U$%z}%$IbM`3QEp!?F^zoF-(CCVg6lBQWw(^w$?*`pkVVHPE5Adw-lwl({Gqi8Z9J#qjGH zGU~y~Vm{?UGF$xqL1p~Lh5C=1+tG=!pW_$xl46HOsS^tIQ7{H zr+GNtWBeblhL2JylbMhO1O!Hqh+80?s3Ck3tT3@R@f~x{haU5!Bdk-Xg7Sl|`Q_|9 z*-jc)$&Iehd1Fr>qdy3&+4FLqlQ7;bk3kX6=TL}v)k#HQ%%K+!-T0(%Jvb|>HeMd( zeL&d!UO2k93B+w9#ZTXQf!qZDzP-dj6MOb*Z)c}o*jO;_AjLb6$($v-} zN)Npbs$f8{%A5lOsK6jz;}s+>2+cajnd(XN&=4W>h1S)lzm)O#hz=p*RQ!K!2%jm; z1x@5ur=SfHVc{`Pf6O%hdQUYqV#3o$V*-nsKA(Sv|B<4{UFNv+1TGc!0(Y0LW`L8{ zN#FC7BK9-Kr>P4Lup4^LHLD4f-xeziw}%Fd=tIS|eQ`?%sF@_sT9 zR)zL5^+>md(b1JO;HxItePoP1(2O!XSZeQ-l$*fKTL;)nzo!94uHWuhMumndy`^~* zz6c0~SsvI4;(EANO*U@{f+^6jq3W`3)ZuUCL}E+$(VlLU8Dk(frAbd z8e$*i@&^FvuodWh!9Ahi@!%99E&Ae zNxSrXnZbJj4n~nYwebQ)e{jo)%0zJvrn-9amX5`mn3w&|9gUVMVYwyKGfhN|_Bw8- zu8R?nkL`QVgTNj2wuer*`RdQs4@)lAcM?@f8u$yXWS;%4q&GA~#Xr;mwC?D~3V^nG zZ&+mq?V>*Pjb@7M+x3iXbix?h*Fg`YmNPf+f||bpdP7n`eJP}IUaSE37z8-q4gdvqXG6szB?N0Ci&tgL0~0OoRG2WWU!Q*C`sGm9`j%|T|CKAoDhhFmEGb^5+P8#m$f5)863lg3bEl60N=5*wXDO=VM=$AylSC z{b5XN{imk{C#v6Tf0ki(p#u%g`x)O-IU9eZ!vl-a)&%L(Em4A| zGCr|?DMnv)K~&rfSy9hbo3%`%JrPwaKn(=Ce)gTs?M}0ddgep@bVgK9a)U|}dG87< zfnX+bD+M$}ti515Z!zFtj;jIWMBT%~LuR64e&Gtl5pup;Uy;H&ZqRdHD zkyS`HkA~CcUGE{wAC}jbEvv1h<9U?Y;qH#F%GWFRn&2?1?@J1nwca`M&72sDsiKeg z3jB*ASG7PT3e>&tZSlQ-{I;X|!z69TF_3Kg)Fc|ZVHwLMazoMu*2aWRna`?o!XrcC zwtz@aO(f`fDVXp0@(b^C@it#=%yb8uQy85Q0=hDW?GlZu7>1O;{|)mECB`S2zogS| zHZRaz-*1K*EXQZP7@Ab~@`4%4KNaY2t*9|r9@4>eyJ&cEbv0zEX%|_zH(mQWcWo|< z=*bEsBz=6|t3|I@_-emjE^~kx0-=nq-V@DDc6_b7m{+9m%F6K(r^%|{z>|LQ`fxe= zbO#jt*?_}Qz=T32!Ckio>MrO2_1UhNzx&t^^u#fCLnfvDlR-Yk0$DYuy2&2FmUuS; z<$Hs9p5}vFfSd!O^9}^Wr6<`_LQN-psf`e{J1~qgUMI?l7@iNDYHis8EIk0GzD#5b~>seT>X{>?qeG*i zju#o7q*qVP-(j?uuJh>6RD^;1^6+rbb%@hhXMTvq2yAe6usX#|Beh|G zoM`d`-=ZDRBMN#f-RHZG=2LX_wY9X2-&tC|;mKnV(IE-5CRlLX+b?S>4(oZK$9J}8 z2umMsoBkfzG(R)02Ve4ZxzMKRGoj*#rR0bx2w^H*?@b$sd!5ac-sK3}JYEn7Uxy~s zJUQtK>brpHIbUh{AQVi$J_Ui~zJ43~BOs9BYn$N{Aiakxgf1%^7p*&S$1j>vaC*hyLl#yCq^h(nUw4xTqBVt$yaz>J0hS}d0- zBi|aCF%#%-BZle?=R`d3qBh5+ap$%}0rPo*HfMkxhIqI0_CyqD_z9=6X^Y}_l#IIb z*t1x&*sJS%o@m%Os*Y?JZ+N!0_^y47KTbJ2t%*;FV4Ss_6L*k|455ElbiKYq@nF|E z>2h^TwN+O(Qv!12RZXAge(>e$5LFWuW~En$vmNJtypUx7~~e z`S8yTl>~4HB#4Ac!|(BN%{hwP7Wm18dannK*q7FCw})I_k_;6z{sR33-(Ff|*qiJS zVZs1DHFAkd1AXHGCU1%)fY=(iuEYW8y)Yl&iNy5V;rm>Dvi7%^0r)gRQ&ZV*C$)40*( zc{W<2HsB`de@#k?oU7MxGN$8;j(0!c0WmT46Ev^02T*QJE1^krS!J<-}U@?(TRr^6=EF#|@_{30F)?YAe<`NXw7df8A z6}ZUXK%#)BkVH8FAOKIu^8?}Auacn$6RaO!2B-3vh0~Ch7$irPHaTp(Huo2k63}hZ zuya7re$CYZoL32VmrBIxo~^LCmXL6_x;2$$R+i1`-$e*uVC@90aY`-BCAzRD6xxz@FQHt>1e5Xws9-QnbKxeQ8EF zdRLxNN&o9QmDcoKQ8{2VCAR~lNY21@29h3B*##!BapgEH*nWC@iH%wMx3t7WjK|IS zPR64i09nIyp?X69FTV=dPQZJAIA+bnbsLh=8*-xVFJFTZ*!|+L!WOMUP8k9>N&0ty zn2w7($`e3h$_Q}xEBKQ5RLCNzX9Sah3^lAhCO{gBgRcB_+2f78No%Eqqcf_x_@8d? zKDiJ>w@0StV@NckN4ns$8y3;OO5rk2qTISD6F5ZRCK3?*05$&l9FXHKhD5Hkz_447 zF?OpzyxDq^F!z<6nc2r<0I(iZGD`9Zv^YAJs2=nba4+7}iV$&zWCrMt&6_*a->Mi% zg1Flk0kH&r?e))ZFDgp_G$R8>_) zev@OK1xP&-OH)vX_u;{>ma=f>#n^>iT+rwi&&!$ztp@)19`PC46{EAb^`9a%wxjuzZ$6JnrHy{-oYu zywoEc_1-7%urM5_wsbJNlAP*5vsjnFl#ZDa2deq;<43K&z)xQbEY)pUc7%b@RbUA@ zGcz;jaI23VaXk@#MljG(r*8qntWfZ4xJ^mGvakC!FMATNzqP@=4csMBwCTL5F6HNC zBwt$6B|hx{jrVlaO1Sd(0ebT682WSz6bz16Uj6rQQmzR#~Rfm zBWm(HMmrb4qZ_Z^bCemN!bUpB^$rj)1gR?<8{cL519gvY$q|F4U#Xu!$Mz}QM9z_b zbGHmT^7y~Z&==bD#XW&9+#y%DA#0?hDh81Kqjd_vfhon;n|E_E*jU3(cK{_lC>;}_ z9yaZu5`qe*uLFD)1NRvi=S%YmhTz`~MOH2AL)5iTM#U?Q8|mMKyJdUrR1>FNotMij zG3OrNJBVec(yz0Hl~{cLRh*?TkEx`zv1I^j5CtL6bEG#H=71?B_i_sGBmMkPg&Kh< zHGS|FR(~XG#4Jx|inD`*pRy82@jy@H+mr!S(;&%+XJj-0>0lpFp&kHvj&G?3TMbfD zQUEk2#oM&2Jy|)|6Z4;!q#vLLcs>D)2OPJAGtPS`7)esoU-F=&No;?~inS}Tf`11o zyw8*YEn3Lw%RDl?{pwyNp$!)XF;gs6|PNvo%@CvY|Jw?_UbTsc}lY`3QxY7 z2?I5s?Cn`}bTqqe4Z`%VPivms`{7cNZ)Ou3>d6~hUIjEoN=qeUsc@j@_(RNWY_e8! zfNM^dZ6KqW^Ni6YXcmcSI%KJ#^!pp0k72H{_&3Dxo4mu^A3+0Y>3($^036FBbva;c z$>35-jGveX4Bo@3j07!9_Yl;I2RNMK>Z+ zOqCCaI!Ef7E0R`b#V5xkYE&ANS0 z2i7w-Aya2kVDNr%(tfu-sfFKm4^r@?c#CMRAO2_cOn-h!!hEiti%ns7S$DDh@8!P6 zY1j1ikr$)hTB9IcO`PnB_<0a&s2`Hu{tvYPdN&Uj94c{gYxbZ8nRE^i%Q7;e_gpKF z)B;9R(W2)g~@+nG&A2Pa$DdwpY>B)!_+u8;zdd zYh$hqtq~60<2g(UcfbB|UcxB$Q?@UWd#I-|eDItY^C2g4&_bIV08e^6mXwKY3gD67 zlxr1-sa}7m+f{}2arIm|9~h3 zTtUemS`bq6pA767oo&tO#f=&Ap@Yy5!;xp`p__TYad&Se^dy zykvEK@{xdTerx-*-c_j5`UswM$1iJx+V|s=(yT}mpEF`1H(?2e2mTcHs=gp2MK9b!F4|1%xwZ-MDx zadk2WU@(NkZNo4WXpkSx8L-k@I6UG$#AYqpA=eSq8l&tfSOE+!JWA0Zzbmj!V~4y5 z_6Khg(xz6rteo6(gVW{+^V3s=X2bv~O}s{s%6;l}gU7uSRXv?+b@v);z4li6;G=U# z6pT&PxdsZAi?wFqdRzEYmEC%RkhP&n^2`fH-M%OgtsGvK-I*nFG1f9TW3YYtPizb> z$^bF&sC=@~{}}Z3G)YCpd%X5-t*yt_!L)2l(ha?7UP39SrP784k(kzh9x01OYSwK; zl017$Rlp!W5y&gG^WXqz7D~n-2CZd~?;<fUw(FhHbtn{NWu@8seI-$ge$N+UXs>O%=7pm7+fQUbzBe9r8MMae~LZ3WYFMdH%(Wq0xm`z3xWap-=Eeq`KhtUQR0#MzTFXuSMcmvHIk7EVIX%8#Fl zZWq3I*PP!JMo*A0o>NA84`EUl8U&(53>D=-{&zBz89{@3{BxkvKiw-_WWVI$p`lby zs6`vso51+&y-S;&H(aS$xL6t|B`1sJ3ZB?Npa)ic;8#~ z`gx^nNzb|F8cr+;KYdr@(M+DFmtHxK^#pmKn-*FtKKy$N76G44&A#x+pL1 zhL(D+wwy^!MCvUHtTkv#k37E^)9|-t5!bgz-?)sHYx1{#g&S-A7a8;6ePq8Apq2Dq zi+@=Ae-|N_5z$%148D=$18vnWNXGilpFcPG9ja0h2jwn4|Dag*BzsR{v(ojBWj6Hw zEL-HCkGj1H()qczqJ2Y7J5w~yiAGl)2aB$R1BA%AJ0TU2x4S)~4s5KH&o(VXx3;Oc z#wEW0etz*S4w~EA-mrfeFy1JxwMY)Hn^&^;O7ESV@!HR&?BLjK_+0SG%`tLs>E>l~ ze}jNn)vu$}G;Hi2_5~n;q&55Q?VU9F{?iDJ^gaY5I6@Nvq$cf9QPo>ED=YWgn?a{P z3T^LyA7=$)C!fwq4tZV`J#7`&P+nSE+VsK(=pjOLVPqbjaxn8u+J=e-4#pZ$+g}6J ze%Ka29UYm#mv>Z`OIyB0Zm%xZQv$gl>VbD{=krXU_jee>PhGdWrIrI$L4LpkK)@YZ z_~k>BLF7O(;kH)mjq30mjKTVu+56-6mkMcWadG%kdq~KAwxJh$&j#{}iZbXg?%i|$ z6<5ed<>5*Y$Ft*cxk)jB2UM&^@`%P%0I%{?9thx~(5LI6golR%VRWJ-7?{|Jp9BL4 zA{!Gz{3nkFal&r^plUkUc454Y6QnVyf02`u&-^fv^Pk8NTmu$PfWN<#J(XF312^l6rkA}s+-JpMZKsr7jcYsvo9hrD6Bi*d9T_Y_-__DiW4$cg#ctTQ z)kGkD$!EQ0La2Pt2S8UXEw}EoX*2z%CH*&n0A}S2J-`GqSd?+x9^+Xephqb!E!|r0 zdgLHcu^4JD1aFVB_BijuyUX!%XEiC=k<0YD_{w$ze^I$snV{50!;BfS$GEk};o_QU zB~~w;iij3KWc0h#Q_TE;kZ|BWGOO8l3L!hxQ3n+R)GUX=?VYsx6EK$V&(|n|%HJy) z8Xwfr_w}-0?cA&DDgNNPTrMW)teLsV(*k6a z&M~w6Ml``P?Eh|G76{f1@_1zGZjZ{Q95n+28VWH&1a^Lf!7a|eW8c|q=6ox^U#Y0* zS$Zqi-*$nuTw{AmsWDCX#cAMUFNnP^g!?S2^csl6fp&NnC=-sl{k;R8FiW@&VAx^r ze*z3^!8GdlM}p!?eOt)Y*k3!*WiE3!d7i?H$B<&6!dF3YwEL$q&NTr>{@1thJuc6P7*n0Y__?~qpjAyx>R zq}*O8Wa;dV z-$@j8C_?HI=bTS!ILzO>sAwmMfDYsrAaA0zmjKAfMtI!(Ov8ysASIi#m%Vc3=NwwY z?uY92;*kCTdi4ivsXCmQFEneJ1@NH)ES=1ScF}?KfTRFwnxKRk%oBokYp@aS(>nk| z^uVkx;J1;$KYl9xE{gK#*~!VyU`F75D1R$Eo1U-SORVD6iK}E4ORpI>lux2@y-fz= zP!Aza9v+#uUf@~+(3rly{tduK62*;fVRaQK#q$!(82of#ZiNo~Z87L%BzwEN&Yp7p zHv=0eu+lqz<<=`IR&`r4FQZS=bn!#i96qraEVo|RXqfbcT`VvC50c<#{YdOzEr9>v z2|PoWlk$?ts34gKxAfTz^6X0arT!=MT0P(k0)Mi6vsXl&YZl>qNFfa3$u zf`34J|E?ow{v(EA0*x`F%jYuFPLJx=o37z)u{P?N@TnDrP2du-)K%cCR@0+en_Ikg zgMn0VnxXl_{W9mdYmb+T(w-4f<;jqWB+CDNGw7;k<|ViJ z2aTr=Ea6rB>{Jv^$*|vk#qxuDLp{#D(*|;0*E``6bL1`a4;{a)+D|cht3mFyet_)6 ztYZ8Z6}iy?xwnRIYt6U6yrcocbKIAHVfqz+8T!mTH*MEhFK)_Zr#R1UIHRx!zY0qN z{S0a)v5Y54$@UEFC6o?eB2rmdYwBk=-5v#CKpb8)x3(Gsu}%=$y$1wD5DA@vAv_t) z=P_)hXDP><$r+H+PP0iGIZvnF@g(aD27d47sJ z`{Qk_V!~1+J7#pq5JK-#@c^@`M)5UolmALP=1E9A78Umr5gKmbsF?>My*>eWyGLWz zsI%>INuzf=5uS6@jC%PmZZ{arbnrWQs@4`K@y)OKq?(|cXu~lolb)(+v)sWF}>xu zE+?Zum}-6EDXX_&){S}%N8PGNbE&hHq6c3#@nr3&z+zDa`jY-`TUWRd6m1P^^}_C*gn>kmg8d&|eXr`YXj zxbwKFW$wj#vov6iMHEKYQD=gXbENb^^L%t_|{}lZFd(bCr6j}_X(EaN_=`qqA4boIB^@kf$qy)?g#3gj znSBX{L#lR)QSy?fdMIL#_zAXpn{YYIy9`!7MaAgh9+oz$@PW?MOHl>M2*Scr(A`w} za?sx-8}Vx3btR=y;;zs*z8Tpu5vPOSs408^elJbLIb8iD?KIdebp3Rpvvn^a*90n< zXm!Rlyw>kp_d!uYz+(7&ziF?Yn@V0VjB!ujUPawKW8@Q*k}1efSQOA?viSFLz@K}8 z>?Z=qzyXsEfOXdZrgXgBUDmg7bbG%wnNKj4{El2VTP*qGZc%|nja;Wc$_a-zt*GK? z$#qXNC1NQNuVg5XymL${lN#9Z&r6R)Beg0O7Z)cYm&J#hl?AGVdR8hOI|p)l&Lc1D zQw}yb@E0p;#Cp0cQ(59Yjz=kTs8W|b>SoVvI|AVfjS+%o`>y&x4DotCKmaj{2r!Xtw9;%Q@2Kqs}q*QddE=1|on!`Pa4<9bYvKpaZ$W5r~s zjQn}oX@6NkFopyph!GOZiQyZ%55dTRBz^Z21s){>X9~URr%ly;NQK8KAmv!gOqx;pP zm7qVqrTuj+tlU)bMqu&_tR>zCJ!*Kp$v3REEqI^!?(n`$p((9y#3XyRtoU<|dFDUGsmSpL;=5WA+Bw^;fJBZ0$OfdMsbsqsl!EYbUt`QrNAh1V0l*RTJ%BGfOD#o1YUx4KnSz%P-RpfUiOdmu!N zCf94g>E`>O=swC@)9#cqe+7-PjWB^)$a&5rY8CFr(Jpy0XsBE0jN%(X0Qd=f>52Yj z2nsQ$2C}vF-e*8oP2^kzNTzwO>nm_ojt&n)9XYqFH!v}bAg_`v-856_{<)Qj@u04q z?bslsjWB}0St27N5v6mDQGs71+kE^e8yDYH_3g$l^=;M`KJh(Hf{OS5S&wiNg|abC z`mp#PQb764Sb#yQM*-208*HaKs;CKcGOe4g?(+@&n$|oGMbSn{|x| zb2AV~gw8u(h(2l&~)cpUGBr`_C;r|jN|jagZbS@s|9bMr+`Yvtjlxq1w4 zPM|D4oi)I!R#0e&1r@lYq!<6vo0&Lc*FzLcJWnmcq|AsKOqs@QH% zaDhs%{rn$Ah`Z!Oa})4vCif*j7lE?N4v;tlCl%!3GPab8PR4)k7|%au@%{h-A&ICZ z?IeMn;lEp>EXiP^m1c1F=HJv#K446n-wDBRb!8=qyFplkmve;2iF>^%z86Qbrtpe> zacQ-^v%Ryq!$I^Aqk1U-&Ft(*?a3HODyH)iM#t$=Z+tM85q=M5{Av*ugT5>fC2DB`hEeB z>19m-p#m&gc2E4TPC<-}2a;N8lh9{gBtl3xj|7J{AJMmYLh~^PVRe1nNCfRy747T} zK~ZX}@fKG1SUBc;kl45<6UAw zqoo?jYDQH8?P?tMv%_?D^wbK;R(K?h4CP+07dW$dn1)M4G-Vtke?fV(`Z$wcKD;9r zRm-mw~#2=fEJcvrUS#$6fl2kruP+nuQ+dTnXEj}E46T~&P)w6p)4MW(exf8iB`}hDJMX|HWQUa)ib!FAc9F2#@j6Jo z5mwWF^ZtVe$$!>`lz$-pqTXU`t#K{oHu`Pg=Z%6!1jOK4%v}v{p6ep)1Bm@Jg%QTM zmo{*RNWMuC!)=$2L<1hRy6)+F506@dcKP{)LJM7wR)e-PaFqc13D7Pq9q#QuMaFs< zg9Puv=5qzfAA@4nG?-doLW%@?{}+X+FoJ+nXTFnzuI?YBnJzgBj=iSUulqN4L74*t z9BZqq-0DsG`|ohB82xHlCtO*MQ#jhhsQUeNVj&J|zr{KTxyue$?EG8jYE{oCqNLPE zCf7#`#(|;`{-hkP^;M|9Sr!OoG>~Mg!O%+<)UyX{+J45mOf~&4bRFFZ6wf{YCiO!P z;8PV7yy^a3_-0Y{@C8Yw>zsne65WN-+EGI^c@nqtad0nj#cqI zk9Rw~wuSu@#RGEGR@X8O!gPUz^lDFB#2A1rR@iFe!2OFxiatiT5YVDo zPQCzx$P8wtV}{lR?+;?7mx@D8jf`lfD9cR#GnDBX0OC?Y@{H2&Hr9EAWRy-O@R3u! zDgI4?v*4Mg!A3#-nPjDE%VbH@RC>N;y7mY6u&=zZo(nB;kB0uRtN9|~Gvx{8!Fi@` zBQvuOEEvg!jo97HCx)h`_en{iV1AyvyL*XF^=^WJXDt|Xs)pWU`}rv-rbX#uIQiZ@ z6?haGY@pd-ZbW(OP~Ovemo^3lqL-ammhqhBJw^^pU%V0{TQ)S)U{g||Dz}C0E3J8P zXIpz`{H*tpNj95*WbXB0#pzv^Sw&@~Y%3fD4Uh{vlDw-p$^bHOA2Wz;933Hm%?^4X z?Fcz9D+HAP;vzrLvqrfY9~=uZ>9~%}e*!>BKk!4y{`XP^#5yVJA+^i{Rc{`316Bj{ z_UAAY^{imBYBG_t$^mZswJBZ;hMthH5?TX;0KY5Hx8XK2U>%RWN|#K*HiX-c{C&h} z*bL$rf|-tnY2iH-C38aX*OPy}Tc!RtZHw#z&}U8Lo9hwu3Tct@ue0;!$a4oVo?hIQ zd4-Nt*h<=;WuX~K?AXryNK*CchNkg??T0ad{6R}g`9 z!%2KY!&NW|OFV}`xUpq46(71*2YBi@m!Oi|zt#r=@IeL!hoT%e&yD4XOic|h6*q>C zjg75TFH8yFk1Xh08>pqG%y&mGw8W1CCj(lRltr5_JV00}8xbA6H1hmB~ci zKgZ1;0`tI^!PQ699st)OX=GnWs|~c|<|P{*?h=b~|NCTOd|wbF8rNWL-?{x-jD=yr zRw#glfI{h0f-rC6{fV(3*lTh*Vy_+Zn6BS%SCyFcYLnS(&dRi}YoaZ+hf&P`nf7?u zklot!$=4TT)NhQMq^UoqdoyDK|1WwrV-Ey(!PGbX+fh+mImOJ#Hiha~e{&f0yF1x( zCc`=n9u%I}%^X^v%K~|{?_VJ`!T+9qEO~^Qp@p7OIPjtA-ci`p*1fkmYINqrJxKw0G{WUwmf{Zd9G$n^hd0Z?hBPVAT-z5bqk3VKu-85t@uIU{I|pkkt^g{1l(%&F=Dc&Ik|pYqdGf;NWc z=H{U6{W9^FTA;p-Q{OfG%Yn=B4+YbXCH*pJc$r&@zidY9rS1uxNyHF0Ww$P=*vEW$ zg3;A$au1n*Zko3P@bd|~l`EZyH+`54>V`Yp+gd&y^2IvvB#Nf%s;$E8iXUeCotXW{ zj;s{=%+9PXq<7v4eEoFOqE{)Ikt2jcq+;yiTU%)(S{TR88X+>YDsT^m8XL(e5SyGq zP~&>U&~njI%A{2x6V-0!Y?etD-gkJ_X!`1W>-y)CfT;k0kR z;F9y*9LSM!Ac(b&j8!sjFX7@QhDxgmXZUs8n=QIKZfW!;Ma(SfFeYqm-5rFpt1 z7mqVQ3%#>hR;vlrf3W3DDL;% zpK-%{JKy*@@7_CHF$S-bme?UKh}!CfBfQVu^z5`qG*0KDC9)K8w_}&`>K|1jHkH*I z-3`nE+TGlQf{7O;+LfE2)sbCjWM|Sulx!oh)46G|YFz`e?xdoUCK8>0GjGEYgx5M* zFn5&5UUm=+eY-dE<2?y=ZfPzx=Fp6!qC+8qAiqw00>I#_-W>(VT?v>t`bg2|lV z34nHzpzq9bla%12+SEB8{_O%3Dp3FvI-B>RA|jG`L`2{co5B`akyLKwDx+bVxw2kt zre!ehXZhc0$^@q=y1tN{7_mciM3P$bK+|?Om-J23%gVYdIe+8zD36U@2_P}R%pA~|&i~Mb2CU3yYzzw+5SKClvBKtPfvd;> zaURzZ4Eim&3Oq@ z0y0(rt_j4n?i-9xWE|f8BlgULe-?fgHvIs?FZ1!S=BhEN43D~Mt?GJ6NdS6nd{c6t zHb~(Wqk-JSE@%T$yBe5@Xd-eX7h3PVhw{@MVa-i&s6uYle3n82WAeodH@(r814`vrfBPE!YAE7e-Lsx@+(%HofA}22X{%_P1j1uQH7QYlE#Bg%~@netU&%nM2vdMb^}7PY-0qx|CAsL( z5D9?Q(?*e>_S&f&uNXdcHPH%@6Xxg@llI-gduEoGhBoZ3b#H6yWqZKC%k!`r9ID@t zs|k0+Yy4(M^pbpHR!El}k8vQ&JZpSZ?BYX~nHq`)@lEhR2_EH`jv?!%0qV+>Mx68y#Q~&TZM(n(M9rc5Z z^cOkn-sJPS!c&da)|9vJXu2D_{@yS9Wi`(#u8}T6?{~6d=h->xzX-w)ghNESo5_&g zRTwkXEJ%5~U`@>cXO2H6r#L^VKL37ui7NK_$$+row%IVF@Pn>c3tManSL@!#Nrem2 zg~H|B=6^CJ1=hR?tWT^T)-hIMkTX4s^j+@nisLZfHmL7rn!}hsb8zfTWMrCck+4s z|BRO+h%r9vDdPr$pS-y+sZnDR5;~SJe>?O#K;O@{a@k&)m9MhDw4pfoz%sfT?cr{( zE}f+qf1R%Ex|;h_unY>R^}4vYIN6>gGP(jrHE!CQ1z~&C=)GZ|KxAe;Rlz)T3Pu3; zZ*Ol0Yuhy(U{0WiQI1u8{kWz#(Fm!AG|j!+JOjt||Gu><$l_^CgV;Srh5GI~tz}8;3P=TfS8um5m2N_R7reAlJw(?H3W_ zt0SHXL1ADA837W|*jJkduCf+?2GphaY>AdB;SF?Y>{>AfDO?~jr(mlBiRF!@ZLYcd z{A;wC7b9KaPhzmT8wgTlnhiQ)1 zyuK+Z8VF(%o<`aAV8PrX%Yg?}ZbWJfM1<=gQ2>Hrxm5@e*TZSsA}$R2-Py3jMqg{g zPH#`oL*t}B{BI@YBNiydexB!lq}Mt9dJ*lFWDDL6&CmM3+@%!M1|NIa3h^TDz!HhP zl9F7NCrHsjlkV5F!l|MJvVEEvZ2EGm?IPHAmUR0KSQqNfO%3>jcAQ#-+m(C=Ob1Y0lbRB5OaqSL@{MXzf>!m)+8PNg^xDQv zz+vB~BjqffB_Qk}8}TsJ-;-rC%YOZC@~ywh`DdWUwY2b0aRcctJsXH-GdVcW5gCwV zAxQou+>S9;G=5D5@_b2?-JD1_?o$mkfK{fU`bvu3Rt^%RAt);fbBKznCU8E7;hzxYk{M!h9GXA&CN z$b-S<{%NI$xO9N?N>!yw876OlLQQ6TNl4Q7tOl=hqham`qcfEGv~FJ-zIJNfC@-;e zytB4pS#Z$)rK;$>4zhuf>WJDB0Nx}NieCWvth86e=7^In!};1Wd-54&RJ=)G`b+p( z$BXE;od3z>D3x9a6-OzT;f|g_fBhySVU~nT^|^2XSGZQz{n9vNqgn8)w^n zr?UR@vlz1FM&YGPw(H0Y&IZM5UArd;}1_eL0r?lyBlGb6(|LEzT55c zIYzKN>!0Gc^AHL-#!6%$>j`UaYj4l3N={yv6A$ez7ahM-I%;DYeG-yD&u? zL|IUQ(rn8whDQ1K8`b03Dpg%@#&W@qG>q1lc&ZU7Z*TZimJY5>cU$^NS z3vt~z2No*l4p>v#u728T_2XJ!i>$W@{?wHlYVp$J_}v>d-XVZP5@qnNFa5ls2p*}0 zMQil1d%RMvTm^qH5D~`y&7xIS0NTKyW~`=`WX&(KOj_b`2?NrTsDx}LqL0324D)JN zi@CWP^B&z$Kbm{1Ibki^`f`V@p8V@@+eXTUk;voT?T9QL3A+{P)8m7r6WFGxyxR>c z`JJcNm#zw0YK`}C9$K)HR)M)=-={^3pB?Sm6j4XBvpS+qs5`44+kd6|ox9T6erS`L zmDv6h6QjFh>GAP!5Rv=?kFi&wN_y5lAF#)vN_kZPGirhkPYVyeQXtog3X^BSky&^0 z@>nI=SJa3tCHP~+UC$uO47%NtMHF#!C$5#1amc~Ezv1DPXbr2?Op0z9X5&S5vHITA zXG5wb#ffzjiwn4KB>p77&n^hL?c&os_VDlkg~exEft6{NUMo3m5!c2>bGx$I$2()T zRsth7cG44ew&^*K4tq+H;yR>;5}KII9v>YfDmFm>7E7z)lQ5N^%HlP3(D8(_-Bcozw8#Ou7DSW<{KHk!!^5%&h;P z@Cx>XqRh-V(}DH9tlJG7XqFO)y6#ElO?-*>PE?8-7W}BC@pg^#$VtDZW5uja2@t+Kj*6+_H|V&@DP9#HspMMl8TB z5Vn71X7@A(NT0dl6nV?67F#Hxl=}RtKNl+_k7o9lcNqa|LUO@}cE^DV?mYqX!wyvZ zsN-lyRZbL(ah43G=GbU)mRxNW=oDYxCcJz(Au;jNm_aO(BI=!OJ9trmQW=mLI)>&~ zc#+Pk4$fHv(Y@KlZ_(fJ4)M*X!+4cXa^GH=Va92VkWuf|0W$u9ssSeQK*(j4%8k(T zNqH2Ye*j1#hVsLQFu@w)Jo-gX;{8jGq^~A7p6;4gwig6T3k#KQ6Y|4c=W6w14^QU*gQOtr= zY7rGazG8~-L~{X#t+f#|w?bU=TVdq_Y;#|(eX~Zjc1?S^U9%s?-+TJ~3VW@B`rG>0 z&(#+H+pLd(UVXW$g!~#31qd!sv%DVrlIi8o_G+^HM`mZi#Eo@P_uD49Y7kIO*UIf? zs>>&JVvO{oZ z;ymNpTwe(N>#&K=-}0_(#?6NPeVr)s?nZ*k4kvm0&#fXPF6bR4(5m)Qvi*HHMX@1( zA!HBsZBN+4?PSPM}oCOt#tn&BDj9)>4nb9k5KsrG#mSJEi)ZPRzjJ@!TYWG{mo}# zck|z!VtiLsYCtdv4xoO&N{2-gt*w)nbKm&usY0)(y>e30+<` zZFUC>CF3CC0`Xdr{H6mzK){9?_jS04a@Jxc_mY?} z)-6&##?;9{qt`!~*%eSG<5Lo;F)YkQ#o~;ywqYWu{dlhPgxjt4llS3>iJ`qAJeIN#1{@SvvOK%dH(LZy9%&|^I8vs{NaI5Tk0}n5{sENF6U_}3GXK~HX`fu%=@yu$ z2Q7lDYlcDvkK><3&EDABRy3Z*3bx^YPPrlaP3Yau;Vl>#an1<(bkE&6*o#v$PDkYL zqlNp6(NjgsOL~6$(@*+_9gh~Al41fy;3d~p%+IF@a4Fs|HMWlJ^g)eC`)kLYY7#2#*f>T-Rz^cV zS@XB-#%a4`yYX9y|Hl0oAO((_Dip< zP2y}U#u>ka7E9^5{P2}cA+$E#YT7Q{xmjjf$-XlkFYXP4t5A;vIkVNWCKUOFi#PGF zT?=pxfZi>&f|JuO*dJYVWTPZSvvrl>wV$LAmzbPbnN}pdn75_jlfp%9^_S=*#)Mog z)Aw!3k-tWg(Gy$9wlw;Rlce*rg|dgEIuUda562hgqSDrHuBR-`w^!b*I&siV`g%=| zq%8&%2h&wE072&nHX8i729!Iov*&PhDYXDTH)NIrf;0vk0NGMk|?Aub0gzY_YW9?61E{DIxc#=Kys{cn~l73zF@ zzCj))$LcQio4%aSVXa|S{q6vWB;=eZh7?pv+}H3(tL!!VI4~? z-8-DGR(~^ST~i25WRJrekv_3*8PcDgopqf1{`L6QG`Vb*Qg5eAt zvxSKYAt@=OK)CyI@gH~)?nyasbD50O?@zD9p7u`?EUDI;L-^D{_})xzQXFev-R5-m zvNxtL-eOnP*H|5q5pW5P--kmA*3H+KpoOnC8p(>BjjM8JHs&5g-M&OWs`S?6~N}8Ka+GwVNGi>hj@Axo+sfYxLI^VQUu* z?r2@RuzKEpY`uNwq&Tltojv{c9<$clFDIjp<1~4qkoMmvv!z3});DZe-O&nXZtUL| zsYIeTTCS>9yh!BMtVX+<{g-eeW{zY=LszDqkZ1K52TBGeeBssARj_h@D2!cYiU|Y)F7vm1LQda%} zGWs?!LHhn$)!p;0PyfF=>z<6*)X8z7q4n58$xh2#6#2hLLgfm!mQMOUzs=imr>u)> zcwVNiyv3B|{Y4}>Q?eES=G>~ACZ~DqdBPf@GKQv~Gc(ZBuhu)pOUmo{MRQ(|74HdR zBeiIDz5>-@||nQ*wLDtn>Ei&B00yZLWDcL@?3(VE4Sy9QW_tYj8v?7XlQ7#7H{6XN!B!oG6W4 z3mez1i)F>{AM#vca`sXd0s$?(T6cQM5y(V)D$mSxNr}fooQ%KL`FpyRn_IQMu>>MI z2mlfuUfm=T;zPFD=>z=Ve)c?8d=)>_8ui=B>1p~HrDxaAh=uAh8jI_nzmZmz|atF9-!k?SFWgT+}-{bX(SJNS3J~_={FSpBYxX!G4C}bj3rVQ3p=N zndv&}W6~%}sXEM649$id%?iWOq9)mS`sPq*af}~(SRbT4-0BX>6uQZM;96fsE_^<~ zj0qBr3W#a&kGZaBdE|Y2Vmvv5tw7@;3~Cf00VO1)ud4NF&3?AO0T`LRF#kV5o{31& zq*k>gl30=cZ%p`Sw2dwVJ_=- zsS3_=N5kpyPu9<^m6Hfh%MQhWQ(A2vCmFKpsj_GFC`Uo(_UUjDqL~2$1G5H}FV|!q zopdG2fVeWqF2Cdx2}U?Qe=bx>oMJoWvr5)1vR&#{!aCAZ-+cL&2=rbt`p>x+5qWFacgB*(3< zt%-FYh1c66DoV;A((1)3GG$@9ye-aK0Nu|9s5CY```eNeI}5%~aXQe~LpGBI5>Mvm zU)h*vQG{S}-ShhPx=J7wr!Zyk2XoSCijiHG zN_Dnfs0=Jr!(~W#>lag~l!Y?z`#jd-4XcP5aYGFLR-c||OaS%l+5b5vg>*I`7r)q_ zlb`OsY#OYR0f<5kUxZ`sVp?xDxXW}-|RJ9lzsYy9QWU0WB}7@cT)VSQ@q~m8aDMHes^~+ zn!=^MlBmZM;K{-yi(5=edIS-ABtet*}T0Pq=L zm+&fJHH-=7(qP=j84=!{@9e5gKX^uF()LKxJU>Y%xG&Aat(ezoyGD7j)z!etN(3Cd z0Iz_u-x43WStZK) z{XTJIMB*&xaG{0Y_p7!0;Y(jzTV*>d2Lj=02QDH6RIO_FO2}I(D^Zo3bpr|k!)0%- z@zkzfZxF%X{=*bQK7|S!eEw;Xk^85|&gBBoIN`E5hlaLk-Y!kVpion|%6zhvQ7K+( z@Re)Jb?~%9 zU7I{idz>!|Q*;j^G`R&?f{YqDqa)XZt($=2fz1Ea};yH-bMX3Uj6~h0A2tAdj4nIyxlSMIO8+VIM29 zjB*k%mJ}9$q|^Bn^#T7@!n53rzeie8OcMZsp=)mPr;ouy0C(I{13=fgKSi!!eWp0J z&vkKm(*L$O2mLn8=kw5U3vzF`EF{Ttu4A{ zMeRl+Z^4HF;SZS^{NjH9eoIpbU9NPr))kYS7N0FD5jQ7bg#*ptFiV4riwl4MVegIe0gr~M@U(0R1(2)W~FCGzNV`J0!O6dU~&#JX42p<5vM{c_z$@-tr5%^%D`KDpw zqJSXM3-(0Q$A@*s#Kbfv2$+0)HyN*rZY*4DUYBJ>nk^iqKNaoh>-?PAGGk}}6iI=H zyF6U%3S!hHa}O(0S|suia-mllzUhz8&A_mBObr9-4j_ESm(pGnzFs9SDfUV*pH3cI zbWr1s*GYZ@{5J6HhzR|EH6sG=dOn=F)>W^sj>LGNDbk#8lUZ0@wFXLQSx2tKgi7#{=^MrCyBMeP4jwh;fwX@QGs~_kuvBgyXHE6 zIC}q5%&H~GD6S*2{1#i~^}P2fJ0+2~Ed!d4!8Z(mos^viPmwl{7z;AraYvle_s%*@ zV@XMwL1RUBXCa=-$>wax(PMrB+dXLPJ!1Bo690R{#*6(QmLwx0R1#skceR`-JE^Hr z@x#&1ew<0$v5WeW)~A#Ot{0_YxF>HaP{Ltwte`#zNkxzp>#`%cefs*8AXHrptrj{y zKBpKL#`k4xvF_KhZ^Ag;of}>p%gf!-7yiF7jU- zYLMX)eL%yybcv_$?3eKbl7aQ!>TG*dQn8Tx*3h@eHIzZ;M3^?^=-5}Gy|p|H3-vgm zXY)E#mA?{n63{Mh!=P*#zy{zRNXX+XiC2S*G{qR`J*}SrPzE*(V#*93Vu*AOu#E9--6jSlWfU z#zt2sCnr}|Sxa5=U?=n^-x><6KQ9E0Fm8rwY>3A84R`O~Tg7IRu1`nX|Gn^e^rv(W z;1!B`RQB6(gEqT4t>9A~bNMt%IA~4Glj-jQk!8tkast#+1iNPTxxa zbFnQQiKqDa^)JE@FE)y`<``M_Y(@k5M0f8`0O^z0dMwc2|7}ky-_>>Ors2|2owY%^ zEZm{(Yocayyb4)7z3ziKI-4TTlMdXU6OB2gg&jt;VZ_q5Ow5 zI3U^agz+aT-Vc^ivie7&vD*&yvns(dQXHQ z76x2DQCdh%(w9q3r{3$^G|^S6?IiG~1*B1?1{Wz<1hRqX9jnUh5y9Uoi;OJy4uT z!vM3p+Hc=NvN~xG6fB$1!JnhO47Q6C0E=?gAAvmeb-4h#gpOWwbsNV%a6~b3VB%+D zUQ>DT?KBrWy^0dWjH>sq{&X5JIlW>zQlWZ-?8_j%N<74Qt&F55hUvO%LaoTcQvcDQ zvo!LD&R<^J*+JYa*TjB+_4jAtSFkbT+aAydLUs}Qbovjm@AS((hk!~pN^jtITCu0V z+$Of~3*W89jcaQoFA2o*OyC}T`z7xEdxLQ9wN&KY7sP9to9DshlMLufA6YfQ&oLB| z1iToIL&o(>6)-V59gC@P2rO@yA)U6G2H^j{kEZ(>`LTIlgW}U4^R9tZZJkI|c?;^` z#mP+}dW?%Ko=qZ5iupvrUJ=etusD?H(%^&Te&{6h*5`!~=re77Kld$;i0<=&`tjF;2&)S!V`c^ zwXbX0PN8z-cYw)#wGUE$y<&5Tcwl6vRtNeZxd9;9tR=KGoaUbKW60Uj z_BgVyD>>QyiFa@8-BW-}<3vyeG0(`({j}^!4cWeb+K`3UfJeNH4+9fuBq0*Wk|~m) zLDycY2u3sxpFSZO5t2E&K&?lm7tbWS4q}m$i#gTcR!!>?`R<*v$I)~I@+nj#&WP5< zA#80h#4f&$_&Gx2r*2?kU>cw-go}&Y^qoZDV6Kbq!AFaW{clizCt%`{uAX{Spdj*{ zIOHL1V-CHaybv|h%bTXeYdJ&|f>MbegS$fN9D+!q%-e2znd7DYbc60TF#k@`b^`2) z=FeaS1c~lcNFwk9Uw`rAS-HH@nn0ugu%Zn*FvdMSJ+D!{5&Yi=?m|BBknFdh(+?a5 zA2=pDnnNeMq^y2SvE~e7m%Tige-qM}C#4ClPDD8aCC}*kaP;U7fj^af8EJ3Ql9&z` zv10!Qmj?ajcl0prL+osw(JKtieK$N32PBs%J)nI^!^=xT-(zEa4u|qyTSrF&`klyw zv(qc}r#*|)a0>CpcKx}p@Tr)>*)Hq}nj;tkO4QDpNpt9O2&G6A%fbR_cwi|H7g@T( z^&fJ;NY2g5Y6(ORu+Qlh!O#~2{_&8-pTbHB>z3J%U%l!&^{jF%yW+tuLf~a7-L=6a z_)1wQH#ZkldjM;23V|`40hF2=vHlJ}KmViPFYZ#9X5cRz8hV9+BM#r6oPuJVC=7&6 zp(>Uthf%B3XN6@-bwzTJv1k)y5p~^Q8d)YKBHh|eJxDk^?To&_aBl!$KLQQr3mhqM6J1JYD$Yit z?xooBS}o1bYb3DlTenmMB&O~%#K(Cm24LRXpML3ds}bM0{nEvYJ(-iIt%U}9 zP(ymDeBi$Xqw{JEO?1bKJeM~(E1~-#n=l&aErQuELhz3s_E$BE=mj3hbJH3U3vYZ{kyPZfDHcsha=j`6tA zoX4nzNad%vp~uodE@Ug2J7eIydCP4=V?|%PwBdTeh zw{-z!oxnaxWqV_D6;1grW4&CpzrWsg(?U?#GDLz4I1^FlXb&uUa9;GlMX<&5NOFUY zj&A+UgYt85<{Bz7f6Z9rWajicrMAjcf-5Jc9l>M>+Q{@30q>NmFS;|KV94-0t#Q9h z?7x9QMX_`^C$d{Dwoi~RZY~2vA2$?gUDtJ)JV@q~X9)%y@XYk<^?1$+d1-q$6<2Vv<=+G?)s_$S;7s=RANfi*#NvNg447!!nyzR~uqz}L?flJKI z1FqK+3;!2F0RE-7uG3OiK^!b7xYd5|8FwC(vX(;yX+L941r+yiMyRAvlq^__KgZSp z(e&AVwvc4d7zB<)M9o?LEts`ge>cZfLQ1jBA0s;y%0-3M^%5}8U- z7j`|Gnxt5P9@B zQ(U?4k@o?a7g1KL{+0J>8hf@DJN_34*!+JBtF&BGJ)%29CG$-GSB!A)d?`0Zp!k`j z8$sm!FJk+sli36D<~Irj6^VX`3t^pHQo_jqnV1pp!~|Sgt0vvT++6Vh;V~v6Z1ky+ zTLI~UMh@bp7n+a5&r^$vQu(^9DWU~z10NNq*R_D`C(Rj_RFqdJq<##PZ#=KPb0{2M zgP|>WZ7hYK)uL7Ru;0(oYK+;OYb>S{BF85rOytKv+h@@CCVLaf)9@Q@{5?czN+P1` zhKo3&Pi#)ECVu-M<-bbSD)uMIM&7cHQE_`Cmd~|W`?R|t_6Fle&>^2=dwpa1_=?Dl z+WPyYd##Q;8ld!U;Xk#0e$*pm;I%Gotb`7xhJQi2K3a*gAn=qn*gke&=>lExis35jWZfzk6eWHUmps z7@Jqb1cYuhNxhXCr(m-5d$*%N*e4@&1U{7_?uR8-&pdqGIqH2gGMHcNg*tqCNft61 zJhcDF=Lkw6$Da+CMv`j$@z+Vz60q?<+AsFmgYBbII$U^tYlb6lASb+YwylOnF5#e& z$|If{?{~Y!GyIau09U^uyU?Gww6T{{>mT;4ep+Kty?Ym{C6Y$<1mKXlivj5<$diCF zOLogt@o4*v$-ja4y9jLY1EjxZ0=Dc~#i0*yqWfk+V7 zqg0c7u#%3%$k-cA_?Gvk)hz3yOT8dj(f@WDf8D?yV=r(9#SqQM*Ok>Ys) zM#c;CN&c%iv-LPEaPn5VQ}Ct^?7K;k@B~~h=)+qmB)nF%e^c&b>(O*16R63DW_20R z032Q-3pcs-Rd5p?Df0(iFgL17sYfSJdNe$*bHcxW7-NEm)E!9KfFBH-cdC?l35HI@ zyoN{=iNl+(rQt@Adfd@i>i@dj%=oT@ezHAln3PL{Uk|#XO1|D{`wA7i_ZQF@JOZ1l z@JD23M6lbVA|hyhsboD%G{5w2FywoBr}nN+5lp~JHt@V5sFM-GA)q2E|DH+Ii6bQ7 ziBT+!A8;q@;wNQ%Y)m~``4Lm=S5>K))-#?)!U5w*CwYk`j=V-GYO?;VgE%R!!llXi-6i^Lw4Yjy`Ti%K~Xfr3r zJJpMeBM=bfo}AGm(0kbc8c*{4286tmYZh8H*^@Dqn7G$lqeg@&ox4{DLntCcqo-EB z=6r0Jc($|c9gAnnHcPw;fmM;;u_sk2wM1*Na_TH2Hh0d3dKD@Pnq*wD3wg;p*_$UcOQFeB` z(Qo77Z_eeq3+;Q(yGixxIY4g1;d7?yo<5hRP@VVi``u*qQ8OtH&RswY6q{w{Gyz~x zavAGM{`rz7w)GA%1~K1+-Px8EiO^~n(kbST|4AggzfuDJHlQv4GMI?oQ1M=~mmZ2y zOFpQ+3DhKb^Kgu#M?(djXJQ6GWB%AYC?YRD*WzLR(q`1n9qU6gqYo~Z<(wP8e-bG& zYJ90U3QV4mboQjdg%;d*tBi_iXYQyluy`|?Lybe=CNt_L?0qoM_3h`q!VIpP;%YN+ z3B8RE*rV~42c-gr3eObuCr_T(eB@fLKO}y?gKb}CvmpTzG+g$-f8G1F`t1@gQ{9O3 zHr!cGE@uyUR|j(Gsw7rMN(qB`Y3^jra)t=3!I_>Kyiw4&BR0~NsZmuOn?h*EExD#y zXzm-w4q2k4Xm@uv+y$@W&sl{8lnyVQ(lnuQ>dhbhR( z75T6T7<{&Ey3r)Qt+s=o%T#mie(Um-yi3a9^ zEWVASVL+YljBU`Ul9bS&Qlucb*v z``**ubk)=w?BvAbDj_Msnw~DPQbX-FuZiBjPplAu)EsRaDxHZS_I&2gsIEW5RYa*| zXD|3|wWg&!;Y}9$gJ)k=nUEf#kI?uLw$XR8II6^UiD*(_1m4O}b!Xp{14vONBq&fxhXL+<5d$KoSoP32}Utu6DPE$U!{( zyuz9IUk{{YIL5XZ8olPmKu6{>At5I=(P%8>`~aJ5C}qFGig%8 zMUC6SZE=3HXck=zK8G!{I8w@ua}??&MJ`H9i{>bUkE%(A+undlt4%;Lne<-*0U{ef z5!ed_fhEw~aj)!(s(Te$PIB_>TSwZiL+zy2x{61`1ZbNjfdYc5sGtd9as_~|D9PY~(yHHHw} zu<5v8U_yo+9`AYQlh|fDlo428sg1K<4dDFesxE&Ohaej$=kapYj5d?m z=iNAH!~r5{z`>U5KT9lmB&WAaPKP0 z&dTR@5vvO>E>j6U1qEt%jaAC#t-8^xeJ;`kzo#Q51fBJXQ8R z_VAFEl|9V!CuKw@_`F89QVsgxZF$U@;%4r*zTNMp`PN2W;-_b1~TID>V<97WxYG>&|44S6T!YuZAbO(!QHz}ot?8<5`_EM6N>VxXZB_Q zehR-yUagMx-ck%!ByfK5EB5pmWf0TSkTg z50ZuSU)5wK>9{-QsPgh^Y`6DV^j~I=7C|vFmalabYPR?l8*EF7{$5(>FF5>u3>{{r zlWW{XT`t^8BbtbLpAosAA|VIUj6ieg160$e5=7P}$nh7$$pIvY2C-8Wbz1 zo^27Db|qj`k(%r8r>1lc3GO!woB2kggrp;4V&15hum%X$IiW8!8~<+{2M@X2rd=+^ zopR|EYf{ly>a@d}-&LKkEaQomNKY35K)n6`@ zhG7$zJ`}UL>-0#+?qu{GfsTAj{C{)nLWY|03+4px{Sau18r$nvxSf#!wQcE}%9unHw_kD)TNAm1 zjm-w*%?EZXBm5UVo1Z6?1T6UwU>^dvHTd7^^mi;D|_tsUlew{e(vcsvF9fhZ(loUEYlvl)$w$u0Z6naK$qlJ0+v3Ek6IXDG>vjWw`2iv$mM+a$^>A3G1 z)goy(l*&*nVsT`4vY$gP%*%vLqR)08=RW-2wD!PCo}6Oo>GD&{UsvdjKT--HZXcB4 z(|McbRlrb4Cw`P?*bJj;z7C*f7|xlufRf9>NMF~@JNsd{A+y$B;RyIXK#|HVF6I?} zRYsm$vC5pkU-hm^e>{pl2!Q;8)bg4W-O`n#;=&XXY6+-1zO*DcWK;_ql#joXmw7O zH3<{YP-0*{Kp*0akC*_=>6U}w`mFVPoex8{OO_* zq@)p~#oWC*Z}GZwZs^F%SGh29@=z_SzUJC;d%jtE;AFqm_GwuYmYC2Ur`)cKV0WWa zRH+Xy16pQEp0c1yqM}*T-p+dcy5xW*?A6+zp}iv5j(-*(kh8}K)x2-(mnrfcntoC2 ze{@^RAU;2{C+P||v~NQLk>{EFwDFHB#Jr%Lq@XgbjxqHR>T(Ig^!WHQp#sG0+Cnhe zL=>57D|6ck`)p}gN)`*JEMINA7TNok70X%zm9wj(2OqdfquvYg9Eleys9fIt*xb{o zGBZPxj_YN6j7`Rz6-kL*N`dyF4-zm{kf+bvISI>w{deEam^yhd~f(hks7UYljgU19vyW5<# zj`V5UW#F4hboNux?0H=(GZ4o8vdKE;QN;wiz32FR`;K(%NQE%lv2>N3+LFWGe)hwz zX8Lm?U$%Y~SdVQpv+nKQy|Hio{CPq0XLlgG^7~3#q9}B$M%vosXyU6uZWPEvKZ=T; zI+OpG1czXo9&uTIrqexbVk*WLh&`~~Y{*iY%}J(7*VnV0ogV(w|9x#6H&UqmM;28z zn1EhRUpd&_71I6+l8|2M7=1Z9NE`hV1=^#)d3E_@D=*s1;6yuEg%WIe{-o073Vk5b z8x-E*88qJ0ARWYL(hL*4ZO3vzcK9vYj`Lalvc%A^Nt}q=Ek^F~A767ka*Br9u{W>{ z)mE(X7SsGqqv$P1TIeQ?{DJsFD2#(k=} zeYm-idmF>#g!`bMNKa;iEzW%_K_Hw>u=;0;j)RtB*qJMNt5*nT-3U@+7jAPqw2v6c z_!Ooe^|iPiD8x{9ZLc>oAN6P2pRj$r$LVZJ> z)_re!gg@W7ZQr?(lm^RP^w~JO4vO!m4i)Q@a;t-E`|*x_r{*CF%x#K+s_n3djpNHh zA2=ef5VvXuxg{sAZ4VW_p$~D~Iv~T`p=ldMNs5M{ef}J#flq(Zzhi-y(sZN~BVyMQ zZuNVFT(Vg-a?ikx?J*Or$Zlr|wW?Fqz#U;rb%_(l+0Hnk&*^U-J{|q?tFJbu8o!&} z;nIQramC(7HP(*QtGIWAPE=2{PX_boe6uS}JcK=t5ApWP(?nvgubdbSGWK?DX^_WI z6h_gn?hUl+x}Y#MG=*&bJ^*7|@B7-2?%yq{IKAifp zCI&TbS)O3d9yUB6C?GKSq3)q!9{1u5>+AN+IYs~ZpLTqNZkPt9vP^GGm}q$ zwcNs^F3oZ&q}^-}Bgzv}eNoBcy7A7NuO`Df*j5F__ADU$FmUbtV$W?N3}yBfw8`;+ zV~4&67uc)5IP}{WY?9-z96MoJoRFL^Ph)7~+P-kZ@>^|>@V#+TXL-}<(INS*+NF`P z?gciIx)r%w)?D$$@4sjylSakJHqYL~X?;gVNYA>9XJrSKDkW~XP$D>1pmX|&0fWJH zbB>1G8U7_Yst&(N=*FKstmYB=eRo!#k1Kg`r&)>OtC~sCVkV;pklyflj8!Oz#tI*J zE6ldX@gBsyy5oP-VIEs`Yw|a-&z7a7B?t_}Ug}qhCqLW9UWw#C=g{_nzsZzBpgvFI zS*sJdMiuL>``b(--&Mw4Z9H@N_H@=*Y+s298HavD$#%LkR$KP+6Jq@C*(OCW{RWC+yZ-D%f=f);hsU;#V!bs>Ly^wRblmv5yc@w+@uyzw9u+p3szw|v zC7nbn<++uK`_DurnP62~FZC%?yyzz{UwW-W#tnD(k#)V_65@s5h_X>z~R6-Z0nCVZgetq!j5H?@2VjTy9Lscahs$ z**M&*w4-y}ro%4QR`dij$VPalbhV$#*E>aOCGEAqWtv(AsyvY=Ta(%;eQ#61^rP&; zt=qTVe>dNpqC}IYJ`7rMVOgf|0&)0Mm4p>=B7dx?_`Tf0Sc%!9pEAk+>6LtjJEXMI zHRqV@iX=-Pt9u8|eKPHtl-TxixRirFo|Hk&NpFd`t z(_x4VmGj{%XnFje6>Sv#bq?ij>Bq4&ml+VAn3=lKB>bMWWmJY=wvaw=DO=lF@~+Pu zt6)ar?~3W}4_6*{ZyU;H%~mxwynJ8&L;CfUw^``4-wsKEYiE#p>j?Af-I$!OH0wDr zd6>tYX`iVlEu&_Pr$63<-XPt(w6|B+Bpe1B)l%;!Sk!zS;Q7xC<*jGh<#_0(Gyiea ziqAn`z-GToTqTlz=>6F8TiD612j=WKX1!%};@vsf*~uAIw@E*~^?ZN^mY^DDW{g&) zB_(Z9yb2~1#M`gVTxYuLW%^@m?09cBuDH-1hoUtBC19$=zR#03Wj z%b3{n*gcQ=NG_5#5?wP%IK@23hZ9k`rvHgyX}7=;Uz5V?tVlwg`G^|?1edM8|cO$lqWKnMJz1rW%OMzZAyqD3+FgMXBG8E5E|~!8O-kV_b`_I2e~I%||gTj&7>P0{@u}6&>1K>$9D5^);Oz1dd|~ z3lp!aopZF6S+t3JEAP*$=BWPeI4|sGnPGiR@FYp}&~`L)BIufZgvY_g4h3O;(@5Bb zH`T+>wl|6$=3KsIYSLftAFZO#$Ix^8>`*PuBT3Vqw@1In;X*?LSLKoz_{%^;wV=4CWQo07oA%6ER zHzrmV2d$LsH;bze4{{I5M)$2UM7~VF!FlJMBU`NhjD~iN<6<`MEf!j@74-O6n7ot! zKto#*7RKb_v?EZl0AA~Yp2eBpm(hl{Y3>GXI%|oxRL~d$wi2VX{LBaq-lRM;~hGh;Hwm zGn}kU_iVNQf9U!Ope(nzPe2r2I#dwp6ltWpkp_`Y3F&T-5&;3}?vhUF29XBo?(S}0 zY7buT{dRYDXP$A!85o{8=U?X+IDDQfzBwtb=g|}Qr`7EDr%)@MPv=`YR#HWk!L>5n z9dLI~n{C&S$PSIeRw*ExIP2T_dhAjKNjm%m=ajBw2-*|$l(?-L|anw)-7n<42JSACOn#T-Hzl%=# z+uBbm+)5R4dtCD(oL&VQ=BYjF(WZHKfAt|E|9Tw>Gp2KVEaS41Whwjs_qb){HAH)f z$5iB-{8H9`n%he11Pp2Kd2U%)jvp-bL*`l5WSoywG%aJ6$t*#t>|1_Di z^z2NB7YqjN*Ip@mJMmrg#>p-~Inj%86uB=anzNPi3v;Dzbv4lX_1I@;WD2VhW!~3r zxD6bPUWoT=@A@!3MP7HfhApY7ankV{b`8ppoC(n^G%0L$UX9ec>Ip{N&2GM*o3gZe zlH0+O8VW3l6{KUje<8RG9r0skD18`ANWgP>_GIb_n)t(#f4Bf}dAj-6%OPAenLqy@ z)PQ5}_h@o)+k80$JBQ~y;CVEZk8|fDPf=PCg|imzG;iq?f7JJgt99<95ltilGn^b1 zl?R14A!K*HksBZxs|g4Q;wh`er61M9s$skg24qwR2Zsd!N(Zwc{?a?YPi9${O&0__ z-2+xL>dYtMXb9Kmr|vk>SSJ;%0r@&>1wHM^>RQJhE+4Xn^|gkSh9le|pX=9rX;7Jd zzQ%D*%&j~szUNU6_|#DCz*$=TGywGxe6@r`Bx%&WnE(a}!`n&wJV@ANVWY z+j0+}jl8RF_+;X*DD#46CHl#tueX)9`}Sa;%>A=~lWiv*_k$xNB19W=PPbRc6IBY4 zb3^x!UXFO2g&d1Zz2-jb=0|@e_uVnJ#)v3^q%!)`#m^i!#gq18Z&9V=d!FRNItD)4 zry)%?cGxQP-}cuT4lu3@bfm1B@3cQ!?7KC)^!)BDYCv44!D8d7r>|1HKG>yjo%*yj z8X6E3J>=ZRbaEcR&1aoddLZ7O>ohBBe9UKPjAq@xc3~nZDj!NW2m2ED8mufyH+hi} z+k>p**|=fusO?hp4)Vk4ESZJfW-Q4#`DLwD(-q}5&rut;JFC~3L`xy!QyiS;^Jz1^ z9)Rq`XD^n5XQ4s!VVnvK3}k4!6heVlOTBA;CUMYsBZ23odiC3FeS@aqdzsbULcw+5 zv{>8h2{W77%yv1N`^|uC&KWCzBezH4T0jZA6|bXc`=I-hAHr=9+Wxd>L$31X?l9uP zWiWsJ8w@;qt?iTM+2y+3<~y??-1q(W`$Sf|d<umRqMgtDkx2fP=u35`PeIIQH|{2p_{PLv!0 z;dsc+cZ-p~HcFOzT)#*ijz;gq=HCsN(o8keh}v4qztSO)C%yr~0k4^xqFd*u+I#0l zx)nC=%?Z|HwWYZuRYqrNIcu)OI;E|51RbqRqKL;&Hi^6!Zugyp#y5O^c)}peBT?I( zoSX~|05TdjHa5aMiQ9vp{DY~_loE*0@T!xkK8EH2F&27;%&;QInvfL=m! ze0;zQGQBY{CFnER7K<|jc-jmH{Y8aLF72yuFsJfZS5W?WTOSWw)6C^{tMjzDv6ms{WQT!_Lduf#lN zcW=*4xjQ_#>cupm{$-SFD3Z!(K*b(tOb-^#FM&bRSb@q#5tG?Y0E}bu1E{4R0Q8R% z7j8O+Bly)_Nqa5-v2^uA_WnS-v(Wd;u!fds133B$!2Jpl_$d*cjlPtX^@{BAeUq}w zn!ii}K0mc-LnuCDt^RV>m1zGR-?Yc=+n69cltTX9CVmbQUhKEBvdtVIg*W0Xg0D9&Nvi`4TKhytuV(W%MtlxNsdE0S3teC+^-pY{v zke5#0OA|@0oQ|ZlPIcT zeOFg|6R0cEXMzZU14t6TXA>CNo@r@nc7z^G6K%Vr7v?d*0l%dTnl31GJMypNJFu*b z;{!E8cC%PT*h6q)fv8>;xt#Gow|8CO_7{gbIe8Qg^m3e^#jxw#v?~tWpGR-#%$U7k zVvyf)3H=t;6)UCUE>qJ$2@0|E{W%({FW<#Yo!Fi2q26Lbh?wCP!veSE z8d$g;@FL%7r5Sm6KHx>w;X`5E{kS2jB(l9vqM3x_c?^Wb}1i-=rSN1 z2@^q#1mK`lgOihpRyIRcnwm*>2)d!X*kRtlAp?oY#kF{HfExfsg;lM;WbGTdv5~>P zp_JFs3TCW?(f#lN^ZE0S9puu6+sChycwC0xn`aRG&C>#KPbfk0-{LjJ`%u5)hIpCn z>ffIPsqw|Mck;XMM-LQz)>odd-pZP|yR64aY1*mcTyYn(58376G&R_8nNaD10tEBv z_yK9_8>~UnQo!fm^+?D4k$-c0Te)A};sp;Bf-wwqP`rExSKd0`E;D#u^#aaFm`21r z)D^zBxX&MkZKloONaIV9#Q=50Z~w19j9%Y!`R~P$Jk{u%oaEs9TUeu5z|A#s`JZbV z|GCvTB3P*s)`6SQ4e{m~*X>EGRBD$+75*Tb=&*BZsL+iD1?#%UB{Wr*wbPGdoA{Ah zYB7|nrt~X37o4fZ%X{66Yg-clZQc=Zn0&JnLaA z3ky*dFsq4W&>?hA{Wv3RXJ;3=9VTb(&}&EgO0&Vu#mj=h?EF1yBKu&n4NwsIihV?_ zlks_~ShtyqUh`}yN#X4Jw|q0|kydb}fVHT7&=~L&jq$iPvik1>P`>plYl6@t$&fy* z4n9nI^4W`wk^^olb1BaQ_5lX><@Pq+`jGP~Z=0s)TV#~>VoL|x&l5acTsRGH=}CFW zVCmohB+As-Sb*u~6hya&*S-i({9Y!Gk%WTcF&P1mv-M)x9pHMd=1R7@uCDl^fJd@8 z?2emtEUIaygeOQ2x6P(rWMVj$7PfcB*wpz zD3y%4JffQzagnQL>`c7izu8|Vhy}h&eaPLm;ZO80WvsvUzvAFf?RwBhi8}M&Bpo*Y z+h=ZKgU#vQ+TQc0KFol>+2A?NKd5H7J?lJ}|5R+SSH5H9_%_UPNlIn=*s7iAfn{|} zGeZfQ0#P_I3hUJ_OePO-o}qxswDU7O8#g6q(DbO*`AAPrZs2Yyx3KUA@R|T&FJ}&a z>C9j83&pE-j2&O?g^_@opoGs)Zf3c^JB{I}dB!4RVo31eP~kbS&?|^X#eF8@X>xgQ z_0;}Tu9fcQYyrkuetv$%K~LLga}cBASuzX|XI;c&())5lor;o(3e&(#h6w-oabD(P ztqp`&HL0or0-dG-w->*y!QCy_Ae6XB$C z0KjBhfaYgxf$4?2sXyPusI3Ei`;@sldVy+&ck9d{XZU7Y7Fn z-+K?W%?Xa7pzvdBKklFCQJbF@83u9t-=6GpGBN_IY;3zuyrB;ZJk1CkX8(NMa#9dQ z3S$x@DPXa1Q{cC0dTDwj8DgJ5i&n14%~t(%nQT-{eh1}`;o3@IVkrddiWhG_Zq{`( zgI}lcUg-=)#sJRSTlkt!yfy%b)u; zG4QohH0`uOY@NFr19)+xcNdlpO46_OYu9=@xCA>)!i}e=rlx)g=WKZWkY1_j39}Iz z@1!zx_Yekw@!@JIr5dbm4)Wtgll_xlq>}`sEM!~^UA8KFePe@=ktK^`qGMFXovNqc zzO;VJvNWj{7`J00CI`-OSEvY6?6PtFDmsM1Ag<*b-8ljO$*<342=Lw<6`C5>-00^TB3y*y#704pENN%p=N-G)+<`1 zK>36gQhpDF@Y?Z6R(lUM3e)+>Xpz|X!lUtezXS_>`GvRrCgb+M7{yK^pmtAb?Hu%w zSEYkmf-5{554AfJ57>W5Zd6m>zWY zmO0$&5iPudJFXoNW7qD1IuDW@qLF@Iutj=uJnk=DqS&N@9`Q(vF!4EtL3reg)Md?2 z^TX!KxPzgVQ_PKgjels@)Qmc7j?qH zd0}I96$o-UyO=cljs7kMYFiKZZ}~TFw!F41)cA(IIKt6UulmD>dzN=K6wIEy7L@7KZE;k+w>1h z7)|EIG)2Z|r=#(WU6&(538)|7*#v{FQ0e}sdLjpD!|!`<(?ew{;0dpeEUOdt6jCr= z7p3#Ix99b1GY%)?mv_O3p+p^vvE#>G{!;O7M;2l&Fh6#4O!sALkzt3QYG-&_FL z*%5VWmy-=#JmE1fn+FhF99_F^qEu8mh}W z$;rvGwK}WX&n*{o&65^L@s6E7Rc+))7ZtTi8d{qbFz==rWLaBVI~<>)L6Bz+_F(_j z0)XI7v8x~`u{Yir=XP-IXLLmV(9#=`;(K- z6C7@FvVS@|*h~yCEq}U;AN8EhHgYfybQBN#EASe*H%dM6Iat@Y4U-&|j3!O$+RvH@Qj8aYPJlCUg;&}rh5)Z;P z4Id{x){oN>kK_H%4WkRrNeYv0I$YFQ$D^Ld#3)BcuWzJqetZ`0d{HRi(||vs>`M0E+}Gy9Y8{;PF=TfA4d`I{6bKxArY?k|B6oZ zNzQ;kg#aMCZDmqGN+E~YuxxPsEUfSNc7TTaEC~BcDwadV#S@lkyw&09j=4A1{oi`k zX37VfZ!guPHoiT3q{UmlQlRe}F`7_&9Ta|H`OS9wd4)VfhpKA_t=2QZ$LK9aMm_iN z$ll6l_!u~bS;jXnoKaX3J7z@nHxTM3fIyhIUN`vqA$bpyre$wA)f?puB=Ik%g%b5-w+440X-uh2&gb{nY2VAlTcOp ze$G6?woDAaAINN%KS`AA+0IBB{e`t{CleMakUIAwo55TzFyu%t@`IRe$$!a+QVvtIe-1EL2z z?9I2YFQ=(rIk4+)5o3AzHx(6G=F~GHrgzreBKxKIclrhYaxwBooT#Gz@w1(V5pzDbC#IUQ`Vsj35SZi*3E3j zy)%=M$t=n;SliS!^hpx!QINh0!+ERCtXLIO^>E7a=Q>S5cgE-AX)4XRwfN#vqLQMoP7j}84)^QQ4PuJRo$b%UN#fkAM8ctVP`TPLN|kysPraR`t}n#i+e$L0pS!F)s*2j z_*(CE8O{}5gaM9@!aC#{SH*1$rgAe@o#uyECgpqM9RJ0-C%{pJ@Ov^e1I0M z_F7Z;V}D?W)Ow5p6Dh0Z<#Fn7TPx_g=K@W+gRR||@>%qiZTc2^KxyfEX-HBg1Pen# z@dNI=tnGz{s`0w%x;j7E965>ng1uwt?r&?E=q%ASg8kgaJbqkx#nhPW%h{4je4I{HjF>Ku^H0*;XmkH^nNV*c*WI5 zu)u)Tp^jYuj>|sv!y9(lFAo{dkHS0u-ObkK^<@bWa+G8s zTC&5p@O#S&ohsYqbf@8S1GahW^cSBI81!`N?5RmeKBzJ;eSa&!0FQ)(B*G8a{jOW3 z<&onJEA<|4>u2+pHC(=kxKk`YP;E(z2@2ZC_!zvXSq&Gt zx2z7f9g+{XVJj!*&q-_0HYM`Bm)j`d>}*XN&*;l^LpKbO&}^2cX+4A4b8vF1{_(@a z^xb|)0~l>P7bXL^$&fP=GO_~cvh1l9;#ZxsGccS>AgR5cc&QQIa!BUIXsGv2tS@#Z z*wpNUWW+Ioj)SU z+qe8fyD#YVj#-r6SG>A5r7bW;Ei3Pn;tD{Jxz07NjN@Sw^gf?bL(RujB61W|ynS|k zqA^`enWH2ds33Nb*UMq2WE+C&v>muQqU2fHzslO0&-Ca8hY(oAV z|2!g>kBXmrUe1KkGnM628*%aoGarM;H2LFq11U@e)5krEB8xxD%k|+2QE3~|UxL7x9n}$m->-n9`kITU1t`+ft7zgvlD&+~1MoTwA zSVEiUXoM--8jqDDpwVTagYh>0$8y9=mbSySac%N>%-C)a{Ty+Vam8_hhF?F^^~)>k z>Rb=S*53Y?;(Vk8ZW(_3B#6)aCs~{Zc_oIHtc7UdOlO!6e6Ol+6L-8?n>e_EvSb`z zKH0Qp@hc?4!!srI47@O#Hy45Pqkxf6G0=9fvm04?`+SY+eL@q9<9MARW=~C@w|Dqa zRu11xjOK*$yD43!e2upV1Y~Z~arN&_d5@Q7wkq>$Fu3whOLFxFV%%5u)pMSgW>nw) z-}OaVfrs7xPpeR6e_wDe`pl^*VQ_}eO$QyBe0fp7`;)sU1~bI_drm&usn6-6&;m}0 z&gr}_(cHW7gIl4}>{?6=g=soEx*r!NSa$x>2N>?1vl*f18^2O|JF5usYHh|Ri=S4q zu1zS+?_*Zm&$p$Zi1o}`T_m;3es>yOzNV_MxOSE^Qf2a0-@HpvFX$0(>u{k2(pq+w z?>sbe9mM>|gHsr_pBdd}@1eLV#+;d%a#0x!9!+xoO}=4XOo)tCO-#9V(hoTZ7Y6pA z`(!VxwfTNwV}V1J-}IeG=efE5RCuViaG$%qdC9ZW=bnMW<2eO)FuCPhhrrGTN$(X+ zs6wXYmH@-+^X>9Hb?0IY7uV3Jmy^!2+H4LLpQeJaZ-Z9j7d)d!7>P?<_e2QBy-(P9 z8*KagAZ3FKL3wAjfeiIF1E3K zyA-(1c$njoWqhlWj^pXI#74Y8fg|*p9Y<>o)b+Sj|Zt%YDjxk z|2+W!!ITo#bqD>oL*^J*m+R6ktmoZsa*a@?*`MEcuJ52XCRDzo?-cU_{k${ITc#8e zEbZUM%sfn}-R(L$Y9m{ILx}Ic0dGWG0F}dT2ua zT&I$tsMWS9d+02%aG$~vDiV79ga%Di1NnZ4~bAp?1Anej#Y766*Pbw@65- z$Z{aYbJ0Lk+;V0+ig!kCop`Z%yYwWX$w2L!QS7wRKh@yUay}O^)G1_Ef%&b@>SJK+ ztA5~rszLw+Y$_`&dvI?y28=#_3``@nrLw*k7r9I1KI@Hk5rX?DDzKW@5mi)Fp8^ph z89YmokFDBgbkovXdaiVnRFyQ)%yrwf9pEga=JvQ&3g^&(L2jl=OTEF_6^5|WuvWbK zK-P-r4NX`7_Unclv#j;uBqVAWZdjF(^0t@Kz!%rF93|+mOY*01w&g{&|3rzD{9cf< z(Z({wL_tS$kUUsvW}!47@p(ekOAW*bgO^YD^3L2Q(;_2cA=(5!d>jZ5hc$fNV$r%Wdu@Zb15N&DBWu5OKt z*%?CXQwdBW8a~n|?!J#bAsamzE+o(>UZ}FkvO>pGUr%vDTdSz?9SB4x2#-A#cD0$i z($|R|qr;KSv&hO}vyu{9-RkaD>2%RZnZGl6XY+fCSL4|ZVE|GRo}Go0I@3A2xzNf= z55{i$O^SszmS@n!3rmpSKz^yaTPn=I`1j>SJZ(*!f{-~Jx|P7|?Oxmmm> z*pIq_elVLlCRhl7WwuX@KDfELt+p5CVkBa_zkCMkLYarg;^TJ5ITjun=_AEMhpDX- zGOBSQRsWcYyYaDs9o)<-@gKfcfkGKg3$Gt~7 zxKjp-D}iaV{kt>MS#+0pb@ev#U^6svs((K{BO(|=J^9%q`w$#kFyLPg^SNFJ37r;N zpK2XVMwWj{BFpYaM#(ezQRQU2d*p&9L$r)Tc8IGdI=Nv@YJFSBTuaJRDgJHHDqpxD zpn$Ey@}8x(M);{>zSLG`4uekdI--u#;m$9ah~A7~8lNb<@Bz8Pmuv^#gPOLBB1i6T z>h;9CS`zIp1V@N4tIX$ifAl=*^&GA-v~%;mzG=qP0Dk3NDA}g_s8JU*Pld^M%T0Jzg>@JJ70E zAT~m9JkD4eZuetQC3G(_jm>tP!x5?Nf?4aUhOw=zr1tO{E3F5N62(R6>KO#Da4QJ8 z-LAfu?MdFd=J9Q8c5>zgu@#~x)O@V1uyY$W96|zT;zpx^Gg!0Ha_R*ELD@Q!u0mJ+ z`euqNU(+bevQ=&_ArT8zY^uKfiOa;b^wTx#i_O(W(d<&F6dS{mv~!=~EEv|5r=VX% z#AOjt?RN%Gm6EwHYpRVVrp3hco0ZQbV{nJxrXDi~3`&TVef|~kZ$pk7BM{0Q_QBuq z;Gas&ol?HaZo{yf6GfwSe z-Vv4Xm3v;ee6bTV2(UGVO13x1#uq7Qj9bZYr=vjJS8RLsdAE9=R~6L)1fzwakFV7D z2XmMzw>B9i6X<5noEx<8FuxGVpHYruy_)Q;V<)}_9r=!9UJ)aw)Crn(v!B3YLFUtY$vzmyjTP** zj=TDv&g3Fzod%-QZVXMal%b>Qr3axpehoEj()8hSVGQiF(&Dw6-Vf6+R#pO6?p3Wv zL%1(E&gNwl;t`w(x4&scIvMXMs2F}tEPIFfyQAlMbF841|N8#(r@PdZY?8$!(+PYi zg|8aZpWf1mPk#N1RTQl3E%>viBQV?}WREqvhYrbKVc*p5M?hR7z5*3-f59`yBy~*& zg{FKj$`Iw(6SAs{&v5l9Dp}O({A%AiY&|}k6tzA)+#UY4xRsA${2@tLo6=?Jn?8a# z1#CG9alw`FJIX3Y+l$eyt29miGyCP53WZ*J`k0B1p*)&zP07L&wKYXh^M20HNsT{) zNQ2K+-@kd1bIjarc|CV<68N{Ic&p0+rosI9%Heh(fdLf4@3XNz%O3;@9@|t5R$GIo z;v$sAI;-x>znmgNDJBY0M=r}NOG`0+W~zYc653Y|ajZ>t@47m)1$$smDL=X~^j^_u z1s=i0hC`%J8wWS;bgO^F+M_evhE%^}do#f{D;I48nsq{%A82(pB(Io$T2&nwUnzaw zsbZz5nw1c;8&TpRH{qh_5ZvRoV0NC-HpKOs#lbLp)Mf%v6b#~Ts>N_qgd@DJFy%jn zD-ob<4_97RUM+Z3UL>sUu573$M3bpKkwIo2?XaFmYvhjfq3jkscbf>Q{I81g!-6GX zz(AuoF`;=V^W)HAeSJi)s;w;fOYavzMNA>lH{-T0%)sAZ-{9FNN2o(8K@eU)7ETNo z>hCXZ%{c4q0)FlEElm1YJFkXDQQ-(L+je_!RqFuXoo+KTr^Unve#dP&Ea5f>jWJfl zLm|f>HR|${MHyw1GA{9xFq@{Ki_@dm*jd6~d?{U>+bVw55$5@i}U z*Er2g_|pdx-fmd9I$A_=4DhE+@wJN1NT8JQMXqfd!A|y;QUZ9%tMweC$8E#E7K(3g zLOWT$yO`Y{5p0LP-Vaepd+V+MDqvLtJB_XIP>{@XlVirGCsdv_)!v-`*yn~V%4xbW z-eFp;QX;UpLWn5NcZ#b%NJeks$Sc~>aJve^LF&hs-g2xivYzSGA*2zrRX1d}P*Dt5 zR%sMot7YQ3S~HBmksW!h4Nl89qrDo|re+I&`3sZ?qsI3aHnzI&iKxCQusJH&c#>~4 z$a+k>YB+pNV=R_}>;GS#mIp|79n>dsf70}Kh%@iuLZ}@2xs#oP?MZ;@QCqKEF=FKO z_m~do&3A_RNq0{3EzRH-(}%OkN$V?k@;O*EVkB=R?#Cq20@}Ev2-r?gH z_EpkIP;m22hAZa1o5Cw5SZJcCmBb_>TErO&mtEQ3Dv2Vfk|+Xh4CkLJc7%G{){acm zQ#H1uT-_BF0&bmgvN#MC!W#^)k9P9!5?q-hif$Jku`)dHnFf$b=oOl%`C_*Tm+fTR46GVP2**CrS3) zbL6O`sV7o0aGPwrBrdLs?wLbE&8Ppgnt zNh-g}Yo3dOkp*`eUKX3o-8}VpJ3({xr)$q8^dAj^P9_Qdoi;}!`cE{lwpb83xVYF@ zSp^xyxRg14ip2=D6A)oJ%kY(di%4g@_~(s*e_g4-ay)oDMe-1gHj@Qz(TUt`$#f+@ z8GOkxi_I)jnoRb*xgFju9F0=EW~gv87Po&^47II5YD{cLlhB>S@RBr4tL3OAZIH3k z9o)=F+MSyzTT|F=)Q_Qy-=Dvl{@&Tf+MAU;6G*&>j?sfJ;qUd7E(HD3;*>4jP!Fxn`Z z5dJ&65om_Uj~}pevHd~R{R9>EYO$h`hHNr#snR=+I$ya2K2CbB)&<2Y_-Y5+AGQ)T z_rCQ}5axOUYrRJ2qvbNg-n!`H&)`T12Ud-8+`&%3uy{#o{K<43H}&m-+3ON^wkozN z$^J~9Wh#rHC5qRHP&%gSDIVhy>y_wS5)mY8jnLeN&5{ic3`S~w)9Os#xQulS_Lz3}40S_nyrSyy(B^F} ztOA>~JsKtA+a`W$)smz;{(mZ@wE3dSi(Q$-j|G)Xqj`nvFD;i)47si;yh)^5+S^4Z zwKX)P)E3%ek2+8d!&vZ-^8=(j{wr-!s6g}TKVoW257yP^H@oQs zmsAo+sf%%OxtRV{6h-capG#s2J-3Flwzs#_($bb7zU8E_jaXs&2b=u4L6whq_Lcmj zBMYwSW+{u3XL+p@MD^#ZH%Ep$1ZzFh%H<|AU(_Pg-;>Spu=>e2SJ=F_gtNzYMAOBCo@;}?7kZq(RoEf)xA7+S zCXoTyhA?eDJUIZWLW5TiIjCi^y%SEPR7>HsSr(C0b}7QR(g>ud#Lz1|j+7)DA) zQd<}pumavC>n&gH$h~2~?JI5Rj1#39nimHNNEz+xx-8j`>Ae6IE+6C@UY2x)_aoUy z%U@C{r3AFeT*jow-{Xn>doVL-1zz8Ea0u!{X3@1qeDyhYo|lYdS}WptQGjNJm}wsE z?>>&`Ps|i=nuRD1Cv>_>l367cCkDE^(+lnv3o*oBJb^d2;MR6Fv9u(7GVy+PZnwOL=W9w$+h3dpM4cf)b^11kOhm zDd7PQx_z!gtN}EFqYxHW5=iNB7>=ywaU^JKt>Jlixk>E<+G_n?e&t0E^J)+{jzBFH zX8SL_GRnZ6j}c z0B;sbPpjr@4t|>!3y+V_HT;W>h2^viQt%?&t3kSN#-vSYad2G>rg&lUZ9z#%Np3D< zbuD_)RZMIwvQJ|}gI~;AMa;kTY?>UXZ7v`7q& zJL2fr7{u)|>P?$J?HhGP048(JF3XQBk7=>pU|i+%YrYE5?15VDW+P^KO~^p?`$@^l3z^P5ajV0l7R!S!wWXD!!7}UL24vnv|WAM1>M`*1L7fR=+0JO zU;IEDN6ZAsz<1;9%df4C-;ggJ958&;Qdh?#B((H{m-g)GiVf0%`#_}^#Z=X_Rf0qJ zcTnc-{m^w_xSC5nY#u)fU7V9cWTqaH*-xY!%L}#=`E&z)Z)N%7qtVqCY<54p?T6Zo zR*+CpA$WAIbkbmNAA@FPlLGcjaR0!7xZ&4p@QxwBUyX$^sI*CDl$R=8+R58dVqjnp zXY0Isrw2+(m;K{m_$N;i%91OB|37a9E4OdXbF|Kzx`~uq*fN_^D)bniokdae1Ul~9zJHbl1uQx`y21v~46H8fLlPq=W(aFA5M%KZ zj`XU6(sNEG=YI6cHCFL>85-zSqai4PkE z+(4YbPL`+B$SF6sIUV1*KhFHl*4_nR)DZKu*VwFZ6`X)>NuUG~c#{OUK7018(X>TU zjP##B%>3n7gcm0kaN~J4;#X)Da>BmHNtRrLSQy$s))#9wZ6><&cOeR(!*3RqAbi~g zY1IoJMIa5@i;gZCA=O$}zajdhhGRzRj5SNeR+eC@nx9oCU&?Kz@pOS7Ba4?FZfE7O z3(yza(*V=>hm8TCSV-eQ=Bli$Eb;vcT_$!I&etCi-vPBAt7qDOWpi@7XoQQrqXkXGuiqaB8wj)SO_##Mc}GQVbUcpfs8#H;IGW* za~-@4ZmBezV62Lc9s|2G_#-?5C9EBVu7w4i-a$oG)%x06Jk1mAnY5CuqcnxBji}!i-JkD?SBHGusU4PeqmGN%gj3bwcL@tp? zbArFQ<@Q(=S>2px)hiMNb7SrK(a|p?w>LLW9CXn@ZUVrp*ZC`kKLN_OpV@AQH@Gm~BZxTDy*43G^K5#}_iXdH9b%6f0P{(G5>*LkHa;*2_7gC4> z+((&~pFd$+nJ|c}04{R6IV+A8pG;O;J7{uJl`P>u28$Q`lVBLdbu>Ivv_I%CUTkBt zJS*kNnS7;EM46>_r!NJ<$X z*CMRo=*U)H?kKGP9hsZ}b7@5^1i!llDy9yP_9rfaHR&TG&^u;Vin{3hus{XihT9oZ z?|)Z?sN@*p4>{eKri5-&&%KL`4KFk5oY1kFsl6XGF^2A0mILgSY%#TLSS{L*v(DfV z+KJo_a5(6lh=B-!U^)Re(@&A64TewQ9@Kt?^ihHl02(n_!F(|d{1IX=4j$mb!0}UV zZLEM79)&zlkQ{hOx-d-xN;86uDw^oOC6BH&_;SG#srnB=#4AwX_UL=|(Gp@^rp;uD z^SbAL%Cqq~8l5Gg=Iu3|BFEueU_e)1S&52@I#%}!?%`WwAtEDVD~T@uoM+Q-6eJ;L z2yBq{VWvd@dwft(ce=tP;}lp`a9NfcV?c<*YZ{OMQ=OnK{NXkJGm!OSf-oY6GHu!Y zp%BPp<#Kzbw`Z*T=rlg(qVSk*iwirF)f#esIs#^qRbu%iv?awBVTF6MvvW^*&AUiT zJ#=-`gnlA$Hy6E4yNAb+ft%5{hA`05nj0IZNjcvu;H4RQcrEGKkKH-(O_{Y7U}8^1)E6M8@*3kkiYke9f>VpC&5Vq+3QwM;}slskv1;JBQ$ zGz)@W5*Y+HO5pwb_duW2uj}`3!#-o5jD5w8ulVk_PzM&lyGK@52Yi)=h7Umm8<*Ae z>F5o3QRy3}r}kU{JIU4u+|GlehDX)2Gc0F4K2!k!sh$0IoyA5U~2fZrP7a_9em8@sSeHx}^ zis(xV8xgCk*qGScNtNvSeHl3wsdE6FUtC;#cXwy2_<;X84TX8HFA|wsP#}z-vb}9; zWVCU2_DdN`LEH-kW+TwO_(qTjz)yMX03ByO;Av}TcLFr8;tDH0aSCf!KdQ^hfl&D5YC4P zx+F;K`de|JI4D{j2_WJs^?whUvGZKj1A5l5U1*U^((^^j30CofF5J*!8XJ(c9#o9M zD9XiW0(`1lQVbj%s%eGs104Qa_6VHM`z=uaUP1|782FChmsFz$-_3d5p{FpedLw+; zEmBy_Cx9YwQOo=C?~Vkugbj!a{+DpWpTNz05NYB5@M}ko-YG&I?dHUn-Ra|pkFta$ zZ(`?fPjs4~oeRwsM^&$C4rat;&W972;dU~=!75d@lmcwB1dL@U$6!r%wphsR@Icd~ zc?*0PHM?B2574Xs*b_zlXg`zy{MF6Kmd}roMV!8NC~9jL4-`*L$UuOIBMS>lnxwJu zs|=F43VN&m>2if1?rtf=RuVj{aI9DK{Y|X4%d6HOx63J>O^5T|#iKP+L+TX(08e)d%7?0k7&a5j z`kU)&w9fC7>W1*Dy?7Nq29wBOld2O)JpS@4onER#N;D*-2nKK@PR?iz2gHW3yKOO- zkUCqq4bXwnfzIf+Zzw}-e0)uw$)(T0jR-<$)ym)*Ms&XdF3Ik$xlc%Y|6^DegvZy( zHxywD&%i5qxO7uMgOIpUXEzT1M=1*M40wJzp0Wi`$EQo2kB@LQWO~CT zuxx~Iy~Q8O58W2{kNrv1PyvGgSf>s&@VyRarlV#O!QOJF%S&x6eQ2CXARL^Wz_eQW zoAq)Vr0o;?s`0J{m(xDJqdZLO__!k&A&`T|`G4F@SttWaI|1h#V zY$n9iqv`2sV1@C*mBVgdC;4!%dVyY>_Kgv768GP95=RHSso?6u-Jb40`K_&}&pqg(HKwRuJ z1?t*BAWe)4aNGb#>&06u^BHuw7W!F_%!~|N!we#rsW0$PB3ppP`Tc1Q{|AxPUx!CW zFF?43++@`cfP>|7_o{tJAL7P_3G6jkaCl2 z-p#^+$I#vd)(;uWH&-WaL!Xtv4}X&&CMMQBT5bnUQ=c&bL|AZ?R$QC_;{DBJd60;k zmzSXV@&h3-8KO}s#)o1rqdctSKNnOEbT#)=KPdh*60}$~>a>GCLg|AT7tfH~ zaOC7tnVOoKtesx&Z1MH{RuUdKzSfgXQQk(rs9l#C3O3F&&2SYA_;wu(-nA18uk@h}2j zI=%&1JOHU>2ZK5>n2dk1#hY z?G4h7rsy8tSsaCTfzj|bMf+u!HM{taMs;`SBH#?6XK8j73`p6fO^DP8u=_1~Nk`?eDf{$ruFKIPBDIo=*u!aGGydW;!Vw&S%E( zLU+yb$w)}jOMaCDnyvr?q=nZLXm(vHoE#o{Rr8fSZM7qEv3HwF8iR^2_Qk)UprA13 z2DXw}QnBYH*j1o8VtnpxGLj9NP694_#3#Em)dN#gE~mz83_Ltg8JGj!mPlciW&nrV z+jHprxa)Lp{`TK+!uS+oY%C&dgH#d!r@+C76>TMxqdz;i#GdeoI{3nQSuJT=z`&L2 z`g%>;cDpu8*q7QLwC&YSQQKx39Yslbw+&+OgndcgXa2LtBa0-0Fjwj%o5C?dx)=5P zb;z(37|L)lD;sE-=T|Y`mYyGhmb^2R2n{7| zG`d|BtN!>bY2U11WN5h5h%kiD6!O`}=X&nh%l}DIRB$&^CLBeS8xN7*0MQwWuvt1b zJWs_#c&|KR_Mb%eFMY(?8QxejlfFRvUgLFuXG>UYKNC+ar2n<~U3MeQgEuesu0wU4~ zN=qso(%lU8JHx%+&-b_1tow({b$#EN=Q(GeefHT$?c4#FpiCb@McR4~<{5;GuMZiE zVNN)^xU_tQp=5A<8jUvu9A~+Svwn2cW^oeO#1Gk#X)1$7_I8mfK%<4}0`(vcQpxHF+n8KzB*7Rm` zMQtyD0mbyS7Xh&GuC&Fg8X`12CzCZ>d3oqtC}Vzo^{)7y?Bs^a@}zReCiL#m z!i;{;j6wLBV->nQ(S?417>_T>StwxAF>&KqSf9+EPCR(a>wHOxihMfds-jJbNCZ1K zCueR(MgTeD;~5Ik#$br`J6^K5!o$OW-0lGn2q}sCCo_e{pVYTg!Ew6y@R;!_?_+X* z|B&paDUy9qbu7MbhN;y6W<{9xzveW=)E+W;5G{8iZmS`NsaVoc?KsF6>>tGIIvsN( z$^X7cz`wy$t&R;Js+oW1Rs>A23Y))}BUTPk9}cdJ9IaTV+Fca`BSnYdXMk!DDRl4*GdDn1qBjYYtZr=d+>(@T6@v+(CGLLYCTPnMk5} z`em&~G1Y?zJz$K-=;KeTE2Coo6ba&zW;M@L7R--(1q zbH0|VTnp~|g=A+X!i`0WqwSEKXw@kIudpj3%APdrfBsOKkPS2+nc1VEQX-lC_s$yT z8w`%AM^CD6+B_IboX*v{dzXqZqxNm7z!$yJNYmB)b6GVt3=J$Z%7s!_(Ig~x0GQ?C zqjX*Z@hSz0-n_gT!fa$j1o(k@9HVw%+LL+Km!GcGSQ|$xMLae+`O50p<3Te61Ib+> zZ-+KF$N{~aWQ>f_ant?%{Q^6X%XAJl%;Oa5<&+;^y*$Ry=%>N5OCoX{#^{?s{@vZu zn*6`omu>+<{ zE6;&IT4%TU0G!ZZx)9lR-RL?eW&u_yRk#V^#jEnSkZ<}8AvFlN6F7}kO8}d!+X;Io zps~--;kL8bUsQW?smCsKwR-!3#EHubQRGZ3a=D}>Z(-@O&_8k_IH;3KkQW>8i%L+V z?~4kPW6SBVk9s1}N}<@8iGZiW_CCJYW8(2nm(}+8*m6mZb4FlyydiUUr?1Cf8ICFs zR(J)rg>JCV^&>p-2okodvTju@_@oW$>C>>A&%|UnJRIM8l@hZ_s6mP{^Mb)!=i-`|5KKWjoC z)#SSjWq;pznfdLo^35pf;oYIGPmH~;u0aV2F);$Say7r)fOf`&KB*?-(;RU7dgv5I zJaS&CUsZr{(&;%uHk`7<)80$_)5nig63og-*Te7HzCRDk(cFUo8$NnZ^pJ|OYvKI7 z{V&U4vf#NBINssUWT+bCzGh}Nr1<66uRZn$Hj|I610ozO6BXk7-$YN0yf)6qoy=5+ z(v-8v38Js`1u4A!kAE&OKm4$nS9DeSEA?sl)3&z+l0G}Mh8A^`oCMpc7^NDD-C>*0 zm3qNAwAUt8W#+f?8~{uK+=WLQB+tyR5qZgdVm78sk~P4&FKkttmd2RLOx*Ld8@Q!_ zRQw}IR|p=+-3n5b(0dx}^4%>%2X&Zk>;P+!*kGU8_Czo`k=eOs+9tRM4q2^bji=lb zOct9^HU0(2K=hQJXiltOMBic>5b!j}cpOjDar{<)Ci;Y0@1=exTdqZc%L08Lk6obl z+~?ne;GQTUDajI>WK4?|t$4A;BqkD^`~3FpIbEhc85M7*?iM;%;V^0BY&-48Vb~>U zVnWwzG)_MWYo#Z0$H8#K0RmHf6)bEYttME5>qo?k)yiID(?ZfD*=YA(tBo9cB>;~> zo?dukfTSE%bF_Ign^CMu-#8ajgz!iN1l;Ca1 zjISmEau687$0>TkW7z(EfSbwd;z?nAc2fT5`vQdmnUo?HtN)U`_Kb$B#Uyc`dYy5A^iekvpbGHu42 zK}Pj9dy`>|9uFVV8VJwWs?{wMWas4gZZuPO&%1nCDNk+$|9*lIs`3Y;5lx(V1c_fj zt~Pdj+-mE)6`--gXDafXQI5|;%!)2>VH{FOtE8oa$4`cTpg83qn%VA@X{oKfttK@grKw6q)S${Agk*d}H~eU#QK81Q>bRZ_IoYWv&(MM?%F`dl_!+(CGXRWfu#{G|X`{wd!Z_96JzGuR?y z@WrN1A17{q=B0M}pe2#gu&RFul-M+`)foei)zyE%>xJ~gpgW?u-gQlzNc7sr*EpdB zn6~xDhvTQu*@oz@In5u(t#cXH`pQgoF*Wn;D)o?copm+;j}_zFrteNql) zkz7dbyVOJT?<>)`p_{ay*|X{)O(>?-efm1gR~{Wr&rG;^|9;l;g?>M z{*`F`zg__EpZgZyo7InW|MK8^fvx1HjMK|?v@{WbnefsVfR>O2fK3)eB-*Zd7_!73 z*bK{k2J>#4njV3fW&J&nKdj|E3*FLGN~x;?`~BM*ZV<#A2U(uslfw13QpT{FvhZ49 z5AOGxO}vMa5tNo*0~nZ)KqLKq;|ToMZ{wvdw(LL$QUH+A6J z&h&;={6fmM=FH=Z;;+0A2p`l!E(<@4wYsIr1E7m$Uou~OF#v;#iC*)WCK*1;Bn zwQD86sOJ6NXP6F~s3$=(rXC{JRB>{`?4M(6(>P5vtc)6N41DAy#DC3arjTRdH1&R5 zdBCEnTQwA8MgpiiF^Vhho^d=#)``oM%qx{CRe%1_oueD%f9|nJR1|Ig zz;xvB8?^*$4*$_zL=$f!bk4zN?cDp`Z4i5 z_SfKEU#K~1bn7P*WCd5ih7iKd<(P|ZlMNx8?AnmwLn#B~jwVT4(>59i`61oX+MBm3 zl05Y5$y6R<2}z2MaiqyBEF>1Jah<@VOP7RA!Nj(89bc5QMWy?jGju6W1t-~gs}0l7 zYNEfPuA2~$npb_R))^DSkE6nGME%*%QDwRrnQSq_$^5=%EkRfBk=YZY`JHXAEGXh}mYAsJCLg z2J{MZG@s8dxhrE$-nR-pzl+&#-%fTANUY+R^THnJ3pirO&+1a)AvD74|4Vv5QvPxhZ^q4TD{LUA)<5z~nKF*DzKEzO!F7w^;Luplaikc|F%%<_ z-69ZpLv9{I!qnK636c%y=A!&OYzVdU>*;1?t=CS z^Hgj3XiMkp#oJtzGd#;*6*)OS7}8E#y)Bdafs!mK?k>QWX9-Iu{Y+m)aLcAQ$|k6r ztau&3vwX_ko&JsO?d^??fR`r_Av07a4Qy;!vDE9nQ~i*&W!m|nI8PtVQQ7f6(lmk9 zJVV|gfLVkP4cxjpv05($zNv%(sQuOLCURY18F4b$xCdX@nnhqIue*4zVU zg-D3@a*fy1$o^19Otov>+h`65^iELaCC}o-4w47L%Mou)2u9#ja5Y7*0|7fdHRZ83 z_9i|)gd$H_=O80Wks`2?^x2K9!tdq{-isUL$1ijjQoHv3VIt*=-9#8c)FTpr1HMFPcNVj}Xu8x$9}` z<6zTDwc&i>doH(c$Iml$d zmhJB&UY14S#@3~i?E{zP2^)qlUfhL+V}}O^#6J>{bT};34cA-so_pEsyRiH&^CD>I zkVDO9g*TSLR+s8(=@ErZBE)Lr0o9`>tNFU_#c$g?^b7@ax5BCp-veR?N34oPpE5c@aTqdg`0qn`;5{U@x*RHSlVRh7qaB!_E zna{GUL$z%GowiBGXt58FnSwfQ;#{tJD^Hpq{DcO-w&Avc!TB_#E&ak$cL69b|K4R8 za)4))w6D&D0R-e^4UEFtN!>5HORnrbxLQ(SSki=`e3>V0-*)0-p}5H3!LZJR#gK|mMAUb62CZW;qmXAh0z*AA z+wRJTHpv=fp3F@PHjl`SKMP=R%`T{71zn!J_+B=|Sxs#v4{rRm{b^Pk31fMGIa5!v zwP=U+_WDM>O0r>WM8SD;nMz^S6^R#kOahF57IUH`x<_`DknRCF6J_H?7IGXtqPR>; z|E6Ssn2I3C5sWhoLx&6}4gYVbYvvdpF05Dr@4|ATb@sYs4>dyd@B^2uJ_qUOG$NQ7 z7UFh7@?$tL_-Pawr96ma4;7f{+14d}_60N;iqpa{wCR$zn5Hg#A&J>yx_Q5oDodgA zK|IfG5yB^EdZ7qsU8je?_9)QgVXoF2Cm~hDG9Z4kqJeEeqTorzH(?iD5Ng~5s9)V! zBFH|aCYb_nBGW)Aff}R^CdvJrS<*WEPphx&@oYk=yFs;axV@m5D}+7sHhq7)LnUAtHvc z=tVc%ScZNzCPH|%$O~6EA~Rgc*qCH7=efJLOXB;XA$a}*PxyESfsDNI?+O>jlN;v) zYktf8lBdi&xNc-oX{xi5vA}w*RwGepsGsZyvrWAh7Js$!dzFu`q&y!~j!*2=)7-|! z?v)PFnq&Y?HBEfS`!7>=5D-LliFScPnELUAGL9yj?Sae}*=EP?D z*-hEi?;H0B@kChAYuC%yo_S17 zO_9ZulG@uF;<|Gg-kQFFm5tfW8F0VdKMZ~e4>fetvr+=g$$K~+H41A$zfz=Ibk!2K z^L(zQJ8qFrhXZ2%y;Wmq68oZ!6K{U>!?k84&FkrFd3+*1mJ=TDYg}O=T;&BZhdwp4 z2JHWSZ3=;SXxr;QMaNYP0yRyzn|hS@{I0RA!{!XdUR7H!fT<8P?6P#{=XargAlqhM z!pGfP;AL_*-KxR=Eor?zUfX+U(Ygekjh4;;9mOGG`A!ulmx*&`1i2I6f@13~sFqz& z+d|ZnsFmr6s|?ZZFh+_U87WCQ^j8C6Hpou>wF&V|{M}y@q@& zV|%Mvi*r|scstT$1n(|#1l6}IwU`5_AXk5&CF>?^He7TNm#2pIZs~`DXuH^I>L0#9 ziisXhLCE{3B!Kt>z1*6O0z(jfL;O*cFFut2tugH0Wqw262aD_z^L%3i%_}KhlP6jf~)A2{*y#@ zyCbyOoc_mswI`@H*ILBI#h_AI4-N)7--3P;x3Gze5r?S;5(+yR<&GDpRJ`_9&4xB< zK95J)BS$_yJE-yhN;-mot5@{va`gYON1YlvFyWjv6WA=#foY-6Mi{kh2=CbeM&BnO zd@FYzzSL>Obz-RJ?Qc(V{dDoCF_)i({kMk1%NLnhvP5A$+$*bU47GZXD zj*nZY*i7lD)p_BRJN?4islN424>}jhF_k-)%JbL}d_`toEah=7Y6G->5icguW~S8caMqXf1>Q zX4%uzFYEo5h~UMt_2WB?j+ZTigufm?uOjh5BwcfU?;DBj^k>nL*t-ihz;CbgIAONr zC7!(ca9eeYmuOx<>t*StgL&tfHua1G3S*C{x4PH)^bBCI;MZZ}Oxu2}VYwI?B=Qtv zgw*%+*wY@Ra~qfV5*wo?W0^m7cd=C?XohTlM!6fPB>1F)$#t3nViuOZ<7mnq8^zfl ztj`x@-43Vv!gXDs#z0~m4cz>1;E?H|+mx0;#lXXInAykwfU2S>cezErEPO!B)nM)&EVj!^DXq~Gzls*Uq}G7|Nm@n*G#^e!>;vyV zTQ{Farb?iy>PGbtmFoFeHTDy3Ik)o;E}oZ7?a+H&=Z;@Oh1$i?A--HsY++_Gj#=6~ zFLgJaKH3DxvEjOlApws6CzlHnB%BKO3qE;>)(o*R31YUQV$N9w^FFLyevZ?`S`0|Y zHk?-{9V2l2?gN2-Xy^i7A)=^#B2vuAG&F5*PBI*3RAyH{SMQGqM%gJX=?@Ifw4;CE zEqo>?`l(H^aUYwpKd($bfq%33&x9TY6vh*2SB%5Ujhd4jQFNtMB;}OBwmge3w(-!i zmMA}z+hYQhXjc_+(9@`>;L>Pfm&cCM|MG?CY8o4zqWv57(a&q?jRn&5L(vKR0#yu$ zW{{hZt>AyAFIjKxbNE``?C{R^`wOUb@}SnBy)2*8BQy{e)(GuIf}Xjcs^}*@sP62j zhcjX}eAPf^)sf>f6}Ce81O)A=`q2pKrgs-CloIwBWBa{u7df;xPc%8`aPj-MkQSCn z!Z4yL#%f`kG7nHe7(cVG`NTHD@cd}~PgtoRM5~T#Xe zt$Asw&00_#KaxPcC}4x+=$_Z^GIzqT;YhX;u{_t>*jMkv-IbnkJR1qomTc~&X#@mQ zkfL=LSWXO8+asUGi(^mEal@C z&`ub6EH}ht>t)4{nSuJYUwHkAYMVfN&tNHgsqF8j8Z7&X00O5E^GktfVD;~Z2_`$b zw6V}xr1drA$b%2Ws&*^^5V{)7Fu&VaRqK~nD^nJ|Qr>|=4NXSoyf;%oO6Pz+v>ld3 z(aBM6W$xS`x+`rp0Ef7njf{e#^vRZ;lhqVCdxu*|bVjRU^Tm@1!6N?Fjigf9=VDGn zmbUsa3m_H{^JztiUz_D|TD^onRG~A_f|0&FeG-i%X*{?SMa%O)U_B&;dG9cl;ojMV zR0M~=#b>ew_neVi+tuQTFcu8T1CXoLjig@>!iBi5w><*gLvg1e0elt6o8L5=fY?z{ z%*e=qgefeF8tX&RkXlRA7P_=o6_Y^7xQH2?#t*8yF5Sd?;yL|E8!jb(B!F@gpQNEQ zDk?Ivt?7^3v8uKCdAtQ{hWB6m97LepT36LRM4H6_%fEdO%&n8_TQ3K0zb0f)*Zp8B zK|{HaUx)4#|NFrJM(BtJou~bM2;>KHv7>!nhp`F=)?AQEAsk=lX+I0A%00Ua z*CnY(5JYr>HrlJu>FrKPmrUd_PGq}DApA5sdT(=6=#fD+T4}3Gz=XG)BJl`7OK1V7 zo{&E$od|NI@+wvRL&i|{uL5of_=yhPq!$=2eZ4{1!i$7{H_8^7BKl4=qqLYd1{CEy zx8L7-e~TONnw?0)S&}*HdC^C}DF`!upaq#o9>Qru=y+A!PW(qZdPifdV(uZ>{CY%h zT=8IKdiv#Vw%&OCZCVGduU?2QXydP?;y16p`I>K`E5dkcb@jP7_sDk;Y4Jw6s#eQ|Ub=kQx%&9Im&4F4 zfyWk@72A1vdC+XBOVJ#C?&@!kd}-V$cCMV=)Xc18i&zx)#y1!fZ=R{K{=q`?=#g>8 ze~Z~?1l@+?-w5kgd6_WeaBvn_PfDO1aQ^*Kkl(#0@T9qF8=^m~?7V7Hew8CcHpY;G z+ZAf{LSY(4^$uW;+8H8fdr%^ig2~tT*)YS{*P#Js3rhvKxYWF`B;025(l)E$?*qM& zvDVVuOdwS8E{L3drR15zH-`7`knfgQ?`c4{+NwNisKUNNKvI$Lx<#4M|A2ao zycgE}lk+$HlFrx$d8~34*TY?B_l`q8*>#=`{-_0mYEQRLjb(IgfWWR&}Ai5SA)UEzSx4*ht4_P;ad8x0`ld z9P~*(F-*7v%Vk2=eUetPRhuaOJK?NosSX8I{uve2hqHc@E=kt+!J}AIk zAPGZ-5|k)wdx_Ba#pqk8uYL{G`uVxIxOCJr54bgjlMBoU_IDeVkr&z=HHwKETc~|V z;MV6T%-KJKMD7%IjVe&)UJ|m!7xG>zU_xeC{K$ufO<<)Ti0Y?X&Ki&mkoIDJr^jM}H<+h#@&3%t4nXrZn@r8`M>Rz& zDo16Ba2O+Qrvtk)U#+eOzmA##*BY8|O?(9-v(HuW(&I@Pq!BQ?x@fv2FfJzDNopYA z^ZD1kjW~alaXq2hvB4I`_+_|ANs^BpVEKdVmv1)CN%#J5cCjO~{P<0>Hr}kn?AjhKG_cYaSITmu) zU7ZI(+E6f=N2%ytBr}0PyT|)m@38ic;9nrcFYcx?+%4l#s4d{TiBpQD>(S3 z3<}*CDTsw`7R=AJgX-#o@G=$@dsJd-j_(GA0|YoG!7>)*-4V4*ny4N$WH%@t@!;CIHo$4X{$gmqg^%A0?K_4?J9!pBRfYxnd4H za@Zj4HnM(0L*QM-0abB_f6Hgo?n-2A-d$w21bLs2k8lp7Rie;cvT0Ik-%kT+H6;HF zfoc;$06qN3$giBmY+{%p2^%Z~F%~h(?lqgG2qqg*a5|}{ol;hLI{R3El`$z~Rc;(KO^V05z$=bBv?o z*{(Fwd&vfc#>58MuCQqzX>R9qMpB815=X5H-fy0^&>KJ;Jy=wQYY^Q16_zwmV%(|Gi$KW!J z|G^Hqwwm|AS z0H-RWCN`QXdo30nfIYM=A!4^W%i6Y*DUWcGy(jIjfn;7U#ZUf2lrWJ1 zWU>ndpTU`Mj~jh^ZY6PQGjL1VFtS;m$Mg|r_89;CC>KJ`rgnw>kM^H^A;%&=XA~%e zI5Zz5T_LV{`s|tVzFg(?YYq_As#%}YOJsIAsakxlVF0rz)<%Ft|b$4?&QDooOtLsg-QKJ|DQOOu+H@-tYSph&BuTpB~wg#-E&N zMMI+^;j}jpC=+a?8qog&`CvL3L11idE@#s*3^))-JWi8-l6m*MRcb*rzWx=G1Wibe zLztLOj8n5gY+NAoR8J&P*nuL|ITbT$fIg$M(N#`BKquey_+kJR;+^7Q|DVhssQGi4 zhuf37y|eIB{=@V5)8YsvJS67-M_WM_E${9wQs8pGfSVsCm*z*7>oV#WijNhttJLL zN+r-*l){w@ON}$~izjr@V=WE1DO+-KVy@mn%6+t*!S8SRlk{CH%1P77h;bKH*~Mx( z-nvnI>F*O_5ZIGLqPj>{b0(RmqlEMPjM{%Rm(>*tCY*)w>L-*ve-yHxRPpTUyY8=B zDV19%wbEOT8ZjFW{QC77=sn5g!j#P_uN|JiJ*)W?W<^~6yT}aLT>|&=E_6kX$L6oW z05`IEv_LuX;`t<55u|jTZM+V${n8W9jZ;}!*>olYuY-QXxF1$@42anr4><{Vd{d)km8%H|$Y>;ur-iG3c?ElkqY+$lt@<Kxn`N~hZA#; zCQ}cTtk3R$mJh%}UlW*35sdGDoGxGghLIE}?O?V44DQ7w*%!|8y>|vIs1QO# zUA(so410_n@r=fa@EEqA+apWvB7I7x1~eHN91BH<|B883S>=DV_86pW!LJ)58W0?e zqqJi@*kdw4IXf|<&cwnpRWa@x#7bO12es}~d*tWmeGb#Vi>&CK@mCky&!x&~tL=5&_q13=!naQ?Plj6mSh*v~i@=)-)olVK1#2qI(M( z>2J2&OD2wmzo2Y8yA0x&08KnEpGx6}(yD-a z6-rGViRpSMAjEW}$<@*ltCJN*>oQxy@^3eS1VIJ!|61V_Xuk%jeLVkOIAbx?@JyS= zq+#?O1BE7FyQcfmUYBNV|KLCZD+ur^tnMoISosawSA#VEt2-O`T4-#%TQC2^RAtEo zc`(F8!1cTisNdL>EcojnrPLpp$XJ**uv5lkkbZ)TKbyF2S#-;Zk|B!tnkU4D5xjWp zvq%dkCy^{2GhgFkU?ch*1=s>HO5VRTIeDF)jY%e8e}5k^gyQY%c7qV1ZpdLIZzBLY z8!ocuvnlKp%3_3z4sl#BC%SS;eV&djYiB3cN`gR~1ap~n_4g;jz4atfN1lfR3VFUz z{S=BK#MhY?!k?7NQnafvE!j|xwzS}39-cQ7axA3!@2pS=1Yu|f_ zNsXcF%G=TGCN{wwx4qTTCtI^1v4iiV|DgzyhWL7Ki^?Stq+sR(Xo0rZYEN0545HY3 z9bT6H6H-)J{pFZrShK;v@%+~?R(-xex&oT_(WLTlAt^GFb33PV9L7R&eAmf}NsUe+ zdFGh6qW&X1NVNQWP$Vaq6Wc&1+bUsH@kE)%%4woWRa+&d$?i>yYj0}Oq*?AoJ2)Rc ze)q?*u!mWhd1`xfWCXRhW>JlJ=qP&S)FXX?^;H?140!fWDElpSgPJ%WDJ#Xb$MrRWf<>n zEucVy6siC65xOfBHBfQLK8pj-N~H*RKf;KIj%XY9ZLx}tbsBM}BQ`^QewMvlvMVI^ zwuui|9U3uPQEX?&G*yf2Z|-& zZW&uTKGsl@^6%UBC>X_!peY_;vaz-g?2Osb ziEUBaLM0OOD@jUA%W7t=ai1NifG1Qv<1Fo`pk!*mEmW9$u7f-xur-5Ag7dUU;-)R~ zz6PS$7Jo=})?ijImE)W>L$5)fH-aQ*W+C|5$Oo*4el2V;*SXcy{ zT3?sIHUiE}~_gE_H)O+RkCm8*m#*78& zWCYcR2iu0(AMo)U>`~ob=?k#CD!fOge7-YHj#N_)sut^yMP-^v>+?+w3KYdISTO16 z|JL`DL;odFhwPEW22$b*&85)AAZYStUlWv?f}Cq!>Wz3x!5%XeQd;9C*GMn%ZqV>t-*HMWY&`ig*L5KG%v^VQ>;6})pVAqp z+x=JcnL#s;l_VQT7RxlaC=~H7D7HqHt$2Oi)gEOhCG6wc?l|}Xtpo)~BWu_jaRCkJ zIN$;)lZ8Kf7V=g$$}=vR2=G_^ZVm_Sm z|6HLgTOgJXZ%nzN2UXNz1)jx?eD-F;tU(Q$3PGN*7*w%Fb3@UZsjuE=li{iRS#9Iy zzb~K!962a)aR_H&*yuH{$wdM;B=Q_5BPHJ*uiM!rA-;XUro*snqzHA>0w7+s8Qd*tD3VZC0f!}W^K>^$Kt$aQuWdRw58 zZ~0hC`Uyx(p~rMPPbUM=7KM=qq&@dqIptbyuTV03Q7Sjf*6Dj zG$WwmqmRZgy*^(zjcEJe`Rz}#Cs&c)=i{wEA0BNS^RR?ij*YSYewJLdVKl#*_HB80 zoqPGMmxmNq^y0nN!F~POt6sypX?1u+=EM+TLJy*vW(5`nhjy8u;WZ}S`i9Fq$y8%~ zz9&mNReGU6|Mm9EyB}+|+kdeV3`Jm+zF`!1#n3(%&mX;Z{#>o=bYKAl@L1x3H@Nf& zp}Rk9JwD=W{$cSjhgZ>H#5@GJr=sA!{K)V%uF08Z8yy9UZ=wTt+uc9e@Gfzx7(FOp zP%E4a7#}*#2Si5q7Y>|e|891!UmKf!FZ|F)=iFKp;_&K}M?_B14~wsD;TJ>tc6Kf^ z(M`fs)kMPxNKIP$wiyA(*>zmnzvaHzNAO_}C2wR%A{KR7$^)Q@%E8n)9Y5e0jq~}R?QQMOx zuA@)>-dAxd%qwBa;#5*-3fLc~KpA?1i+~(nN%O$Mq2kLX6yd~)(pFD8N_zWkS`7=H zTC|@&Z@HEq%lh_en8jL;aH?`}XsEKR_aEb-TDPYo_X~IX?>NviWm1|GD@RZ*bZ1~& z_ltIl|FE*QW)VSh9fI)uRh~5C%%{go1~`Zal!$+TAP|ZeVYzB`5Ad1Z@?71y8OMcr z6DgHIWx8V(#qrIP(k|8*U%+pt*>w7 zn^!!XR{8X-XPdkSntNH%vI>Ewg6@8RTb-jd@%;v~8lPW_A5eyl2^saY<1g$9l9iThW^{I$mso!l zjrv$ikpKJ8Oqd(^Tqj-EH@b_@l4?v8hELbV;fU4kJKx3IDzYjn23DTj`f_8XL1OIv zEp6=wCgQ8fy0tFim*Vp_oqusOZXEaIkY3KL136hVy~Iz5K2QWyR{Ds47~j^Say>^e z1Tk}WI$Exr&8aux3DhgUM@PTH(C)We7qdw8XCto1JHPNRz;_y{B$pHiNNy0{U)N4Y zq_DH`nly8#E}muij?+yfwRG(-3(2`{S{XQbYg5<9&;bI*x-Bg}vaf3&tbC zZ}vgl!>Yv?+Fbmi2=IDg&fP6v(wu6t>*D|obn&)8ywTV~o9Gssq1*SINTkyqiS3Gr zz*+Xkc=2xPTyhb3LjJCICnF)XLB@{Q0na}-JNrubo>tlQUp&UOKobnztfRDpK8T?A zdi7bQg=U-Tu0w(7851%qf`*8i7^O8d2UE5(_eiuzF^i@GzJ{ISkR}@adiSutyR~>? zlqX(46q~c^V$r*~^D9#z<5B4Cg3X^Wt$j0|=PmNL)flT9V+IP?_XoW>&+=Y|!gPVc zE*9O%72lVerIR^FUI5@i9*c#-ANy(`nv-L)W^^)sh12l-!|=yx zC8g?@Wsf-0{Od@Zlc`<3#dHQoe4PW2emALJ9{u#hWW7JQ?Mr9NV8QxHLBOwNpm+hD zA|e%UkmKPUPat%FQ?Ti}90t{v;|Mj-98 zPxcCh|Fhz@$^*gf>jPmq{3=2(AO=rua1%2AY+$sbm}|SOs8V+VI3!~ku&{<%>;Dny zsG-dWy#DppxIs{7$at(f@S3V>MJm!<*lP3P?H-{JLh0kV&a&)msa(%fcgf0w?uVK_ zq8jU?n*$DF#RkK{kzdM{Rcm!V-%n8&3jw~gz@%aorqm%ZyNuU-Ei0S$ZI<)e#!vFC z@i|Cei0r_0Gl89rqg9Wc9Ul|hAbnVb?scZDKB@=1Bh0&$by?!w+roD3_&1SvyOeCU z(X^q{Xr3C#|m#t|S^auep0BsiBk5u+Kj#|fQ9qzOhJzmK$5>6Fm6fIl)k`m^rRd|g2Q-X85 z;kgl|%r~vV$;5Im+`-Kbschb#zG2$rQJ&Dk>d#aroN4WsT~S-JdRpet&x2#!5QML# zWUYM}SzrGdDido@+e#p8&tz`trdX8~tY$y_F|nKQohmL|3|?%kBqF`_R1gCWn9)a< z8`B#b8|4uLT`xIF>4VqXjR3x0O~m>i(uZn|l9q%Z7IMD+26;vt z<6A!6_vamk?sQ*~MXjBtjebs~B!iQ=~WS9<`z%Z*`=A2`?^IPv9=6DDXQ`$d73f}Aw42U>03hQ+#;P>yXuj{-v z+`tf1l*TbCw!BU_tn{&_Py-?0)`}8{4f|> zPiG>~nCXqaZorQJQ#H>QG=3mD<*cu>wzjr>j=5jn5tpC(E=Fi@)?9vZV;vpCDXz&XSXb1YZ%PvN0;MygjYY{;Q7&r4NL2P z@M4Ji_bbQ5Y+6t}41HU3?EI@C0PhJ_lXo@Ic(m(&$w+WZDEFffQUTjzn897v8>*j6 z)ITd^(T`2`6e_IPy3&6%ToD*iJHS>)rlqU&+p+&4bKN7|){_`q8m8^pLzs;oO{e8D z)X>%F$7*}usVmX9U5~Sp!wY-+dqY~fd_Q!&nk39?W@PiC80ld->qHWvE9SrxBGr*8wR(|fTI}L5XgkqZRLg~ z)&>uL4|K>tuq>@XWS|llZI6LxyVq*j6KyKZ8!-Hi`0I`!xot=Dsj%hvK$_9+-;$t| z!?MRhx-^v*?@aii&IGOhOG{U*GBg;o^xvVHDfcWoPn+Y+1O3)N$M+7Hg;QQ~E9&+} zg!#`*4^~`jPZaTK(2jh?fBg7I@w>yV=ksR$^7o`XS3$PQULF|3RG|{!j7!WD!;{YMr#c3(o17`<*Vs<%POgD*f+oznomZ zN>3qj426CK=Tf8Z-0sToE0?gk06gJO85Oc#d)wPeb+Rivyhp7Yf3+Y`N5}1~cq1jG z;8!!E^moB!8N8~!%&~Msa(zP9!H`=JF<4fBNf}n*ra9^A-V^L>wf1vzwy586b}0R~ zZ_S4Xo$o92+U_vSeh{-CNYLK?Ll*4&75cVNo}I(NVY<#Icddcb5xPCapBaH?RQpBU z>*rfi7TAOf6|%bDk6CsIg*7V58ovkE~se8J`I=P|&-&ct6;4 zb)??H;8h!{E0uk&CU)6TnUNT0nbr7o-0!siPQd>zIIwBuq<`5oryIafm1pz&j`O{x zyYLXTpaT?c8t!zcG^3Qa#S5PMYbj5S!ZZulUjF==YjMa_yQ>m412ga4USyvJLA`@+ zf<6pUyk+#ogSXukSo5~m+DQb{GKh)hjQ&Ht@z;4iCpF0b`N zb57SM6`hR3#l8UX(%_4yt#33GlAb**U4ogp;|Z=rMJMX>lGeFwK5a*B*1rj7lgTYE-@#5Hp|1?y;%k;=7_YEXZw}@a&Y{=- z)H~>1g2Hh>-;_NahJ;3a-a>u}y}0WHz$O6IR7BzZf2J-o^|^y`fXoXxQx0?fS0CFU z$jn2GDdxqrMKEnY6IsjaQe^V^z;EY-nFVCL{W&CRQQ@e$+M$HT1yRevr@;vu~pOBasJU6&HeUDLa zvTv#~k{G$~F8_kWC4WlMl_eo_r)fb|S@|)P@i3ac(DfrDxU`1eAi#zRQy9N)bP4RJ zDs!1f^J!nVoxT9o{M3?0pAK>aqlECmXKYeSg@KmYh>$0qg{55hL%9Vm7hX91ov>ei z0g>yAseu-7T=KLy3H*0xm$JR%ZiJoeds(d?&%C_R)wyD^cbrhf(P88vy=;}V7_u1W zUUkuD?vu{E(Wkxja(4aaje{C=5=Sli{l5oSMMRO z+-n}LFc($nnzs;&L`CghcXeF{&#?_6*{SUnQD1%UPL=AgIzhmwz(p_>^oK7Ro*Gb@6 zB;hX7JUQQkZhMK9mge=76{dmYMnkjDdNVQAWW`8p2rC*rWw>uwu?<|lRqRd8+dfVTf2|4`^`?t^0b*fhL_2%6VjXC~q zL^W1hEnW6?$3ft}2Itxy`@(5tt0uloF>`1RspIJO1^P0a*aW$w^&nX52|zWQ%@w+G zc|5H)3sX}pcWZNYm5s)4S;O2?IXjphhM6Ei70fo48;2O^-Y&EK&azvEUwQLze?OhI z?H(ti(Ku{1N4HN)d^CTZDIC$T_5Z>tEc}W452Id`)?mF-A0#syU&8(DxI_1wZ9Id0 zX*8K6MfMSxc|AKnhqzbGKzpeg@vV4Lj#1sM>ap~4#}_+m#cQMMY1eY%!#>!Blxikl ztH1YI<=G`jBG-c}Pa;S>?SMD~;CHf^UzDA40M?YnEWW_94?93$pD4&d79Fi+w_QHGDNjJ2ZuCGlx0!03yb%0u&epMxKy0G$`JnkA<)i>m>PFE^=uH)p zH2q{bUjy}DW>@zV2?S3;g=^q%OFmTYY@()y&w$NiSe)+uReg+71c_4{xl$M81NTR+ zBBB6lgy=BtyX;Gx?CgS;7Mt39f8ALy&w==~$X|Ji49Z!E1kZL>?i;oF2-Ud!$&FM! z_2F&S8yBsxNH$l@Jr&8&9yXSJ;3C-D6mwi5D>4}z91I=7RLRHhK}P_Q>PY8ti)1bI zv%9;yPmkv0fMR^*HakyMWdi-R$zZ0twX-{Le54-$ou18!;aCL5UQ-5)U^(g=>tA4s$$O8Q=EY0t#11%p&ONo2UE&94h>HhYQL(sO*-67M(@X%D+!0owD z=pOMnXr%k>F-pdLk=d(#)Hpu8k|$)lRWxihG-E^=w)^4M|D)Vu3t&q^@F?8uB$$8=$RC?POSffD?hRLtLLux?3uj}qaa$hd~`MS5)BEV<+=WWd*r7b??N-T%kyy#_6aQmYoXI#8&i?Bq#nJPaRPnBX0R}?_TntzyzNj6blJGZa5a|jyibui>aM8S*^y_FGo zQP9mAKtL0`yBI;<0sIz_HBqc8Geu|zaY5IsM-cV}i3_cThU}cn|J{spf}TMy z>NwysC8_&va1&*VKs|5jtK2RDurn-dg*$rwz5v&#KLCY{N5+a9?(9FCWg1E)1mD9F z!LH8Ne<+8uxbL8PFWIbr{|Lof)-ID-zwWcW1m3TJ5WZngRZ$X6KD*c0O^41Dkv670qP2VSZBXl z-pFmP1KI%$(=%~|IP9r%WAqrv4gqk~oQ`r@@!AC@;-zfCzxUr`ansQ~kSU_wrq-c& zeMiJWc)j+-UCw+)x>EQPlKTw+46=mUo&VlqOa)OXuM)nK7axwm&oHInI$@>OV578i zsq0MpNBwy%PN&8B)A})E+)FTC zU4haXC~cTLbG?AY%GgPjXe**A@MQ+$889asc|m(Nb8n!e4<(Jm>@DvGZ|4lfZ#UXI z00|S+FhIVLx$Ui6AE3~4XAg#zj4B;KaI*9LA@q0%S~LPS1zr+vqjTEZ*@?9XSFGt6 zL#9#x%@b32piMoiOh~$~(B7jCY3Z}eX9*4YpZ2jkOEyL_AjUCz7B#!6PG^&5*{@Mz)e0^brEhy;fw4$lbM{GJw3d1$?K8ZwFc*eB$B^ zC=Bn)FN~ccr4zJ|K0T!B$VSd40)`VmuxS1_YE^y#{(U)pu`s}u6OBYVaQOOt`ioxO z`kkKJrar&)(bWFV)qEvl-5nUD2`JpTc9(dOLU{9j+D5At7X|j4>GA|a%>lpj1E`d%#>GgX0AMZ_tXT7{w@gW*Z02~iOnt`?6xLL8pA z{3EWLC|dcaYV$`EEAt5Ar&Lw)(Riz1@BM`R0{j$krtN;FUtBs7Gy&#TSqFN)*P5x+ zf06D8;OGW1=c@olSMazpwD%Te(v9EPcw>Xo!=h638*)?vtmB_^)^>L=3LWL6q*moR zUn@{)?|oM=*p*T&+Rxxs^aAVzP(s@P2@7@RD>Xch+j~#ZDHt*Pd!+Q!(B|G}+y>1c z&qjec59Io=OT%2bSBha3u2Iua2q=kUya_3c%+0}%;i zo_T;f0`Ni>%K#ur;p9Rv;93!Tfr@Ml*%W!c6A6G*O06yD?Fj|DK{z7Lh7-rM%?xE? z6ml(+?bS=P1-2RcUEg`}JvxvP;}Ntj>3}=y!uhq0%%L zKzIx0$SBO*H~nqu>#FC(KMbrCfOD6nNO~&9>wD zdE7_Kf4)TTZE3Gb?_YV3AyIB~s_9#7n4A$b8Bb)+V+vm9+J_nAWs`cv|CYPN6npuu zt-|!Il&Eh&I+LcvJ^F*)K1KAz+)u5>w7}c2h|N7fif;s90Pe~IB&3a!japM+o=3G3 z?O)0&ggo^GC<*})vk^!SBcRR&Co-}(2DfV(B48M1MID1nrZS*P?H15+_;F= z4-qRN>N^^i6j<_dg;6KssfgoSsuF{=?u>grgHwb6kbWFfKRLdKh^4 z;j~{zCp-Rf4yymKU-qmfr#G*gAVc_@prhfdj&DCW2h3h|T#X7la?NG)`*-?~mdeV? z9t78Xu6dO(+UlongT+jaDlKyQ)>LS{-gP<1bdTO#OW$gQ7XHk6vI{NWLGpr_F#zS zzYiZ2epK+cAFA44uEqcif8`Y%R*s0rg>ph_gZ)~Xe^~$p-_23RV+9e{O6?;U!MO+T zj-F$u$DIP?R5xagVcLCXDDq(zGkUz0G7># z6~0peLFb_3;afTZ#Q}?x{!$fyyADC_0!$6>cX?Qv>?mbpC{K-~>L6FuT*a^r%o-c3 zj<%0;A>+fYXs=u%w0?j+RO{M_1i;S^7jDXf24DyOz6)v**k@|=PAs(e7-4$=>S5JKMGyCk%T%c&D7d>^m?#pVn0djz*xDSfJ}`ac zvgJ7M^X_KGwi%yM#i%=i>Jj2(-QLt0+*9yQR=o!vz9$fWstxtvzdixuAT}rc^lBw- zeYHlbB&1hQ!5y9~(yakS5Z#;H4NyyT_5c2|c>Kv6&K{(Y{TzT6vnV&%PXwSm zQ4~%SDyk(xf!;NfRzUMJdY2ODYr@aWL(f4!rd@4Cc=#^S+rT?dAPJLbYVlWG_0DIeudAU}k0p0#)WFCe-P9 zLX8bi4DhCPcXgHJ^yG8{Rw0iO&KS27#lpY-_xx|}dA44by+rrXCjo#MMEPvX_;OPm zhD-hBsTkI;QT>?bA+Nr^Lg&?QL3}cha{1*kn`UQ;%jZTLS$f{PauymsuCg%w@@?qc zB#@#35x^(qejuFPDq5MMPxAoB9|Ov(4>^r3l1T#C1^`h3j1-mgtz1?M%$og!V`W7J z5x|>BmIs3ovMnkW|E*;^Qv=6iat}ZU1a~W|fN|?a4 zUjh8wC;7^u#N4kMRN|@_xBKk1`4|GJ!SRt8bU z>(XiF<3~(DLtAEuavT3K$sZ#Ml0NmHpC>~Y*hTWpribqx$hcrs#Kz8ai=@tj{w?%& zCEM?ZFv%qEbBaz!yPGjus&G{lLJB;*RM_5WR$p-};&Jv9GYS^1Qm|DaC)HC9(Pyga+MFvWcwpP>QXa)%j4E!+y^U4i16yHIKn@;_Ym2 zlStOhJW#>4>k*>aGpXliLc;dv|7@_pY~6zRVB7{55in4LX_|wxbB);f4sg$D*+T_5 zd{B-PgFJ+pI>qCjPjSyL8Y#4}vSL=L(3JVu2Fz-&Ux}=5B=|6 z2z%B{hIPTMZD)vKz7Ag3Nq52a)iKC0K++Wkv?c|w%HID_(Or41gAg>NBqI|KR5!og z%g(=)mbU#>iKfd2I`t?gW)stp34&xz~U@d z2?aI@^Q>Z^BVz8!uH6BwXs^|M1LEuEZiYlc?L&TQr`Oj+MNtX=CV>P zUP6r-o>02tbh27!`1PgvqhgJrmAD^=_}?lGCps1ad+6UU^?GoYJ8$2PKHy<1dCh)` zJ&gm5c;QXw7*(^ouW@e2#AcRIabSSV;!|)BBCx=e_VA9*f%x@v?noG!z%PqO?@#2l z^&jP1MI(jK{5kS$pjnUnIwf5NngM|Ns(7Ba?l6E1JrTP=wVk1G$tDLd3a<~#?fw**!0*J9n6%mpujy_l5ne(7zf~qB_0sdG(=<=Q-sa@;0w#PB z=TUF)E7#6YiY!P@WwltjxlrB$Ka~2q{&ufnAE=$djuWbH$UahcU-&_Y;Yl&^F1Y8H zTJvqpHaO2WIF|r3C>OWRPwk~{lQE}7VKEp{-jPU1@;5IWx9%Ar-SRuxfGt`(BEOj* z?oQ^dJL!Yc2d}Mviz!G*4;3wQ{0M|}4VS2PPyaStYxiN3ocHaYwK!|oE&Fd@BKdyf zz)VuX{nP#kwkVy!1@MCr1roW@q=>VAke2|d6}XWIU2)2#7qab2e{PCw5kSDS z%wzjWagY0YMLzA(T^VUCjm1Py0MS*qMxYy}BK+Z9&jLFBvkrh6!yWxMmny}&M}7^- z_|d_$N5*c!8@*U9iO~;v2;|UIYp8^rTXG(MfXX24V#&c!5ZtU{Ja43 za7?lnm4NT?1)u45UF1&o&;P3haw-QJ3NrGv#;Y!ocF^eVNlW`Bs@vNkb3v?J)S`&boraFZ2D*0lY_&!I!6uwBcsHHZ&hgqvVky@JhlVL zTw!tzCjGb06GV~m5);xnVD364EPgti@Dz+Q6#+HpD=}7TEVK5=7woD3?Jp|7uX(h$ ze++|$lOZ0?2vQUC)ptGZ`n$fKu-#5lQ_0avU-Ysy*naRcnKRflQFeMJUUa_l)j zC_0=rY9@2Y-oun{thC=OdZz!lC(R!0dL2vE5pqvhh(t8Gs@DfqrU=-b2-?Q&iSg~h zj4L@5CLy~eB<39On|Yu)oKX~FXFmjU+!hg0L9TZ*kwL!%{tb&mPra{Zz~mED-&SvU z35(tuT|My{7oD&LJ$1)umxLymG6F1Sg4S;ey`x#TM&Y%tUz2bWTqKsLm@ZB_g2D2? zVQRq}$*gVJJ9vN*B-3)kwV6E6pO-^`h-;Gatq3%dY@$bp#s&v1&r9{~*<_zW)D z{~(HtsFX?1Uc5*chP|&4u7CG4TRHbwc6;5{HG6!zkH%EJZ8L&i=f|^HhYaCe17MR} z%{nb&y$g6zzzn7lbTI%xQ~VQ{yk*+}4uXJyfZdg_=Fc+st_M^@W8WoEvf}9yF+P_= zr#D=v(BdUUt@FZ@<)c|E6MKPPBLax@qk+_rkvZ9W&__i z=^-wKSF(SpvuVuWpO3A2OA5$3-YRI64_iRV&gSI2;JS9I1 z>v;4LF#oIjt>ghW#QL(r8T>_QDsTM|Ag6o3#(-BnF6aQ*9Lox~okEE;Oc(hCUwOmc zIBnf%C6S!oa`9snj~4yFlPY>adnb6!PWr0J#@5GMd2GV3!Cg zF99c32!fJC%d)0EBzDa|bPmwLFws~H@>2O7Oh_=q(rp6J01-?j{dNiijqe$JRhBS9 z7Fn#AeJ+?wpJ#zjM)Q&H=*`Vdks>+C2-qE~UA}R{*|H_1lO^VXx=1GJ4k>6cqW<^J zrb#079ESW0vIHrJ+T~4kJb0>eHBd!DkXpHW7Qwh|NEaf2xBb}JWcA}@Vf)Ni`8{^& zmJS0Y`oSr}3^~3!n}Ht3X*s0F&Y=wuDrU?5!+Do&qEV1qi}mUd%xm@PVx&&g4B5qh zbcK?vFD|l!IK@9CI&e?6s0P=^4nE#wD5KE!0T)mw0Sz4UX{7Ft)}krO?ey8qBoHru52E!YggbiFKSNEOG{A~ zzsJ9?_x^lc{=;_qV=QA8at6U6&ff8rbuAU$hN`3Jga?+Xnt_uUSc2hk?`!9k= zUCfhI61tD&$t#qS*+pW7>dkL_Dx7Z>JUq8YPM5e76t5dK>>oE5PlCMQ29R5L#0Ovj ziqxcQTox{&um>MABb{PWypB-=wU!ZOGrNCION;1CsCaz}G9J0qw!Czb5I;Fc>l85a z``lrBmdA++_7$R|`_aC!h*XpQu)qi`ZH_v0nk23xAJ|e9@G^<{p-%DAS?;w*R8@Q> zbC$5QY^{712z|;qkx9-ocjigbso&slu<-Q zv{Zc`f|=ka+o*y9OW-I&yP=5r=3f{E_?&=uk^n6YHkps+el7qIcwn9ACSMQOav<+W zgec$e0zI?aiHzN)eE_#_(=AHHny?^`b3$Bps{P^eabo9yu+_Vw|GEB%t^sjCpN9{h zAo%p$DdC*=fky=62|y5OubcQi)!U5b%&|`qT4{qnI;zTEnDS&O#1+wJC>izwYXT@=#liUXd*d|`uE+T^xj7e!7|56(efzVcc0YCj9>HMeqZ}mP4NsGaNM47sH$kgN6ziX|G zY|s8V>-3EIQnLWIs+RA0IC&M`co&;kvDUFzKgZ)5miB!LK#rfItq`34riAMN5MN$G8tC!#F zCZ^1!qdhJsH#{;Cn|TauMUDg8%YCoAf}s|nf)709kw7m;DgFTA zF$^m$FY^zvF8PsR{-At~j#g+;`MUx#?;FDq3~iDlp6H5VRp!4k5oLt?p_3MESR{CJ zi-7zxse`Unt2lJXJaHsf~CZ{Sq85y%=W5Twq0r<-fQ8#QRfJT z+>2z9v*%T&=X%o-k~QZm!`ye0i-zRSEr$X$|D>|>X(e8th>deb5_-x-qLad9Q1%ki=##C0|g5tOhI7j z_+G1Eu>lbjca>HnNGy3F(z3TV=lX)ixfPLC)Ao&ZoVmVr4`PY0%q8*{|zqAC@n8BBn7V!eN%5KK$_d8#&z!7yU#2w2BGw3hL}YYv2qy<6aIXO_nXC+!Oy;JOL*0ExFJPIMOMOtaLKF$EFp zh04h&p^T%q^=Cjq7|&Cc#$Y+6G)*2%OiHN`aCxou!D8`>dcM9u#xW3p{~Rq*XW<6}JagGsMMQK>h!}cP6#X^!J^4 zuLV)5P7s*S{$@HMpSIhp5FT!{>(Q2J|$L}SvYS?`Z0oF#S zh)F!w2_s(P!ID}P+8xDv^UWa5dmx{r?qu5PP`uUumet-HHzYfkUXKgvG1`h2bw#By)Pcs~L( zL%Kj_CD!5jlV2GD3Lk!L|MZ5`k*~9Ckv@#P4m&#%jX;-XSlJKw(3#g*#-nANsle13PX`qdftuZno9urGW37^gp^Jf1}MdEWL|*<~H70P|s$> z_;2GB;oXP^Dy+Hf_F*t6VMqm_&8ID0VdA?s4ZWo3?7y) zxmwnen(tKzZsIS!(-Sv3Sm{xW?(~Q{dgOX^(F?d&`^UrR^db2Z)m6?HpGoRwFs$@s z=3DUa@S1PVbiqbufc|e6wA*w004zO~&n_JZpzBW&9Gfap!r5$+styh+V#Enj!|P9W zU$=5vWAx1d*&7k-9l+)uGkzH@AD&*YV7MosREl+h@xV^d{dNstwmJ(0;1sFn)izFC z0-V|JXOtn2_-JS{!6XAzW?Y5`6pbJY70 zjaIXT$mK8T7uW|OXglXH|BXzg58(a2hfHjETmL!f)OezgwSazG;xo(jqYlAW%1s!} zadk(T--U>4w)h=A*w|uw8P^V`z6s!QC9uxlR74F7D%t2j1~R_ED^P?VON<7ZqH{wq zT->mLxdXgHGZ?JWnZxoMycFC@HZwK}a7>>l5koi2$8nDrQ^)T;D0vOy^|#6D{gQzz ziXRJKjwJ*E&_KFlbn{fQfUBZzvm zGY(dBAx|$MVS>a_O%;iON_Ka@Fmz6c4pzS+CeQ~;H=Z&tN+{?P@W=f--~ay)d=lL8 zs*VACYTz}O_2--trNy--y?`})q!6OhkH5!42lw`f1q8OwCAJ#xI2>Nn^2b_;yjR}! zfWZPpf%kSTa2>keilk2#SAZt^LIm*VYiwqL?N>VMm|(5PiA)M{RnrJE8tJdnbL-oB z{WDut8tKk>fpuQyw;%a=Hixd!T!Dni!0xB;`;kaGg5aqdTYX}NyswDmw}5Q2z#7n{ zJQi^8`}sTtZpZmxA62_Gw7COn`CjQ9Om3^~$5C{F)r2C2E2iX<6~(1JG9QQmwzgFg z--#0)7R6cf>1lnTJIY)Q4VJ{Zjcv<;9TlKj zKT05RR3I>;2GU>JKF5#4si-~SwRkwk7v>fMbz84U=N>(~+a@8QXBMW7ktYAOGvOHn zKK>&lE(jJDmIW-2=9GepgoWz{z?)m6Cv3&vN}mb_#${6wl-sFxucFvxJ;1U>QK0B| zztq?e@zjZ40>+pcY|}Z-5*Y)O3amUN*wH~;b;@U<^_}d`#KJlxH`|v93;MNnQsGp5 z^?|`t0`x0E3d*uPJ_UuXt|p|g6<{_1yJk`0x&g@hfph}dN*}&(;l*z^H@Bzlr2~UD zt!=c=s`oa&>dKEQ6(kD3WV2x-^)6yrV}t2p%3E zAD^9_Ra~RK;d*+WI-pYWGIV9*svEJwOT@$5C| zF(8H1rUrNj;L!QrDE23CgTBft8os+!)eJTzT4j*ysiyXrXZfY$LaT7uY&Yxz6`xg) zFx4nImG=ChW0RpxHvcKz=Jaw<|o-bvfSKI@*=`n3uFuYi~2&;YhGFX6+Wou#xbG z;n$|rtQ^7|G<0&j(gR{se}79WI3{VESfqT);n^iBKScWPdg3>i=@ls`)VNG0sp@85?CvCU9*RmPc2Iz zv@Hk5>H+sGFd(4SGcbH2ul1Lp8U?jaAy%@EUdnz* zQ}J3lMMuDWmEG~N_N{ju#En}Vq1IwHr%~p@N!4s=_$8iRpy&Ju?{tmOQ^D;8#vAmF z`AjLkIY5>LY%)Oe`H-j}%TIO(H2N<7cR;8QhHABQuN&}lDXr314z890Jw@~0{RNrNq0ro zgy^t?2WIRwW?h{v)6w4MNB}u32SZfAAqyhFxB2C5q^yhi9fryu9u#CYS)A-5c(CsC z-S)-dizh{^0I+y(`A~PEzA;k|0x2gAhkW~8S5XkIr3U|?gW&rz+6IAmh^^nh43CdE-i%-xhMRHfST?NdWOuo-Deyk= zWlS?-d@m%JQCGcg7ZCA4(`kK@Qr8TB)~J@?5N#)eM7 zKvYsepUo~5?;Y}ceUHNco2+F8m65AJdKI=_y}iW4CXS>F!=<=_ynttuYZ6^3ZW&G| zz~v}8+{=0GbUd_6|FY_2viTjcf*HBEAq=eP#IE>kikg(%KaD?R)&DjeI0Bb2^|^kQ zUI27W;K9tGl8L(O%tb}5CPGKo`KcI2_!Hm*hj&ZJQtvh+?YdGzP6O~e0=4xg^w>&5 zY%30aX z*DI<>gpR0dYM!ZIEbV*#`gqcj!d>y{=2ofU_06$`8pqlJkR*VU8dxr3HblwGD}}jb z3|67B5vNeX3n9I0Mx+PZ5`VL);mOI!@PV-y7i30m4m2_yagBk2VWRe@$}IdX=VYuk zyQC~c%}`4NUH`zD6k$jLR6Nq20-ssuc}Z*NY379~)}Ee7x#42lX#Qk))LqbO@ZYdJ zfJAsiw?4i&1$yQf1-MIlb^w$}mAoI>GyeL;3zlbc!x0KoU~05nN=in%BY%sdQlbx6 z-eP>rwtVUQYT5h&u`l&8i0x!}R(Wbu8b7@~`riVRJM;Bm<>? zV>aV^axSeU>*9n*a~0ctfl?Hyv?4^Z{k?0`xX+i*tE|@BeC`WB??zNn`qQ(sfe5ji zE7`a0(eq~MZP7{PIV5rNhuO;yZUsQMGs*TcH_G_N->k%G^jEBsCVLfJN+U4x?izFi zRgLlqg~B%u0sd=|T3(b(%VEre>ay=U!IJ?BI-SQT7pyI# zUw}J%to-+=eE-YQwFj_uPdDD{iJJq{Lt+kSg|tC4E|z#n0VhdbiawOQn>H5V_6E3Ntc^(+3l)OF{CG($CtA3@~%kl*7+PK1>3K6rwtuU%*1rUE~ zHjvP@&;SOF;=}{TG^Nqz7Veo$5lh*GkGr|2-Gf?_>N(0r38QXEI5qC2p491Sepo-# z7#3BxJ#cxjLu3DR_C`FVEAZBhGV|NXV6il6#$!T4vv=>FgYk*ed09xTC?Hy8XIFVAm?dt6G&1TDQvk{0F#e=sSXJcVeP*C9J z=C+<^t~Cdnv>wcGQ#y(8#s(B)cEf;~ZlI_X3p4%pcDJ96ZQU5o$eP>EFR0D5Z%>@z zN!56+tfrIA6+iPP9@qC_Gyj<4T_@WvWC=TcFZ54Ze;$`80w5Qo0#Sz6AYzdicwvOv z5-xRN{-s=)4J$^_5Pp_XfF4#kh^%3JC;z&t{JUae6T9FEaF3fbQ7D8XhefCl0wWj4 zaGCL*>gY|k<|zL5WpYJ4wtWE#{izp1BOw6~rp;N7c23LYmiaCetBcDeA~du?dRLvvgg>CXs=yYT25z6 zIlz=FUU7`8tE*ecx3#gs6!3AeX8Lt%P=#1t6z(1$S3GIp~_+3KtK2K4=!R*Loi0kh`agJZ?TWY5R1a#pQs z9&qvG=jS6RG-qe8fz_#oNO9(k0rW{|aI7(>{M2m?ZLhqYw!OXVwXq0#GABBr zdFY`V6hw?vUB#J`ANaY4PN>%VK`O2-WTP8@#>9k7l`lNxd40i-y7y)aA0Ef$ojYE~ zB~DKu`W);zTeY1Fj5zF@pj?671pX$@TSa$?=w4#E$Mi)G4iyxG{R7>f=q?D*DF1yP z8;S_ERmB3E%TqFNXhF~6Q46suwnyf(p2`l|_j~@pZuZWtk$iphqrHc~pn=Z{tz^9F z&Rl+!?X?r<`Ptb60^6X+^Mp_0-moxd;CTySl-`zk-Fcdu*FWvt6+5rZ**myHSz4MG z*nGBK>-B{v(a2A!Ema;PsU>?b{eq^BA8JW&ce@6!n1Jn$qVWT?Z0VVq9oZDj7RhkE zu%e1qfi|TD2P>qP_Ai!`*R6x1^?&+~j}q?FTWqP*dtxOwIrFU75QyFz!3ND>YS)6L zW7OA@FB~2`(!9ZE()ehS(|C?-d;9p|kBN!6^K8LNZ;!fCd&)N5*2>Dt@`{RS>`hK5 zlN)Ifd3!`Bo0%u!R}aJaeMAthVfWj5UGcOmnTI=j0Hm1161SAYD= zk<{>D6WtGb5B-Ojz=)TW)8vuKqKPZCv`<=8@3i$1Djm=J4%8>;0_V7I(cTD2t`q;X zw58kzW5t?ZTM})?+z)E0!{64s_ArEMY%gQ{6M*j{iZa@V30b6Zi%MYG4NpUP zfFx=xrFr8dLaU zek+R51ubFkC;z~lF-;hgx8yGq6BY~a*L3~XW5<&@-T;=`+kX6b0wQ+*&!0L)V=xSz z*RAX1O=>G0=M5O(WHi4x2phtS7$%&`sDyj!?FCy`7B4ysFEp4o>)G#Rj^S&%^3KL0 z5};Hq9;(>bLGzBs)Yk!y^>fo?#}gFH{9v^LaN6?GM6zXZ&BM4vq`ZpEUF$QBfYZKT zymNVZnV(-Mwl6Hmw@Y@7U_ZraF_EveGgXJ0eB$bgZ1Cp4r-2|T>fr@@I3)2;MtX^` zP&9~Y`|L|JUK+nxOB|S3ieWnq-+rF6TLK$aOB3M*Ph5M1MCBi0g}KDM6{Y6FDB9WS zEEuyM{7L}}?c-IS^YWpmE{=+WhKbwhaAdt&D#&7;Pq2>~=X-@TmIP$i?!c)Y zk6v*_xhPima`Fa3U9r)_xr(B2>3RH7gEk}9@wNKj}f9m@anT~$7_Vn zp(VX{R=X>mq)%=nUOn!<$<$HIy}evd9UB`1G)=oiWq(!Er;bV=^iF|Mv9^xR3;UIB z)>U*i#EtoudnPO^H!z$*(WY?w#3#sN42eolfI2BkX;5{H`bnfyGSVy*hC%BhzlSO6 z|9+=~f_xWvpR@0ejLUrz5*&QU^779MZ{3e#V(>dB1mw{HBO~YptSvmpfVTy32`vWt z7PgF5PJkk*OG!Q8!2Td)a?7t-LT@Z2gM{IE-5gT4b*kSMcQ=!+o3nFHRyU$4ao3R- zoc9v9GJPk;MOI5-@i1*i?c*161J2?@_0%ZY7nKQBp@ zd*h|U@TphWZQJ4aefx_xInnnAxJ+5?uOItT%0FHoM#bnHjK@0s>QM~!krfxtrvA9IR)@I6iTJ_-rJPyNJnXo-LR21W^3ADHexL8J8hNYnCb7}LjOupV!J zxpRopuZ4 zv=o*A6@V6dkYWdlBY04XDtKbYiZc!q3XQipHBFD+pc#+3!wa9IM)_K>`NHZlL-w5X zW-NCkon#vqpSWa5T##8?TR& zqWtAmV=wv0&{|(GqDYFyo6WU%Z&L}rM(jmCx%^ODXKneASP2mVNOfTMV`=m|;JdW@ z`uE%?f0buwy=1Z(SzxK0TlndR;;BssKN!sRoRcHvEKcwn%~Z9V2@ov@CZb&>b=S%+2q^i{Kt zt74rB>}qDnb|i8KxL2cqrC zJl4dP6fSSp^RZGN&wCsz)U3MZ0 zm(hF1ofZKxJHAi5dHOHYp#3b=u z4?G1qIT(O8fly@_+L8apAnM(j?0040HLt0;{&F-&xbdr&2-#<`?0jx4U)FPN_q4if zCG1pkk%2g)xXT%7xJF5;GfbClzf@23;qOJPealKok8*J29LR)f1l(R|4Ub<>PI_Ka z$EA$=+R0!ftZqJkG9Vw_)^=3&16;PA^g{i+i^Fgk?A-32IQE2JVCi@rBPK5sX8lom zzqW5S)?6>4N-7J3511}B+kZ%>;I??It13`{oqY{@UPMH{fi2~TD+AEf%{5t?XS(Tg z`!XoX%UQl0rDrtTiVq{?b>7Sm4LdWjuVNhJ)N%5q1rh4cO^ooS#1^%Mh`Bs8Lj-3j zT?)?kU}Jkb_*Y@QyD&%rU=?mmS=r%z4w`x5Lr#${14t_778YQeYqGqAIR&n#h$|0& zGi=|vpearwL2o1mIWDrx@V(c$>U^W8IE2P>gIQ$d#F3c2*l(V^MS{Y9rR^8u#pe0; zc0f_v_8UR>0IYRp6%j_J+S+{;@If9;Ou*+s@-AniEB6F#Hl;bn%mGZN zehX=s<~%x9t&*#XN~E|rEG|3$iF|p|t^B9G(CxEHRO0_yU^ui=`k_{ewH<*UDu0d@ zAD(y9%0UB8)2YBYcSLlw1EA>vK9AV@@qu0K4mU*24nVlJZt;d!R8-VcELvLq(Zq|2 z(sg4gJJvDOjIfu_H+2$CRNieMDK4sa=IB*}YF&XN!R@X*FB0OSAufaY_FcP}+_K3~ zT3Q;Y0Qvd&9+C1zB*K0=LdPt;_N#W2wtc)9(M`=K=GnEYXTs{K`SOm2_eB`3j*{{; z32Xy#*+kA!+(|Ij*3uyZLB?ut--eY#%|y(G6UmCAD66KBXgFKaV7vIu0x-pBZGtP%#LMo*h zFI#ifOu39^n4}C-{8TZDLaJD&(e&HE&wdMyTuh5L8~p+w6;dN8peGG>kQctJTwITF zX_$f9V?%i^6T!j&9^*aid|WMJR7bGcYo*_WijR5HrGM;`EqCQXJNfs|n^?B9uhgrm z@#w*diH8ys0AJbdMr^B=@oq*s8mtp|vH#&=i0xwh{7g%T+SS<_8~nM-!o275>zgwv z(O3Lbuhrj~3Tq1W0BR)}#({I|!(evYtoR3+6?n!yk5br1*q zP?~|8U?4hyF%4M)35~|-!2w(fNGW7nGGiow&kCyV>7k)}7g52Vr+s}F*NwsvoZ*Yl zbS1JHW&6`S7+Cke3;pgjIy0f@8!J7K{>C4mF7#NT$W-z9W}n15OTKU9;^JZi)c$8Y zJBy9|GX{mmk0+n2E1Di=M3s*}u8Ww-^U%c)! zhl{M8+yFij6|J&CC_YC!D^Jnlw~jAJ+w|%#lg4*766li@Wg6YuF7wkDQB~it#O60< z3b1b~7is*Ms*Uwp1|+xe@Ngh#WJjVhpQCy^jD{LhAiQJaAIR--aZs!=L-m3PDIt~H zGVY=gmAGQ<3=H8e3$K*Eab!6|aJX57x6H*&_Hveg~YO}`0XM*42i>)Fx^7&7rHfiUN4rA_LXVivXf(k-e$mQ5G`RTS1zqaSgC}m zkQp1vs9*D^2hpXR{YV?Io7GoQBK=%drf-ubS)=Hg+vN9y4W50Us@+sh@1vYvM$ZcJ zb~g9h=c(Qw-GLfT2HLy*i^_pM9fDa5!=4OV#|kd?K}bcXtPLk*Pq(bM-NJv`-1-2M z9j$OM-U+y#E+VyUOQ@vNVV`hD^_=t^5k;X*1{%PW^kVGdmRaBxP(i$k1hQ$ybVckHk&KyfAhHc0R2~iX7Hz3!2 zHRmu)^^<(-9I^gOi5~BV`Qac~5*hWS8k$$eQ+O@X;KJD)J=FwUGC$trq5mQw$- z-WkX#%~T>ijB7>*ymdd*a-U#I4U$KDG9~bHp*+r}_bPLorcX%_enruAx>@o_&_C3B zF_V^rmxbfqO8tOWY}DyWZo}xI?&lD%S-N1)gAaN}`Hb^?EJO;N?gUB&8a$Xj*(JHT zL5t4Bc4bMTo%Y={j~1N?6WIFR-0F)Xk>lgXI<06gg1B+o`Vuax=lGYh%eK6{{Q2f+ zp!I|vEK~SqeeM-V#JTxq7WpmGUa(}m9ExGylqH}4bzyw)Cldq*DE?~ziKg^GdWeqB zj-Zqy$a63hxO<7M-mL0|H?DYnb5l`X4k!!vE9P0(;|Y)CMgJdDUl~?qx3x>Bln5vd zl2X#$4bmdg-QC@(bcd86Azji9(%s$C-7M-%-+kWw?VmnBymYP^W8CA8DbLiPyA{jQ z?_UzGr@VDcMkfW@8*)O*2cFC+$|U3Vj$D z)71SyT!W-=A*w-T=)%+mlm0yY>oF@Zv9>v&_IJ?{t{7UROzFPx_A4|#;9vg{#E zo_s$HiFMQ3Vy#HwyCSIwmhEt3K1Yyur~;zSjqR^d+}CEOYaM*vP`_+R{Vm|_-B?&y z=F(mfyL}JJ_Pp)4)SP2T*Pp3(_>}7TSm?Ty_l+_)nca&(ZxI^z|4`Ru1*n#*rsp@= z@PDts*U+aklIm@nqf?8E1j;SKA0Nx2>K5%)nko_3VAGyJ3Tge!r8x$ScUj9FOfh8b z;n*eW7+yheYf5fdZoQ34js!*ZzPK=g`M4tg893SiG{nr(QgqChmX_FHlR}5j>B^a1 z{mYBmn0tIC2d9vS)bj-%iU0`9#>0s9bF$(mETYFW8$n_;IH7qaCMMhZ&NeBbJ|sT( z56r1`?IM~XHm=SRVQt?Qfp>4Bb9D?w>>n<3NcN&gzLuq>rK6)G**A{1p@_w}? zYT)a0u|TZ5>L}_0AvR>k9l+MeUE{m^Wr3v7&_O`S6x9XN@9}Mif$h~>!Z~A8cDQcu zu<^4!pO1_xXngSMS&;#OS}}g@*nJInuhMji%+H4FHhUQyE?soYhuvzi$1z4l+6i_U5 zbCD|$B6l~iBmkZHD|`bJzi#?*!m78JwUJBOCSQmdd$?Q~oM+7vKI zn^!YXOLIF*zOcXamB9?_>c4BBo^w(>U4Nhc{^2O2iA`EncJWF8af!$zib{=IjEYsu z{lFsZ@fjS)a<>%zA1MIDs>3sFO4y+tM$N=>};qw0@uBgHyK^>=(h$4!Vh=vlZ4Qq zS0H*A-%cTH0WvWR64&~s)@N7o$x1V(k_dzU#jy#YuU8}BM?SD2{oP`u6mW_&Cu@NL zhvhM|KtSJ0@Lo>{vFYXXHjrilw9-KYP>XfdQ!}!NzNPMj9y;Go&%TW#7HoS`BsMITg>Q~qS*<4J0bZ8PB zv^0x;6z|M1vvMN2+5AlaIxp{u07<6EWCh24+--ef8LPB-ytM4~-Q5s=x8@F?I=NHg zxTfwdFid=5bGt6-6H2kd1fyX3{(eKYC`$LbtM%wwLgtrqLLX}FP=4>0g+0(ClC!Wl zfUx(FkPrqN17Ut>yGTTLH2`ZG8XD@O;`x$O3HQJwl`w@c!Knq@&#JtjeCCQ?renXk!5)-dzMEmGKPozf{HvN;7EL4 zU9WIuDt~-QT4`gXs{He3#_t^dl_i&D3;wY0PP=1dpFQWA-5r}UOs8K^mn^e?4~nz9 zQa+t5iIe|$m$PME*imIw=3@sjugW)Za2|dFo{JSC_XXh(0 z#}}U5F@LKzC~dD2_w5_%_x|qpFc`4tT!+>7iepukpLpyiM_HY3Upw-{=j6OUv3A&S zlu=?AmJ$**4ySG0ed-+^L_Gi-?VrkR_Y=&m{c`E8y&d?oSQM1#%+5ln#@6<#KeE5Z zBN*!eqRd6FYdLI^i{>PlrNUAwmb;Bz?)4Onu58qf_Abj{Kj>XBc@Rq(3W2?~q@1=kNulZ6vzg;=k{ns} zasA96_GMv-?`OvS zAJI@*Wsh4U9rWApqkz{B zYW)pn!%WjVkX8qJ2vHZUQ{SD^O19Jw&oyc_%f~F~#DnDbQ<=FxKdkRwMPBwhRLp1A zxLLDOtFK6y7ot7G0sI{aJq0KtVv~1xA{3M!ps9D>3fE;)<^9Mj;0v$!7+a=Yv-u=i z4ktaYcPwKnSWdE|u_wLN{l5GI+e`;GDOA-{awDucxR*&|e3ZLQmoCjIPnn79|HXR%M%k5 zBO_7ww8@If@hpO4NaF0?u#c@V63X5Ui!S@ot^GAqxx}WmN54$_jACb%sjRQq7>87I zJ&#;>Y2Lkq5%i{|r9GWgQ&e25EoWjvuMd6Uc)60#AUdTuxnZM=)TjnUfK+xzLhdtX z)zz1(KAW1}70od*!rCJ6uD6g5M&Hw?cx@`A}aCTaDUU9mzL$CN2P znM0F}tINFMw%BU>F5R2_amonK2k{VE%^T=bou+pl%+^G4SFnXs@ewdmhAADm&*OmNrXjqXHMOTb8F7bE$VtQII3PMwtDizh%L z!^1(&0(65uz%+B%%16>rrCzqSwqo)@cS?wgNZB+oc-l|ge{@ifHy~$ix0t-C)rixu zys_bfY>rQz2f;Q)ji?)_KR#G|Zk@oTq-8gjv*ESP(~*`&HiKX?&ySBQNq-5Y43P9q zPt>X~gfWBINyS}B4m%RDA2+tS8hsi*uf#Y^py{4jKyWO;!lG1v<0Li{zv4B_2nW>`=;2#|78Wfvd=;sH#hqL+?7!B*P zewA7hPo;{3W7PKD$i&1*k%cA!16~~SEJ|^JfvU!nb-GLCKUd-3p;!gd+KEAj2eBC% zXv+W(!N}YG>8kHw6V|r{_)a=zAO}1D&;(dMU}1KqQ-&8>=5#)$ z#OQ|SdviSuCnNrQu@F{p%X+SD&s3ATDrr5thWExM(G&dk}6TlQPgLn1|m zRxmvzc9eZpK&kpJ+8DW`oTv9}M<-OfJReW@ekV_zj``Q`b@uiHLV}hnck8gLSg0)I zR{{cWB!1qY#roRVS5PvEg!amU>lJVl?RP{-MhQ*G}EJHHjq+&hbn#Jf2ir?w`oq4(j;gvQ)yx zX%6b(0{;tr!UcnSFW{VF4E^tO;U}c7{-!VObx`P_WNK`z{r(iKq2VT=z}e4>l@tk2 zWdL$WC!Gb*$w){@SZzO@J(c6NS}pMXyffuKYFrKOh+4U**<*N>P;w|&;$RT?5MW?F z{Zle(>BhB+Dq*PZNUg<@fj;n^sTudYOnKrdu2qPUF5Ehi-W~K4Mp|o8(Yt$@(!?3u zS1(`Uq`C)<`)#XAZE}`3;thi%nwn!Apt@9~nKSg+ni3BqxS(!LF%Z;H{ z%<$utVioI(5{-MpVKcc!Zu9N=0#mGn0JcW|Cfd5IP-)L8Z#oGT1vY}ZFYsS)He%XY zGL}41-bc@b8E`g|in&5WCzK**e@;J?iWZr}HF^YNzmK0j)%HCk_>SCcspLm`U-rlL zrKM?Dz&bhYHJQE$wrezF`M2F?CMTWIweQ~L-uxH~^>Pgr+HN}3UU6mz;TkBxI~p{> z;0VmEmZtFPcrbIt+5hiPbNL~1CWOeT>fI-0%68Aik7g)huh`dAT{n6zvYD| z&6cJbR6gv#lPxT!o9I4@>2iQ!tZEH=GoTsdDqv*i?(kc7{Yiaf(nsR5?!!hFb*}dykM5J4B){Zt@`LlE) ztqKt-Qb(@Z82g(Ay;MZ*40!I1P9CinI6+aJv zJOl{t>h ziBr-L3T|x>zC%BEa-nbgHfZ&`^3T^kq5MSo%HTK|2?DvK(Yu4~NkCn};M*-NU6cx0 zQvU=1sJS;F0$MhxZe(A_4DvT1FdzViKwb9}&ZkY;f!%KgbA;mMD~XB9)$bTmS`90H zhKTf_)u~jEoucKMOCNpB~%+ zQEU|osD!q8Ocqi?-b@mwJE090+0?_W-soU1>TDdX3e4(q;LK$Gj3Op0$jhC{Y37H= zlt5@J}ve?T_qP~6d z$lB$e8h3YD7W}qxik-Q5L)>yl%FIoPWr!V+%a0o_M-X#G7Fap=0gw8f z5gdx)=q=o|B}9*hVleSYIt%6sHt$FNQA2lcFOV@{vU!IwOuASzmqBNf(zXZ%ul_78 z^*&wm2W3Q>#*WvEUw3X~*?eA^C?h7p9B9XlC~Fm?5;N2_xaGvMaZ>y~@0zpYKAny8N~St&nm)Hl6I?e{ zM}8OXdsA|?z^wJ9>`=D3#euR|L&h*CbQV|Qd}aS(injxrT45TecP z2U}RfhJ}eAiseKjIxB5Y#7=iMLz8SW`iA@aRx+jt$A)i+n7~W&A2it?{bf5T9}!&G z@IRmAK$U1~&~Z%9#s+dc3cy?#m*%^FM&1J>Y}O6H>6(2W7kYY3z&oe)qMUt1YvaS6 z+VMf&a#Sy-n0?rd&Tsyv*ZZGW&j_~j8g%4}=)GPxv#zh-x~r5#TzOpGdhJAjgFnM_ z@$xXBloB2u9_UEF{rEv)2CIkb1Ip4vEUdb$f+K~hA*XU zKF#kl;kP9#x}TIEkx1VbXMe0S8LuKrsu2gpmxNG7?Yj+cigsI=>2ea&Df}Z1R%8yG zJR{SW%5PgUw6fy6>;|R=TJ-0`L(P8Kyb4TU$K?r1;wuxJP? zCb9vEv5pBU=3|3oH1S#~lOks0U<$AR>Ka?CC@(G^v=Wt`c?@bSX&4Ho;KpGfk|UDS z)+QnvhUOWnJN*Y(_@`mUr1?vCp4t>L1UD$A4Gd|XxtST#4PoPu2humFR6X#QN9rWe zM|9+#0{1XCFbV*oAZod^ZihNx_1(W|;boJ|xVmkkcK9Y;4D-&^ho|1;1lO=nKsKhQ ze17$2rV?Af97*H4WzEmCM$g?KDGIxZuoc~zB8{1U_*g0)SoMZg!v{t)Mh7t z=UAax%)+8~<*G**X#!`=8{LmN<#kM!C>pb}vcelKA3Ve@?dwRP>APtHIYEWkQdTK1 z<`L{{fH~F+NUvs!lB=h@u}Jk_MPU{}F3A}K1t3QQWGDO3b%Q>ruDGY9fxgOTwbBx> z4#fW4OcCp|cYan-6P1%w4JKy8!1#N_|1&ju{g;LILIK%Y3{>B*k=kcW!c2!}+|^$X zooEn{SuX=#{95l8unKE*q0m1d3MvBRu`rc3vvG1%R8-=j*$NKw<5l@GE%Sz+Sjacu zT>=sEP0&wk`pjo*-UKe$L0o;XWhazbQ3`)o8L?{{dIzIFD!_tZ6G-!dv8%j-NcXzC-Ig5EWhm85ww z63z%AWe`^@VhI0yZ^&7?n!P%uYt#1$YS8n~pAV@bX#^qSBn8k%|i8gx=Ik#&dwZVq}St?I()uUNALs^yAR6nB})n?(@uVea_JS?m& z#Fa^@@h|nv?~72V?*{8taKS&Xkv&y=n1_Fr&)_Unw-vX?W#QMaC|jSb-PO`%fn*io z?2?X-?&#>q9gp`T_2)r>a5osJU(}Db%VN$v{rgjaHaiXksWNoC-%_I9u#Zgr0I42x zsz7g8cy~%TZvkgk08;^3D&Q+9YtU>~E8}4?XSszE~3Wz{!zxCV&D^GnngRmnLR2 zF`QGFE|a1s;<_PtEp2X3*8wU0!Chjlh>4Oi7|Siiye^JCIR^hSviJD+FuV7DU<_1w z3&0b)(=hQ29_7YuGnMuRW&Oq??@^EuE z==h+a+1wo?Maj_jG12yqdSM>=?jHB%>zw|@?*DTxF)3h%@wVanS!&?_ek0Wav$Z|O z{@v2i^Qq>RmJYem-@k+Nw1$|3bN*hiAjPNkZkqsTCS2GYn2wi8;+cehAht!IB+U{F z7|y&xZQ0(l@r)*tZ^VlISi2C!7?euCV5rs%7#7Moq$cH_9j`M6@F)XaZ8As799IT} zR16R6=IZU=Vzef;^m=fq9bPsF2mh$2*6xqGH?@$hL{Goq@~2|%HsJCh(x;sCw+t!4 zVD2F0HAZag$d}57g%hAi=Yx1U#EPllonf44Q6@xEmuN8e+Ee`&yu>M`;pJiDZEQto z$?plG>^U_0?A8_Zf%vsg0kR35Dd_^BP-esJ7bDTL`l*7{6RSU6Wr_Vx*{_%A^=LgF zZqJBzMd7}&bRM*M-pFfeegn1}3Aei`9|K4=8J%kg^0Xfz2CsvUfhiMn$E3UbcBC%M z6MF6feb=g(u7eIr*t;7DyEuw@73AWk4x-h#N8-ns%f=wk{> zN-!V0Vu{eeBk(IxJCI&` zo$}wlnWEhkW|z3vbd8G;d?J?xRRSi_TJn#+k(k~u@CXR4AY&9Hhlqx8xD zBRhZ*16lXqj4jP?S~4Hb=Z>BT63j4Qd{Ls9COJ6iBNY6N;@}vbmCV|66-Bz{EC!4S zSgnmC%L}fNP~}Q1ISPH3W2qRj2q2RCI!>TJ694;y(D7$Su z@%h8wglo;B`B9XC_T96{n)r{#R%(UM$?CPE#TvOB4V#%;`ek$cPDZK~9BV3_fpUY+K{? zbQeYAiBRc))IpxMWn^={A_35MFq{;4!Y6u=MSzi_?M zBGf@fM7{`|Z9IV8`LgAgwFvae5i(&$Gsw)$3?ZBM`#HObW}c&OMhz2|nVDiH5+{*} z59w+?rR5qV-Bi@H`w?vg@dq7awskJ=2i9Z6xYs>Ny`FdCXgZCKFg9Cy7x=+A zM6-|7WrA{}C6kxm5zsmlbT3^m+MeR%uC~Q@OIOnobEV53$OH1uLKeZ(G6eEcOD zO@>vIa5`yY8V9D)G$@Nt6`23MnRiV<@#%U2+rADK8}kkL1E+utCf+e%Fd7-v0Q1Kl z5oW>u(NRP>L1t!qkO>4dzB`#cw{o5Z1Cq~&F47%dGaxVUUTbD9#d6m67u_HdMk7?^ z-sA~K&w773e*K*N2!y-ehNyuAFoHY6lJBhen0; z#>HlRNMhT}halJQ;K&8$mzR~TXb{nxV)|j=*(u@Gobu`sc#*`!#H{TYbb;xZzL<{< zI%WMA#h70?7u41D;%cnRLRmP)4u*& zk47L((ZL$98K%Gcd*NhT0z@6C-d5PXQB}%8Zvn)sz)?-N$))uD{BmNg2+g;(wKY)B ziMqk9OrxD&9%X*KS+>z(St+aT_m12tW6NyH;o_#+kau%3!P4d|_aR99Xs(@Xp*%g+ zjP2tE3=O~qg36&uuhabc63b>TfhwIRgQIiORzPfu_Cxc=8#~<|w{5X}!Up_JPOEo< zPF#oVN|_$qG}8_l1sVE_XGZmJG^X~w-0kmtu)LM45FC@3QuO9(GW5^BE`_h$KQ|G! zj5*`)Fxh1vb{%6Xy$!s;vVy^3dTvZ(G;ZntV!g2FvFM3!HP>KP*PHh;AT8m#-@@r+ zW*d$>t3!2lYzz}St2QL8fpE&PZx&{KIR&-p@d1nhG4YTLXwK4;^I-G4&U-+x=PkDZ zV#FU;l^&G&Pz;hZQ$F(1Us=RE&k8agE&NodGj9SsB7`{Ww#>({RqmX?W$sn}3e<+KZVpdSVTh)>GO3ysdjW}^xB^Zs3XV8pkMa}LD) z=Vg1WJxVpp)m;x4ZHzw8A~DVovY%!~+$Z<^t;CB*y>Bcr#e6;wN%mx*KauG40ALjW zF)ldmCa8?;-mlsANA9_!oa9KN5+oJ2h7X%(KNO+13w!&VaCcd??5(QP`sXsbH|BC$ zK%5Kb9?HqD+@Xd0UJu{QH~Fhi7N{kD%reTOh}Bj6ov&R@9&;s(WBn-z59}G_txSF* zS*cVx1EZtu_n%HGQ6>92ABWiF?fHc!L)#$$p@Qw}(FD9%8Ch9uAvNzgDD|Lbh|ceyNx{PXD)%^UKD9QuBT}Kf^PzpPxHbO#2LQe;>~X%Nef{1HJ0n&y2b1FfSJrL?1N6Nie!o$yk zonZV$a|O|_Yj?}9iQ`ABJyNHA-l|#5!YCXwRF`n_Lcig6EJOwhh{s1V$TP5=BTZos zO&h9XG0b&1NaT&1e;=7%xHk7h;KHOQJ@<`(NK&Z;JLf(s&3yb7O=bf%pDj5l>sUH{ ztR2L|Km|*9>}%DD_NC*3EBwZV)ZPm#R$rPvh_Nx6v}-DFrz6GK9h?~%qR6X~BrB(s zvgwLm3hLvc*;;-zP0zrbj-*$eb79^+Rsh(ILNS8?5mbh4iP;qrv#HPw39m~Dl#L}+ zmylRUJH$&A9a)?-9<6cT5dl34@6h=1-0ivjvxFusS~!~LgO^W=3?_d*Z{yujdb#4$ zR`+mSx@S+*e@iQQqBASpJ2Fs0*i0IAT<-&0_%l*Rq>IoGNNC0zwG$%9Ko6h7> zq1Eq$i;14V6d~k(# zZiqe5h{+$Svne$<s+q5o)Z${yXE05#oHgw#r#*lTLvi`L{0EwSXZ#2)BkKmMX``#%49>4g)qI zF6^cqCt0zicrXh>Gz7$XNgFYucoQq$A2~F+#R+CY`;rD-cq4YvRDA3v*x8C3&5!XX zOSdeLX>%Fw>r$P$q*=vmPcJTD%Rl!y43*~V5UDDo zEVpG)a+@`xEppLj?*SezV%G^<`^F`nR~bMOFz{_w8@Zil`fqY_bHDdqn|!e?V8--E z+_*J_BObX03L|SY2FD`PAI9SZGy6PVngx4Z?xWBMVhnjuPe;nM;xD^*n)7{sr}wu1 z&;D)$*xxAA1Q+_>Mov!1%J>n$GeN<@XvBPOz$l;mF{mMpkfM*@^*B8#2^i8eH>M>~ z1&OuzxOJa+oajR$15@c2txaF}Kx*uK>k2W@ev@=0*GG!S)!UrY4o_mJOV7^jwAgw+Yl=$?3rh=&Yx)koYO>Z5aM}J!DQ+!GM|vRI zo4s6dLM$6Al>AbWMW=On);-TT*Y25xMX%rjTB0iEw0)56Czfna<(GCxpgKxOcm*zz z2iIhL8vG2=Px|KO=ESqncg)Un_weD5CMG5iALrJO82R~$5dQSO2zGxVas5`xvz-N5 z+Z)2sq>=aZ;io;0^A&~e^F=SRZe5`lCYoKcAS!D!z*7HVhm1YKoo+VVXF+THEe8PA zMqO7yK>?1cbMloQiL0GmMMy}G>#BFJis*Rp3xa28c(_TRl9~#DNA|Z3bWF5(r z)?@q9b{cv$_Nx(X@Hok`xiHlCgax{v+cleY;Wp7ZM}fm=g|HjOSGOr5v>QCMT|vrhCa zoSA6WES5Yy6l$!ba2+JSh%v-Hc-*suE&X=b+TIQ*egcw4@Z^p-pfYK`^t*(uC=mdu zR5~plznT|YC>62<;A1mJ57177e*hzh;_}x0l?K$OT?-2fJqoKUKLuYCOJg>~xG!+~ zJGWC+caa2#Q;?t7dp~K#FTdjFhD;_O{jUV)3)IsL?vDch+P{!lI^5b09h|n(r(-+5 zSKrfoTwFMUrVw?Tfub5EaXrJK^XV=i;qu}< z0490)vjg7W6Sjit?;H4^UH#=Rzf&w_4hMWPkb(NBozn36F(oK50#J0SOG>N&Zz2M1 z*#L5g46`xkCxE^OP4)td;Qdi8TfzVsQC-n*gh#v zk57>My2UD=ZdnYKDB&)JT$r0h`)Xp+Foro?c;_msr>lL0S&*&{6!ZGpn$Bj4$1k8l zhu9@eqLWm*)N4R7wg+bxEfNJU*<#YHVT|zE`?-bTZ5pEbq}37ebAo2Cd{9eK_|mY6 zJ_`p2u2!>tF*e-HiPNB1dDqsKQTs2D#i7HYafS>_!5@;q0CJ|cC*7R8{4B*SEcR>6 zCvUk6Pov;<7w7sQaQand(Xe-lLSkDw1=L9{=q^bMEc+563wddV)bd1my*c<4naen3 zEzA7~&mAZysL%|O|5_mx^=UUiT;w0J&%%K7c++kbz-M~jio9k*SK2hw z%gf4KTAxOG^J#LwT*Bn`VA_Om$>aT%bzZRYfrld^H7V7w@igMVG?gDmk4r0?NRL~J z3`XtEK9H90h#{UhL;Fkm1J^hv%fou|w)FKLO`(33@gInf@6&BeF>BQf_D}+Gk6S?T zfS~sHT{QHZyUQZ?mxwD2!0Hqmh`TVr?MkX=F-Xz>drP-d103f9nGHSg z?z^fivWM=9p;FL zxSXttoVdEG+{Te$EA>-pX`+ht!4*`wH0VY$_-f7Y{L=}5^HS1Pv71+c5q!0b?-N8)lN78NZ1k=6;dcHAaAg~Gz zYX_i1##nN@tNkOy*-ojhwib5>sV^oOBmL6Qz@P_MR|7+-Op=9UJj5l@5N;3K$s{t1 zLUa2%Cb(rf(y(sJr_eAGj>XIPiOl_mvZbT$$FH-#Cr*&wi4O5L2DCakg7%AhU|qi^ z*5G{bX-;R%MM<^u)RZ!*v2obzzNpN_d&_>`qkPIWILRPqN!vy_KY@J~Jf{pR| zeYU7Xn4F>AG$#wohC`F8j)#cMHlm1<tEFuHG4P53aK^(owq-Y?RP!qDhWRrefZZ4APd3fE}zz;HBH|4 zOB)H5s*g4Ou*76&g&sigfhD884)yjI4l@XlLIBmmPQI#49~1Tdd4|XzOX00?7j#+r zt9tXfRvw@c&?6S>Tz9|MxbLwvOH4@ksI6^nVlt!X=etPSF7*X&{Yu@~Tb+Sbd0PA? zqi(B6W+3h7?V)GM$OzrrsXue8-T&yX{~5;nVZb%6SO;Rnx0C%donMv_`e5Weck6q! zzz$fV9ZUD4S;;~k9uRO=$;zR2H;*PSk#o2hek!Y{ifgK6Y~Eh*@hc95mp@)q*)X!n zd@m|R?XEg0%qIcS5}25J1_m&duJyB*sGCej*N@YiTfQIkrjt*eR>D5YO8p>Sc4lb` z@sTu%DZPQjPAOPe`kv3T!FoBL?GnPgW4W8PZ7n=04#~#FQLLKC?3L8Mzo@uRg|q1a zM3*JSPM!=Aq1g}5D&0qHGM!)OPD0AGN(V15a!3hW$P+8`y=`9Ousyn-tN`!ByzfxY z4&+e%`@)YzAp38ARUuYG!7+#jx1wtQSWx@eAXwHfZKJ)eV3 z|8Lm>-c3m$x4Y}bm;9@c{fG=m^t>D5&Mpz|d3kx-&2Cs6Hoy#EJd-wbp5N!;^}{7_ zS8M~(I@feazu<#|#Ng{S2r9+WNh4S5-`N~gx}FGP!bx#Z4B~u~a_+F^O6k7Cg+qk^ z0-hef34s0q0`o^J_0DD_Fx&4`jOM3Q>z0T7xTSmGNommWLW?E(lN(Pi&;0D>Qllyr zt-?FpIiJlsyz^ZaSHB%1N=0$ZVB`YVV^uOG!D`jdFCv|}qoJz=y7RM@e;TnYHv!mj z%o%VX(uzMK5}(Nx9vL4&aNpWufV1#95`zJX|qLZg& zsF!fNdWV6>lBJc^3+Cen>&Z7)@7}!wI(GXyLsOxz{v?)4#@XN~udDVrY}CaESo#uN!7I zKsn#a*94yWIYI#YgcO$83{q6&?2h?+bDd?Bg`WNpV19sD7zEdV`91Rv*LLUS{=UpF zX4fe3$az!oQT{gR;)=c_1_WnylPaXKamd!ErZI}dV8M$s{neq}+?-!D`5Fjh`cS?C_F zcr4h(-zf86;HZ#4bgf8xwYDwZR3~1&Ct_z|=OyF1g>L>fea*tDcVhwnk96UQ*R{@f zdwJpIMw@>L5b1DPTOV`iB@e+M5r7N585yJR3-NGZw*Lbtxj4QH1_)pFQqZ+UVc&ceFoO{B ztr@!m0i2!T!|L->ZzMuD3*lR{z~+9Dx;*1dVPx*{UX1t*j$yMooou!_A=PbhCGpgb ziIDCTdZIPsP~@1f%rQi1Vc&|@v@{;81SJ&}rQ^Jk94V?4^7JVTm3R`O3;T#XvWvP# z3H7!|URfSKAXw@*u!herHwQu41a87^qb)Z*XC(japNrv3hc z-{xgAi1ro>3+w04pV!ydYh3hk2|V7)fW9A!l{{}|$n@yIj=DlC1)mt?(N$Hva+`PG z^Ld$v05j?GOZFdApQ+^ymRgm=Ahw1IDcB}PV5!HGCwj~aO%Lck3g=b+8)ZTM7Q4S$H&TH+^w6s5gf6e)iy~(dtS-ldHf^N5bEk3rb=QN%>?w7Z( z_go*M#M{d2Sa)Vv=jVxa1Z{M=dfMLeR8+oLOEsaSe^D z8RNp?044D7tl?vWFmSMeCkEbp`P_fbm4pyr#>#~!O~u4*i&b4%v?oIYT?Vd%jCdvs zB5GzX*_*TvckQP`7RvQT0u-hqib+ z-z9gE9F`y?X=6l?E4YyVn06*e7gCRGKaeNQ=JI-|P++s+$;s>G`;Nu`xkzN!2#83K zI=hsklBKe{;QBV{5q}4XjCtiiu5b#D&T3b=KkxN-8SBO)6Ub3v8dJh2>LN+2LquLR z!GiI`GIG~Rg<)6g3${mq%h>M~DQxk{QcAs(`2{aN4aaZ~(H{*H#-1t9$elioT2`xb zwJ!fcH|$wW6447N-fiCZ9JhN1dwXYdHM!Gd(&g#tgm9N9W|AATt<08;&1GrBt){+j z^A-wILMc%Z>{p=A*6m-xIa#>Lu^+%P{kzFYpuvwahkZ-v3r2~S_aR=t*W6Jp%$0;!dY77S z+#5*F&#MGqsSL^v2x)vLIV&F=+TSo^Wn6z=tB9`hd5gxkCkA7ou{M7HNhMzF`$x>_ zH)Ns9oe>3A8q6&MD}y^tPk2WB8LcL~23}r#T&{}?3oelRcZ=&{*{2}<<>Xr}whH!KtF z+t|30|NJs9cS~%Yyx8#({}~%2p+f`D2)ZOZWK}z>9V2NIlWp+t1eFrT25Nx^ zxkofWNsd<=+mj~kqlH8NxD*pQHTyni*ttX zMa*k;&Zt(xM&p}wo;;m?t7DmJ_4ucYo0bH*vCc>Nlsj9$7-6bfK`x9JO+?AGc^qy7 z31^WVX_{}4!g>9n)W56AP3dOFjr?+RDOK4?)cn43jIu3GJEfCG zjT`0q0>+8l_pOyRbVz1z_OO+rWogVQF%?8rY~3{vPhes%yps7(mJ=5dm7bXvVqGcwSAMmGR^rfJe> z{-8&!7AC!nurNop7Po8k1#w#!)P*jQXgKSu@2>>ibT^+SjMKi@i~vcc=GK%(UM2}bT2Bt^$id;;EVuSsN@ ztV6auF+T`$a?XNGx$Ykq&2JX1G4aqk49_0!E>7HnA(hcIlV>+i{vUgA*oQjF#$$znvMH4hOPYJ~uI1Cp}jFkmLAtpdb)Io5)~sgI-XS4EDkJD%QI<|EvrH+Mdnh##hgCk>{FRR14U z?-lg?(5J3| zaTk^siX&eAK^k&n$h=p(%@cK{6MAo@n|Ux9{~8Oa#`x*l|}bL!#P736T2i z{c?nNm|^6wW%)p-=WV*y;`6-8K(*#bTs_P@#h8lwh^qPj_8E{6>wR?$>?v zr~yq49?S3Cmxjg2s#HG`!Lv56Cz5&iC)c>DLy_;qaVS2|moVRHfMr3^T){Zg_!dJ) zL5y8{IDZY_hV=(98m%TLy1yBP~+$nwO9QbIIW@tvy~c%($S_}XqIlK zD~NS!ua4!@+KQwIhA-h=tZ&N9r~Azws`U|Jpi=R2?q>`gKL2zw7(dJ2uaIhcFn=kz z1d$)2WzkiC{s6F%#>L z^+ukg;-0cFo*=mXD3*nUabcWH+cMU+gKm&7NF{h*=uuOf0or=RN^4>?H2i0zKvQ-Ic%7_;Dm4?_2GJusP4?gS9SZncQ(Rr8uzLl6ckl0=vJJg%mOpvydWJ0v zuT<;tnw^|PC7Py)0c%@hM)g;vo|P>9HuLk~e${B3ob;R5Ub$ZBN-oFKD~9QAQ1bqk zrg^Y}fR1uZCI}%NjLC)5Hruh2+^+ey4U&viAJ$+oq=bg6Xg0Hg)4Zb1`>@?HKXvBG z-TE@T{RH3jkew0VE=dd@WAl3mCs%Y0`zJDCE_9F-ROo*mEAz(*!oSRi3d_Lv{KW}^ zlw-koyZmiu5PjgB1HPB=hZ?UowBotpMnN zOhiQFan*Ese(lGtrU!oOPU(-que`0(Y0Z5Gt|_kZ)9IHc?Ofzvn;X5Q#`^wyt-FQptZe*AO7pN#o7IGNEr`eu_-y>PQyEyag-%Y1ALY<+><21kq4 z3t*6>s=@&z1>xQt#A`y>kAS+a2v|eZ9`wRLczWwuASEf-(DLovMY8}E066vUPqGa5 zr&<;!#gORxuRkThh^D{V5E*+lws#i9goVG85)LMWj$G4pNW~F>K?@e;=g$mRyKUA+ z_uCS{ugF+u&F9TaS~_-7#k>iiRtxQV*(uv}=t25vuWc?^r?dz~jfISfSnx(>^rmg*VEqgFxpR?t1 zi&3@`q+&T&=^id0DmePl@nEI;qGi5M0xLto;5=g0Wf@-`@kpD1h``}~h5Yq`TAs|e zTNtqptB0=GEsy?>D3J4I{hcY*!gSz82RnWFzR*_=@pcVw$o7jPLZk;SE`Cbt-!YER zTOf+-x>-3=8A`P1Xd8Nl>RuXZn2m)c*NE7urHr(;?WX82no=R9xDI^#phspLqwbQ^ z(cHTA@A7YlaRH)4OXUA80D@3>5nw5|_z}Eyz}G71Kr)d&0(yOYMM=tHHV6(2`{vNd zPS)4kml8)$(xjd`1@6WUDGsg{~To@m=U@+?&aN6B6(D|qaj7l33 zsNX!BkLiT5;I~O(8=UvfX!*)jH58e)VW}NLFG3}!ZAgD`K&-@WgP`9S@4PK#)c6QT zO&Z7R*)YVDaF?(58NEF|{hR|6j_ods(WRFrH4giO`o$5i?MMj0FDL{NpQ7Qk4F3tj zO+Um_$Xfi$>N!x0TTNYlcXC}oi$#aKrgX*KJRUs!?T1q87c==NYY{f{+@_HSPQ7qb zfXFp#l6Ry)@Q0F`+O$o#I4()uL*K z@Ew;0A8)~v$vg>S?}txmr2cbWEAu|Si~K@s(>`AHFn}UICE3j6bHwcgar!euQBqQ^ zH)4U`Zaut`7gUZO>oG|~Nvg46mF7Yo3&`R`@ z`jNRBFVUOO#617f^6#MDiY2K0|EmRXL{{^z^{Tn22iF$q>0h`EPHSAnQ_0^KTHw_uAhlmR5C?wj8s%M>R8q=qMPj>q^W+3$e=9emQ>&cuc+v4#C_0vP%FMSAQiR_ z?3W1K=#SC+a^s<<2Bf}aXT>He&c@Y@#j5FCsuX|t^J8Ru6xEi;e4|dNd&2xzj?!WB z5 zWWc`j3SHiEnU&tzo%vmN%gW07sW?WZ84Ha9`}kfqgZmcH8@jBynE;-BLvOKk)YL*n zgVTkwF2{3#e`<|@C3B%Sh*HZY{g@M8tFA=EV%8zBc&MS1T1){!qc}xcO`u)(y7L$z zZoA>uVl>L4{KET{hC?AcM+?iL+WbTOlqpoBI`j*{1uO3Sfw$+(Xs87$usd|U7lDWo z{=PioJ~06v;C?%|9iGoQ z={N!m#y_aLMR8d6&K|A63mCsFG>lPD0(RVtHa5UD zs{;^W_RO;T6i^}F)DOrG+Htwv>0Ii{T*D2x?3(U^2<>97EqSK4g~iw%|GD>;t45zW<2 zB{z&OZvq@m_a{qGm7_?gxQ2!XE+}t;KQz$g0@UPssSgPW8C0*tm_X)V9TOv~ub&H~ zZ{wJ8|>@fJ)u>$cL~&Pw}5Po{l0HT$IS; zmx)zm$KSPXUj`m(1CbzZtd02=H^?(HOmI0x~r^@x6ee{V<-O66j;O zj*Fws=wZ`3!lX&)MV4pX2OZoxBEIAt$=z`MbCkh_&`oJ(i+5+E0wB)0-|SzQbz|Q@GJpQxwHoi|yHlFQ zauWjjr+4`Q2LXHLvO9!1SfNq=%RqKMDmhqA7+wf6S44sf4AS1b%$o3t!U)3^-V4=J_KCWcBj7bG*HrMe31X*Kb3&-jG)cy zO$QTB>LvOb#`QS>tg~0^AcERmtmm~5Q-qOb`%rDtw=slDRtXX$vFpN3)^h` zmA}Q^b|RV{mjRW8x(b|giua}>JV`o|7t;bq1*l^a_U}h$SK9-R3+N|VU;*3mwB+QD z3YzXz#=6Ft=Zp0axbm6jsAN}Q=c|gswC-Du+B^v+9TDh)hK6Q5mcIBI>wH@w@EfeE zY?I6R+2&?WaWQq;)NpdumfW#3gIN}qyKS=##umBrV5roX_-`H|eN__gnVV6 zFFOZZYQ>(Y>8iY|zel`n4!$h&+DetxcQ-jHU~Dc?H}5+t6Zs)69g(ZF%<(eK3=Iwp zjf#BPE?G|d8JwKC+1;lySr!AqRP{@(orY5~R%GKg!(#EsA=nAcSYh_C@J$)V7)qhC z$EXN{7w%P0OO!F9;jI7FWQToJG8F^nj8XM?j_OW_Yg>M9+Lpq15%0RulDmjUcT6RZ z+oFh6o`K?Viun4M{4ci{AWnqUUh4GuyZS(-kcf+ zKSrnoRdvkQsj9J{xE&9Cm9VFB_w1W-6Q7;#wmh#VF-t~eF0L<;WKcmU2aZ2;Z&g0A z68_&69!?2d;k~Q{SU{&_nhIrUX?fwV#pspAr02hK(0n_E@+3*Zfc!|vdC1}M%`5f- zX-!LymifyU!e=!2gwtU>azT4xk)HTxvUZG#IQPDo7S%Wvwby-!|)|K z=$WVO6`TXT*0#VG+og}^3?XlE-7VcOXelMaZdy8!o3~5Bg^_m(zs{+y6vIYC4hgiH z4Ap3Ce58l^*n*Kcq7Ag(+!k<|S2Yf3nHZ`mE!Fj*zj<}%C>P;Uzz%#{)z-rY_d;YLANTrHK;3Q1O1Tk8<_lP8Y-Ay_7W zmYYHtSRj>vq@_;bBuZ;d!I31iG}fU6_CyGWSgvYWr&;CW&M6y=!f^^LixX!g%~M0a zex&mEP>_b7;}iIdvKcV}cv%{Gq?SBQ;59%WTzq9cW@WcoetZMnmsFx#TG8FIrL2tH z{9Y(>U6Yctk>ZZTF;dMUF7FExEJy$ESY_G-HBh$hSV?%B4xEr)G+zlEgmoZn$jJ;k z(+Kdb0vPp9#lP2vfv$Reop`Oe>X1xVuywwY7c)E%Q0@C&APGNH%l4NX4+4S*AQIR` zGagys8Rb~4WSD|XYkV_+oNN7T?OCarK5b1)|JHhK`X{`QkcxEzx9#2v9Jf1skd(hd zN*i>39WTQ{t+A6p&Ec@%Qez=!IqjcurTK}e!`$-uMX;lfO&N7{b$_@`9fnt~KX@N2 z^9=`mIv=HAKW_h)%SZYH(eEby;A!xUnb5Hul+uluZZ@BfU-+4UhkB#I2cUWXx+U9!l_fwUSv;qg~2-sM#hrg(-gcQ_4>Q`yjeojn5BXr2R zCoQbf+9!G`qvui4r0+Z}vI-|bv(BFDh|g&#O-(2}!qIa}#e_oJv@lR@oP z(ac~vmU7aQJ7zW5q*_~p|M%2 ze7T6)%v`4{oP2W30T#rAI=0FWq3SpGg z4>m7-M*?-rUtkTzP9Fub0$Xa-*3c2=I}K(HhZ&~#IDcnFp0a-Q-gxdm6yrKneVJDJ zl8bg)tyAuyaddz(|An95dn}zR3QbALJSm9( z)#5%K#z9Kub%_qhRhp@K&aJAN7#u8aIfQaKL# zK5MxZ?qu9a`5e$A15(A)XYa6^GUTyR3LcN}8mVJSxc9RHhcyfR&ouyyy>Y%L_uYLb zxOLL+V1lV51Z=to&@?nTLe|{&_4PRw=d2BP`Mm&Khd2u9nyRXz`=Ny2!m$@JK(-7v zz52}OGBALg1bCpm{{stCi{pmXAnR0pTe}=FiIP$M31JO%0kGd3FWo?TO zNv#d_8g-+K?nB(i)cJ$Y?58Obg+4due;h6E-(H_t!_Ds5zpRvc)8!N*_g{OzfMTIR z_~}=7f(XO41O!7yjwPd64weBbs+r+!IjC_-gTn)uM&H9O)_>gz=ct=AJ?YQ6ZFKnA zwB9P$wJI!pq+{+8o$Lui$D9)$E>|UB!>;x$k)L3%)@dBdq{f1+`ccRhq5$HQOFcad z<9j+&Wmsdcm7SH37yd8W1wdRlekHQoh;v`q{^?kMI78U#`=8=7#>qo&4xRGp5B+Uy z92lMZiE9l&!gnWXWx_aI_J$($%RjBc0$r*Ui50M=g#$S#DE0sc8DFW}seGwpJG3t2 zSCztGFo5kX#}_cJ#N%14I4Py^+(oqH)?lHDcMM1rsh5z zl=A-6eLAyf{GdwPeP{0>YwYzmRNXl^vRj)!<@@pL_I?zxVf+*4Nzb{7e2|dJ2W*Bu zo^g8ku1+RRR&>4OkYShx9jq&PxnnbuTY3nol`7Sv43{;tMMVoH_lKNq=I-ff1?!YQ z4o}@zO@D1P&1S2=*f0w`-hI$n_i=Nhp)tco4qjj&(#Bg?wH9L=2Ybx$_xEQu=$mOV z<%<_g*B){1p3WsY%B7(}Ln&VhY-v-h^;XkXLXr@+I93~37jVlOH&RsR^XX$UoU4S5 zkNy``_(BI>jJ$)m{J}ixy|Kyefx5uM#mCoZMgp#Jx~DqY(olJnHNR&hO^A2pU?>Q` zRR*|1E3oyUfxsFl&B+fCegGy0MQbZZ(gN8Uzr-vGH7sIsj#YokWIIx(hD5QOU`vqb znRb5r*kBRkb9?J3XWM(Fph<>K4l+!<4}X;;%U&{~vQ+!Aw$MaNCnC%tuy7D?_}%24`On+`xM-g!v?aS2OvO_6X{LJ8LDiqfqKfY5HYe??6m zJ|z*|X5KrcNE4ftXp$XYmX`JRKX~mEKJR3$Y_W*CA*XwD5KI&`{NyAOndnXCUa@^-D@^Lb4-GL~a6%Le*& z!hpfFZd818bHxyOli-Un#`b3jwlrWp%<(aeKc@c)@VB;JX$0rnC6`K^S<<~^aerIu z;PL#9x?>dlbiU^K5Z&Xn)l{}*xz1qtk`$t#^N9-pIjwT=8LXru1^e`ZF@M+n`nc5g?ZYt-jxhzK4 zo&S|i-PyZ*Osuns9Nz7{z{WCH^;YZiN}!szcM+l!5Llm_j79XH@2a$;PTQT4<%6y> z2>&&8{X4zfL;mYmSnfqtKmV<*Et0N)l$a*uK=UF=u$UmgTk`?{KuhG|2w;^fpK?|T zD!CkIe#m&=5cYcR(6C8|{9A)V1)ZR1cq>$r!lnQahXjH?1x%YF=UAQ$B0M@CNX=C6 z@p%Jsq^qzQ#a{s#OBcDOtZb#Yff-tgSQk$K407;=O!?_HBD?$pQ6`TIlKIz*UmA7h z311iM>&xN&qt_4l&+;%0+zSGUR(A0dFx_-{Tqcjqwwx|g73LXawJy7($H{U6tXSXR z!+&aM%tR@lE$))V=BgF|&R2!e_~v>8m~KWGCV@nkcTt=~>1;i1pvEAkV-ayWN=gYJ z>uR!35RsM)yz{S@&lT=$ZdoBjfkKdqe{@E5dSSB0CuvRyr3`OpvQm6F3hGbP><)Y1 zci+$|tYHz;Gk^VpsO87HtG@9E=+uArpXR4u9eSJ0w~#c(9!F?~Xmva_eR$r}^x)w9 zOZbOna(BxfBHH{u4K8{jsSK|em!-uSCCJJnf?N)#$X93qM-hu3!8LP%U=^0e#s!gR zo@+{3eEmJ2>JY6~u^Jc6idbYIgBwbBNkwqWC#nDUS!Y8-4QRAOS5eWvKmIsD`W;Q+ zL03$C{mXQWjfExr^98sFEE(Vu$3p$9A1>QM?ikAOBy0XY(6$*BLg~b zfAfC6>}NF@BT2s+N}xT~pN_e_Zc9%7Ij=YIWZ=H_3wLQ$=bWLX&FoYA8UM@IEmFLg zYAYBs%LB`inu;|Lt(>oOLKv99=g+7whpg6vV^PGM>WRxYvA;_UIQD@$|Fa`*m+50I z|CRiVrcqOYzo`f4;8<{DN^9PU7p`B zgsoJ?v@jXP&tPe7IXR!CfGuapv5S%c5D@PLq!R23@UtqAbYOe6+5iIiesh5lzN)fE zwFAFV56CSywP#*$YiLGZrUN@WGr38d#JYGBAgCiCcmarS8Ad;_c|q^sgOH_|ok>G? zx7^38+y)LCEg|haV@u*V%F<2s3pS{~^Wq3jS%H}R->E*Jyb#Uo7$`yOcz_|9p37ZY@Xxkfn=P?Tp-be_4~NcLTQ(r;FWpH-FQpl`8j z{`jF1{cZJ8Dpa+Anr(;O-e!XlUr@_=m1DS6|55+1i?2=Z7^)L)s=ngG&PTq{m$O+Nn!Q5vq}V$)PSc8iD#s!Zbw5zuhh934Sj z9*&v=c%|dsdZf$l)LBsAyt|}*km+Kdr$n3meWj1Rx`(ouq{Idw#^)1m$q#Jnv<;;itIfG5f9zz&=F$P#c=@P4WJE3aU01R1@bjDn^kyN(Q zvFi8_`<_Uj6bb89V&C9v*6G+6kC*6G$KLEzHo841+b)&W_?zcTdq(EF6U%EStca3Y z$)mTNhzzW!rH%%&f?gSa$uYZWV|Dy6C%T$T3eOLs#Oeud#cR~vUupZUTsEG$o-0Mh z(GuATTgTv)!JC=A9FoZvig^8SX~3Du!^ueka)E~#wtryC@y8*e2i+y0xvBod#JAmX zwAEjH<~y>qG2P|uoLw`F5Ao_*+n_`s`-*UP4|iw6sd+l4{>V#|WhUV8(&O$^+1EW@ zQX-f5Qa@oHE9)6klv~?lr`2w?(H(e@7R#}{%~G4^quSsvH_y(>I&Dl8ED9@-MUDw+ zF`w&1^M7sSOpsu!!0T*2t776m9nWv53#5Ps!oPCno1Rf0Zo?zuX>%cCzJB{YaoL{( zv^I+Ft1BxrGl8#mTZjO$AS*<=UA~D^L}A6RfoF~wAQG`sN$XpA3k!Dv5+{@yXJ9h9 z-F8wV46V^ZL8FHgT92^qk?r$QypS>ZIY(17cJ7Gx&K5 zL*T&3Ywxb+2cP|x<5?5+mJx0jtDEfjBQYEuTiF4wOCmG>$fr81Haf1EJL#0H;RS=22D{hy-Gq%|GzZw-6s(aAzYIabd2Z z2Tync7>_WIpO@+kO3&g_(o5J3*|ixXhzPEu&Dk8b(;mU5y|GH#HKpi@^gBS_jsQdn z{1o9S=L&_~N(r{)mWHbl%iCyrq0>IYCS~vKQV2vY9224tqmrKaN;*@tF_l1jpqOM6 z{_jo^%mMI(${#I??+Dvy2-uCko@62)k(k5~W}Wt68RHpR`VtBT_9h$?t(3MQ8K0hJKrV;Ty1fn9QoMRK;Fi z?<)wwO3I4MbYx`U&(|E!T@|E6=W=FIwR#6Y1i%m{9J)VEx8v@(o=uGV$&u!tC}x$H z-at!h^DYa0&dtCi&X}AX`tyu0UL(~Y*@NgjsiXN5ty>dbsN#kakFFm!ko}j7Rp~wZ0>uyPpIZ zpN1UhiztC#so20(z+s<$z=n)W;c|`gRZQU(kt)S!m2v}zKE-vDpcV9Sm zA2|)oC=SgzA_L7AtkcHPk-80qebDB{rR%$Dy_?J*vzrB?vYVSdAqB^o`q|mt8MPtr z)ps+m9ATL?ax5~c^lIKsY~Sd?^RfGBouSrLV`;!g?onBxWkq79T8yRw0ZRQgy{U)G z%4H%z16yXjWaSg#eS!n6>!r#V2`@!$+ZUR6#~Zi(QR`g2)1*O5Q4Z-}Va z1bZF`W=;;x_lWmV%9!x5F-#60mEIEv?Tahm3Y#z%ZuBMqx?Pr&fq*5b{KhiLle1gZ z@+p%uZcvJE`qf7qgt5R(fh`E&zi9 zJhJQl{827b&8e)^*+(*YaI-?(OOTK;JX?=${B5**7t~A`!fd<#e3>cRE{pQ{RHyko zppvhSSOWVX@?oNN%u4=)q)11P(V@zoUR%HHI>Y_)Zjl8}N>uXR-b_9{*}$;T44WbE zRHwPaxwerSay~ABlV})h5$=+Am67f9-+(ng04>(GXCPj2O3`zdQ76+z_`ImKaAQ{~ zG+{p|x|AeD0*v;6A>P^9S;Fm>j^-^$$FejNy`G*>>(NJTwZrHY+9R!<{l)zQwivLR zFO(OTVWx}Fq{%C9O7|y*Ow^fituL6S;)tk~FwkKY``AAo6hky2+fJ60m0_ztzBXWq zS&&fO=Gr7|yl;!#D^Ut4-J62BH33CWN9q%A@4WO1k1+`Jz|^f*(gGyMW^Th zb*)v*DZRN$Te?#)?k6VPjsW?k!Ga_Tv^BqmW*|}okT?B820QQjF(oT0SyNjZP)Zh} zaIaBLvTYAG`~qR8^S@*+zw1b91&9Sc`3aOs#sNwlxbnAOcM9F6MilJZ7PecopA#K%s&x$uuWD^x!+Q)b#3+@$JshI% zNyXjxB7MEEDQgvRZ&jE=hFI-ZUw8HJy{o)2$$r}2r! zav#-7DPsRy&qzCh1y`*QE!A^)|LNQI1Uq_qL}g`xoXzqnUC~&3y``e~iqLL=7*8i< zqQJ^~4=8isdNaSpQB)6B{&5rS;;Gsbag~q=$(PQy1I+kE1`b4GaN2Alu;~2@^;&Pp zj{DkZPYc|m=+HF*6zo>h0^h;B@IFXF4EdGY)neoM^(fMP-6+CJS^F>7!`-J4P@PY-S z*XNG|SVjGCWr!6xGLH`R7~}6~R&5&%H7W(#uA&E=9&O=N)MEhBpfJ4HP(q)$J6`A} zDn#cgZH71QDSD4WMx%H12~T?JVti12!+o%kNpJth}swmQ?MuLz=(JjtM5 zL}7JWfgO+TLj-cd4FCooDeE|-YJ}O-vpmvC&`YPhlm-^yuVRCNp?4+H|lfUCu#ZJ zTW27oPW!-F@0)%5`%+k*!l4{QlSNiwy2+laK@KN+RxqF5H!r$&xW=Ji9JHASk-+bhpEn6E7JCjW)TJ^yeS*c3 z(|g1#h^ni@M60Z8fgIguTp8D!3;FH6yKMLESA#5FpPK-$aePGjuBovB!CyU}%f}DN zYf1QudCn&!=ump(Wl;v`$OQ+n#Fn6?U{Ix7i>9A3a_m5KNpyE4IZ~W`95F@yR%vlv zfnQiqEEa9Q&kgRnB4DxCukN>WnkhQWym8@8SWqun7b1eh}9R`iwn6&m)r7w zZq*e3d2xZMfHf~6j_DZwUakHqU_ZdXS&nJJXq3JIM2>56FhU{#Talc)Lqw-mfvPJo z_KNR%`kR!5ja6$PllxH%7eET<2mt9ZF|&Qb-~EHnioQ9&krnvA6dW&i{`mRYQdIOg zL63&fp2N|7oy*+l$I)~lf+SqQ#$(PT_Q< z_^2f@)UHeI`r_rHW!Ow0f`T3q@($bV&_%DOb6;O~Hy$;E-Ei@5PWL<6t%+(+z9($h8@1F6iZARq5I<3c6Q7X$n?^!o9Ya8baDw~oZujfy-0d`CNznfuY!Nl^ zVCUm;b}4~DUJTuL!#65ww9aB)w9~9pSX#RL6X786b?nLJyM0(p5{-WZgz&bXL+rBh z6)R4-F};*Ow-{JkBdPo#k4c7c0!T{RO$dbC7_C;z^=tIU1TSZ&N?l{GXe_pNMzWsK z*BH&&S-C9NIFL@_!j@W-^C3qzrVtuT6CQ|ju3fBw?(+dZUC zO-zu~dW-0=lUp3M=yTkj+?^? z6?LbH{(yC~v$F%RtK0Qi2$iJ3m`ec$>i%dNOc?v%cd-~5H;EmK16v1H$=%yP3yQWz z!_$*6HuR|hkq4;W<+K#)uh&qtOc!F+mWwQdGRo~)aq(+E6rF$~~EBR-M z4{UGz-bst#+-&cka~uU&$P4hg!b6z!x&qzfHBePFR*@nH*lG@?=Fo|VUIBgqVA9^% zE6<`;eAnB>17s&cYvdnk(Lzc>D^Zc138rKw0Y@*ZO?B zb~z*9a*85@Q77L|S^A8gZ^mH|G&*pwe)o)(R4yMM8=u3Wr?p!wswaCP)OAl}+(=JH zchqV!S|^Nhtka}~r`uI0dhDiwLQKxF8-CBIulr*73ox^%Py-I$36F|)H9t9=SV-4X08KH@8qUT-7F3iYTg1I><9_yCh#=-a}w z%3OxQBJq$i4+Lo~f9{^LawwY8Jq<_Fxz6QwZd~n_HIxZr^?Zreu;Tw!kRo*O zLvIqNl3#HDpjfGF;K26)Y~LnP^aibxzy8(FWjc_QJNCX-HSj5yW0FZ z&&vD@du#C#oJ;8s$d&IX z;M@Y>k&wJEw?5=fOVz8^>(EhUXp}jMPcIa0WZC>a3}|h0kjVa(ma1Lsdm=JI>x(IT z%l^9|@Pwi6XYGYd$5*|%Be~YTz}c8m)GlxQ48ft)yqe`b6wte zI2ocA(mE1yU@-Wnj*~!yQ;@ve{+KM6e|bL)eiYJR_OwiV2C&^Qn4F!n}>rJG=}F+Yydf1nG4(NC^Ug zZZ+EfEHzeGB$|}J@pH?HQMGvrnH*5@u#(ZQIla}!&~sECQ5NlAG8@}lMrkP)8|8;7rQHsxkb>IoqED^x23WEtaohNcf};K_et zp_1!%}He*geC6@H2S zDOjK!ZOlQZ@k1TwXC!^!R&AnN;36HgS+DG*?(%4r6#p~6XDXu6AjWzUsIuQq`x;j6 z-V`+-OQ$IN^kDx0R5H{nt)Z9u_EP*d?#$tX5!Lp&`do$jMHB}L=DR0GFW#h9i%SZs zij9@FJlNTeO)_s0M0QV!-TnRPNIArxvkJ=9urV{B8SY!ljK}fRgjx4M3?uR0HasCY zhy1BL(SP62|BspK4yDU6J~ZUtt!@oiLTIB3z9*$tHrtY-FZKZD6JSYaZcuqRFR z%;#rjdhk&_Gk#V~fX|a^SIoS2AS1ye#rGHZ04W*(whmZ_N8z#-_?bxlzKV^7_1Xan z*;Xz1uFg&UW{;0j{<9#-KqHpp6<#;a()g9SN#4SEIs%qsiQdO#gzA0K z7l%a;2da<&vnraX{m;DA^(AW|DgSE;l9o~_fccYgpWD>-qe`WCTH3= z+US-WO$U4u==6KD(+F{oI;ptdPFd(?R-0Xtj$9eo1jzky_NL4QzpuI;U0HP-ysjO4 z5xMg$$ai_p2WmN7;#`l6S5^gdLzoSHb=vm?a)EeHb-N18l|HmUEgaxdkVPk%lq%_6 z{jj(|Sf0l3s>!>ziJqI_XsBVDvR1+Ip{DwzQAA07!_@B^y&5Q>Ayu5WkJ<+Vb8a?|)5D0qJ9j};|n>bO`@{~Qv5+n_R6wCnb>GyL} zAmv*IE`7fwHx8_xieMnBq5-x=a{so_3beSxSLkm2;Tr7)(96pUIcs-K%={#SR#V3! zVm&KXh1;+E-Shd?GURRlxHxppO$4dr6zn+DzLb&79;mg_%vhkUZ9KoPW^wPZY|hXnDuEN45x*(5AGDOH1ZvN;iFsA5!MMc^Ce(9B^FMO-N&zl~OmE45oU5S04sb>~7nIOh;& za)~fRQPgp60E1t1IZaftsyT4zmxl*esQ!SswkyZ7BVC)-rVnZS%J#rf>FmroOR6zP zFH_*eBm5?n#Rj;40~&+O>E4V9!3{jM8i~JpqOJFu$!!z^)6x zwE*^Lqqy>|(TEkG4mQB;hN}+CMDvahl_2Yv&Vbu+QDtpuX#&I9jFC_z@;T4vbQe9G z@*H>qr`;LF=4n8BAl3;)$~LENaU-!}am8mz$$tGB=G~$v78)0_?<%ELkas&o$kzqw z1zFi6IQOm5t}!vsc-*0yj`7nQqvq`-jOcN`o5W)kMjT|R%;uiF^yQo? zUS-qEITV4PmtKhXLpYQgAVLdh z(tXuW$*N)F0Jc~BI$p$DG*P4k>f#iPB(~slm1|j*(Q0W zy1O9eiO`x{eq3H(34+6Vr9+{=#%Q=Cv*f3I`cZ8se2#)>*z-bFt-DBhpA$AAX=wG6 z`ZQSOIAg(`n~iZ6+=t^su&#hmtbl!}MC6a(kT+9f=(p4F=THFfX2m4gdFD^bfgWot z>dqy6%ADpcUc0Wim>AFtZ%}B+8swUre+M&uNls2a3Sa!5;vLhuBEP$f{kP>HE{U~- zR_yTojr`LV_~w3`c_@~eB+Lc<^Qvy8z!<}A>i`WTyJvUl96~prA)wcC{8Lw_ca8P| zu{1Tx2+s2C!lVb^9H1rbwQtK5m_8PVs{&8QBv3EniQZ#pX@0;5cS?Eb(i7!Sg z4o;#)GD0kvxr&jA`^XCtKuXsl$kwwbe^zm0+m#TZ@07iaIb}=4*|8de0s|unc@oo$ z)x6cS6&SYrL}cYc@N zXfcfoI*Zlu;p5=I-hU|DH&79hH>{a!Tx7rOm!>ppnm(~U1^q{k??$IEZ$k2#z zk>VtV`tTtoIeEaSw<09IlpX4VDL6zGR<5f~O!9FBpP$FZ>v{Lj93v5*jEu}T8SnIw zZ6kvq*~K~N<37{>&-juqNKe#?*Ag>B@{VI=D??>Z!|8BUe~xZM;@l7!)RL7&hI_cR zPzV;FGls1STlP01E_s!%2Bj8F(gi5Q)q$JRb#vZ}?1@3#b>g9)`rtR`bQKn6xf zXs^Ms0jeq8LR+zo-!V=IlGaoqi10afc8}Sd;G2YpHh&Stqwq)}NG@m&2hJ(wwGEBB zeodJ#aA;32ukh7x&R4Ophh5H}Mc%5v)JMYbeAg2f6-61w`kK9QmW9pgxY|%whbBvo zz2B40_v$f02u{Y?dd~R77!-ag&Oj?Lb^bX4c}`{1+TTsT*o3;81{?r>r%kFGKi%pp z4pxzCtdMxd>v*yp%!LNkiYY6xP3N2<5g-f`882@Z_f0I;N;;jEvMcsu@aC?xxJ}Q_ zDzzbLf1ApJA#NNyW7UoBx3y?x776Z8=RWWfD<K81Qf^x>R&Qej-}r*MNks1pPSL z`QfA$wEbaDfkCb2cN0SV8W@!5EAfSNET|)IZZy5r{`pCIQr-g)n_h~{XUfaUk^T=& z%>Dtvvi6Fx#GoYej$r-3@B>m!b-5maiJl~jbP@Wu%?4~RBJhkIgU-66gAv;vFZ}#X z%_QM?(|>LDP?}>99WO97`*J$jLid;XMvhg5B}L#(pcs1UNeb?0oDx}TLuAN2O1f`U@xJ7s228oDrD3joPPByq}t{Mg1tSH-QAeckGT9W z6)XsWJe(=xqg;R9@Vs|gKw%~EsEL;*L62Vq@SJ#-(> zw>qA^?Imu+2CS$s3og4$nWw<+r+e|M@Z^@y?W@NxID6%r5i3&jXoI+}k7kWW21*2( zqZ{Qqnc_b9e!c@`F-3n@o0C2YDjftGtMDpNgvGtDo-=Nn@p18S?^zD>E5#~6`d|Kt z{1;iw7J6s$3)v|c0CkWm9?Y{eClw73k7g0h^ZJu}Kz3b}#a?jdYLTNzhZ4>`Y85up zQ?7TH3eu+PrFz8u5g@EmH2Lt;Y7QIshS>)!fPaqkE834>uLEQuR~T$lAhHGJLMfO* z=@gQe_TBh|8m#E7!#8Ny&k0$nRt^r;^fLtEQk~J7y=JxO=LtIwDs`|l{ycu$wlp?H zeuRFVkAM4z9cD0}3{=hvIL#SSZfWz(&iV;tj&n`MaSFm&JS2{dEbQkzuT^CDd%l#s z>}>Qcm1}UYC$8@y{W^Vk1teTRDk(B9i>SAsGc;B@rCdvHwAqt?Ef2jKquTF_TUrV} zzBoM$Hu%h+KYe{gfbl@|fp}4iUXrJdgDy*t6e+!4N4w&Qk#1_z@r6-j911#2!WcW~lQMEg zc8pyoDzel`!rUVvI=#>9BPIV=)nhO&h~kI|d_oGRY$J5vC|QR{krZO1f%VOcMq`|_ z1v@XPYTnjy1*>_${$h*L-xL|LHA2U`EBvsN zp?zQij@FnxS*$fntz}6ykFKVo=jVUTi{UTX^UOCSG|1EUsZ`p>EjVwnDnL^hWXyXZ z{XjD-Q?FQ3uou$E1GwEIz{4$m7ibx<6fk%+ZhdM$ge>?KJEqpbHLrX< zV#JSGWYeOA&4Pxq?3h*`l@KHc79K<%7rY-)GtFss?^xvDD%&*#M8d{R+0>|0{s%6a zGC|a6zu}dk&!GHsPs~AOQ-j}|bE-+r=>jgzKrYK)2a`cjqvepzwHn8OYyOs5lF0xf zVMn5FAwywGO3LIz@+ZJMJUGdA5F=KIn89zLIK6Zpu)Wlq4v~3DW;OsMyxJGPUQ~X0 z6`kqUi8?xdDL$hb#LTf)6v@_VuIK7hAzq9=)V}Co{7LzzJl=G9$(-50Y$4CXTS_D{9#a7&Q7yWwSB6ufcFVb0zh5%f0 zQI#XU@NFgzOWc>$(eQqwBg3M?{Gv9<9(%#EAbLN3WwGiJtUb>!rLICw;;>Im&iFaF zn?Lb)D6&~$5Xz5Z)?RH&UQQ9V%iQ}TUBa|#nLEf49+#Po3kAqKs~Cop_Bl~$8n_k1 zPw3~Ho1bp!$dv(J6Td#ZfqvUjF&s7dJ<^N!xLsl?_{fG$lqxrubF+`IkmN#jt6u-- z>xATJLQNyTAj9CLv;E!O&C6n-_r97x+gbe>IfY{blh3$meE(W#M_cdp_VTDjD;^;) z+OA<1qk5|+&3B9Y{n)hv@z4zK{uT_}&*h2+64uoJAYpzZBN_+=LtZ0-S>RbcPw^ZZ!bhse7BDtWrNxHu_JL@%0c*{WQ`Oo)+jAI$o2 z!-=&qu0Q-jdMV&uHQs{Mi3%Ga*mR;#FX^bwszzv(FXFTV^v@NA zvGb3<&)3~di1SIy2#@TM-sc^*H@!`2nQoh_9A0~~a9x78R|K1e;R0gAUe+PT&9`c<4GG!qK z1^;-ylOd*6V~~FjB!^XL!eZ9skw?CLb&)`PxL>2fJJTF_bx|i zP2MNkc!wLf>J!}7dfak<1PE?KSt$1Nf$T)eE{2ANP&jA3XI^+)lajgLgafd4-#Sg9 z2gj)EMuwX1*hn-G4Kb}blILVQ4i)J4_^fx7oM&_szIIvU0HRrZFK}}$Qf(D>!MT_;Jz2X=To;c<3 zyKSL(B-UOp9!uciC32dF?mW09j|6U)#LrVA8}LvjHxVbEPfYds%$1`rI&%a%F2iSC z`)fO4#M!BkMQJQ@vmb*ro&kUR*YQXB{;oJ@p3hDHg~nOTbTdlM7?8Yy?JNq!7=0cu z36k5zN&SO44wmfoGGeo@i|(Pt*g0QbWOP!WO%RB`Amg-}1wC*LwTV%}HyF?XPs2%E zYtQI(UPaqByJUTq7zKR4FnPh)V{m5zuM;Ca&S+yHzp10{9^z+W&Ch0_t?8zJoPz5; zM?@A`lQGq`XQO&Io~_(v-Y*rXTLR~A|(O_-#UwC^7xQ5 zMeB==Cy=fKbyRV&MxZziWL07(-~_3tU{YNGPSnx$B8NQYP_ykjw7Z|5ADERV&+(^~ zPnIg+8*q-u6Jka(7AKwaJaO+H@*j$kA}{R|{3)nI!w~lI4Nj!~bgyk6exA*cpSvbqU%% ze)CxL^z^A1DpdBTptxEvZBPvMo+|LVY?uv8VCU zs;IvKNn221OK~v^u~(Dok}QXEhqKkZLCz=av_6~}+t1c35pKBHvG%9yI``&YU3-^} z0r@U{FAXE8lV9CV|5@$9DEg?#>o(jN0U3WwoIn zF6{KYcT!wt>-SMgQtIhHpDPU;OKHgJI&BaR)<%ZkdX!$fh2XV>H`i^PhvhAr*T>f( z&F8~G|IkRw+@6LZ#iRW15?23v39E1B>wwjs#cqR;izbO;3M)fR(|^u=XGHf{pglA^ zY`_#uM}a!Q?Qyy;{(idq9vG8IObwwW!w`sb?``Z;l(1#qaDIW(ccm9<0ct;6ut2Em9;JRi{`~tn0q0{po8CbD3YN-5k9)%<)^XmX>*xJ= zn?n{7fkka`Xni>pp}91bBNRR77QttIbTV&jcGDq!74vf6v`@1Q)U{_2L#>jU5<%~w z;^2ycTxnXz7f-7pp~_p~Re{k686vr#>~emqSe#;>;`*0t1v>Fb29kk(#T6pmR50sH zTY9rczp8nLLp11e#E~SL`Tqx)#3TuC?Y!GMV!4b2I}~3MO7sxSDFpe4e%>s5+t#zR zPX8KJJ5QghciwF*8g4ef70gNkIUpiJLJoYiA~IhQa;3{rxVw{13@{%#rY`Vfp0%Sz z#2#T>Sew;lAco-=vXV-1hET?&JHrpecW?bTQGbq9RoqWzfT62wIr{XDm`93YnGsQy&>7J@yv-xrYchC zR0DdE&|S03G331ffuB{zb}m}{%(c*I9g;!sXYlUOwCMHpUF)Hb1OCW?R;rYb}A`$$U0k+6xMJGR?c#x>3k`PY-?6MPlA)tzkl%fUt_)18t zgP+vY;IN>PbvTN>HiXswr$>hsnxa z{CMeOnrmCpKVZ@U^)hr8c&mn{zXj#P86yymsZ=vgrB`A-Lw<`q^;JCdwh0x_ONXcZ z#iq(15GAqMCH{ItCw+>ce9|;#5?i1wHtGlzL9`c*I%vRuchZcpqZuPfK|jQBAaLJ2 zFMeb)7aaE!O`e9PW5S-5D6+~%K9nHwENY(&Jy7i)SFJsP90 z-@Iv;A?RyMDX6QP?d{E76Px@ynB;w@)98G8aiLt$jq~f!*BRF84$N1VRZ_6Bx+QUr z*aqYCGkQ_Kpru;ddc23pQJ7;-E^v;Id}YwmcpPW==5_SOK1vviXZh&}6_>s#O3$Kw zFc|Z6a#aGtDtO+FEb%aq9~;gApSj4rE`BUx$6u2TnEYxKSuPwQR(R~|TrTs{_|cSW z=1jh*=GI-K9%X*pyHtEmVb6b!Bo+J)!GRtML{;<+XP=e^iSgigdeWi#&Xw8!E5 zG-c>dF@*I#4!^%WGDqceT4S^EA5cHdZpDzsLMJC zjTnB7bgO)?^NrBkw`WPVEq=rl`0qSHKO0!T(j~Qs3ZW{yWnqq1Bz^wF@Ug#X)rUB; zFf*dCRPRV=;m?5eZxS^qvkIi^%>0;~Vq~_&(x9 z3M!i7ovNunrVWeEZb);C(`4rZshsobQByuxu3O)lh>TM(5D|S9gh6}P#Dr0Z;JlHG zP3TTJxBYic%V!ra_^$~d z+=&H!<%V z^Db&H8y`9x!oDuejN@83Ks30$Yv!8$pUc`1} z{;6R81qSjqk*5927^BIZI5ljA^)3OY2jvfT{OY`{PJ_eay((`uG!iU~KyUZemIeR1 zY}F@An3L&|!9gbG7=qu~(`bq!|836q>AFbY5fz%(e-D6_&0`0EHr7j3Wwb5}VsY-4 zKLMZ9ktL$YfQ>B#G#IN8Fx(%4#+#|;gCd2LT~0&=0jG?9k12|P9~Ji3cw46TuDERC zJ0!B4-%Oby7*JFB+OWKuo@L!kPZiHd&spBIUIvcr4hvNdK~;3Tv6DYrd_Jq&*18+T z8-bw;ZM9G4?mv@~P%6EFJR?wE`~5COX*rSt2HFq>fRr(E1ZbqRVTMga=m}556utlG zv4|=wdDk5ug~;@fvPU=kSr{*Dt?@UGO3(LCZ&5d*b5Rl5Q?E0fw#I;keN~8mlbhFC zYge@i7Q(WSfpm%MFlT{P#WvKa`Vfrz7>f+ z(`2n>nhDeo1P=GDJM%j70*(!|u&c#U2RB0@N4Oje(r*8+izq=sarmkXt3l*1-(46s z5Z2SntDDoS$sgzdU=z&2mc>8^9q_pvQ4llVmz_~d6&+A<2%uOBW^PcS7bhhp0W~za zFeZ8-{BO@-`+oP6czpTR7&FG~($_yob_yFB35J9nq1FlA#)P3jg={C35L$@+Ni@Va zzo`J;K_4;c7EnIF&3}$Zd|kF@W@1u)`P*i$ZG2*avK6E=zfuJTVGRnlP9HjgY;Ad1 zbvHjNvEpVGY`pUcNSXLMRf&D39Qro;En&*HZESYiscDr#9B*%LSI#zMp|~v%4UPQw z+BZ*7S%RMVv7RM=_a%$%e{TlhBb3{bdIsAyX5yb2h+tv?BVdLpuYW#%Dt&Gg`|0>QHf8j&2 zp_oj_BobK1L_$B1_Ym89Ws3ZeOMC48nol}#_J#O8R>+QM8c-Gr^0Fi3!E(ii_tM5i zK|$f(t8}>d`BCpNxnqI$fZ_FS=OqS?5F)#;{)@{`98;pOiBx`S4?_eRdwU5Qd-?ni zZmGBLs0Fx=BjhC}1NfPc2+>fnt`fn_Bv*I2QX1ZSC})dbmssW`D_6)CnLH{ex1>dy z0UCPC={L|G3fuVk@CUl&hHD46B{K16`jy&`2IN+IjkeuE+!9~SO^h{DSxY;J(j=ia zfBkwnG$0e@_rSQ}{NB}ZogGZ@PwiWom@bXTDij~1nmwLSq+00qu`N7TR#)#CrO)wR zUVX(O2sEgly4RRCRA~p6F`z_3(GW+343c+3@^lc`T#u?)2IqPl`>7V#yanuE4;Z&x z61S5M-V*m+iU|%8ZTli(NGnb*5*cP4{I|SB5r0{RXaXFvfVWKJClk+V@S~0%PSyNu zB0iAXg zngAc@Y~|~mk3^N842v!1^*ExlVM5TqryF|?T(tn*$zFPFFYY}Qm{se~l&ZHX;W-ik z%F)_o`q0{|CObaPZqMDQPOP257IJZlvO=>x!$xr_AKdbt=l8>_7qEkCmkYjTG&Nys z_=JUpMUnCaF}T%9EBDid@KmqdcL?-fB(*=kB>~dE$8x&${Ff8Q8?gRWv#U7e?Y3tz zFGrFVcZMk;0{pAZ?w6rkp?;%Yt>+2YE04YK*AS!Mr7QJ?oV6q2TYvH!(T~bGHJR#t zeVy)Oez1UK9b*9|ChGs8*2*x()3(YFliZ+g&xXy+%#=^%XYd~bQZ?MD*uN67eKFCR z*2n}sL;S%6y=IM-MsW%x<9F1FI}nYBOcl-qlY7(a8K>uX6}vqF-o7IPGqWrOrf}Tf z!qN}fQWq^2Bws_y*9Th4C+{+n`RRL_9G}DJ zDUZ}+&Al4KisP=Wd`Fc-Q{y~(Jug_OHZ-s9P`nU2z4Bl}Z{4H-w*ovInJ8Gocnq7U zD*J^dQfPipg$L49jh3_Ei80S3$<%6f^^>!$K_Kj?Z$g>W#t-KbbxRwU&SYM0KxfqX zUSKoNGU*y7JAB3YvHEt9p<3}O{;iU9v;@>omHqvFb$o_HnruL~5dJ^dR9X^1Akr=$ z^2FY!XJfg7fDT|&@hpk57D_S3&$%`4h@1%7*0-q0NFN|;I0(PNjNtIX_0{d^GoT}o9o zuhGQexQ3RNj+R*S0W{mzu^iv=jxtfY)H_06{-s7R11W8#xlXBlX;(?1=EXTf#?+&% zqN34vjL0-mjoaVX_WU?ONu7mp{LI3tCH{(f-psNpsHMfXH-NzB89mgUU~q6y43=nj zS^1WWAbIXp)n4HqhGPo@p>OThfLVR4U zH%>`4q+KhF*%}rn5KwvfaylQVl*M;oWG2dDRzeaE4u0?YJ38Zk^gU@Q;g3<#(%j1X*kJ!kOQn~Uod6tpR9R#N z6%}<~V6hMzUBgbtpO0+PzU&CvyYBVTivu7VKF1Xgruw5?->-E0Bh50aBdu1HHgEG7 zL<{@89`?of_T$GGF!Te2x#L7Nu~&eFPyHRoeh&*(Tb9Xa2CkB1*wG|>^vey+!c6I^ z4byF#B*4bIH0K$*On`41KCK<`BcI-FzmW+genj*6A3oqFY45?*CmkB{L3U)wwm5&O za3x*$PBoR-rCU#M_mKSSbFVfT21m?p1gYh$w2eyjASRfstm6Z6HU-N3r<0NCxGF#2 zpu~r>F$1L^>q8g0_n$jNVFUK|}>GpPK^~9QU=);qKeF$3`8y#&k zG7cjZMEff+^>-r+Pf$_Z#?4f@%Qpid+H@Yw8xY40e0zX^LlJ7rnp_G)oID>d@45-u z3qNQWUVf`&WXe{QDA(E1wM-6=s{;wW*M5r4qibkLNbQ->b%Y)1wx(A-ccYV~SeI#t zld-~2oqTTJCr2Gus)nR>bWKQ$&E$)dC4nxlr+=gt8T4+I@f;ov!CrGnl0u*AeLf^?9FJVD~%L@1@AbD~CVZX6-V?GZEqagM5uINu4f; zWR}n`vMK+g0Dne@#l3hvM>0D466B+b@BF|Kmz0nQ)$~gf%;%p$`QobGA^hSa+@$l} z#U4oAMdqa>hC`B(K8f4gzm*p0$*n*VmiPdd?7swy+_cN@7+B{10{*d~_en*VD_@#w zG!IrfZE0RJBHnhQG$)A!9l^oXxGo=F0Yy4!iwl4f>!z992`ryCImj6|T`;#yfQFBK zSP#jH>pLnvu@$MLez~pK4-*6@RM)oBK0XW5C{6)CJ_$$+6ldEIuCiwo7Yo=MXFK0` z3n9epHaB;uOn>tGHaM;^GEEvWz|<1|y&ZKTUra&i)2FPgEI>w3mx4k5LxQNUtn4$l z#h&hyrYq3{ufyxGK;&8GJ{fu_8QG;_uq3P@Id8vvS&csP-uokeL}#O@x8Q)t|2j?d z(9rIzO8#NOIzZ+k8j9u;5Ie=i$T_Xli#Hl;q+IRct!-=~>D|g^s8@iQxN52HxqX&KtCMQAak0vYdtA?pyZvljJzAUOHuc zG_)-@D5VGuRaaD06ozG>+Il8t4S#|9`UQJuO2UdKcD=(f!^kb!ulj4Aky`=jw7j(p zUomo#)9-Z&oYq7Zv128iY-YyBBtqUKc3IqQY%fUebai#-558Of^j*g88`L*9RjEOh zVxV@kxA(8{W`J|;UquprLbuFgWo1R3Vsj|5U1{+G^aX>?AW9!`!mnv0nl7Bh=bPQs znkob`wC}%?&9)p$%LY-rFMYl}IOD;K@L}tO5~%}dIv8{{9Zoc{(4;SToa;wx|3Eu@ ze;a>$v%_WI)$%=pVGhGk39k2TEo`jSC}3s(pe305CBqu+^XhbHB=}tE24)89fi9J^ z(qdE={$YLJ>8a3kilq_{e-sw_J%qXk23fxQEsJHQQ;i4=jqpED`bQX*ck@KV)X>-z z&?-o)1`5pkM@RAqB?XhptO#5YSy{|ItUxpjBnI6ON~v?9=UWV1TcA7t9N+qojqP&58B$`s3H5{mMgLCe`%{JuLhL>|#o~{jAd6~I#`B8>O&y%9+8!*`QXh6t zMvL_Vzm+$CRUmJl)M;?c*Q};MpP$9dS~r$L026 zkHv@VG}!}KGxw`zo!;sq8~^N6za+t=pH$&$!Dix+ZhmMzqDtEf6d zv%Y}FHDD1q!yyJ`Is6qaOo{PR5ToMSx1Gd2+(= z^3{uCj0PYOB9#_qv)U+Gp+QbgUM>TBDk4G7gMB(Hk!V(hs%0NDV)kd%(W zp#0Xp4MSp=kq)-7v~1mFxeCe(Zo`2Z`Mtjaz*!x(hZt-;RU`FPXH8OKzcmp^>&gpK z3%&WE(?ogk(W+ooc{l3ghdt!v4ne60b0Z^>ADn-zG~*S&FrM8sC%7K;@-6(NUFKMo+k!E*<@sHj#RuLeX&iU@YWjv$iha_`A9u?W<<#m zYp%DMerP*Ebag((5%u?ZGQD1h3)tFwwbeN!E%!kxG{Pe__2%8fS;F}n4cckDFS}E5 z1Z{r5I0(+}93f1GZ&HnJE&vxJqrjG5jCDl{%ND)X8ywzpSf$@z%OZUE6d?hv==2ru zY*b%)dVDdIc{W6M@Hqb9$AX{Q&pIAXyRzKqf`QBra81992*bv?YwrcS38zVf0Cu(m zWNe39Q}N>`o73{1B{NxKZMFXW)ubXWQJVl$*3jZShj0%J&oQ5ywcveaC3-FKvT~Om zA31(ar%&9&Vx*XuOLwDdV({k6O?kl8Oj3c4y?u>i7Svk2nq!{mxKL(EkR6rhqL3 zIp?+-sWwE!Uz}uu&Am%gy^%hG$Dxu7s!oT)O4v9!sdQaEz1hFFKODDyr3fSa<4P9) zs!F+ON6f}Lxp$egB(GWA#Z9S@-qn>cTPdn@pQR59557gjlwi*GT>E5;TX= zY^?*Z1~LRgp03c8)^%ksSE2<(n_fKd3XBud<=nIod|~EW8aFq zz4v{8Bo)U8bcqf3PxkoK$T0&M?`!owpXz4t5$5`eq>2o7e%*Yr!h-C4P5!1Ts@Af_ zLFlZlHh3e&w0bnItl#eiLUeR=j$D%JkfunPASE7EM^;=idVTZ&~IFu zV=II&Rf~Z#w5L?}*$85uAz9pQxZX1wFOLfd_M|!P4)06?IwXqC?qe@|LL+}(erUTv zoHQ3Y$hvYvp6B`UI*!}BG($|g(fM_tf8(i*GQ~$Xgeiizdn6?-p{UDc{OXuP zRwOAb<5@ULa+;A(P3HYR1zVhWcz_mqDzMP(;eyB2R*xX7J)N<0#34i$H8hx;g4<$n zOXoC?p2EY~w%wm<&E=paPxP@R#j$S|x>)G9oG6XR9<{sknb66eQK_&V#{FiUeq&?TuDBZx^q8GIxe+P;ZYgRL0#) zTCvN)WXqe2@_g}c59k(*jC;mHv-K8ZYxrJ*FIQGxDg0ipxXsIUfIL>*UZ1#rloVom zMYdpAa2t`p-q5UimAAFIDQt!@eRaIj8g93AZ$0J*AGT8!k5{Paa)Kb!@SaTTc_MYU zsqT?3K0pwI$2cxFRcj}OL2&CG9*@V5KC?DoXcKj+t|v0b)~&!-IGTf3u-tt0oTvNT z##Z*TsVtDlC9LrhE50!o`C3gAjt)p7UE61`VRn%@4vI$t$rrJO<-!h*xz1zzV#?u327TkS+W}+FW#v7N;;7yPu%w5Y-CT$3$YiNw z)t+GW+i>IJaU@}ev5pR~*JN_L3jbX-3M?4$^+p;)ykT&FUkk&`lA#OWqZZk1^n_q@ zv|&lRN`Y)RWFx?DY;5eL#Tko&+8HoMzkE-2c*7fo@XC=BS{C(I6n7N zamv61iBa(FPp*}{&)(>FPf0ytrsY4l`<8Y8d=F8O;abL%{d0ukQ>b2C6?elUOG=jg z1@&?hsH{;0^(a1$?+`Fe%xZ^B?J6lDBgiIwvns2PTo!|`VMUCVZY|qrbklnxxsyd5 zCn!oLs3_0jaLuJ))7N25ndcsagZQzF*yMSuRC2Ytk@X(EV>e{iv#pI)9{Q>K zyw&u6WN0YR_X$jCqzQNg8GEUV@6K0SCa}T}&$mrWwUr??efY**b|v4bfRIE7VsCd> zw?Kr)-MWV7;vmiY8Ai91C+p`?A5^9-K?T(DV69e=C`<33$FyU$CEC1y4~v})%!`N@ zSKska^d0NRYSuqQHj#(rOypMG8y{v{*8AwxWcJ#wc)GAMiaBFZUCQhwaFlUmnsHvT)e56O=`u9!MI~K8wSJ#ET*Tc&Wbd)l3%Jf@@*yE zjW39TpG^t5(_1R$P110Vp@CM9nW3SQv-u_oW?+~Qo9#(|MU49x}ZFYCK~%%8mh!P%VmQQ&Y5NuZ|%mp1D(4Z$;{dCHx;dQA??#QqW9IO4UT)`pah|n z{pBn7GP@?t=~1@MY)7?X1U4+kVdrM#*%>mABA0W+fV^v#B8WZZ{pUIE7@viM!wK{o z)v1BsOSK9#N?Phsl0Vgkk5GWr<$Bn!tzdF4iFgp(Gba=|l^%*s`a3QlRlBp9A}I}juF(Jdx#F-; zSK%0Rb=*y3!szMdAHha;8wfr3(|QKFLER(g;cHmF3glE)QaX{a+d*8sauK_rKl**8 zJpy?gBX2xsrK?pNL!;&XnbX6I<4->%Wkl6k;Cx}8bcbwfDnh8i4i6joR#tsnkuT%! zK z%LpR=lJoOlX?(-@qgQ8tt+7trKG2@8d#-A&Ri}#K>a&~Sb0kerQ36-of^WRgWQ1Ug z!A0P4o`=)u&q{Zhx*%u%i9i1Z%om8!c{Vz)A;G^lpfC&`jqtM!9CQUQ$Xe!Lzq_MT zvCs#TcuA#eI>W_{qWghu4m903UcK4^(C>G{&%oS*=ME6-(5ML4MhPyRHLES)f(0*~ z2SlP(q=W^)ew3=$XckXHdR(YeUzne5p1YmfAy~>~{ROKLA<8y*=hb^dqD4>vQB>aX zv&qgb=lIwQ1FSmJJny|qq+8Ml9x!=Tb76?A27&(r z;qlojKlZV`zik|bYp*%abMtN%<$NyVd8eCGmIlJ=x8gA4uPS*uVKV#)9A5O+iRP{_|on2VogYvRG?P@Ia%n9&0bHX+gro_+7Z$gFV` zCkD*4;ySg0$)t!^l1}@FyZu%ZvYk}=N-H8|``j%)KMQU*q&-+%tJIz{5+5XTtVy4- z4Og=3akn%a_NKXtR$?=={1nK)sYpf;nb_FqUJ$JiV7C3i^CjZ`q7Pav$bVuVC#s&( z_jF221TQC+(V154Vq>`~uM?`uw)!KU5s~}dY&AzJlIYcttbTR!J_6SGpmK9#H6-_h z*^}Zo)7`K&NfKujzN~Jl&TV7w&N03Yk@8ID^V4-JIy!n^{--KKrM^%$E(rnwCXX~y za*!>0mbwryYmGg2v^=(}+4eTO8|h9Jm)1(g@?v<$=Ln8zYgadH-*n zGXF}rw#E6j!U>zgFNw=Gl+TUwQ{i;h+cEP1-{4>be+R4;7XWYqAXUt)en{8e-u`c~ zYyt&)^T{?>R-0KEMw429*>o3*4z9FFTxIPbqti&>9fB?bP3Aj7(Iv?akT|s(Gt=eGC)!2o$40ZKWE^_;t@% z!t>|OMCnA$$mp^T5%0V(H}l$F3JP5$Nj=G{8p5ml3hNpf35>*5uDlC*SCw4Y)#+Y6 zSY2(zNd^~ZiGNpeABX;oCeQmt(LXp?Hg=uoZG|i8S!p)@rnnAfo@O;8?Khj6Z7x(mhNMJWpM78g9)y`dxgdrx2_B}%SRgJ9ZxTfJ&T_NalG`iC2&TssMe8l|< zpC_UJ+t`l6fuI-r$9I3)Gr~jEZfr1f$#HP9?%T^GQ~utZT1E7&pLoCY9f!2+t#@~L zKc_jFO`MG+CCtt8@Nx6i%`WhPKRk_-W!2>_jwIJ(Cnu#YUiT}+GRrt>ed8O2E~)Hw zcBj+t>`eh;fvI}88h`-|P4Dfcy-fs~)XtBY2)2ce(e_*2+IQ5L=e;lNt~dKa5HQHQ z_(Q)%$~2Wd^>Q)^+Gx(w>DecJEz$Qg05Er-gre26-t>`eiwA}J#ocO{-_aDx{x6=o znwPI@x_$kyl3KOAUfD*hb=;|t0ktoq|i^Zc{Dsh z-GLq7G<2#F55pG}6bxc#l#AK~Q353Whhtg6(ZsPk2wYqD7v~kqW+vHPy{oOkU#)!^ zn`J4U9_Dpgn1bp%MQk{X_dULbeLf(4(*JSY0z~UfXqlw>?>h_FmkaSX&0qB2)skM% zw5zo&JDeP~C3r7sRUfm?$6WHt?6JbCFfRi}6#xAw45QM`Tt9hauQJlF8sqfFx53Ri z6Lu&Bd_{K$UzE`&JO1dnA1!_T79UDcG*s&dQKR*`lKXFGVuI|5RFhc-h9;dBdMr0e zdUn>vz#zla8h-{E+F0(rCu`stpq_*-*7q4PAH$oiLNx#`peWz)&=3ei8#_BQzd!t} zScl$1I|1pGa6BxDB#ZXGo&Nom(TvhrwEXt9_4Y2;MKdTtRNlay-F=r1FhbXIQ)U=> zdndjM=D1(~I^)Y8A`09_C*=(k3IsE71Wa*H3(urO*j*tv%nv{2xaWWO65Lth6dGr5 zKf|ToIa@cXOzh)ZN`0c!KLoKc!y<2Jz9VIGS)=u_<=ctlRF5k;#x*>eHiVb=*VKNw zzJ6bY^-6G!_O4EPqmgQL@<_mU^)8qf%nXdX@%U%fqmT2=JE)h;Tc5fMo);C?1+}mf zEwG&+e!{ihYjcH*F7~zFTZ!$%Z({-8TsEG~UJy9;^#dewkFY^v5k;Tz>xcZpRH70-G%F(Qag!u{7z6I3din)rCd z?#>s%m8JsmB_uRm$xCJ1wIMV=wfWM_$x+QMVhIU|Nw29W-w1M|3;jja=yGy%2DR^L z*H&y=9!u?pt6^U>A{!bR?fglxw9xF)O-8ZwiT27u+51S@exp#;J2anRU`?~^TJ8Oq zui_ADQl~Pp3(!&^8G*lYnH9hoIri=pGeuwn2%bTsuIdaA3mdp%Q2 zNI*a*%&W}&|8N=bw;ZP8M!N#eQaKv&8DKC%(Gfyz zS3(~XlaPQE1y99GS@WsY+p9my7akxJxK#Q^#}-Cn7_lL9U+AwGOeNQxl#N( zpbug)4IJ;*=!1aN};|U>LVo zwU8E75^Zo3Sze@yXL`5-7T$f(qhN6>j`QDAcj#5Sxnz3WZ3JTgC0n5gnf?WZ@3Dzr zxRgwT`_K;vADc8c zXDjk^1HLe1^l7vdTfu}+mmU!6KmP|CF#3sJ8x*J-^88&YML($2xNfD;w``1)Mp(r- zWwo`zh5#)PRZ)4yKd{n=C|#U*r-OQr&Zyq&;#8N+wG(r{Cz~_lvI_X2BJ1Sh zYCOkCyhPAifqR+;uf$ZVdRint(eIEnAb;c7CzSFt7L%Oddh@CCULliQATsh>eYuxU z=*sL_LYSg2s0-J&+ZPtJ%C$s7bgm~fiikE-WCiQ(=Jv8$e&$A68mQI9bTrC}ye`&h z^)#h_*C{54jmc$6Wl};*Y@+qQR9KS&fOMRHuDXSdJ6H9gV@+;n%O*RzgMcCPFH}C!TIUNUnzU#H)s;>N}>8DMz08J zOAWVIH$$KKIezdFCc&3cGFOgvY`N?@?M zl|RBePviT+4+g{CH%U0@vCJC$d_In^*7Jb|?ChBdH*DFXf5u(50$mF<@Vf&bn{s3k z_7$0zknY@N$Dis2|LaGEzBjEIM(!SbI*TWlA9{h$=~Qo^Ar_3gG_Y-C^3qn2djuZb z*=^*}Us^)>_Emt#gITBS(pIfdiqBJP54Mag(B@G(4UXvlT7AMW zCfI!#Yz&noDF1+pny^PJt!Y0fQ6~L6L&jMxGA88K02mR5~v$W!86|<1Bipob{i5jxun3XY~e|UyP@oqd;!_>jy@VI%{1=1 zDjpL@O%&}?e&gEWe0%u&83d4wPZK$9-lnzO6l|~`q%q;6TWj$>d#PR=9pkgzIlEkF zg`Wmr3k+B<&MY*z%J6^c2N|E)z@l{O3un-x`{&geG3o2hl``nV{(0|x@B;)Wod&<% zS>h&NLAzdcA8r239$cOy09*JyVu*$VLn+&jy}UX?aBhE6dft7}V04ltC?&2r_aX4C zF}^+9N_+NNu85E{a=;Cpt$kVh#g3dSVLW1_i@^!66mKRJgK2 z`Cu*hZ#Kbu5f>mj} zUf*(`NB1Wh1wNXw*k;`&zLxc46wZ{GtwS$+$4_~Rhs|~=XC)`(=2-ewEmhkA(|=zM zJiBrk%S9=v+QI^-K zbLE>>4U7xj{)u$pGTKh45dZh=j>7o)-!rki8>R(s6P_Tfto$TqYqzJOV_5I7IfL^t zVX8n)Tv6l7vb}QUv)&GN96NjF)e-k)hb+E#T*@UcOP0j3-7#myP!!ul_AoC*Mv&vI ze_UhcAOvJiys$$o(E8!9|3Ozgx1bTCx3ull57ZZBTI5TIGd zIKTT0X&F?&y!AHiEjXuZ>K!3EwvN&Yx^Re>TS-|cjgcSpLc(@yeK?aT)~t@PV(x1y zcfZ^?b+}{N&hYwaYPcd5H@rKSPwubx?(XC)H7+$@bfe3zi``kVLO;tb+w@co8ZO+@ht&ryYZGR+&LCwAVb> zkb6S(+1pnv)LY|oqtTkK`1WsTIapXqHX1@YCosZ zr-8*RQ@|qY{ACIDl3>*LmGY`8<@Hx;nkly5Qz`JNI0{HLl&zm0RQEe_oURY|8SXCL_y;Y^>5}j{a?v_o zmh`gF$(!e$B*$Z>K&iRM{gP7K(;wt@1AR@9@;8I=BSXVfrBL!4Z?A@gll7ZgkslQ# zGPZgu5cDVg{`EZp@ybSd@}G2=7gxMdzd_e!Ae6x&o1L^O@ZY3TnkxNEqnZ*{DrmP- zKyx7VWeJj!GTr_d?#MCiRDF^wJ`XsOOVvS=i{_~aYfjZelZ#1tznY>yRxT;2hLB`v zf_ez+dAAO`*3Uu-4MtyoKq0+kfbN5|0>lCRJvC`-1}bspKZ(*?g;ah*A|m8lQa(PF z#gkLwCr+2A+IZM@?d63~>FAIVz{xQAkyPp5|)7?YiP-W(G7C{ow#+v~1XP^@p zr%^ZDr=Z5F#W~8dv+2TQv;B3ONj3lI^pEiM0m;al&PC%|ZO;!sp+0R(JLrDK`*;7~ zQi3cTi^B77fdUPK35}^`_vOo|umheo)xxBb6%5ArIki7atjo)Vd}_FX_QS@}0zIb{ zwY3BYjA?H-UUEr(fT2+gf!ex6SsPv8I)MT7?|gc;tlBK*+_jK*!4LAn!oE>aQPOfv z;URs|-O#^i4j%V}e;NL2M7S`1y38oeVm3^NBqPm-=(XA3m$p7>0*mFmeS%6(Z-~!R zSHD_@7J;qc&rH|)#E~1pw^(F9<8uY|TGslyAud@*x?hLlLZuw5Z-#1NJ(CWw9%H|`}}Nh04)@AMoP%e z)Wg{2&gp_N(R2No5Em*TeUx=T`%YW*XgXdYA&5`k0fetRVW_dY z*FSmr?_R6Dn(tG5#2(w!@l{v>Ox*V4Y=WP9a^Z;a9`9_)b!+vXxY~b|ZmNAPKts5h z87UUB|JA5UmQ!Q1glymifCf!B+k3pgyyg|1@{6r9SJFSa% zKHTpV1chGFIzCTdu9V(+KSp}a__Wngiof?jW{f>O2UBWTSXjVVCD8lho_MFXR{dk~ ziTsjzT2#T+*@0S;EpCCy_`!w`3v(^NO>%A|EJ3W5T$o%NBhu%T{WlsXQntm^C$9CW zg5nJqsjt#1zh*?IyudkfZMt-bP;a>Y!%V8ck`-o7yqoEH19ASDILyK_&Pp)Vb5d{b%wZHJ zgo_w+wwSD@nNCljVrTpjCdxNkCfNH$Q_5f~sjiJS9EQbY9%T(hS_Lv^-~QhAQ@JFvVJeq?(H`BU7TiO< zQX3K9rC|rIjNFx;+PTVf_St&f+4-t-K5sO+zWnvoQ~A;wj<2&De39>amjvpzT4vI} zrR=Lal5q_?kMvY37{7VAybH&yJV=~@2o?>8>8Pb~LD(rra-SdidRC6jesP>1Lk!OM z&&nsp2+MZ@rnAu#r}O3gu)cas&GFT1rOn^*8vTis5>EUZTWVY0!3RaxRcxx_sdEiBz2dOPtqpEBK2Kx^T!ymMGOaDU zrPEzpnm7w`B>BezbhnVJh#oow9iMX|$fag@2~WV>PB-*R{dn zY+rDpuJD>3h%#DT0Xtugw)4XfdLmwE64bOjC}EbRv3P1%uRi9 zcC0AfS}eh(slKB+QB1m8$>c}UQ|SsN4*J*8ql*{fIhr??;u zO~q-8Tnb0k@ck7L5mWKU)NG#D>7)$TVMnml@m51{d-7z^u3p9Bo}=O&%jmuvA2d=t zdPgoBB5QzzBe2m`9WEJbd(&*P5dHO_OdvRI5M8#}-38O=dHuMjPrgrgF6u}Cr@-CO zg5Kl1x2uJav5&FAeJWZqq?+VLH?0XjuD znD;vhHfGMGw7}HQ^Zrow-3scA?wY8C3PyVQtU8ZRvze)yVx3d-Pg%7@78HE z_s#dTFK18*KAxO>p&=xDYmDf89y8hK`ZU+i(YvuFTrvI=n@nQ4!TqS~n0Q97T^E58 zQd`Q^6}JpqD*erOcRIJGC1yLY=HDgu#RiqW8qC*Ru=g27SAkWlp_8R0OJKQ5TZL77 zl}<2xt^E6p2s5=(+W(KNw~nfE?Y=2IkA4j9s%UPX&*D#YBOJ zGK<59uiGs37Exre6=VY5c;ol^`4SFkv-3lAam90_jO3g;a@TTuI-Dkw3?I_3j979Fb#)$4cJ3 zN`BBxejg!l=~r?|hhlXv@>K5u=33&O-~-?TrUIQ5fTSet$gpc+I2BKm3%SaN#T_W} zW-Q$=<(RYm7$FuVmJlZJq?Urmef;>88z>D(>K#}tMSdsc_ET5fTim#A2=11;Q|V=3 zpcC;+iaY0|=VtFvciTR7Qf14Ws4LC)Fv3N6>w+R!SjHYPkcIOfgrLR=`{P2 z+@$fpUpEs*Wjfv<3GN%T8=h}Ml$Bn!U838)J2(f#EDI5L11A$}J$zBlKZGAF^XUGx z)R$??Mb`U9{mHK{&-&OPTP4x;;n{d+ea{?Y+?A*Afwsnywp(mPm=#)`K5w$^Vp?-J zLDx!&-WMzDT-*nD+u2F2{=rhS86lI|R<(*BzCpGC2|S7R@JO8hw3CK-OAAZOKxH)? zf{C>R`EX_M76{`y)MOR=or(BWwh|<> zD?!Y%>(fowr_IB?Mnj)!LIjwKj5;(=PEN$d#T@}2I|i4H^sS}Z*qlRzgM*s-F!iTj z#<9=|w49%JE_Bscx>oX~Pv`F5doMcx4{lM>zHrjM$?f37J*3X=7U?HkZwwJ>O_fU= z{ff5tCJJZ-hCpI!zM-luWRdvx}zHQni;FiQ3H$hy4_rA2ErQLq)cca*@nv@+KgY5^^JAVTiVspHyc7nNeW)2 z{R4}Oi-2jkYCxRo{JA!f`1Cfw9#wuodhtp@p;h>^FWYw0?4+k3dDeVF??PmCE+8wL zG<)Ej3Ub>|vwBMpG?jL5ho^M2b_~|p;)SglAQYomO>4h7===H6tJdx@HaorbJZaiW zg`Ree>E+9p8_D?wnQf^59cQX{pcb#_9cV*=x=jh&h;2Pp+>?30?8k@s`yieD0S8$! z!HG;H%x6#8n>sJ|{BtT(@n*9M3k^^D(QsIxvUrP~qy9{z*I`i{;mKCfhK;z*-hvqR z>HE7gQA_iq-pxtpq@iDfyOssrj9&z&f4UbF)#jaLp4zT;efc$4BZ(VF2#c^U87!;M zGnQ73(q0f7XBWClw1to<@E^kr;JhS!wML&B=-*>i(A{`y@Z{72vmua{!5 z6NAkcx^AOSO7#m`+D>-~1ikiWtNLOoM^azPwoaFi8bf|jPG|a-7*@R9JXl>|t$Pld z`&F*{I;Q7)iw$|roPIaYHG94k!G?o_`+b>p_iS&Yw<1X5cm5XYwdszU`=&b&pts&+ zK)ffCqFD0lbbb?$C`7;zrg~UE`-Ym$_TMUI1%rTDA?p~pIx%UK@Ehl`z(!Bh!_&xG zmt7<$XK;3FZ8gozOrq?tTf?j$hl9RENCef>T&KqMC$|`qwtQZ!x1Z}h zS#0&7KIm@vclS!}nZA5@7)5bWgE<=bvgm-|)Nv$WvoI0-g9>XIJz?^EoZQ`iqkL zORttJqpM$dL^Cf;YA-dmr<%-#xG4)_eJj6)lEI`4x`R=&@5Jk!{1`IkuuE4a>fgJ% zR#n$kY{qT1y1Tuttgle-DlTTo+_^klii#GTC{X3)=uin6H-6pbLx7(^my(LA=SfPc zEWE&zaC4q%DD;qHdqv2}A<>iU*n;$`d{O9;s=%j9;vHtBr3WhkNJfTeUGvQF4*uU% zM9IHiE5!2{uxrFZP^tPA9pFhlSQb-co?`p#IDLMpfU{os8?{F;b(J=}rcF zg||#Lss@yPv>he4oHV{6XI?14>e>M8^p`_@{ZPw8OXt60m^bIIU5^uEq#E{jRg-~G zqT0Pmx&YuYsMx}8G7cA`&Jo4-JZsc#*}QeR{ej|iMEmIz(-qU*SeZ#1g+4FfFTWa> z)9*he#~Hqj&Je27S!!-#_DYVK5KR&Boos4>|51Btv$ngDrI+~t#MlFag232X_}9p!rN2Qx0U9WPvi^k3m0kF#j@NmqqHiIG zCwr==Q!cEeH(QM>CG|F28plj2N!TYslFPJtzt?a1;`;hRZ0Atl$pz}ClUJ` zT!O2;6yXObfm`kVG&uek`d(XQ=#zGzlUa!@PmKu=n(7H-e+!}BJ1(i(Pv6x(w8`eY zRBA@L@@|5sYI{q6=b!d{w#0jlxdX)wq|{WfaN*No1fOP-+qDWU;3O{;JAq9&-<*tF z95}pI6ZOz=(`Np#y`tQs>~G792}(lAwrDzbdqkE@Jy+Ku-D!#VY`Mz+83Qpm zM1&_~`iMt2iBRZ;Sq^3-*bL3`aH-r>)>0>Iba=%65@{JvQADWTdN@?mOzK0>cSofV zJMx7ee~boO32PzA}S+3(8xdYC!kAn2E`0W^#F`Xr;Mq z`Ivu*Q2E?VNwCIsYLWg6HBRB&3`jXEXI=xta!UWGG0$6HW~IM`YkJ)YA(a^}fNXoxHXfSJrRp#tG;k{GxwJ!1aHy79AB%L^STfIwJIV zKNAgLb`ATLlHCjjHB6Ei-{?6aPE1P{Y{^ctFd3C8A65M>VmHInw0Pr(Myj)w)^q-$ z+b*B{_m|OLt;2S|1?@V4)^%%-7CDc2CQ~d)ZdpNk%il@md9;mwZ3z(Tda%Wxm0%hL z4h{}T%1j0(vh=S&aHKWG9`hth17!-T96yw_iQQ5ERHV%BxH`y&hts3)5#gxvo#S|3 zd`ZyF&{MM`HJ%g*=U2pr zOjDlfIE6&bU3F<%T;S}nsm|Lk*zX1!M}9ojod;-o^5xyAz+YA?oAb?yJO!JKP(PhnGLYGI zqqL{X*0*;5k!s{epVApb6!AgnFJx&u`(7mALxgzw{#p13g@{Hk?(1%xRMq9%T-DII zX10O=T9oB7qo(Q3PP5Kn)J>aTWoO|PI(Lp)s>A&&j+ws<-tp~|JRR_@-I@ql7-BSx zy%UJL2E=5yHsAZm`Lnf=sbmv(hoY75M2foC=dQesKKuRf4vyzV_usWO`rSp-yA14$ zHgrGOKaJI5gsE>8GD_O5)BVdY1g{F^5j;6GaGhhk1!nMt4V(u$cAspSTT6=d4cCCn z)^5lg7x=CiDMA(kE+Jvkr%x8vjH-;~AAM)7LQupQLb4$}s1$;3tU^NG0D(L`4qUSw z6f!@$_+>o=5k^OCeS5Y#bk3{&9TP)H0OB<;u3~dp2XdVP)y#)YcE74>W~V|Qf@EVn z(WCJLd<%H2QiBgL?k0MAY_fjFt$#ok9-olFX+K?RW`C#G)V9pF!J}MfoFHLAJ_z4s2vya>c)6{NrRL;eGPnI-(++((Xr z<|-;!dIanb3>lWuvA6=`g7i>ZAU`6b4+vRVv~Zx&L`oHlQsn&PWK2DIU&aUScn1iO zYQ8dwUj$Vb>|!kWd%%@s$zY$U(u2=D#HhU?dJDh?(Bwz+-1shvWud=2?&$Gsxk;WR zF!f{dV?Rew%SNl0Vtsi{Qmx?eQgRrr$=39a;8=x_f_QdzHXu%Y0BU*UWMq-Vi5|9H zC)dn@@)81Wu=r6kM8!AWoV+JPue_)l&zPC-ytNvdf0(QU|2X9p8kGbw>)vYKn8m*{ zoJ*^gqaQpLzv2%vdM+uL-JfQPv!i9%40F+hsQm@>$|Bj}o@uRTZ=jko*Hl~FKEDP>@t>29V-0X# zyp?xj$Gw$X0$K0Dl68I*Y@+7r)|b z`lng%{sP(`E&3csU%u2+pkxe3B@YOiZ{oawd;I+NbHPOdh=@P%J94G{J%#Q4C@=Jv z`x(A=(Ze3#6&knvm_?<7t6e7bDtrbDt{h`|4qLSdcn=Xgv36fDCHt!nLY*i49=1rszev&;ao-M zVKH`NIze*3S6dF%#kuezM6Rv-2u?U%B<&xc36z7jr+E`1{=ASEb4PG+bnN+sty0)z zJg4c2nwOuS!z^);%30rxq8E#R9tq>>ktdW6YOb7uvu{Jy?m?Nt4K()j)1$VANIn7q z0HE;W;_j|9)@&fycJDhecE0$b9MRa_E4DSW?CwKt+jjTJ55n3H1I5Cw$mjz^Vp>&8 z&1SmoU6FEtqyrk6Bd4O4k8j!}EO}9?lV!cI5|eSRT)N%q@4h_sGM(ysY`Qf@P4?6u zL1{0;iYn^LwVsf10XGELzvyTfX^&`!PJTn&Q4v_c%d1M%sW7%}g&8_fvIW;o=hg4m5uxoqMF z|9lGzk^ZgBiK7&}#FzuuG?0Fz?c23=?b_71Dk>VrkP15U_3%2#;^@fxfsQAtN2UtS-cZ1o84kH9!3^w5rIE%x|hPR-he+WH%J%^sS$A~ zQVR_GpaeQHt!Kk1Mrf4(7PW9+ya$$n6oT&JO_${Ym!F@1j6(}gn$QXD-4WB?VyKjS zg=!kpopX7)%W_&gk2(a3Slf88K-bz~IlH+PVGCWvry;bcm_0LXbaWILHkFhY*`V}s znwTK$k44VETcDJh)9a)DC!O?oXRD?2L6`d~`f5XOz2!jptj}iIYND!RdmlJyRZfNhRN9ptdOi?Nc`Z6{d{@gk`7$ z%5e^5G~ z2)g);i`T0hHj-!ly4^i4>(;rt^mU&Tu5@=iY2JNYQ}qJP zJ->hd24rIvW0v*Hq^$mWlA26IO#RanqT4sZ_G{|1mQGfiyMM;A`~0uqfF}<=fU`RK zMbYh1wI+0Ng`RtH;eKet|F@>-e}cWTlzj4&TUG(?2kpw*8jV|0N{XDT)zUkiHb$j5 z-GR9|z5976SJyNJVdE5{Khcmd?rT+1)Z)ErP>#LWJ$>$??vX87PffSSuoud^oh4tP%w4SaYGMymU8ZxY;kPCyYp%jH zk!1*Zo{q^no1jhH|nFB_~Lk7O@74ek==1&D}eB1ZKPghfk=i*DC>T`dyl?i%p++18( zaFTQCms@)pLWYQt-ZSXQ%2SpOF` zys&_4Mtr$3w>c}9RgI#nrxzh0gbmkQQvK7k?HgWrR1_ZCB+$A&PiR&EN6!INpD4-6 zhbI-uYdK-e{AD_A5o_mm+PJAjb#`IBw?PzLRbS68-r0(F197zqTKtX{TC$Thr~bZ_gjnN;Uh%B z+mA>~y;zE|uXFF8FjjsxVq3r1t7wzh_8ZTWXJBBk#Aowuj5Pm`>rv$D%d@1z8~eDN zoLM1g&*Pav;qc{|2vr7CA_#pfY0Ul?%@snOk`4ra6bAQ&65Od$Bj90k|HRpSEZoqf z&1Ai&#FGWAt}GJpE&EXO5Q6|!QGWi^YzPc$@;Nw5K+cK;G!DNmpLe9<S{$=%kY!*Ns1#jDH;9oprlDxKZk7~1MLf(2ZF9o9A+44c-%CC(Za{s&{dNMddp@Q{fB&b?E0U!X0VCWa_ z|Lll!>!&Ct!UTGB{k)Q7p~g#14Cc-K!bUsBFUZtL}W>JyV@v36PUs6JEJPQ(dAqw5d9?`Cs%?GLVJ-`%W*o;>`iLEMW8 zwcMN=Vn`wO5d`uUC~is;*`V@^_Y*cJxxdd8}hAOCwjXgT&@ZjgnGLZy#^Rlj{b zkjMx`Q;QMMW-U>{3xICfZt49(?5)D!Kp~J)m^LE-jCoyJt%x?vfKneA+`1Ai7UnjW z?j7?Plnr<0@qO-3cWA>@W$C7f zg&vRWHVZz+#Qgr@%5ut94|IDr9-bJB&od?sY``as$O7`@* zj`!icY7625i4ux(Vs;3{T16UwNDprQU^4*r;Q4jpDF|!Sn2_Z1G}SfyK-L3lRiDD; z=~LeUwd~{LV?YcA3OuW<)t4d@gb+HZ5aWio!UFtDi^ZXuLE1)wUWN2+iAhP{t%jeL zqPN^0Hqp-+-gzrsOP*|GiryJIX=l}I2KhFxy{lTV7sn1Esq(&MBbh z<8Q#DH8;1^S)6B=m*G`sa z-=)auMp8!&R6L{^WT@ID6hXuK$23G_@WN`$u)D{fB4rJTR)4kn-V1m`dhc-96n592 z=K#ku4_B7$2Fiz3zV{y-97zP;7lx*l+PnnlcdwJWsi`R_l~3BRyc8%?iLKCijHHaz!eyw`rj4sfTmw}_%8l*Y6DOW zR%tZsA?<i=`*$AQ5FM>@FleY*$i4l%5`uQ))gRgpjM} zQfY=iiPqL|<_$ZhxirY_B^Xt{itz*j2UIeX6*98#8B~Vwvf|;TjxR?^Zb=F?)x-9W zZ<(cwp7(1ps>va4wnzEKUs%gV5RRji>sv)ssT}{iq26!_4Yyq0Kgp<3i8){!&DQ{9 zcNXhSHys_v`D`Xa!X*FQJr4vACo2z^L7aI1 zS`?}yr(K=soB8G~ zz4!v@+w?PFCT))79z&`5_^x-SN0TAJ-^go>aQ5NQydoiq}qLRdN{C;>|GI>@$dK7l;$n7}ME zR>r{6d=QG7be6?h`~l#jmT~9kNMr&;IgLv^-!LMUo0*yU*)tS=XCU0mil_<1!EhgU zc6Na8s;R70o6Fl+7BRuhY%MW>*OB#fHl*Hl9!;=pny@UJMT*{GLeX9aQpAnQgk0Fr zL)kCoO#JUkg(~`w{pVZdLZN-8aC65n==G-ng#b;c&q*ethvGo^8V(<-x(vovHXx0R zf##r3QT~R;fTZ}%5Ti2TDKO<=#{4&qzekse$rczjJGGMN9ANA9YpgJW6MCabG)eAR zcFtBhg1{wB=XYUnlV@%;>?~rGo%yO*#=8?0+j4NKu#qR)?Ca1ML(5Ubz@QrAB((I- zDp_CRO**yt9}k1%Q3O@e=b+$$;r@o7^B)cX53T3Hr^;alA<(I`FE5+az5rsTYxz%$ zJ&(ovSBDC^)R2eXL038wkmD z-kltKDRfK%tK1caCD~XA)W>#b6MZ4wxmI{qe}Gt7P2?;1%z(4x(kJH?AKl#p11)af z7xU~-x5fwzSCj4;P&+=&0!7T3cIPcl$F?K}im$lAV4cx+iIL zRP9|M==3TxGPrE`1$q=1k5jag5(qeXn-79cpK{FBoUD3hr z=x@*SZD66OoE7Yw*k8>9PVI87Egl)f=l1f|w`Q}{pEHq1zbU*h9_>vR>?AJX^&p0= z(Vsya)hlpD#``Zb{|)U%bjptmP4I8U+y33s#1{-C%6D#c&wo9Dth=AU1BhfbpT=T@IqjaHYn|8c_ezctNlo z$1u~|3x!ufwy+CKn-ePVsQ7tBZ@4pV7N;Tk9vN%WWaR@ zglCJ26f&s3+5ZqRxV%1D@9&p;hj7X?x&=Gt299Vt)b%1a;1oMqThFxmc#)se*F6Vj z==z*KBk2Yd3F9XF#;+URtgu!I zp_VgtQ_;_P^gm68DN#Bf+r+-@F;G!dyglio#K93Y??t~BVEJv@V-Ox5J`3cB#l&C^ zO9z+r%P0aKl68ZB4pd|7meM)u{SFuT-(3U#8mQHBSmBci{-f!f zLPiyfL-7sou+PFALWb!K60mWHwOl zn=I1gE}7rhpU#oIeNOJJKUPq&&i~oL9T2J9>}-ht%b&x+J>PNywHYyb?6@NmEHt#- z%Ah2M70{9fq=sx32L}}}4HbQ=^q{-SpjKfl42YAyhW6e`fD64k4aSMrRUH`N(O+bv zv7fMZ0HkaY=;ecUT7ckki`;J7AA{K{*b^JmAlmR|t0Y7%lmq-{0`aQBuIuaiqq`ymh-}+oaSqHnvPmaig;LNF?m+g!61|-iYk1qj(pv8 zgh4vcOpzL{`w!X$+y`hL`*|p{CDH!`x4o;ma5pY3?ZfUQ(>h{yNPL^;83P}muEXre z$jB_2j6T&MwC@SQ+e{LXLQir)A_(}cK7o3GMYe)B1)TBaJ1FK&j}d@i1Xn_H$BOtn|9`HZPE8MNN(Jlb zelkFoh&jxHuM5#%hXL#;F+3lI>JvK?6KYvwLjz28eww8q_?=$RQ5FtjKqNopa>GWr zA}(_SPXhW2Y78Ba?O=3HI%w*WhpAGEDCeDl%Q)(=zG;uq#tbZw(|Fzhux>*$aQ5;3 zKM;-?sW$6)UBkMd_#yOT>H+lv&HS6;a$-&>REocWN0D+_U25>UD*v7G{Iw*`t>@8i z;W4q59wa+dQpE{ikiH!bLpBGfqY9kCOW;?Dp9TO^OVcbs zL(@45zPH}dcL5AnG_#h;r^yPssMSf$4G-j6{LKl*z&{9PV4wu9TFwmzDB zz-_0icFl_O@iuROW|<8xL-8PFfr85@&~V3sh94bw)C5j$dv|9C^%jTnYAe%^fst|V zNSAr$6!7~bePwI2K^-Gqz_r-WYZhE+Am$);I11dguY$dWK4?u|x&#eH_IcbbHV6q2 zISDX4fax~Bipv+oYCvl8gO`_L0CVRz0MY>Q9$+XIW@qI(=;lp@pwA=ZG93T^Um*N| zD)YYSawAen3C>>7G(wd%k`-Ez*dgR~iN4)k4pefuE&8$3-w&yP-HHwefi5wds|T~l zrfx{cEpav^DJhA-NtsRif<(wA#Vm^tG?8uY*C|!Piuhqgwufvkd}bx-O zN_hz0oQHQEn64F#zTr++!iTWD3G`cxTfO=J`dWK}KWcl@l^y`F{0_{Jq7F(ZNsAmU)1`?O@Kk+xp z5opE=K=^cZ*mTq>#>W5%h8LNScUwd3ZHp~lym?r3R@{J%j5}%$mqRRPVUb4>o2{WN zyDhK?0r56K0!p+b-%mCgYM5xf9apcgbE^SE3rj<2E89WgX}DOQDMmO7guNko6r5PL z-{5%^;EJI=w{~B3Bmdi1=8sUj9hyN7lFMiG{_iVJQ|@PHby0?BIJIo$X+>|g$7tK* z?T?f)MOuLAF=NduGyxX*J&1grT5qgQ)_PEqq9Y?Yoz2KVQD3YFwVmYS2+81H@)tU9 z2r4S73@$XhzrDF}1&G&TcdZf5avrE;QgGX80dch2rDWy8-#)?&u^X5ii zCd9pD@tFcmIo`v^eT6Xxa;MtfFnIU3rXE1s51@~1desOIXr;*8H`}(*=e5MmYW9`h z?kWTEuuFtT91CY0=N`yR>+Gfi?^IGlSW4P~;vHz~S4CL@y6@T54r=>EyGme=neeZl z8NvqDY0{BMXS0>4FD%PnkFd+i{|GpO8sJY{BQo z3D(e1J?lLYj8N|QX067F0RDT#P z%dMZOHGm^Cc8e~{GwC7Vx@+?R3+x+uq zO#=uH0pvE8j6d)K#$uz(Obiq=6^lF1puR9mP7GhZ4NRmJ<;AZmbOzmNAZlIL)cM@` z!AR!QBqgVX9FV`rs^@fU%nB$U=4c@q7ru;0Fae+Q=aon%GD2}JX=vZ9~-d3bmj{mM;< z@MK-e!Qm~m2e{g!95mf3e8$M?a@iat_{MSj=nN)G0+70jtL zF!ixIK!}EBa=N>-a(ouHmW+4Tl0Hx#JLfQP&Kf(3x8t zeiuOh(lQ@K^xn1xRh zEGfbtKo;B}>B-Kwzp^a5W+sD{6cK|c*KUK_euhFsCn8L@hC3A%Ayz0dU5p{3_q;h+ z?K|kr`30hdJbw0LD;h?&yM+F=+?0>`xMjj%^6VSu510G4$88j!QRY&bd?DwyfbfIg zUYQj_YRmC5F;eW9|B?CV0*IN?ym}iI<0Ty&MD_W{moKF25|PKN2LB8_C`o}x!4)Re z2pmkUPtb{HtF+e#^~=~w!38)dV=Gk%zK&8@uWD@vW@M!ZD6UWB81y3x7@i@C^=8v` z8GLuJef>HDU51einvFv;AdOYo>Um%T_-pD<;8w|WGZ@qsLSukP)(`5xopk9Uei@3Ede7iYw)0Fv{$ zK6e9P=&PhVnh7@g20I?#Ux^E-bCLlmc?C|i1K_`k?IEm~$#7xcA+D~jZlhx{qX?d! zok=cq)0XzEC;)zy8e*(CFNUAW6 zsxM?bqPC}X-j##EDKojF&rH&53*fCFVqmd6(33Wse-_&PGJGN|))~YKJUl$}+Y(DW zReP2|oN<;8`G(weuUZHdpAm*p@BsN^U#bFmU+_R^29cQ9|AvsQCcexkIwvxYdX(ZU2bXcTY$0L1Q5i8b3*#pck(l1eC^tc0S zn>#ym`O2jB_V(l?*~4#+{e!J*K$boz*pVL$dPqy2+kk{Tqcw6%IFYw-4b}(39B9UY z`$7UPPOrU_d5ss8lRmz^w!Q*Sy8c$t>MV|_y<6=Zv-VkcW>ea^@cM4N= z!#z^UVmLwsM1oH_Ii?-BUPo+C4JHdCdedw^+%obGD8CtSY-~JX$k#KEs51W88(NiP z))kJZg^wsM4vX_m;ihe9rGeG)_Sb&v`q|3<{!jkpo8iOG)-A!KtM%CH9>};N`#?7= zS&bPuPeHb*(_LWv@s}`ouPPC^{^2nCd0=`TM85e-1N zFiQ2X-<=2dz|a>7n|BsvBzD;ML)AgR^(oom7hVSy#1qZqvInC$|-cKzVb6OD|{`-X76ME6HhC<))ZG>766~{ z9sOig+-(Yty*bOiO(+w{(KOA?=IKR=hM3eOX_~TmpuSF!q;?qsG|IHeKti zH;qB+=LZY!qgmpG=J~wr{gCSz9W}KqS+vTRA0BSDE*j#Jf&ZArgiT?krN%eI(8izp zRd_+nq%>uUhuo+fYftbFmCNUju*(U znS>eo6B-z9O8WdsL?4gob#`Ni)2}pv^&(JhS6UZDO8kB^Sm=;pvqrYwz>Uuj_qQzJ zqEK?h-_zNMlU2%IFWBh;0=oAMFsc-#rAJFmyyMAnW$ea&MHZ6Nxae7{MlAJCg2$M+ z6Uliw4Q8`|X(R=~)%f~8U+oeOe>n&Xi-GU`#cFoG2_YRNNT{_GU^KWBp08$?nqc%Q zEY#&NzBqtw`pl%6uRE49h>lpY2{g^XBgHI}TV}-d!LtSh-D7Hm0%1}5u<*FzyJ6nt zm3vOV2k*_NNP_`+7`^_e5~YIb?21uszEym$4Eg9?sDLDB%2GEWC1osNo?hX+c)*p( zX1zvv{jS(GnIisX>9xndt&3z3(=!>XkL>{Ak`T2&JI`#~9cGPs3PreTyE8s%Ogqxk z5t{n*0mP))p^=d=0&X7fD)l2uCNJ#sc*X72)okBIfEUa~MMqB-g@BZ(Lt4B*(F0@x z{=>NKVa&ZTSX`+dU;2+_Oi4)cZC~3nbj_k;p{mRsPx#wlzWQ}M9sL{bfkG^GF^SDU zp@iX<~ewkWC*UtX;?0GbAMc0>-8$Sqwq;jiAO7J>1*361zyndUCR#s%g`X z%%%D}14s!vWgZ>?gm-ea8;(P}(YPkdJa+RLT09!)uUxCmcMaJVm zJiU|&Ff!XbVrI_ic+Ln6h73MflVIRghHYCjXh@XF^+R;!8R|F92xLn3#4Rwo ze3273PgxVa07wWTJBr;cfU|672)!cB)fdyqAX;xgJ4P>Lg!LWl|W$w`5jIS#zW$DaJK=$NmmXssgP%eFT}P7BMcTA;6h^$L{zoudMa->D06`s zuHRF38jGi4)O79?*yo-MwMsN!l-XnKH+hw}!EJ1JSIVnY0nI|*I^tz79Cn`S1PQA7R$K zII%}e6PeBwpGbb4YIye$GJ`QF+I&8)=$xY2u?#bMeqt7wZ&IK_9%THgdDaSLC3XrR zqLIGb#9R+=f;BM+&Ga!~l%AM%D4!cxi?1TcbPtf*<=o2l0Xl zb`ZkQGt>+45>_P`{=8YWFKD|w-9Fa`imKY5_O2%F6D-G#S;zUfd|3S@Uy+i``-gj;=zgs??SSJ~;vP&Tjj%xk#i&wmzB?pjB5$Pe?uobu*PRqfZV+!c6Vp5*wVD~RN{fqc9wt2k@Tk`U79H)g+<#9n3~GNS zjHS=bGOY~ge>3HRi`!Y1kHlaDK$i}y5!xm_6H`(1CB_uR7hj)smM5$}tLw2&OmQtr zuTOK8O;bLKl|P&OHt6jUcSk>s8@yfVb@k@i?X}f6u*WIA0h`nN#GOQA}o z!)RQro`r6j?IM?%QYtQgcUq3QF(pYF=&9!CV5fNcgY0xIKKl{sMZ#95bWB3lGYJ-b zFgqp@DudsH1lfZ=ikzA#53r5pSU6nUoNY`>eW0=8J94&Dr-!r5MR z2(3L>5KvnZEdL&Jt|Lm@LZI81G%BuSm!IgXdCM76jb(y}jvVtykTDVjwL@9rFna-& z_&We-dfMlBNA3Hi9R{Okp)49+yKZ#`;g7S0E`!eb51g~L&0`kgq3ad;KpnWd^Rn^q zfkER1XX0O=b_$>}2ARNG^gO-Uv>u`98W6;w;pKolMy|BOd6s}h8&DUWf!B6!5Rd*V zxC>U-Le9Us72WBHBuENg@WOh>x7Qz@nl!7M5t%SohEoV$M?SYYr*(W{Qor&OUe-77 zaY!iYNwtVfm?s)WzFe6!<|jx8NK9$O9e`e;+GRXd{saCMw3L{|35V^Oo0C(yFCWUY z_qoSmcO94&&{k3eN!bd`kF7HSKkki8qPyCeod4+XZdN*76So~mpw~qSPiyfu*?!OB zc)0;mb4q?^DKe32NeD177B~xeuq8e}f06^d;4GZV|CFu&djNH;J;^dE)(yLNDw zuSAEfp+B}Kp=SM|;5>=<$C4!gNeAQ+)r-IkM#ROP&5q|;SR_$9DCc(AfrigQCdUVu+1zTB6~v&g@8u_CT@6{YYW0*& zn%$4f$p9`VCl`MGf`$g}%;E#7IrH8{yUl+uA;t*z>B6XwUX0}2WXFsn3}G9L2o@Gn*pg79J@0t;hyPv^Q6gCaQJZ|fI1i{g|l;t*)L zh#}^Xcw_0DeI8-V%ieZZ4K7JlrPI#%H_$EyK-Z)){=(UnFH6VNT|7?^iPfql9G)D8 z_fPOP>+RXV@%gJMD;v{xPKxS&?23Po+2mr(xuUxE!MJr(3Qyu%CA4Q4#?KWidD^|4$Yh3?-_R^PGhJkkRCYT3d5&>=!wp z&>``bOvt0M6bTW7d}wIJ^(C0vURqe=;c&dyMb`D1MF&WPDzghlEo0sw!z>g+oiSyG zSs?(n5*KPydq_e>Gb;rF{Qpvv8<3;0XL@z~WrDWTA*dn242qH5ut`KfUE)*Lv6m6PqZ9sBJeM| zdM$F&j}TC|?C;)_Ilq#PNBzm6HjzA~D4o$^VPRpa!OX#dvzmR$$Lvt=FsG4`t}eNlA=#vxl9Ff>&fysW?c4}qm7jE#i-->#B@ zT}|#XGDU~lB~yL4%AiqBRg=iBo}NuCziW6_Op%)nyd5g{zqd^&K!%?5;KIe(`B#Q; zl$K8klRK(1WiOF4OCp-`V;`kou-`psy4Z*#!eaRhRlC2w*)6Hi_{#Qp(@y&oK@A&D z`txgw5MUcX$oxD^W8|s$;6ZkMJ+GbDimE0;=*>%*?!4T^Kno%0(?B1{7hH|uk*~sk z%Lz8Moga>PL?RSF~atsC&o?~*X6w{aJahSmZ3Smqcd@>9!TGLwY` zU4n(>CKG~v1E5bDHoSce6j*ZFzFIC@zJ-M5V`USzJ!kr*e`);eo4b-ihtQKT0Re$d zuY%WIp$ZX;)$i6~HC8&Ep9RC7(U{gc2<`u`YO^5*>?qkGocZ4~mx}9e$)oT{%>mxE zHNiFW`z=-Jx4hvm{PQPA1CgAVjYM zgnlq6SU*&plkR`P7b7@YfAWS~p?d}F2+&ixUvwD}FAGX7I?b`}2FqkcE#~EO-7IO9 z5H#&@jnRiJvhNND5C6K&+{0eR;Wa?3K!dIC<(zfqhg~LGo)Qzy!k8#jr-neWiTbAt z^!IAV%xtKph(AHqr6-Z&^>m&*9vl0HEiP~SIfi0%G$o%kd||` zpCBn^ZOh(?@+E@QnV*+nzXB6&awHHva(T<`gFK4Z^=nb=ME)PH-a4qN?tLF7r5g!p z0qI6c8flOa1nCqB>28pcRyw3by1TnWX^;l#ZVvTZN1o4j=6z@Q=OE*pz1QAr-S>50 z!F>lfM?3EPqqSmiWmO%hM0U$U?q?sTijpwI*$1LI!k}aS@_Yd1lZklG(+^kR{CC5Y zpMx7F*JK$ERC>M$Ly8$b5BKm$m;o%Ep!ytM;00DdxXN&_GAw;3L6dd3{x9vr=@U@j zQ{+Q{T?LQ*_y-%z9e&Tk%(R=UtHkD0xmT~%!1Hg2^ z69+lGg7?IZ5tJzjFJ7&(E8~e{no{8pbJNhgPL|MZ^%i3EU~=a}Py2VSfmt=Q*|TwM ziiUo+3Tqoz5?KIMIk3M73=Eu|>f_e}7r}yD_3<~HS$l|7m_zYC{znKuKmWI(ocWdZ z-*6-6^f4gUo-szHP06P-2;QIw7o4d)+9^gamiK9X~p^i4n{Wuc7k|Ea*r?X}L=>0t(h z>tI!?ujJ8$83Bi&w>j;VOG!*jO!t~0Oekd;2v7+KKnMt}CbM@XW`axR5rfP9=;s8Q z4?r0pA1*%1ScVXm3N@4%8z;^WW}NIO&mt0|hX`&UZD2cVfUNK&xZgnF*+i)s29 z;g~Z5Bts)3ixs9}d^KP+-Qm|O&}&|=l8J!47-#e#8o@~y9~LO|e+?<}QK>4Ls96H^ z3xg>bIB30rAFcP*3X+{}{aA446(~x=#bIV4%7UK;^d0~bW4kO?(316OsvHK5mEeQ{ z?EF5n6{fc3!9k1pZ|Sm|_%O^7s#gJ6kJ5`(L;{EuTsaJSmTwWD_HIK%3h2a$@qmwN z_12j8j{(Pzo!#BA1Lb9tKQ*5|>?V*w`y1#!@&7rNG6dm#t_SrqtUbYh0mk10VQDzv z0up^iL0C(4na$f-$geY%0+rwS5M^?zjBNS_g@JW{I&QC`C!aS{x{D(W4uD8OU?P-4 zz>(hKB6(S{^F?ud8|b-#&ng3EDZt9!y?R58uF!$zBjfpEkn(xZxrx}lNWn`$Rs0PlbeD!Yu zirnb5%VWDN%74U0Pp?^F(hpqbR`izu>^vA0V8GsPyV`|<3<2+Fr4{?0t{nn!E}-hU zop`JC^5ZX2yJIGL1E4$UFF>0qpO+SE=TiG`>tYW8U6{nyF?1KeEi;D$d{hvzhJx)0 z(nEPL{67P}wyOeIwVCR#0bU;=;A|OCoD6nnjqeZqalc0LvHyVrXt-@^!JKpW`zu~P zzL{^C$l~wbO+hE4lJM+s{&$h4pA8Q~sj&$Nf>7QB>~C)~AW<`P!%n<3DB3$dIq6V0 zH#Y~Y_o;LHwEw9DP-(%YmB|}v_;0UgM$S-P2NRsn%Al|jdXLaW3AQq3xeVEu7iIi4 zI3A$Q1tOo?I+bv5UebH>vI*c$!NYw1SNR1m8`~kENQFg3`G6HK-;ih(Bi?0@!Vzr7y#Uc*6vQIpLCJCU(iX%^-Bl;zy%$Ir`a)qNfOx}Fq`LuXg2e@_y4>q5Y- zLR?@D0-8NhID4AqwKe#sPu|C;dqeU`*NhdwFE?m?1H5Q-mg*gPn--EAYrDE$yP8Ry zI)N(KWvOQi%MGI3lCjNeJwHRVPdp)IZGBL?Gh-_|Djd5-gH6-@X5!$9QY zMse+8g)94$s2~hYUleQx$^w}Y*Zr-N|ep(G?gz3J)$C2d|86+Q0< zlq=B4;o{=PzQUFQ`{TdoT?@c(rL^>EkD)lqPhoS4A}K`3mp){Us5z0RgMGcs8Ln?C zSSZ2i&u%Bj2AD4b6AF77r|EOVYJgU+^+sK8k3(}B8}uyCP7Mto0003DFj^ryg|@bw z?l%H2k>wA-)D_SGcB-fK0MTH4TPsZg?m6gZF+*N~^G%9D!-#{<4;<*L#_!jp61iY5 z9>p}^RRkyE($Ie~ahfcCz?2_@OaOm_WOu5#a!$;;^QS*_ zRmvXzq^lcN7EXzv;wn?KTg|Ym z1;4Ue4Tvqyy`XUc>uVJVgL|t7a$0F^i}|u4;E0L<$2oU?v()#+xI+=qYli>!Fj)9ORNN-^0Mb(1zo^^ z{+)+ctXeaOIa=ku103lN)uq8fOTgv;g>u#2;67**ex0GAqAt3wVGL@`05qsivRZG# zySJ*!w7kqk60c1G6k`E=0RV?!bY~95BzIsmx&_ta8%0I+Gfg5uef_tUt10?ccu#s> z{l_F2K`{7=?lBJZfl$O~GtBMZEC~frRFF_^rjm=JQeB8JGBZN_t<`|o9+U~0663)Y zHnV-lg|NxO;hfPw54X3?+YPK2o36VF_18Byjh!<=oj?#CpsNiF3-Fev7sBwx2Cq@c z;82YN0s=zX<#{DDjSXiWA90%hU-bgPzFMPr2pPP;sBO(G%MqSFeFhdc5YM+PkXzOy zgvpaMd8HGXyh2#@oF&D$g!o-tlp!gWB1J9y;w#(b{1crR5foalm5#ud787jZ$4|K#f^L2jD z>Irx;#TCy1qk#&FW&Rpxxnh)pO$4)L@cHX6_yvF74vPjUzp3zWbaW(jUOst~<3Cz} zfC&Pk1N@>a^FzwJvyEW>yF&(!pk|4PjKz55@8uzjR`}`o54{WFqY(!knI*zAUhBQh zO|!TPZ=fUr+@RuEk_ON*fuTFrGwwVwRDUrg8830UnLfZ}Zf{(lN3DOQa?V>C=(-eABKv&?IAM#nJ?A zq^Iu;xS_Yvz!U)xd$^6dLIB$i@Jjmzf_hfKpQ$N|4rxV1L=;1~w`WsRNzIq6UfJ2% zvFig;fFFS^ zXCrEAVZjSPtDsQ42al-Qxf%Y6P`6QiJj+=D?end0pa2SLP*Er~xLH^*rjTq}?`kx=iaN$!L9f&{fJ|xl z0AfM=zOm}Z;adn-v0FhyeDj){nwUAA`yg9{J6tp#0a`$y_%qEN^{L&UkE$5Gv})^f z)pJ0407q*S99{fyQ{lxE=l5AH09PFTVkBsjugdkwF1bXVX1tBwQmcbZ> zCNi5#bZ#iiw$-3e8d9j%xp%b0raY_!Q#@DHQ-}`kuCJ}Fl~iuWd(wc_z{u?@tY3dqYY>Rwmmd}R}QC5VaY#P z_=Od;{)TP|5zkHsCeHp|*jM|z=BNwj(U~B2mRC?^*f;WJZEbyIB6;?oh7RC&ZB||_ z=-}IfpP|>wpt6Bntwn*%ia`LRMG;hK@QiD@QdrU(0~`kQO8Qh2;VQt8N1gW^_J;v*|bJ2A>iJ zWZGJTe$0f33W{$3YYQcy2Xp{=>End>mmiF((URBY!YEKX0~U3fC^}qO`5GHb^M+l9O7@jZcOYXx9~l|h%Ib5ngw2uAr%cKS zu3$&W`)HJp4AM#~KmLA>kLh(f%**1cZP@7NkDhUHtBnBYdl#eW_@f zO|)FXu|^ecK0c286TqN-D>rtpl?DtO{~g*VbfAL1zYM|o2Y8)h`;Ic;1L!pGFCQQW z>=F}6gDp5iN)%XpxaV?ilhSd6E;=vCQtsrW9AagUOWIQB3H5@qrsuhq=2A-i{f z7QnutOpHsM4~)ACE==&g+XBiBcxMP?I9#+_GW@>?uq;`*y7Il#GEjvj5anKU>-d=` zTGm_JIz;`aR!XmtZo*>1=IlzDEtrnZR!JHTNI(G0PzU(-QlRS)&>J3Cd;^odugM-T zN*fCSVKY}9Nlo(3k2MG=+rU6&DZFg_qB zzH}IdC2Fws0c z(`+GJ@63{7Ex<5_)BwuoUnTZVoSe;vvvDP$M*`(382?t={ivp7Vxg5kIzGV<uka+ukY?hmeirW`?E&`_W=CFEe<#s|9&k`l7!YONxJkKU@V76ZSzqZi40} z_8eogA4;tSKPHj@Gc2-3`uq>HNTK`%XoC|!KmsO{J%47{Y*!+1St!$k%Sw(Cv@3s+ znX{qsHKi05K8S@tAwQ_76eo%YYh?!WD>7OXCB>RU5is(b(=p9*0CJROvr%%j!0*Cd z4PP4aYG#3)Y-rkO)fsqvvojZ5c)m@Ij*ky=3j`hqL!bJlrC*g&B0Z3!_{SK3;w7ZE zr|)x*wtvN{_;IYHUi!A=ff@a+Y?8ladh3t_JJ!5#~5nLs(soRd7rZ-4D}2v+haVq zS!+2ddGA)Qw*r7|cok`8UH2&)hHZDCWWTn?{Wt$E8k)RrEgHh=sH8E=7|Mv z89Hp|q$h*5l9H17((NlqKg8%nIIG!q1Z;v$%8M2UvJogTguXN|-VViThR?&MSCOiHq-K|vDq21<6 z+osOD)pqxq6l@rkkV5o@z< zb>qnu-oNisP-Gcof1Q<2#vYjY?ZAokwWe#)UnMYJ(n`zu@Lgjhv2Q@SW29NR5i$$>o$sin5oMK%Ubn6aDe{(m{Yg;bPx z1GULGc9f5s-vA{$U<}gV*c#rqKHP{fB*+nGa>;@HSxl5_7RXq)p>G5hV;N23M?urH zV6Z%@R#|>nx790~k%D$yn+4fRpojyx4-W1Ah^jIh^Xcb? zG``2y=FDfqg{AAx*ZQA4G-iNn;bExgYp>MnYyV=nxRNgj3yQ|!)G4+PnUs=B?NW_T>nh_R` z-iB8;(+5Nb3Gz(*wYh~ZF+5}`X^Nr!9mK;Pn1|CjLzckzFnh38KkvxM8M55N)wOE?ADSwC%w2Yp9tCAMnY7V&Q_yw znxCOrV|YYrvuD;bN;c5D8*8eXQBf$}!ep>IeN%X@yZXrw4}9oC_>e440*`DzdSb(}Iy$>uATp0)dp#|KXwb z$b#31rKyR;43FJL9JXlCb}GL^^d8jVgNYb7SPZV_wW!(!1}VpjC(&}|2GmdVtm|H2 z;sY@oJ3G2An?4v7fVpd$&%K+EkB@HY%Ue%R$A?Xk2Qd~o&+ty~-lHeyAE_7^w)Gz% zgiSADe1JA;Yz{{#nX{j^z(y-l=q4dPnqf#`e5<=~f86K3Gfx%icrWj-wt+L7b-(5P zS^G)Zk|l!3-RjU6vO9~ctNla6L6Iv*#R|%a3zDOr0Ln=>=ii0SFG2{QcFa{BV*$L2uGuQt zs?&tcusbd*AXoaRqce6$+osucohTF)m7HJT9dq;!xm9P|oH(DSq`ghoCbBN~pf3;6 z(LiEYx{dn#g!wRJC9)-SZ$7$r-o#0U8l}@sF1@uK#~_K9R~K?W73SuANlkc9bQVRd zE9gG5<#TigpMOz%$r`qE^HZ5O^T;{%qpnecv+MpBs>+5gNx~UKB;Kzb#oPP4&r=6* zP*o4sQH2W-5ik|y;N_JX*|TrVB^i_iJxc;vTAu{p6G)h09%;1ZB;RG7 zg_P-TPJFwwL_ANKYyHuqUYqsYJ4z7g)x)>p>iy_)Avg=!&3Qp!1e`p8$D@SCfC28=+q-zw&v%de)eNg22qJDh>Ocd!6i`Ff?yD~M z$%>XEppVYh)`3~on`XRbVCiJzt>^;=OnKvf02(Rw`AO14;Ah5mXeV`c=CbJ4+vnck z{8>y0o(Ph?vR_fn9lC4}h zJ%S3nRc>8%wP2FV?ZZL{#kalgQ|wT#kpn)o?^8c~eByH1ti8-ene2vePO>ia)LyP~ zx*u@SUcUz+QKUUy9o4~fq6yl~umsYdU|HZQ55 z`A8H0=p`B}OA2M$+gN*}NeCH^3U4&Key7SgYE|iEk;X^k$d7 zETLA)2v}{pSo!DL98Q|@q_*Z_b#`NwlFmC;IJ&RdNz4)|iI2P35@^66&u(^lqqbhl zc=EU(R(vNnid*Xi{Wu6vEf;-Su7mu^>U6GrVXKlUIwNHojQ%93Tj{qDVH<$5-( z{Uw2I9xGAnoAu$riYl|oEQ+|6fDSmS;L0qYuD*>;3~(&;4_GhKxv8XeyR>$i;C2&3 zCYoRHvO_?v%1vvGpBBgo`eo2Ibg;sw>iTJLs&IJUcq~kO^L9U}i}#^Pnm$~w939n` zj>0Kvx?VjDBKal%qP}*F0&Acd(?ngY?^UBnR*H1WiwMhvsQu&o+g3rpsw!0tT0C{f zx|++MmdVZW^X$h4@6KvsYB~7FKaC}dy5wDyu-t^dX=oATAuQ#d{BXG7bkT$fqK=~6 zJP}NZv|P5apE{wjmAS)a!kX?Lhia&wpQ_wmYZq+8BGtMV)JiM1z7cz=lF56MnLGYw z4e%5juV1Y-+d8hLp%qMPpS6U%;ezn#ieQs#C>KtKpm-%OXN=~VyD_Q5{*)vOJv>WI zpIbgE!l?!7U&B>`Ae-&&v9P6(;Yxdv5EAMpHIm1~sSw6(bBZtkP#*jf^k3(U@S{;-VF2CwURugVx{qNc zH$6xdN+os=3P7TBFj+6IOe?YUdHNk|DHDw6);<_sC2Hku#mYr>A8J*x7lkX#HC+ZQ z4y(ys)L}g=e7Z>&UP`CJM{2%ENPw@>8P!pXe&Tbi&1*9suGHkbGSC_6bN#UhO~<+< z^%b^VwP>D*ac-jZ;Y^lfF?@csu)`|usk=w)^a$A z5u7ZUUI(mwx%G#$v!`@jEy@UCD?#qav=I&t{bTLut6Vxfbg?oxT43_d%VM22?D&~;Y4N{_IT$0lnX?>@m zdh@pliA)yKsPcd4^Lq=ok=5rEOdt5KtFelnk|!)N&iq`D-9vIKm{z*D^9b~Rh|wtc zRp#&|H|poDN93-fi<^nu*J#4@RP99XRZd26!6vsCR)>h5s{#}BO?m=Zf??G z{c1jMzDuEO0o?5@DQyQx3kIP!G6~v{=CA#O$t&(Wx8zVMX+o5s3B$#Jsf~praE4%H&Nl}msyJyKCMhk;_N(u*;Qn&-lH)YBA zOnt?C+^SBo&h#UR2#fX2hh(Y>^~bkeo2IRG#p<}kVF-GTQ) z(ZnN#hzLbZN>sy?q6gXD_2(|*E|}rh13W2CjD6wQ=XQ;P@&brJQ%b^6wS%b6!4v|? z^dU||ue>jnX)LQ;8HVX;S-d4+P(WxwIy9o_rZq>o{?*b2<-(nzm6hKwxm#BJN>gIg zAqa-IQ%GW9kt~(NpH>lJm>5!NX|Xs4p96)f$akYnyroNZY^_@r>ks!4mVq5izxxAW zjqJUS|IVO$q>o_y_^{(1yOYYltt_5Se?EpX7Fn5XJluw@w1Pj<=#pFIUvgGd_qO#Q zCKZzWDL4EqY#v8nO{doDO99r8+P`6M{qHL-LVd!XSP&9SoKZlGreDC~A5UfwRfM&w zcMX0mwvl8F}I_|&F{IKgzHTq})M+v)z2lT!E6<7-vfVJROz*aR6{t90TX5b#!WK^L)vu4D2 zmKPSKrDwK2?)dFoW%bYV(Qbj=JoCAqU+a_FF8X(lN-J7AcXp!erCU_CAsgw z_}Fmo%a<>Cw9qDU=jXNv@q68+23o8=`?1&4DQ9ox2`NVCtp(5Ga1_&1b8B4gfBJ}z zA6~EOx7^;IvWcXz#@9BV4Nk9=ZP#3WKixRORjgdDElr>c(=g33paZ!_7M+>02K(QT z>Umec8tGcgM+gR2!`Z5#Y)useEzh^4=*uQe?Bxw39SZYb-+#YxUB;eMA5KC-dNS-v zK4w!~vrhv%lYJU)=6-3pwAKIPm#((s_SDE|@R#cyW%Suz<^)$a06*%+p_|v8SniWx zlD;gCUK_%L^%l&ga)>KirY>m1<$;Ra`TZZD70H*9Yxi1)U$CWrhj zUZAH~j`yr8yf*Bp#vhI*7m^uPb|D(`Ts-O>`vhRwmEv;5|4zkd6hBU`UCu9G3?JvL zGn}wkSW%m49@f9JB$Y1!bI?s&s=YkMR~%(4wvsKlOE&h*S6y=Q0+MLmwV%lE@B!?5 z!#?`TG&~^j63o6MNO*b74uO@Urtsg*O{66fhJ{Hh*fvBD02k9KrGZQfzl_r0<2Q(z z0=#JUY>~b{Jul941T(!i?psTn>5*F;^hn+vUuNx9k|>!?Z*g0SH)@INjlkbPU}*!* z3}bzk`>GeSQ&1v;M|R216|X+w7wA@Pbm;%A?pF+D!q)MBmOtFWck`s-sdT1=wX?WR z+OLKpOwY|jxSc+1D3`zUf);SbcKt&TR(e~_^9a<{Gl0%>^KYcl)e zj{NJ@1XqjF7#flg_8Bz{~8!dV>qdMFNU?R zoZOE~-i>{9c_}T-tZw6DR-%Nz{GLBQR5rOoh$Or0~nh} zBbhf!zHhZZcTlKp1@PI{))wG^)R>R=I5x-HTmv*^q1J{X^{o6>Jav-ZCvrQIhL(1v zR@B`c0gF!e=xmjQpL`!xuC{!kP0L=hFQiykxBS7TlMo zXG15U{GYo?XAN7)-3-M?aet-^&Fo?scE&m|QR#nFRduUp-;Q^s$yfgPd}lvvAT2$r z*7xnsFpUNUg~p1rXMY#2&cUF?NrC7=>K{Ayk{O0PbuSN&vfF?wk9L-YKrN%N3^M=O!KLpIZ4lzQiGxniUoSA)#rcQQBhWefxBYLgf=3iN32K@&)4q+ z*h+l;iDF8T90~TtWm(ZA1fU$tjGHdhP{Lf3Ad2gUV4p90aG8ye(iEqNOgxUQ^OX8O z)GTId1M8*}AeI1QX;KhTF$;P6N}T)*5I8RPW!~Y&4N{0olrH{S=&Be^^**Enu4c=41Z4zI&*#Oj)m=?bVO&j#6D>dE)~Xa{4ire)AQ_IQQTZz^-tIA3{Icv3LeAUOB^UX-N9|Di)xsUb28~W z4hcb4nUZD;s>-(eWcco6GUbPqBJZ2MFqw1v!c2D*0u!@bnqIn`JZ|^QAb>rUTW)&$ zsgSIpA~sf`6>8+xSZsSAat^u4JxOsh?8YM0!c3YS<&{t$Civ4Ce)c@GJ*J;!Wutw+ zu|!a=PMy!S2hDogNFEJwU3b=L=10B<29h)t8{4j!h1OVc4ifF`z8{MxDXH8hi8ZNC z>uRi}H-S}%T*F}G`}5Ag9xr_&9r#0-rdp#*(oZwl>eL#Ww+=ToR6#s}@jS`pFz4Yg!4jNkz0MO;=OCro_y5Tt>3(;d@;fp9pATHzi^@4Idy1WK}= z-d^gb8rd8nVlwN&xxo98I^yTh5jzi$Ga#JHj7b-?5I5j8ipoOWG=zbVZKT8pJqll# zgQeCeVniZH=N|H;UFP~b!K0?ss+*+xC{&9E508d8?`$`0igLH%4f?p)L;G!IXRUbM zPc8{a#MrW2wO%);Z_^d=emE~>5QtwfoPR(Yf8GkAcfRs>3Fa=PB-C^IP$FO2vanOA z5C4Pv#ci@#lyWJqdTqO}Mo_9Qk@MTW25-qS%el_ly?RYd|AKl(k~1`IZ%xlJj$`{h zcokj8qNVd~J3Pn}jjQ6J@lq!?v>{saEsLFME*37X)8mqz2gch04kwMQ{)Etz zydRnO%41uTF6b=j%63;$!i-+;3@q9;6_@zWTUiPaZpVs`yl?Bdj-x1qu9tq88^~I> z^e!i@VLsS+D9lk)@m{N{RWzF!h(=gWA5I^kSo~?wm9MSS6$w~iRbLESeW+azj>h5g z;6)*wDQ|aHXG=n<)k$}EZ>y(BC~&+OD`scdP{?tG)F;+Ngp2R(Xx0*@yynD%xRhkt5^vqR8$4|R~2+{Ky}XIlp|GRcx-c|I)J z4xJe`^6bme>yrA`7B~!gXWvu@Z0TR!OqtTXRa+IVkw#e&xwSQ)IXm`#b}zm<-tZ7> z9pkv>X!BX)v0{n#-9uKpLdx08G7)_|^J-)bu8vlIVR{1(E&7}G_VyYSKOhB`#LE+( zl_n@on2xE)iYI$fup#$hR+UECe;Efsaq0Ouma=^7-Gz4IcBiU7YX1Zv7tf3RRBnMX z*ZAgA8eT2oMp_Nvc-~ISpHRN+dr$V^f?Iqhj&EA#| zh?9VLbKKL;NTA^LCI9Rh$~pEf?J3?MdjDDTGrO0*8k=^f0=u~7UY~{WJ~$7$N1x}l z{q8TWNvVwf!?iREoLBH%BTLGvd%pJyE$!%~Eo&mZltjoJZ*qg8JYzmuGM9TGqN=_= zUcu~Tnoy;ebLMBu;cwFZ4uxsdTIT^<>4$r(N0eNDCUqwzlZtbfj?CGF`ecSjm>g{# zOE%vh9;0BA^$hJRZ>R~0}fBe~YdIE4gX^KZLmtZH5q*ci`1T^RlF~`zMW*=o31t=>a$$#~{?F!;VF=gTKhwpZ z0xpNk@YE|Emv_Zli1>hi7Hni$KE?90rfqhZo#lR_%9?SLBPTl z;g1q?K#If1?G7gKyIHCo@z%k*=@)5tr#(pG-#O3iPi^#M%XWF~J$<7}m8cIx1wGZo zlFT~8bR>#2rjK7*^{SZSy9?=|`1$!=F<7tS?8%qB*!ZGj-P|Yr!r8g-Zl;gi;a!Q+ z1(WoKR->M5i_c#(H4tJn%{eW=-xz<|=F)@knR3Fp^em>b#^X*dy)erXKJD9t=XMYA zFCw=8GfjYs!LSrT+P1ea~ z#Oq95uBS?yAH)v2abyZdBO?ZJ#TWjx-d?A>c%9ak6*6icb_BquHxs`){>#m;vLMniBB>=Mi`GovOv4X+v1WY*sHoe={hr2@%Xv6!qbT0@%S$WXgUl@H3 z>=tnG@PM^xTxu%1St8(cG|Q8TPxV&704I}=2{KQlF}>X~^(Cf)e+s7lWr>?cAt9YO zyaRq+v?3p~7g2|Mlsg=|L!ah7nBT+I|C&=K;{^_04(z2Z5DQUJ-w(DsQHS>gB_}ia zG`a})Y{gKQZR{w;le}wg*yTpdssAI3b8r0f~$MvCosAc%L?79xIdF`G& zO)cQ=tWc%5wBU{1M}f0W*-vU7rZxT~8YiJCNV5I+rfJ;m@bu@vk-583;+Ek+(EUuN znW87BH4Z8=-t|Ll$Rm_&FO^8g;SmRpQI`9ENz| zCP0*9_u#c&Q?CumM?)vbhvY@R0w%f^4xJ8f$GRCSO;d`wGR($hCJNMN_e`8zN)P{y z4gTsy^yAF6Nl-j*N%x5esl0Y8AH&IfTN+)g!M0i-yM7~nIwtGCyuxdwk$_s1^IG#o=`2K>rs` z!mXnWEkp`7A_e^w^$5slRwNta)i;=82{XZJ0k|=NwAEVQzv9K<39mZ4^2kN9`#c)O#ZT8^|mYG3JODj z_f4tuZi<*&pT;p;j&9zmt0Gm|j8vY7;|CcJCpe+yUZ}uvn1E4A%_GD6-;Q)=@;`hf6`+jX=YP=0^N4UJ+sMXZtb+$OIhbG)F2@hW&AsC)QxX6 zjG<43fy?Q@C#pW_zIwcB?_mxx<;lM4MxVW~sK}?&`=^&my$$${foCbgS#HLMkTh1M z4;bXN+Sd=QUYFOAIp4P7#6;gJ8RClVMxeG#<*hqqscj>1g`9+j|5ke~+MqZVQMkoS z{!d~E+L1uu`BPd((qjv~OKm+UZK2X?7b?Pp`HehpK6lc=(&GB+3iE9B$rOq>j-+*5&8oJs2E;+!7=f58%0At2UnR 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≈pZ{WfwxylH;4!#MC;rM*S#lnK+ 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�###;Nl|l`~ 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 literal 0 HcmV?d00001 diff --git a/docs/networking/move.readonly.png b/docs/networking/move.readonly.png new file mode 100644 index 0000000000000000000000000000000000000000..95db906bcd4cbc71083a79a2d69ac3c7c748dc68 GIT binary patch literal 151622 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

tmq6`j6eeto%P&&IM#Nm!5ZF-zFYDLG6~ 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 z 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 ODaxtdDwQ>R`u_kuw=ER_ literal 0 HcmV?d00001 diff --git a/docs/networking/move.readonly.yml b/docs/networking/move.readonly.yml new file mode 100644 index 0000000000..6d1245d772 --- /dev/null +++ b/docs/networking/move.readonly.yml @@ -0,0 +1,65 @@ +diagram_id: 91 +name: diagram +states: +- id: 0 + label: ContextMenu + x: 826 + y: 1008 +- id: 1 + label: Disable + x: 914 + y: 115 +- id: 5 + label: Ready + x: 702 + y: 327 +- id: 6 + label: Selected1 + x: 397 + y: 332 +- id: 7 + label: Selected2 + x: 268 + y: 735 +- id: 8 + label: Selected3 + x: 225 + y: 1021 +- id: 9 + label: Start + x: 704 + y: 128 +transitions: +- from_state: ContextMenu + label: onDetailsPanel + to_state: Selected2 +- from_state: ContextMenu + label: onMouseDown + to_state: Selected2 +- from_state: Ready + label: onMouseDown + to_state: Selected1 +- from_state: Selected1 + label: onMouseUp + to_state: Selected2 +- from_state: Selected2 + label: onKeyDown + to_state: Ready +- from_state: Selected2 + label: onMouseDown + to_state: Selected3 +- from_state: Selected2 + label: onMouseDown + to_state: Ready +- from_state: Selected3 + label: '' + to_state: Selected3 +- from_state: Selected3 + label: onMouseMove + to_state: Selected2 +- from_state: Selected3 + label: onMouseUp + to_state: ContextMenu +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/move.yml b/docs/networking/move.yml new file mode 100644 index 0000000000..42e42b6dfd --- /dev/null +++ b/docs/networking/move.yml @@ -0,0 +1,107 @@ +diagram_id: 87 +name: move +states: +- id: 8 + label: ContextMenu + x: 826 + y: 1008 +- id: 0 + label: Disable + x: 914 + y: 115 +- id: 6 + label: EditLabel + x: 765 + y: 684 +- id: 4 + label: Move + x: 118 + y: 594 +- id: 5 + label: Placing + x: 263 + y: 89 +- id: 2 + label: Ready + x: 702 + y: 327 +- id: 3 + label: Selected1 + x: 397 + y: 332 +- id: 7 + label: Selected2 + x: 268 + y: 735 +- id: 9 + label: Selected3 + x: 361 + y: 961 +- id: 1 + label: Start + x: 704 + y: 128 +transitions: +- from_state: ContextMenu + label: onDetailsPanel + to_state: Selected2 +- from_state: ContextMenu + label: onLabelEdit + to_state: EditLabel +- from_state: ContextMenu + label: onMouseDown + to_state: Selected2 +- from_state: EditLabel + label: onKeyDown + to_state: Selected2 +- from_state: EditLabel + label: onMouseDown + to_state: Ready +- from_state: Move + label: onMouseDown + to_state: Selected1 +- from_state: Move + label: onMouseUp + to_state: Selected1 +- from_state: Placing + label: onMouseDown + to_state: Selected1 +- from_state: Placing + label: onMouseMove + to_state: Move +- from_state: Ready + label: onMouseDown + to_state: Selected1 +- from_state: Ready + label: onNewDevice + to_state: Placing +- from_state: Ready + label: onPasteDevice + to_state: Selected2 +- from_state: Selected1 + label: onMouseMove + to_state: Move +- from_state: Selected1 + label: onMouseUp + to_state: Selected2 +- from_state: Selected2 + label: onKeyDown + to_state: Ready +- from_state: Selected2 + label: onMouseDown + to_state: Selected3 +- from_state: Selected2 + label: onMouseDown + to_state: Ready +- from_state: Selected2 + label: onNewDevice + to_state: Ready +- from_state: Selected3 + label: onMouseMove + to_state: Move +- from_state: Selected3 + label: onMouseUp + to_state: ContextMenu +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/null.png b/docs/networking/null.png new file mode 100644 index 0000000000000000000000000000000000000000..f095a1cad41beef28360efa422b181e2228eb42b GIT binary patch literal 32591 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

*iSNO6u_`Pr9rTvp*ezB%?a4jc1oj>G{ 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 literal 0 HcmV?d00001 diff --git a/docs/networking/null.yml b/docs/networking/null.yml new file mode 100644 index 0000000000..5218cfe1f7 --- /dev/null +++ b/docs/networking/null.yml @@ -0,0 +1,15 @@ +finite_state_machine_id: 17 +name: null_fsm +states: +- id: 1 + label: Start + x: 391 + y: 132 +- id: 2 + label: Ready + x: 402 + y: 346 +transitions: +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/pipeline.png b/docs/networking/pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fb2b12f760d60a150da6a03d4ace6593050e5a GIT binary patch literal 143969 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

rtoEH<|1PxQZgcaMF3BdETr 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 BqICcO literal 0 HcmV?d00001 diff --git a/docs/networking/pipeline.yml b/docs/networking/pipeline.yml new file mode 100644 index 0000000000..43052f2b2d --- /dev/null +++ b/docs/networking/pipeline.yml @@ -0,0 +1,233 @@ +channels: +- from_fsm: buttons_fsm + from_fsm_id: 7 + inbox: '' + outbox: '' + to_fsm: button_fsm + to_fsm_id: 12 + type: '' +- from_fsm: buttons_fsm + from_fsm_id: 7 + inbox: '' + outbox: '' + to_fsm: toolbox_fsm + to_fsm_id: 14 + type: '' +- from_fsm: details_panel_fsm + from_fsm_id: 21 + inbox: '' + outbox: '' + to_fsm: move_fsm + to_fsm_id: 3 + type: '' +- from_fsm: device_detail_fsm + from_fsm_id: 19 + inbox: '' + outbox: '' + to_fsm: view_fsm + to_fsm_id: 2 + type: '' +- from_fsm: group_fsm + from_fsm_id: 5 + inbox: '' + outbox: '' + to_fsm: stream_fsm + to_fsm_id: 20 + type: '' +- from_fsm: hotkeys_fsm + from_fsm_id: 1 + inbox: '' + outbox: '' + to_fsm: null_fsm + to_fsm_id: 17 + type: '' +- from_fsm: keybindings_fsm + from_fsm_id: 18 + inbox: '' + outbox: '' + to_fsm: hotkeys_fsm + to_fsm_id: 1 + type: '' +- from_fsm: link_fsm + from_fsm_id: 4 + inbox: '' + outbox: '' + to_fsm: details_panel_fsm + to_fsm_id: 21 + type: '' +- from_fsm: mode_fsm + from_fsm_id: 9 + inbox: '' + outbox: '' + to_fsm: time_fsm + to_fsm_id: 8 + type: '' +- from_fsm: move_fsm + from_fsm_id: 3 + inbox: '' + outbox: '' + to_fsm: device_detail_fsm + to_fsm_id: 19 + type: '' +- from_fsm: rack_fsm + from_fsm_id: 6 + inbox: '' + outbox: '' + to_fsm: group_fsm + to_fsm_id: 5 + type: '' +- from_fsm: site_fsm + from_fsm_id: 22 + inbox: '' + outbox: '' + to_fsm: rack_fsm + to_fsm_id: 6 + type: '' +- from_fsm: stream_fsm + from_fsm_id: 20 + inbox: '' + outbox: '' + to_fsm: link_fsm + to_fsm_id: 4 + type: '' +- from_fsm: test_fsm + from_fsm_id: 23 + inbox: '' + outbox: '' + to_fsm: mode_fsm + to_fsm_id: 9 + type: '' +- from_fsm: time_fsm + from_fsm_id: 8 + inbox: '' + outbox: '' + to_fsm: buttons_fsm + to_fsm_id: 7 + type: '' +- from_fsm: toolbox_fsm + from_fsm_id: 14 + inbox: '' + outbox: '' + to_fsm: site_fsm + to_fsm_id: 22 + type: '' +- from_fsm: view_fsm + from_fsm_id: 2 + inbox: '' + outbox: '' + to_fsm: keybindings_fsm + to_fsm_id: 18 + type: '' +diagram_id: 85 +fsms: +- id: 12 + name: button_fsm + x1: -2438 + x2: -3026 + y1: -934 + y2: -1532 +- id: 7 + name: buttons_fsm + x1: -2650 + x2: -2850 + y1: -16 + y2: -619 +- id: 21 + name: details_panel_fsm + x1: 5669 + x2: 5140 + y1: -64 + y2: -521 +- id: 19 + name: device_detail_fsm + x1: 7667 + x2: 7214 + y1: -110 + y2: -562 +- id: 5 + name: group_fsm + x1: 3685 + x2: 2256 + y1: 278 + y2: -906 +- id: 1 + name: hotkeys_fsm + x1: 9692 + x2: 9281 + y1: -124 + y2: -549 +- id: 18 + name: keybindings_fsm + x1: 9223 + x2: 8370 + y1: -71 + y2: -614 +- id: 4 + name: link_fsm + x1: 5080 + x2: 4436 + y1: 154 + y2: -732 +- id: 9 + name: mode_fsm + x1: -3760 + x2: -4453 + y1: 192 + y2: -1439 +- id: 3 + name: move_fsm + x1: 6968 + x2: 5813 + y1: 146 + y2: -935 +- id: 17 + name: null_fsm + x1: 10125 + x2: 9925 + y1: -129 + y2: -543 +- id: 6 + name: rack_fsm + x1: 2214 + x2: 1047 + y1: 127 + y2: -753 +- id: 22 + name: site_fsm + x1: 964 + x2: -190 + y1: 128 + y2: -768 +- id: 20 + name: stream_fsm + x1: 4376 + x2: 3868 + y1: 56 + y2: -643 +- id: 23 + name: test_fsm + x1: -4569 + x2: -5140 + y1: 72 + y2: -863 +- id: 8 + name: time_fsm + x1: -3122 + x2: -3693 + y1: -69 + y2: -553 +- id: 14 + name: toolbox_fsm + x1: -680 + x2: -1722 + y1: 265 + y2: -904 +- id: 2 + name: view_fsm + x1: 8311 + x2: 7734 + y1: -25 + y2: -684 +name: diagram +states: [] +transitions: [] diff --git a/docs/networking/rack.png b/docs/networking/rack.png new file mode 100644 index 0000000000000000000000000000000000000000..505cfdda6b8533ebfe1df9b75982783f0bb93f7f GIT binary patch literal 104796 zcmZsD1yt3~x;AV&q`O;??vPSMN|5eOk?w8*r4718KM<0_3hgzRpXX6Xg{=#rro%M zCL>8-f1U(qzAX^nx1ke?`k8YpxKIA*%4;OA%J+sypfuT8lToDhZxFnP{A(v31O$4N zxb!sDv@{I!HMmKU9wcc-$rDT(y_&`^aP8vo=8**Ol&I!&@Z}@4+t@VjcuD>35KVl% zWlVDgxJif{PBrq|bSx9Sz=x6T;9!q9- zd`uxG!*oW$>E-g|s^wzlO5!Eq(&ky`5lZ-?`6-Ycv#Oz+2CP=bx5~X*LKxbd?8}yV8>x%?qF)c;c4dx)(H+y z)KdifXlLPSLgQ&?YwsfBDMt6ND@4H0cOP@o(fsQYR~s=pZAE1oDFqnFn}6Ra%6T_||CrGKSnFS(0&|I@i*nwZUL5^FPt$WaI7v9UXHV2U;kRed zwBy&(C9X;#bdfZukI+#+KgIb&tgasQj)}n@e`I1cLav{Wdz>MiDc$S367T9P5L(t5vtd{zcdAkP( zWlT)6u{ym+-UK0_n6|&gB@uRJcpgg?t&;QnFVo`;1$gv7QdxtF@w-l#b9w)>UUzok z%9fOpLKq$yaoV2XHn*_YpyJL+5f>LnB_sIa^W*CJdSPp;(5LH5VqaSug5S*OHql@9 z{my9eSGKn8RW{?UrBlCC3cnT@8Vfb(UaNZjS+6MPRwW0(vubEY>^NpRm1w?4{ely9 zE)UVs(OpLvXg)54snF+enRnx)@}2$&#pv$smD)}IGbU-*m&7eAFaP5ci<+$}3ei(| zD?($()grBgozMR^5!4!yfqA;xj$B?|-Zj-6OWW3#jgFpfXX<(KocrQ3nplhCBRpK( zpim5A)5GN+_hMp<{J=zgG34RPjP}+3RJWJ$e43DogQd=`-vs@m zXT!s)6k_fKb#-<1x~hX6k;Ge}&-&C~j)g)jiH#jCPgvV8RA3KrhExd2$jhU<@68rZ zb4U^AzsNAH&)Ocqpno!z-4?DJZor{k2G^U&*>c1so)3p4f@|zJhK6bM&vKvOba!_{ zyQl-AA|lW!qxxl&vcoZ^ayZ1;)x3vJcINW6{3rx$Ni>VJgOZYxGAz4}nCE$bN9wPt zR%XK-BFVt``r_|Iy<1_)>L`oa`?xsclSdx95;~tH*x4U+#ZZrQu^H48^z`(kQw<6N zm%DQ(B-q``&;bZPKfiXRW%O*JZPu-1)1isBc2c^HMe?GBMOS)*`=;1vo>F>*MxkbF ze}6wuGA}W@P%?1phYeY_Sg-&P!>FvT{>Ee03x&r>;RM6S_y%Epyfo8{f03#xKDxNZ zVGhgM#@w92e?Npe$^Yh$SW0^hi!mXHBbp`_dG~{vpM`*A5&vkXA z@7}#j5;9p78*~aoz@iWe3<<|Fg$BZV9&`}9y-eZ#9%+{q$av=mPv3jp`$3RB>YW?6 z+-$YD8oS>%UUIu{PuQpJoQ3D*=bP;b`nUz2i&nAiECh1LaXSuw%7dSI(gKA&&MeO%Pm zlnlnzxymUi!K%c!YS53>(d+N|$Rwrdz{bfr&Qx(Mj*p~o0A^em0GaTIg=t$Rn%)(o zMo-+24_|h^LBqGzEYZP`Ps+$b7`?jkZHf=n+3N9Dfog(nW6Rj@3=6rA)5p>hh zh`@l1y*A{@?q1AL~D+n`)0B+p5#nxEyhy^1fV|He^ ziyfC8+OrmTf7z7OFO?T(OR?oS zyNOxUuGOA7F>`Q?iM^`k$Xro@VuH!_ITC84z>JcnO2f*EVQp+^58`2nUztr)`nHfr6 z%kSEDvyGFhE0vPy6MA3?no9p!a3~AX$@XN9Pr&D#99$}@yLUa$`P}v0)56knas5}$ z7;~_fD)c)T;|m{aZEn~YDRhiMAgjMn%YWgvY&ns;82C=Yw=H=OH=o|d{w9ua2Mf$DpN&R+H-^p^1D3Q(CzH(6p^&+H;`@{;^OzP z(|L@347^ik??EyGY#IbZkgBRGA>XsIU2Y8QCG_Z~Ywd3N#Ouq`E|4X?Qwaz2|33?Y z3XzGS`1PBD*E@<{eVlev$b0ma>`<@8U&=-2@()b}AgC%xW+>e!eas-Uu9%H*`kd!v zoCnQ`kc!ti3~kkklLxNoNxHy9-#D>ZWnjBG2bE7ifDY`HAow7VW0#qK3;2n({rGV_ z^t{~Sb6_O~*d8aWeJ8M>Z;T(by>0ua(F@;X!9;*%FdqFqTya4`M4>?au(ZdoY91k% zUl@ayu>EVS6ZGegetI_Bt^N4u#G9~AJ^osg@f7VM$F@_qGbHFPrBV6d3#v#GM-JNz{LAxTSvj7 z$Cuey9{waP`LLo)6&6c{QY1}GXzwb9ofW&pf<%ii1<)e2Bdz8)iH5#ttgA7S2%E}w z$Dgy&9%55;6%Q4sx^4H@&9D`Ov{_(fhKHks^Sc|jC8j9=lerkYtPX?4smG}L^{!~+ zB?eN;ao0qUZzW$f**(JZWiTZ7zn;s+AfgZSY)MYOkDl=Vs!sV!od26i+^Kwex=-m5 z+j;Xugo~6^2xFy%ApuuBH2qdwQA1es{?X_{!BCKM-S`n8kZnx^2kMP-*0uzXaayNI zx)&6?Azv>u1C1)FQwH{a)cY}BT=P2=x3*iba)0I{Bp-=b*qM&nyf9{vsMCKCPy{_V z#xysNr(JB8OnWgHblQ!ozE&e4X!3QZ%U z%zCwNB)z=q?DJbPTV|%nKa|0h*ProxvC|IWCm&W`Abo3W^gC0)m*Yc@7<>)0==qOF z445MMkV?@v;>Ua${;NpspAq$%dl1M*xw;Qc35((vJPq+XpIx*%47H!EP0@1UtGA7n zRg?2!@P0yqa1YL#vhdHYOTapQdL5HGONU5yD<(helfFo<~- z@UBxnuE4vyIej#)Yr%fv?*iF&%MfOZ016+UoOs551HVUczn;6_s|m6(_hL*?c*Wo4 zR)pKrv#u}A=p@#Uzr)9?H!3l;uOI6jU@!@5bNB(Dw zCGDSuU2qE$rxHte=_mb_l2x3oPOrr-w`xGKF7o(IsTy+%SY)_O;Ao}=}>R&i3K&b}Qv`Sa0AR|-0fGBN+&r1v+OU0^@mHj{C4ru6pn z)?io^%#s{LOgUDTteOqeo1V?jSovL9DT@0wj9Wm1qVv}4i0jICW|XT7`7Bx<+V(NT zEul3VS5gT}plIgpJB&RU)PZczIm^a9>Y!Iu6=nnHQfC*hD6iyQjt?%!OGDNcS4fDr zW8Bv`bSEnxog5Z5`{`W}q@?~v6lOvf`gT`~HI>;C!NP0_BbQSTHrFpHzbYGB48 zF&Vtsku}GWeUpOe#Kn2lOnkrh3bz&dgcOKnVxu}tFiR;h;^5$9O{=+7b$*+&$m=Aj zMnJ*hQ>)S@M%M=AS+cZnGAZoZ5@s9%f-k8C8U`y7{WWr_5A@omm_jRX&?mIPmC>y_ z6tF7?9&)*^E6;rW`BQ$)CQVM)9QXs%H=@EP@R6Xtsbqf03!52~5Ggn$QK779g>ENyisviN-KrO+ zeb3;nh(N26i4q zgRFw=Z8@;@Ll>4`=K~@5OfQD226_iwpi`-pKfS!z6PCJS1+9>DSkbkWfvJ(WlL=v_ zE@9`g9%gtDmzugYg+pZPx0|egc5(3o)t5bqzw&e72AF5^?uW9cu+az=1jfc<=eXs-zS{H7sN|^?lZ=lY)634$PNgo~}IN{*D+ZXz1N6US^1F2*nFnE56LI zL6-CRb1jZ6_LXPE(p?$-$EGUKFl95FOoP`H)sq||TqE9G+EwWRj zg9qCp9L)1OJJE!!n(GOBf9mkZON~69gfv{bFCrYpsRq64JL}plEa_oB$Q1%D z&?vk^MJVJXf^%eIQ?ljVi`#ClFP#?K(V{4X@#b!CPIq*PiIm`ZLEFOHreRD88>}N* z5Ih3%eBD|`NT1a7$7&SNEbD)sq2yPEj7o)L(+UZZNSw7|57ltCB)BiPkLxQ0-lFSo zc~@4!hCQJ}1GX(^<38lipFd@0U08{jLkwIWv(x?k{k5CC`H>%xnXeW8SY$EA3IcE= zkBo*k25cWN&x?RQZLeQ&?wN~A+K@H}7?3@v#~bWg zW&XK!6(TJy?YWljp{%PL@%1ZnUrT_vsF;|YA+`;X=ig0@rqICe8br5o!j=?2Unx4R z?tH!T@V`IH?@K+QXh}uI2RDa_Ei}Bm_%}Bw&6ILttMTffbsmS_C@2WG=)8(!#7{d%yqh5WB-2ka*+ zEIiK^!r9;RgD!XE&ON6Tdc*F6-djk;?PR^%ma>XU_L`+!;OeVL%d`QpXwX$gzpY^Rbbjhlb1ie< ztK9K;2C7{wYT>BhV2HsxBn+ZEXF4gwQOE;`<$r}Du#ovm=|2E6=Cs;JI_rl>*WV3;1G?jA1->1gW30W;OvFysV!Eu9i-?JNuBwU` z!jq~GPqg@dGM7-iq^4$qVgJD|8S38{_7oznsEUe;_0j$aS2#pedBpRmq+}4ZKWy(y z^y?tW3YxynBo!}p?CgpM#(H}()bf=&qDXmN9bdhxNJ9Dl;4l===bKJvN2EyC2GR?+ zd;ygFB|m@b-0-1NS|A&PDFZb%b<-~wNy)(3$992xaGqX1*)3K9o}Qjr4s*>H$~uRJ z-iu^pWUeohY)K3>G(ti3@X3fHeEX#(gOQO@w=YlfDU%fOjAZpwwfD+Hs?US>!^YLd2tHF%lM^JN?%Tz%Jch_|>qmNJpNW=cV zV^djp5eX?Fq1)h*Cn&XsT;NYLGJo>I;t>GWZ5bWU_Dxl?R9&ZbR5wm%8hpXrR&9qhK9$$?G|=+io>7EOA-+fASSm77(?OcS!cnz3gCb| zk6=#pUg-MDW+MlWehdmXo!AXeI#{Y8C~FSHwEDz&DD2R8F~$}IIRQ158R0_(tbGdh9gTSEw-b$K8ar3qSD$?W#odoZ-!~;giD{QU5M@ZorX$twg zy}V2&%1n;IHqn;_3xQ!^D-aJ`h%Ouu_k;j1u|^Va!49k!c{gejMqU_>9|%bRXt*V* z%<5Bk8GK-cS6Wejb75BahJ=oR;dWVj@LkQwQXmFG2_oiJTqq~3V3LHtL^Zdxq!SmP z6%Vac2i{NuX1V=y@W=f^N%{m86|o^(?Z}-)K{)8aI$3b=li#D5lSn^*{#4e{c?ZC* z)TV+WY9wZ3B#@UhB`p$QhEmRY2mzJPooxb-?r_2ko1O%i5$5M1cxlm;kH=c|S~r1} z!~jz*qD`#jAGN$DB3$ZbhxxErQy+qK7gi5re3#*6=rKCR>#9-xfz?) z-QC^qcIIu&l0gl3Ij#YF4rUGDJ-9+ni-_TTQ_oUDm7#YF+UH}Z%?ryw`m&PV1E9P# z_}XFn2#MJ-9|Ysa(r0O~#Q+@&jf4a>h@GhD=oL;qY%zdm`UpnCYw!RN8~>heB7t#f zvVx(zKni3IZ$4_o;r;$URG_h55D0)K2a6rm?4NQ~pb-#3huQYug<7=ag<9(BSp@~? zfJCTu-ytI-CwBs^-|@~gF%=cn`do{IubFLpzyA6XZ)1A+SYoIH=iFc$Lg@iu^4_}Sl zA?PSue?x;1{`$&$?f#y_03=F?EirAA>2Sl?=;)iR@lvUi^TOid-|#c>1*~z67HaNw zavzo~LQcR0O@M!(qN2XiOB)g0%~7@&QM8%Bfh`F0yfMUPyhIxC9hQR`JI4H0ZU!on zPs2k{uuX4ouH$y7qZrz7rKF^|Z6~-eDS6bf75HYiHkvRa*_yN?Fo-#_YQK^Y#uFCn z$i)fxUAVqT=8;CRFLn?*LqN8|@`u;)gT0ADZ~nKIQAe!*F}>JeXSv>wsmGOp$FFw) zThidM*BQ^M<*F8rNgAx8l7l16g9ndLJok~&W&Ch(^^T_W=15hi zk>Umq?DI2)ZshxY{uR>E(cyVI<8csaboE0e73%%CE)Y}j58&XMuMQQO;=)r?$vZn^ zeoHx_U{l%Fj3p){JS7?yb~=1RMa2>s!3H=mFeTUZI&0vfC+|&L?^nDYda^U^h7FA% zWYtd(-Vw|W^c9!5y>byFei59Bw)Aja*eD|-Bkx-=Vl23cRdr0OTYj1 zmQmaEbc4;qeP(oR9pjNa#k0joPFI$SCaE9Ch)q+U<0GrIPk5-6h**2XJ{BJ7SD2$w zc`f5ct~l&O)qZNO3Qd-PZP5~Xnn>fqjIa`hjdT)mUm=9b65|7sLX10nmD+b7{w{yZ zM*=Qr>$;=Juq+fKF87iev`No0VxH;hk}*ttLVA|ry~)z#`8G7N($jrgxKfCP_db{W zMkFIIX=|b`+qG(Wc3kQoEoYa%JgS~OFikz{H(e;Z_sqgIuXPGIz1=7~6+ z^}%*?bBkLDlymuLoabdBwi0);JynHabvUp-+hUdBg$#OmvHk(3wV4B}A50bi0J+~P z>JaO_E5t#GTb|+9fmePT%xra^wce`EwoOmWSt znfoXH*hM#Ox8wc;Iw(7znsV&_)CPgR+GC%?=yRYOlKAzpT2uLP7W9cXqtqmMpN6Wm zG?Mu3qKmqQ#>Vwf--$z<%YgpM$8L96$^->DHnsyIyYEIuP_p_{?}6n+UncYkT}h(P zlfacB2#|8n%GatIGmQlW70kqP3}d|Mj%673d6m~b-}gc*#0^#;K)I1#4c=XA^ zhjP!q9%)0ztJe8%o-V<%h^oZIn?r%(4^)+amJ%x}@9*`Mg)hV)QRuzAxOlp%|A2%G zx@nP89tac_q93kMFHZNcNoT$0q3|Q>A6*7sxUgvB!uAt|CR7kzrCHJ~(_bj59u1|M zR3R9SiaycoNots!F%clz_*fUKtjVZTv?WhC^%~rZr@~6HsdUe97$lum2mn||DPTYK zmfvPfa`jI%h4An$0|-F=XxjJth{6bpK>x8hGCp#xH76TA6v?Z5pyx-co%KEm^+<^q zU}5sk@Fq%BeHTaXh%3+A>r*#!;FwR|ym=$!=F>1$TUN$(*Kk$`G#Q|jlr~>H5)~D# z_paB7St@&8SjM9Un~T1D);ZG;8RYD;eaLHv3tHPg1~%PKU=Ki@@1cK~A7ap4Ej+mB87J_Fl+NZR7C4THszbs}^* z9?&s6CGNdsZ5!!!w=W@7W*Zxv0^ci zueuav)S(t%_6DEj$PffiwOaz|Kd0*4 zT`X(P-j0KsVMmi3ASO9h_lZC#MAG01*5=S#*8bU~1U|}-RK7%|SJQiC-f0qe0kvcc zcFlI_Vm=8+{b>uIAIp*p*iK{#>SgfTBp5!OeNteT_!>~wBxq=8apzu(mZhVsgP~0Y zQ0MzvFmMUovrFmO%zJ{yiyyk4?V-k9xBiqHp_AR+rB4kM4I0Aa9EnItCwKY9vjh1N zD=I3E@eXLEq_~`ZJhLHKaP?A3=OL_!$gvf6yg0UQ_#WGuY%rn2471JvowBny>g`RT zR@p^Y4=P#88!z~C&&w)7$1LRi6Wf+UJy5;S(b1*lZrFyuLo%R(+#8^Ws>St$zJFce1f&j~9kfy$;Dq4d@sLo}?@SoM3+&-LiNn@x!_^Hn}J z!)uqKs0Oix7hwt-nwoTce4_``gG5QX^8j-3&N%|r5v?fOpHVk{y_M(BV@4&i?$kY? zbFcLJ3t;86G$dGPnBRx^iN#ee@Hbv<%6zry?xY^`eKXpWT&t`ZnR<;(YbnVRLnK&j z!{pOH<+T5f5UKfMlQsSF%e#+7+7U;VcD1B2)wV2&!!@}Ay|DToDu>uuzC3qB$#{E= z@kLOBehaEpWqX75<-2vd$5V||D`=g|8W#{o{cB&cvBxDRqa{x>`)oWQo8k=KC1MO>OTv@dRSj)}PcZZCzAbZNRcG$>jVsmkj!M+=)u7SbrjF>bX@7@$C{xU;W0LC(@#%X>MAvUH>#! zsTh2V(AE&B{T~ofTK`IJf8ynML6$C8BO56wm=G7-dM{r;{tk_L&;E^d)NQS5h4-s!5E zvND{71%3WfO7u)^{drLGhzxS)bZLt^X!SXQI=X%mJQh=Et&iv@+r20yAAet~RrBQb zf^l|Y?(O!K$Z@1$#9VU{CSU&2eCD)+xPiJl$PXLh@T;$f&X3kq^!20O;^OWf9`b0d zP<$5vO-EeElf$DUTB+wDAtA-HcqeH(=}AcUG8DIA>c4NX#EmectAb)QtDz> zz41Dr1!Iq8Cp;dwCQ+xbq|&)Cms}2TO;^R*o;nLoS@UOK28Q;lYWI4s z209zPj@VMYQ0ln#_ME$}O5*5nrPrGqN3Zx~tl3rEh47Bp$D|M%vMfY* z-_$jVM1D!_dE)A8=!cNfDEiAOZurgArsdh0m%DCb?i>lZ)^KZR#w(SX=}J#F*m%-z0HL0*ae}autN?( zOhrrc!OL6asCU%i@0LE#kC46PH@@_-ajcjV5@+N(AA){R<(uuzi$u0W=jod@@&!~vhCSS3X5IUK3AUfF8w z-jZjoO4dWV&gV4VPt{K$yuve_=q?Y#3s5LeDW&_>e9+{fdBN>5v2iboJ|bh27V#!% zXceaevRE(a9OjMl!;nK8pI2XfH_SoFgSgDg#KQdbT3;qIew92cgx#Yip_%LDoN-iY zB_=0ZY5^qo_4Wl95~HB5d}K-`Du!#E+um?0S?Uz!^4eN)hTwuG_s(ocK~Vl?W2Fp_ z{sSwUvDUGmeE#VljsGNy4a8-PnGcK#FSIx{yS)&BM8M4qf`Cvw`qeVs;Zj-tuhWB< z(4y|83Abv*hmmCJXP-j))E+uj@R6`)CSzxk6HffCiSBMF2`(w%G2W&tN+AwH=2v%4 zCwnxxz4dOY06EHM9j%YjVzddKG~i{cWqkheXK6+b>BBcM;Wh~td8Tb86ntOl#lmCS zDi~dGx3e>}g^-ZHkM=uOJD%)Rm<{SY#uA4WVgN(I|5{oq1Ngu&%a4bph|;*DPV z_U(9}0n4QdWiwo1hHj{Z*^Hy_mdR-KH$|s@Wj0*_>TE;BZUu!Wx*O>x**_U22)EpS zn@&b@F673$1Pw1~*RbR$awPaLV+7YWG;B2uuqE1b2?Jl62rY$W8Ob0+4E}cMgYEi(;{dbcN+7Y>o2q@GI3GE50;6M`YLL-@k$NbJkAh)%x)%@XYQM}fSRJZ z+O^GrOFkVp2T7HMm(Zuuizaodn((7@<6lef-jYy7T5K8`N+eJ6J?YlFLWpmd^`|HZjAu7RazaWG6(5wwTzT zwOl?Q=opFZllFa!zNwz|TjzsN|H#@vJ*;k*muYb$7#*!C^h-a zpmuK_5B1HixYbmWrp+jZ@7-hbL58TZgUS3S8Jp7ao&_J#WM9TGU}Liuy)n_|AKmma z?^OHqO7b9?ugJ?Ga`X4$vmb}yxJ7e(VGrvIwKVqCKZdYdBkhxO6S5L@6=-b^>8m}c z;(P1eLPYqxqq{qPz67gdEz+qf+fDR~_k*w9!X<~Nt$)5{Z|Y1p&fs$Yc*(vOfVbx` z5o*f6#<7=5U@N2@p6#(6o5x!L4Zs9VIz-1k2?>apx_=PgJGkT?(wDU;ph}}mmRf`K z_p`%Cr|R|oDIJL2o_wrmL3*~o^gW?YUs!K{jMRgVU!f)G;nOZAW9vnA#Le0=w1*zH z?Q&wz4j2S>u{u8EW_%SdC*3o87icBT9o$vW^b->HnPGyGbnSGht*Z5HG?t3NYov+8 zT)~W+aOF3(-vW1MZV@E-|CHBvV!LE&zeA00S@yfqoK4l3?HyIU!RIViJE!Ohi;XQb ziNn=CiTW#$FW{Rx`bWfu3Hg7klZpUVR+WLVytlVkfoxm*7s#P{od*Cn2{W5|AW2yW0)VCIpKLlUK8k4RU%1uojEv5A> z6=6{O7P{;psbHr2%$7r80=dGvo=-NqUR77o3Q2QVP1R=4e4lPbU*bKyNfejCi)_Vl zjaw$Sgn~(u)mwgowMPWcr#c|K4;B zr}?ksloa>J!jDz)DL$9*SMj5w#<7+IEx`H7*5-UKqii%}FdEJAAHepVV z4o(m}5y+`N;n@?g{qQZKptLj`(}uvx<{?;HBFh_?o7bj6a}^S~Jok+gZ3M|}so~Ae zY^9|`{T|IP=W1$dZW56VjM;L8@Y-zhIKU#8zA6CV0e0KK?|rtvP`?c{=hMYl1Nx44 z8n%oD+kT)+tjyy$f+^3TjCb(&gv;s&ty%GCENtv?B%?;xU*d$0hpL?M2E*)_7#Nk_ z3Z(zLRC^5&Z>Q$uLIXF@+wkzH5_3!oa_JamefjdNbo7;wUh|u2VV?h@XXzk3m_Lak zQMA8Q^tCS_nVT&GjwWyH*Yb~?J?4N&&_XZF4$4{i0Odz2tL&?C3mHECNNYnvz`Osg0Ow#E`tt`4Ra1+7-#@2Wz5{N;CU+iz9OFfXs1AxH0IUT# zCWRw;DLQ^@g25Gk(YaU2bI<}Ff{~+=g(< z*?l}Pw{Ro{lmv&%H^uQGT>9sdnuMRE+00j-=Vf4YAD%mZth3}P`t^U`0a)Z9C{!}> z6)U;5cu;%$GlOq{3OUjk2zvb~IsTiCjptOs=6|Wd(S!Q>UVuVLO5iG9EG{J_#qnsh ze=N0Vw0pjfg`-MJz_c@p)EnWr2w&&oXd^DV%q4dPg@}|)#P`f`!nrw_Z+d3H&g$TP z@t`J%<}1}Tv)&j$-8=5q?6edHR9!PO1}&>AE1?RB943G-_hz{Uy5Aqu)8ycU%g%0H zUvrzjb9m7@kd>F)&xm}n`a*AQ|3Rv{H6xx#P;T+n7U(p_7TIE9J{?Z@HY(~FP^?Ea z#YTm7f0za!O{W?F_#6M00|6_8=hk#h>8`~`$sNI#bH%O8fAvy-0&cI*F1AW(n;M6Gw*M*Ddhpi>ys128tjcfof~KH5K69nx%#_ zzty}bki6OA($b0s#T@q~V~-p=@E>rt4UQoL=HklF zo;~w;xpF|TUVW!6!o|nWS(C{S_p6^?Ygj}B2UDH_;Vu9O!-1M$A{2P`e}WA$k7(NP zS0YRQcs%S8t-6B**9fQ-f(+5v^r~NEW>yP6;5knC<^oTe(0H2KbP)(*iMxMR{M^Q@ z5h1Hq*d3ww??sZqN#eKB-1+YlgkIdnF8LD4WuRZoA?FvmP zD<}@?0JX!A74MaMA`d_`#=(1V#Zeo6@{@q5Xk+hRY98~qyLMZLXrgftB0OyXd;`QY z6v(BUB;STsxE0v)#8?8ll=m&bvjEIxJ6_E#RLLrV0XsMWRY5X0!7(zL^U*Z&?xza4 zI`EmPCfR!4SETShL7_va$Sfb?SG z*ST*rB!MXI_fx>vM+42C?fE5%b``j@6 ziC{epKm~BS4SUU4gMi}uktXaS;v37U3N?hh6#M&$>b1}AA7R@YUtas!M(fgoiQGSV zc}z3Ai$@*abFDxbHIYk|PWW@ah?lhaAJDs=ChpUxFH_9tzUyd$N;*f<5)7L5o}EZAqF^c8I{#i#1<}_ z2TNUM;DncfP-?g`R2%{4+sbthZx&X zYD%u^_lWyecrOs^HA90!P$;U|DK)NA{pO7CVwc;`J^#}NPvABpB7J4=JJ)jY##mH`NZ%=9#_8X-p8Q1;Xah&*u@O}zx zkVAgC=4C=6qD2rpnc3NY_d4`mQK0qhCYXEXAle=xc>ui(2{=mjqpj_4H2n0;@>xa` z=;*)4ieI3HV^fc5$R=e3epAbzDijgp@VE`S76KAh1@h4w`(ZN>v41aJ*?Q3di1KwM z8*CT9;B;L1Nth<&^e@E_05WL@rXS~6iG!DuR$LKt!$Y{h`qY+gGa*DB?T zt!->~i9b0uW^1b9GmpRaRIIAyz4+B+^L(-~KTr&5IXrYw{1WrA z$L{FwxBwI$a}V`VW*^PMwu+Z)8J$2S;B>GkeSLG6n#_Cfu9xJ(A10kigJZDGLPcb- zWgjCH@;PAxhk-(xd`_$FJ6WZ0=;`VH?n`;uMl-mKn4`H&T5a04PL9!a0tFSw%#=mt zg2nNn*xSrBnJPNU^kQNL<4?mjI-{dUQcD{erX9{B01v%2Yqzn|tnZ{Vt{7%4emo?F z3d*w_?Y#)#YmX%|?ki#h^zOJ0z(SxWCnwjR0O;-QyO`E>?i|{-TH9pvgE)X#nXj4W zfO?Hs4CL+Z(%TAZ{184p!v_h!lO+ueshV#tY{aM)S552d>Nc9L?!q?!^U~8T55`we z;(4tGB)xmhNy@J-;~wO$^{0k+cgsd5T5Vy1XsMgF0vbai+_+n_xCIR7kI{@4dmn&I z-<>sI?#W>QRfTCv--GwUbU$6ABMWIp$4V5Wl zVvyz$|0q}QsW^+Vr7%#}|3bX_o{Cql0+NGh0Y>h+uEDtHYU=2okD3W$NghEQ+Fx+g z(A&A$_JbD)k75hWHSzJ;*oO+KzT6*s1}ZAZz=7&{U&!#|DyOCAwsXw}f=a8TdaGMKQOI0E+HHMr9-U z1jr3spbT*zsg+j3RnD_DsM2v zi!>qJili7`(Ebg0YD@yL|GU7e>;&#zo*u26{oahH&&k&3h7pKxNZ`D-A47@7XW0YJ zTB@8=F4_W$7sxiZHvj&#*7&V)arB5IyXsk5P6T`_+N?MN8NJS^Prmp;x@L z1H;)S8)@t0%xog{3z;Yqcv2A-iL9SyRxg3z=o-D!kHAq9J{hCJVNP7k{Kr`vhtbz% z;-#U5Z0{95(!5vi7~nIv^%yRR35f`+2TtP}b8>R-4ift<4aq3H;>AXvFqAy8ohmmA z=kNrCeUh+Q-VUot5Z=n(;;h5T<}A{r?5WS(CDmOQ8kCldgj7UN)0&Jq68{b5(CjN) zFg>fJAyDc%aMB)iT%7C_)_ji35CBTLCcJ{8w<4guYI6Q|TwAAW>bUw^D{Ut?P!5R! z_2a?TA)`FgrThmmdGp$AwrSQjdfWHrYC7k?IZt1dP%YVy(zCG0y%eZcSAG>)U51HX zTM^i&;suz&zjOFH2KF6=ajvkx1u(EJ?CqaSpW5AQ?c=`p3xl?Ms0=7FoHaD`(rckNI(Kp8>GrOR8Jzm~EIhp3#dk zJ@Upv4T_6s&U~E0Z*$CfT)}tU_{B@9YJU)jyUKh9?fqDYI1Qs@sQpH9IWV?>jKLM0 zD%2s4^8`uCm=az`L|35p6Zhr^^REMM4)ATq6vlpQ_V`vUb;Zyxy$Z|t&So}G%&z<8 zly37il_*dQl|@5=wDbT-F*S$f2w* zslC-I|DmNerV0Yb6-{UA-Mn$Jbe5%<)<9{IoF8oP_1z2G*_r^Otv|JUzH5&?=Yvq{ z9hvTAgn$%v+tMsDYN4tq3*5$F@?o!g`oZpb^_5@b?5!M;sMpQ;nnCuYEj?12CMd>7 z40Q#fAglOs(MA@Tv~oPY+}zy1ve$w@B78b{87HQDce>!=+9^QheD{lRk<#?%w@O>a zBrdvx#UiN;%r6W*6^yWSlsAqBC1hIbAPfu)#=i?R5;%g-K>!uF6Wh_hB9emFB8oGK z;t{?DY+fAaOCDjPFdh-954ph}yHoE#$<@?;dslLSRzgT>gd*=jr#nXVpIsUshezn- zO$*V_N55U4ZEEa=hR@Obt&4f?svReFyFSlFe4{CiO(ptP;`V&-?tu0zg6%raxW1v` z4W;d$?<$-{d;vGUq6h8xG43onMt7^gCA8E9dInH9W5W9aoi(VfVgcY>^-5207k98zGdnQ&-rm)!DRAX>~QfgwS zNvKTU)%Yfns=6O%@-U6h_q!!K4{K0X&rux7ZqN@il=n`W0tSD(8rO_EJsvi6mWlZI zuMeaNhs@oa)YPLZ5OMzUp`EG}KjF|@?I@WmqXLAP=hZ>i0jQy-8synBAq4ArfzzBC zo2$a2cQQE1L_~A`a*3-2Z0`^&9nqzQaH_Eudyfrsl*h6H-3f+I(4OXoD+~PkG@j)B zXg;j_%u~N#wsm(KZ#lyrdWEvI@JpZ}#kKKZz~XN(qOF&21m&XULx_Ost3X8X?=LJ1dgO3@c~ zbi%V(3d=7{=CLJw5GLRL81{N1JNG^eFFxRf8Pjm2`Xv0w!O^WR(Sl#DKuMzb**wCX zo~i=%`LAB8%NFz;vxpr6BDcHjis|4k%KL7GH(tUtDI+gJx>co{a&1ucpPjF8g>+Bekokj zm^fb`KU=XWU7#l=`ZgSMdrfCJ9T%kfTko`2E8txp*jLWj4=Vst{taX);gz^4!Ytny%{sCV$$P(|j_ z+RKLp^_0*AGmlSxh3EtRHGY@w6vEEWPL+BiN?+_C0VzWJvGoY8i>buLdd`W%Avj=X z{2IVqdCj6l6i5^=C7UrD|BmThD71nT_(?oo5lj4^6Zx^QF~(Ut<*?(9vwHOT5|tiI zdS0Jr&d=ClgBpnd<#x@k@v3nXS5@k$;?)1$r_z!4&35eWn$jk%%QVY7=dwsI+*11V z{-$Yob(t6@d3&=EraJ5XsIHGv?8Zt8Uw*ZbSf5@H(-=7}Asooq;*H;O-Jhy0HcgHq z?qz_au90*+QTbLYd^7ufhKhC_$QVa)w-0T*+lk^m`nEe{4G+|cD2QVoX^h73JLLTF zz{~q4E&gMaY2yP{(d6fg-ufi_Ws{wUllw~A?~qJhzcm|TxVl1O5!9<{K)m^wXk1^Q zygH{BeCCC*()84~P7eY^PfM|kr{OfrBDg9A9)mt7ui0WTo}5L!tCvx#$&J)-N@xRI z(0cQp5`Q;sKUJd9m z-s*Wlp!MPFtE+G33=);kP$=1@kpCY~Ul|r<8*L5a5JQ(BARUs@A>Ew{NO!j&-Hmj2 zDkumDNK2#ACEX$2NH^!6_dDm~7jRuLPwacg+Iy`XrgZ*+C)~KtW7DR6LbZPTmUV9o zx1E|;+`)U$JaU!rX+|d>?uUkOVij+f;@yDPh?C*DyK+v@((k(C$JXcl73;)tp67;} zx1ybP0dt`VN3Ahlh;9r5l}p&xQYu=6CwswFcY*`|7By?myvTE9WE_@CDuY9H9OBQ( z7xA>85~h`S1Shm8A^9;KE0wjx)sBq6597Stmo}MFA&B$E`RCnR?4mY1^>n?VRvR;( z+neYd_VSEkc0eh-qF?Lz_qWtq zKbTYB_;FnB3>A~LOSjqu*%MLDBln6}2V4&rQKCO37ihDTS$^xAdA{j;&G1xA|cStHMpqJ|!X+RgfQg73LH z=dY@7`j_r?K6<$>f-xsO{YI_9@{%`&$Gt?J_g13fwuu6&n<1F_;T!R+JuY=5E5Rs) zlr}M>4jPzqu8dUs^>>S5+Wp*!lpLJy7hj$6d%M|?12mm+#8F`G$G@9-( zuXfRPF{GrgM27?R7@9vBNiPFJC+>6?^;~NthQx2H2Z@@=X6kY(4HGm8n7{vvFsjsa z*a;~fVN;Lx`L#!t8Qaq?byE=+ojpnSqvC^nLvVB(hg{x4e0RTqlyE{!Mjy6BgdchG z@rvBf)r;1{gE!VE3i63(15GMPv1xS5s@B9wy8^67UWyjb5U359yPhiRyAXPj2Ph^p z5+Gw_P0>LHX`op6e&2H(?DMd95BI0JEp>}dOXZn0XI)5~moz*)b+^R)XT(d9(Qi(> zCca;y^gHqusj#N3;=cHK8&e?3`(A5DYgGO1G+tv<895V8-(9)l8D=8ZCC&x0PPJ;RbGw&F== zVY0^F0x|1usC9r_aaRF+tr*M3I-496h<+k#mqF#sfqzPf@mzwD`tRT?Na=#Uy= zb#y$LL`eD?E2595qUSkZQ7F{fuax`T|JN7F>WM4rS6-a{mFdOPqla^50v@ccpYf`af7+;}UeKAmw#RXAJ4GonvX#0& z_r@SOMac8BH5|BW`1roaH#0~8J3vWEp_{g@N>*Nm=^_mh(n5=nY$!fy$47~}O4Eh= zE%=Ai7nya7=VFDsT|j+JvByAzDQb@$9<9c?H5%&q+ct14~kab%>tRG$jamO{)XAQXM5>Oryw zUR3?r8pbr3Cn<#yoG3$jVFIy@`UY{?c}!yJf-e1bR5GJ9tu~y8pTv>a@dy@og_Bv% z_FMuaD^9cvmoPQFTw)?4`QOh0F|miOfCm(SW%T6Cx<3EGdhylwVb-_xG83c55V-Qz zubrSr5#)nf@;G1ypGq9%Lev@4g>bR4vC$6yXmu7JadCa9p$!z)b0(afot>$(BibBx zE41jiMK;OP?QgRf`aM5ACn$H$z3BgyeDP5I1Z%qlMS;u|s)yHDC)hBQ+(v%P7u>|l zc3NnL*4s`bzt$7$F^HT|F!lX%cBL~UF)t76dY<((1kUt2g*|&DvTDn!mI1c9gTxEY z{$55xLLgxGX1BFjmK0Q*e1q0uV%jo_p%-m#TB5Iau`n2`1hLe8{esa$D(uaDcP|K= z8PahZpJZ~h&-peEQcvZ#d|HtZ0&8635n@poD0AYWMVDL0t3Pk1->y9#`=lgB=hEJC zDue0O2rP;&Kp*yCF<|oXKNouX^yz9W-%YC;H;)NkE8J{B{rB23g7&?Nlip!H3(9>I9uVyKt8JS-q zjgP&fGkU9hdwDdioStX(c?lM2GPlGXxdVpheA0$F}L-IhVv_`B<4{9;H#^?N&79fel!9=VGf|Hw`9><16s+^fVTA?{UY!beSLj> z1@KP*Oe-lR6?)1y)5V%%Z2b4vhbzlvzL_>ZpLp*Pk%ud@{rLvffX?(?0Nm>~rlk$3 z1V6q0XrQMT4O$}~KY#vwPG7m`m29j8$rgNq>Gcxv!@}*=6(u`+6^~dGS72g+jN?hz z1a5NeGAieW;oZ6+livoxxO()kA4Oke>(PMxpsfd6WuI~|M^j%l z9*!KsC@*^gf+EDvYY3Hm-7pyXRs~OkMA)qLUBVtsXC<`7A;cJ#z^30gNei!O-o4|H ziL2dD*M^m>D>g#|x(~p}2D>9(f;_g$Jlo-k_U2Tdp<_3Z@AY@KAmro&By;Ug`q)iw zbn*#oowc~yq(Ke#2&~ids9hGQC5e^qC~$BP*663(8ibw4B-vVhB_ps76ehith>&Ce zJUTl^!^iiQt#<79xaTvFWj+B}Ca;(!E`c*WqM3_7s7{9`Sek3;czLx@tT=`K84m0Y z{H1b97AyHg+e-7DU(O?h3)Z$i;0dT6FOAyDkLE?v0+k@5Yv>KWFl;omV2oJKwgkm^ zqU2Llxud-T@sP6v$BLvpej2%SpX>5oyaO2=wxaN4evMIH;8Y4b= z^8P5v7na(g5Pq?kxsDERKSl%7Q+dMmN*2QM>T?aHYA_$H4Gfx4O47_PnR^!fE7oZf zQ#_`NMt)}W)j?R}MCFMH-|Y}dvJ1Oz#?VAo#}R<}4W9=GXGUw@G{`U|rKB-`wR-hRbU;eM7O6M0|tZoBl!`2xBT4sLFoL(e96mHJx`KsWE7FUrkmO`4jE% zzD}6)#dbsceZ>Q(5YUMri|jj=g!Qg>OCfT>dULp&+_(wede0jM+`F|R1)pu|ay6l- z{u*b#ui8}AQ`>tQ`y$^6f#Rv$!CWnIMEy6(*@YL?3?fyRn#ZwXe`D0v9as{JG}x(HocC2+Pt%*CwYh^I)cIt}KCJf9Jr4ufH2|NIZ^6YRH zoq9(hlKJ)@Fl~PB%%92JW&SGYxGHUhg@@-U7@nAjX6g0KJ*3xJkkqm_cb+nMMef9l zcwH!wBpkl5OR1b{U|>+TH+mTT+Kl-1w1b$6W{CF)%GxGd*TStv<$FJl`Xc-8CQGNr zNsXUQ`kY#WHpC`sJ})sT9ESs&+p%rSXh+fzD%N`~?S*uaXW31+*QMq1R$}i-Uni)q zMnt@QDH)pjI7m_-Ugya2C<#gcgM#E`_kBY)Er6o3X9f=PC3E}EI7d&Eml`TZ@qJcz zs|%4+E~@;t<3DQp0gZ1`uX17ar=F4rP!%Oo-R{5p9ydqJ{E8%+;r*G25l}lq?X8lL z9}BS@pU{s@zV@1FH+!q78l|5{}SRt>`)-rm7f%W%`PCHCUCmRj$J~tq@)!f8xqC|U>8vVePz+8$z~I8E?-?oI>cZnxdtf1pcYNsv%{P1n~&$*VlgoLKFE{&g~#@>yPFdYZC5Yg_3V zj*3D@ofFrT1q9Q!#!-IqEh+m;|CgVU%Z$IYVc4M1mDtUS2_JQs@Mv%n!f15!P*9Lc zE_ETQux`99skQaA=ldh&TBmb?l0Df8$Mz-WcIW2yK_=RItzv{=yyLw*=&+<`c;X;2 zP`ZAkJ$HBPD%5zIFC*$S*#fLZP`5m{GCc90~?M3&dew?{29S+G2uxS~s=(ah#6>s~H(R|~y*VqgK z+*+|^xM)N{8k|3K^g=xA>}%g%E*+Nou2OiVU{avH@5{b{fo??-@7T^{=1*1s;u#rU zwNuz^nI-bKY%F`ZO6Gn|6xDcy0~_wHa0v^UB|#*>AjGx z_s}N)l65u5+nL!bPDw0959=h(ynuQJv-n}HYEVX*mp6>BJXYXe9_SQQB1G`aP3ITC z0YhN4&d;_*tI`#>r$6el(qOVR=(RI9)7UPdNfkc(-QYbYfb`GyI(Xu=GfcTxJ3shm zaV`okS^5E%=+Q;Hne>oO=og9<5bX(Ixg@FU9Tr!omU3L z0_fo>oN@!2>gd;|bggI}j(?ejon0z27*={A-Ija2%iljWArARTR{36Q{QmM_X|Em8 zq8E_8u+VyLN4K%-6zS`eq*tRm`A-ay^Yv2CLrswO^#nET64A%_kY?<=yUCxtq70>V z61qw6D#MlO>17ajdj6RxAb+$hJ>(*#t4?jF_&!1f8pfdiG$o3?S&9cjCR&EFn3i%f zneT^x{tm*Wl_-}=WWcAr?G>&i6 z>36iY*}$Ql;$!vpgz0{vhWhrDGJ6v5D&q)&ig5xQ%AdJ`K;5_%@x3gH!co=&;N^W< z7V*2OH5B_bL-7@eka&m5>S+AR=$TR%0kcS!MC@8jsjXx7NfS)5GL>m@#o_cN*Cupw zvtI~rt~}ATd_{>nsc@N6`qh5oEJxhlng2r8FninY?kQRe!PNPhw)=~-&SQS(lh==a zi!HPjQSJNflgAnlUT1a9>&M3{c9Tez8S4U0req0vlB=AIl_!jr*5Bz~6%o?x(d2>GhA z{T*)I%8R%p{&wlprn)s^JU(pZ=`W>vX__K$bkRvhVQL zvzf1)_{G1;8b_Sr1sJ|DWD?JKNT)8c5dywXp-fY-D`uZAePe^Q{3Sd{f7$7&rO9{} z!|F&XCW_Tt;x>}$8r)MaG$DEwBCN|1$;9$$T>RIDpMDN-Qv z8ZN_s70^ycJRFbmshOR~c8LyQ{7X#G-cbMT58qHI*Xk2aiyzL%0Z*UkOZD9FHH>j& zT#A(zvAXw$wtTK%sLbWzMZ6|tH>i6dj(??y?nVO(&&qVc={nf7o zmgJ~OZtcXaD@i}dDDnpT!TfyEvk#SEw9IJ)Kg*K8S!6{GrBgJ}V)KfGR& z{WGO0*|;Z4oE$_S-*Ek^6n*{>bi%l+GwoxwfCye7J??1xMTSNuGY2BQaXvIEn(^e> zXjh?hF2cf(gs61X&c{9Nw0&Xd(Rf%4i)qinQxymz_2YYGDf6?!{Xrxa?(*i7D2 zn@2%<=8!2#F^b}Um&*YSdE(Q~Mbj^%#pJpuSQX6~i7#TDkX9?ghb9{ZuCo$1l_=_} zyD^V3;YNs#NI1NU#LU+ZwT}*bKq_tec(>#iV8x9uVv>!O&)Ujyls<4W)1;}` z_#V%nw5y6k#1da6r#Z4^yi3*WjrRwgRRbR&7cVX6vy*|nlhMA_Q`6{acokE=5NwBC zPc+AGyT&qA`9Ze+cu#Kmnxj4kH!UtIiR>y{I{n?XDepJtdEz6*NhGr0SQi!f7nD?z zGn`9J!Kdk=QwJB)9Q0%RsRSq=x_slT&AX+CuQkK^rZ0!uGXpE|N4NNz-`$4~sno?z zKMPgOj@qeImbvule?ZPYIwz%5Cpo8|#GK;r_yx@nONH)UeAcuW_e})Gr$^;{>-c@r zQu#>0e$w4EYHDot{;pFwQh;HQ^a$TF?(e-AmvfBVib5=wc)^IpuP-0OD5*nvUJYYE zdyyn0XxQCgbnx=s%*aBlu%lVA*@HK3V&e1D_zziR#b#~Cm_MIkG&Pd@s94eXzu_&| ze>q5t(rd6Yyqm_Km>N}=-yCI3(TU2!Qa+O&*qns1!E72f1$Wl{yC9w0;Gt&9CGnu1&Fd;^8Gg)lfbv=5!w3)GMG1}MmBb(o!n2eYm z|1L0ZjL1EgUUD!&Y-WhoqrCcSSt*0GpAe&aW;2m0gT=p*9g$v56gpRHLqpo;a-LWJ z!{7V+GMEcz{6{xcQ*VSP13hf5)1GSdbUk5qznWD&S%o6`7AaVJbX`XF3^Zl!9v-NxYoG|4*b-qZ{Fal-C zMA$g}bTaSMu5-d(;cf3-?>(g@OiV6>UquB$_UYv1X3j$ckH->(1g(F~!|u7KLuloM zv5CeqiQ{MGc7LGDT~Pb*4!_IAh573rXx%#^mUHxdq8MWqi`PzPW65XoQ)VWz|b#wd^n@>A`0TWA7Y2M&@jsx#jDPmq|@@e%63OmLfd{G_X)-x-Jc z{%DB|K2{zUO|)-4buK!YUni>Ac?kyVOCN_DQRq9Mwo-v%H6*Kl`;|KUVehv`5#LrC z0-+jeJ8q&H{NDBywt3oN{R{+r2C9{f4LWT9{_78k86P^>yxM!Myg-Y0Xo8yWCp07Q z6|BnHQYCAF5WmUql{Qm#b9rUz(I!+4*s;;Bq0((}M@?P{}qD2g`^JY;JD0XJ%s?v3GEhq=QB> zZoC@jvU~|_IR&R+0uPWjIG1MuSo18J7&S-83lDf^QSZw`hw!OJ7`VSdWne?D_!}1) zfw6d$TfR(IBh{{LP3hVrDZnU|)8=cCAb=f`K7Pe;=Hk&|{3O)m?EMw&OHUizx5Q`@l zUf&N8_&I#Nc|)S+&&6q1l%s+zbR5`}wc(x-sEq6R_rr^I-#9Ws%QvUCy?xfMbp>~p z1m@C5k_!pLK@0a_Q|GHX*m91~OEvdb^Wl>6yCgmKlKu zxW|{5DA)O9!rlbaPR_q(=E{!~X=K+OekJ{P@D$+(&w!Mi7{7`i0x)*9vPu5aQ)1$% z*}urvVNV3Sc8i|#2yW@E{{5Fg8WSi{*b#|Kxs?uFL;tGbxffG8?j^VKQ)1zD~TQDV@Y}ORsUQ&sc<^d* zaKt-S2ygG`Ap&2tSAIKF_c6v3S*hRwmD^#C08T{Mr#u(4y>~M=V@+nO;ho~& zPKafFsoFyix8dZ0{$x|gxok&Y9}Lhi;PO=onSFCLb4hK0e&T<%ic+G%bOr{Z>xYj0 zlsdklj@Cp#F-|7U5B$y*RY!J?E&?LJjt43RhLRi7Mr3f_V^zQrlQDvjBj8twK)^(_ z%cyKz8h?h~G6qS&FK_q#oaHOKPMWHhkP)HNnCH$eE}YiMA&MK&3%a9Ze{axTN+%Uh zonB{#`)@RG%Pbajp3jv!qEOY0amFt6TRao%tm?G+(DL)s(!!@I^vr-HUY&K2lL_QP zu>k05Jri^tS_rLGCbKm4F@NrpF45yCMB{++ zR)|LptC_MV!IZo)*tPQdwnfxU6z&y<#f^gq3ktdeT>u@}#$kvC9hlgjb8#UKp zN$T< ze_+l9xtj(Y@(yzdOHO7?6f+1tYI$PtRXqoDVOV4W@Z*st{<431b%YMeFv}fQORsDO z+6HFuKT?8*V-QpS{FVG_dCEcWABeP>r51a(;oO$9uV%LaBfM1R&kxIvPZkiRKya$M zm!H6WxzY+?Y(;+fkhmG+)Ot>6Ih?~77MF7L+WBH(mZSm9n1IPVln?SBT(Z`49y^|3 z&)lSV`70HO!(zyVNx%-4q~O_Uy|!*Z*CMHX>^`&y;}`s1C$v92j^Y5dg73n)F#vqb zt{AXqfsQr3%s^p0xG3OKz@uNS?Iap)M(L!j5Fn9^)kb>y-X1i7N#c50?$qJToKx_{ z0oWX8N6YPF-o8P=jn3ge<8yq@5M>64X1qNP-N1+2E@z}Jcmb~aFF&$~H(yly60TQQ zSDzzJDNMyh^g)?uM)!|CYPctka3O;0z<}8={&)(Mp~qNM^CNWwa+OH?jzXw$lPLf0 z?3A{fPq)9pF8VuDZFYQo+yS_C2U$p}d9VpSMDIfe0<>$sOS8>*&MY%M&H0RQ2q7LZN4Q7Ng5>z^&kE_T4MW=Mj7klYHGn@#^54OGGt zbZh!IuYj8CD(JSpeg=DntER)#%&#gzOxLGbs*xe=!<&IzuiFJ0jVz>CV=5NmVH0zz*I8w3UGc|tI8 zm<4@J%Wgk&WlJ#g+i!t}&nB$1|rapJE@0Lt2Qd0*^ZG<1GDNl zL9N;xJedlW{RL$SN3aNyZO59VvEu#=J%UwbF85ngw-oem3X1F5Wu%{QMykTHYf z8&qTG8$wC9y%ca=(EXVM0l86*ecYNmjh&nP3Ghyqv>^Hto4CAD>d-<3?P3|*0freA zn5A;|l#u%u7%t;=ld1Z^uMiW6B|I;gO>_xS z>JWg9eme!of;eu69J}3eTk93cTeNw1Ms6-9 z5fM?sa&Fyz|Fp@RFEhTi8XP`{t1tpdhjK+}ZBKfn>d z{GytlHr>mez`fu|w#YQ$5QCp0CAj7p9>Xu6CFbq;Itn2m;P!0UA%04hhszMKqXYywC}1cRYm=+oz9YdvL#5?*|k*?=w7F&&Ee%K{`cyAfMEfx{Va? zzL%SmL*o5xfdaou2fl@;b2wKpF*{qYSj-YNu-4?L^)hd@6s@P$L~2Bsv-9)VHv_@a z)ZM8%y-(Ixs#f`FTFB-f<-5%I@j!d9^q2xcQI=jvxO_Y-U(H6w#5pXTKQBFmTv>;Zo;6-}oc zx7+&W5I6-yL{7~jEBx!IM9aC19aGI`)}KnhN*p9o5Wy@7!Y;;&72zUH3=XL0AXT+` zzq251+o9LsXrTF~uI=LXs9iBA+1Jvs-1qbetQpS2Vn3wjqiRt6b20Pe6_`lrTZj+89Ot+$e(f9& zcqsmGAzn+pP=>TCn;{6iYmXsItm~)E#Ba`$dpZzZw1^f z22@Urnw7}mZ-(w%y+d@s-5Q0(*W`8*={P#^KK2Aun^}hMO!vxfwdD4s=T7ni9&|t+ z{Ds73ws*H9MZzzkJ2n_ey?_p{pW)wyPzF(v!HAj&n9_a-NS-dL zhlLdBDLq`}^<;u&q;i!W8=fp0^~5->Q`e)NXuaOdj>Rh} zqBq<60Z-v_!PUB1^|G=fM!yhi?xxLf+np%oW-Oz_%5wjbELE~UF)=Z=DtveRbF|TJ zU4<*G=i8CQrjmKR94^cGXVoGqQciQZR%E5pBd>S2N*!6S(Pe6w^`x{tZBQh%26P>I z+m``(aA&LdQSaqQpc4Kg80M1A(n@cidX_I=!v|;znQHS@kH8}ba|f?e$Xc$zE8!{w zkkHK@&YcWq1k#EH4+F{kA~jy?QOdFci6CBvblg{4$*{z>#-^r1vxvCj;p?I7Y2g)? zSKDtWY6Vr<{HLAt(-;3JlH2qGmMY~?)S21JGPqrCazs3!VO`h~(~V|R$`JZMjEneg zD2u0B4VrmCl2bOZRY2AWC@71 zrzihy3-8QVXIZ5J6HszZRkbwn(T`F>9Z(pnpAQH=yFfxw`srl zN-oD_brqi>dT;rnNl37{{r4t>Ab>HQvbtg9bHMY8|2ry3eHm+-B#7j)RYst1^3e&L z*FvyGsxn}EtYk6QJ;Yc^V+m@M$)GSEJm#i=pW!FaT}OQLCpeyHN*!saz?85-V#3NW zD2P;9RV;x!vTe1}5d=0GB(m9=f0dUY*}$RR@|}D4nJ9a{!6_sdWjptj(Uj!X)|)Xq z+Yo3?__0Y1-GdMj{pd4jJy~%dXsfnET(sFx4KYjXlgH%KyViv#v*IV53U7@eL;bC=u9cq`J#cIhaig&lw4M%1gf zFYR;CPY2K&I(g=a;+Kd8 zo}j<3l~!n50L3TTG^T2T%t2+qeVLGS*ARR|DH!b;lNt_pW4)i5_%#)NN;yP~Bv)u% zCKfaMz?w z53j;q4LmiJ3Undsw5m-Vz)4toP^I?8BpaG)yJ{UwhcdGxmQjnnLXl_x#88tm_OU+f z?hixOs^C$}6EtM02kUN^h(yLBx~G25$zQvrf>hz7ti&^s5Yh9aWeVD&cmJ;n0Pml4 zueYX(2C@!Sv5Yy3ztVGRrE~S2{d2aV(RZDX#O0&smRDJUPwBu9v!s!YOM%E5Y>^TX zMSwY>{tI}$;R_-?XBN%1%?>(DAnmoazGg8(L3I+2%pY4ug$ke~QjC$?(+Qp(G2Gf@ zG8PE%OPnCu10l^I%vVhQTJXI^c1_OGQcw)D0=<=Il7UcI!?M{3?IQBhX?N*Zl}q#%5V?!V_!x zGBEsXH_xu%~!yGM-Gpk-b zw_Y~pteQn|ra#6ypOyTa$bI7#A2{lp@CS9R?V_mEVGjlA(}%~~_Q&KlB!bsjiaL;W z*D0_j==+One7O6N4D@ZYeiG%9nOO#3vV77c)+uUVm(U$^7th#049`65lR+%6Z~7+0 z%uFxVGyvZv4D{>ed)%1w6k0KJxvf}oCP@t)0kF=Dh&EL?X^TrpXj$)na&lshgX8Tu z=Ry_GL@w+thd7kB)#qvkxljUtjgO&UUh{8Uz}ZJ{yT8zD0qFzS&{o~}Pp3Z+==6i+ zEamsgJN6(FoPt0?P-4Gkw_959bv!cizoyIc+9W2{E<*lU-T&2mPHv714jRqCkF~w4 zftQw+OrV5(z)9Z=Af-@FFgRgbmpW9i8m?Hb&2PuQw1AJ#PJ|dGpjfyfZze-R>6!{Q zXJzWoB?bFr*g1C3QLVy9Ljm}~>z+9R?^ABTGsP36^;JF5AW${SKXOKC!!`A_%V>Ng zE1)n-6%`fCNwP~gkWP%6fRLJ#!-B-+y2&s&GJ;|=1PUdmaCKc-{FIEWI;*K_Eg=PZ zHBw+(li?)GKcRzCD*)Yi&AvBV^MYO}!}zn7Lo+eB5g$J<_j=jDP|mTeKby%4#hiOk z#wyeeW>;5NF1zNmCGASXR-n16u1G;29}fT-U3fE#aP*3h-WUsxICOxDuP8sXHDfI! zWFC|U+RfHj2@A;h)(8-Sl}KM3d3IMYylNNkb9tEh*$=oLZoB5G(PuV5w%~iwhX$UQ z^qyXDLp$K+xpJRQn?o#V;47Vgib3v^fQeuRbgR0+?gsWC8A`b3h>gqDoLyxhSU+Y- z)(Iqq#C2RFT~%8f=b1RC>|`0=A6Ftvmz5cIXtEWwy1k6#iS@ZvRxA}Tq91n>)Thd^961v!f6&CZv-g}hciaBN!xo_Ic_DFiC*YgLFX zQ>y%*7Jx1t5*Idhyf^V?I3}rBFtNk?cR>WhGqs8BFr|)qc#WpF*H=@`4q3-(H~ozk z8X9U06k7;+1@@j}p<7KgISSd%(*u$<)lhw3%0GA$DZS5e@rS1E(Ea`WUa_B@eG0b~ zST%%@IrP7A;{T$T7$jxd3P=k8v8S|{uNFa)wf$6o6jAB+H{s99;=e^LB6r7%_vMyj`xpf&wPLpO9+A`n^woFOQ&h`~xhS%a*vXGSR&{!QWF{}PP?6lC>nZ>22ubr1XiEEA88B?Hk zlL&6T5KJs-^^{N{mrv)wc|cS@)e4z5<=wsMDwQmW+Fp9zqGWJA1sy2=1Ma{2pJ)0O@mZb*?dAI z5HW{nDFrT*S9t#O+)k6EWMfkekG}vh$Jn@F17omDHYq(XvgqtNfdpcmtSA)gq}=5f z=ry4Ly(Jh5N(9s1KR5uk8;_7VTA|ut^67%-{K`=% zNs3zJi^Na;_-`zcDSZMqs0wxX%c!is4Z~e9z$!rbVT04T4=iqMI_5;ug(DZiW`scw z9UcM8vq1!$YV{Yo(Z%T%$q8(%#81c!7%BSpc?h^8$;}0-&T_D>g{O7#aF1D&si5iO z^9YGwmCDeR?mA12%&;1l-YXio>xDaJkV>$-ZYmLbeiUiGJ7`!oHyRPXe6Cw(tC?>w zt3MBt+=Zl*(ESvIY@WHDuKBH6H#u$M4{K;oqp&GD>MMGxjWwbAcBpFrzD)Y_Q;z+O z7M0vYl}_{5Z^QzSxmT_nw^i6T9)x>|mC|#oN(9jzlvI_Tn*D!4UWGzuumNuZ{lvKf z32T+jIK7%$91w|`@cBXuO*(S)+fXXIgmIgqm`}-#$VNH#UGtE>qiVFHR|E6-Ig_}Q z%Z}2}6YQ0w9s;SW(gaj8NKcr2CySu)lW$9g&fF$Ua8?)Sd?Vux@@urgI+)7-?uSS_ zMNpm?Rm=5`y?sq>eDnMYy;#>GEUc!#_t=Zx*dGES&~MPqDF&ghY-m}$iIA}XN!p@_ zl2YFm#J@QT|Er*|nz_}r=8QmTxL>AD3qtvW1C1KVC(0szTT~JM1oCrbO@GYJM1_!` zwWZ)&SMMeGe_-Qa-FvlsUI$%CjOov}qfghxw+`dI2JQ=)nNmo&ij!$hLJRa!HH-g{ z=lXU(BB`rL%g7m16e(2tm@zZGu}9LK7Mns>7uykcrReXR=U#_4ZFg=bxH4g*{;46-&LV#uCWYMlhhlDzY>_Yc9jl{vl!^SmdI$S*b3mg1Wkx^s{`;{VSBz^?;+TtckEy*y_k~b`uqSq zzbu1ifs?Gn*o`vKs6cW_9yx`{E{_xvDtw`8=M5GqEOPXA@|XN=JS2V3qE{54HDHH> zLT5RcA;{7HA+u8Ut=w6yfS-?e-i3K;fIlO);|tGIwn#4897ckTCm)_w^uK_22#|s* z^csSbl#-37KpCZ5t9Gg<$BvGQD(1JPTz1`Ceu_1gm&^Yy_|#E%>*QZaCW1Eq8e32- z-YC^k3)Z)n3Z7VK;=}#VtZ~uKk5O6PeUP2#$;NF}4Rs>=?1uh;XPpwHRhIfW26SFQ z*PBl_LUk%GP-P4Ck!Xv`yr2xHQH|Ppr#3PwGPHZX(O5<}(Yd?Lzs_vfgCl4cRc$4H z{EMn$XL%tto#RZEIIqze8p@PD>HBwwWuN| zI?T?4TGe{E1RAGC#YksF&g=sodL>dZHVk6T@2nbyons_=H6I8-|6ch6MHu9#gGtHB z?*Q#lDT}-G;>ngEGY%ytt<7++Y&twtPRR!HSWPup3jMW3CD@{+c{@pwwcwCJQH>ep zE|S4%gG11htj5pKIA)B0V+o|BiWt`v*;;?~6`!jO-s`1D&sU5(+f+BC#)99e?-g1m!rSvN{zzP2=R@vApN3eZXeY1tZEz%}}#a&~5A zNXQfT?jgnk;fonFOUK-7GZ66FK&h#<9NKh`{9Q{`|ZT^>2(V;b z#1BRmy4N;Im0lc=N@5oRSbZ#h^sKp&9#~SE7_g+YDm0;R65L)@Rhf4lMkVJ{4t(QP z90QoEj^B6s1!k+wPyp+V9Y|_je}AnbEaD^sL6hA^ zOR9EOaj^-+4hx}qo*RZe=f#TFcdbOQ5hzZF;_*1rr9=GuE(kV}0aqa|>K+8EFF`6- z`tRPYDF_m%RuBcin(1QVwrMFyk?<|(>HjHx^8){91>6=)8v@5>OrCmT(Bm@*kw;(W zLlkK+9T{KB6r$3bXmkJkiSsQtwz2to2&0mi+-deZ5@^`HW`Dz!8lHaDC<2S*dD9r% zebSCpIPv5-j@@j5iMv38f1K#;^mjX#tzojMoSoHUCwetWc&{u~-4^T{ivha06UT=? zG{0=IgblsujJcQD>mHkEmgoO8BpW3#2=}CW@HyP`@~80x*O3N^45y~7hDSkO_Tgwx=_aAPLDxGsxCuk6hv8gQu(oij> zz2s&HqGNA46$K|Sp2gx+`|rBws^SfkFlFiQ7+-->76B1WM|DRs&;Q0I!Fehntk%tL zsvJ?e!_C#zuJ_x)4>r7E0Pj8a%H^o2P@mxam!brN6h*6ANvIbO!W8rDtGK1NNNw{c zj<-bN(#djSy}EiVh~+ROUqB?%^xJ!;V(9YW^^4}y+3yiOijDMP(_PNlm)Iz z$8qbZ@cWmjv^j^2;`js^k=6G?MYFEfXNK{`$GcXKv~0QG3Vea9DuSiUJs{!H^^U_P z$>_+nmkMVMH+=i~IZm)w@_pHmxa@U?_r?APz}w~lGgpH2#<|efZ{AplaCk2X1CmiI}3nJ`dlm%IJ=6wY@eV?e! zFt}>TI&RW8h+usuQLS;Op}8#`bU~xdUKaKGiN4)e@lf{avCl6k4+r?oiyVL1{&s^3 z^FTBntEcNH?K{QUchi`~qS055JC9nF-V zns6Ay=9mM&BnJUW@CG~*5@mLp*dZ`KcIBmajmOjbR3Uw3dmFW4>WZsQ9JwF67N_two5#ic#uj1^cF8=PV$yj+Vvx@x;>^#BH+3hG^#NlPH zdA4Bve%R@nM2Mx-sYIVvAJq*2znvogxKd2w_4b4ZIN|JVJlQxBSa|1&x_B!RWD@TY zVb-Z`e@Bg__siSR`VcMu8+K~FJ3u*VotDW#Ymr&F2L*0SbXETM-q)NeB6VQxI-^fD zq6oZK2sBlbMOv9ATPc+>M|nIicq#GoZZcs!)8tHDN5fDMTrAt8KyLjH?`{zGJ zX6}3MwbwcqgT?G2K{aW}8bxxC)5j~^0TygisQ50*GNl6)bC!l#Das5R$p}TIYv*_7 z#Ip_(0n-E5M8ohTlhd``3Uo`5DqW*4BulV4twZD*8PP+=B6?;aJyzMFpvxSqbtFSLJjD!L0 zR{7KPkX0E62L}ub>(!O7YvK&PtA_MuX>hTEBzui|+{DeA#La=^bAu0?TFcNmGl;UJ zlU2-O2!TNE$WJoXt4FWg2QvXMo~orda->NG-;NFXZGfdAy-T0XQcDaC>_IGGqSUvx zx(Zmw#o^ql5%Q&3Gp0Vx^@;ZpgeVOlUQ54AOL-q2u2sm4e^XZnH@y=2yt=OL)d;FS z-#?J3zsv=?VQSl8K3lLP_v@E;)jKYlYN?Z&r|BmaxkrRp$nJ?qK7-VF-dj`C&?HJl zOK$}#vP)6k*htl1G>=jibB4OR{gfWR&h_otxD@J|K9^%TVQIVRRE~O{!lNLnC;s>@ z4NBN|4EL^!zFyWNXz(CtsuZwXm^3T*Qxf6^omv5ILPgsn{ZfLMVkpS8rQZ64=+{O` z#&-a&9S=~NKZym)vcF9qLa)+$EKP?;6ACD~euto- z{CV7yEGpRGYnt#jSVZX%t@y{78g;>EO_^rm$9qMiC{#Xzkk&S4S6^$aZU&U%pz$Yc z@GcDSNqZ@5(5uwP#Q*w>lsppeR7SPgtQxEKz#G}B^g1{vTXei+a09a9DKCMPlvq?0 zO{YE-!pyZKew{Ok^^BOoY#hoIH4jvcldWu*ET#;E%s<4mhw)g~QTSC{aK-S2&;Bp) z?mK`G*$*sRkY7Tp7V}UAe=pFr`<;?5@hzANj;G&uK#An2A6j7rLGGWMk^(!r1 z@YjQ*!$j*p3e^b1eeu7`BhV(0qCTpF)(M$%Uu_7(a9^lv^S~|VMI!Xw6k4gL7@}7a z`qA2+)VSKjW8~KoLmt;?;Mn>AM!SbE?A|rFcS}aSgg55b%kU~D777PrWTJLcfuAqy z58jtO%CF!fjrb@~830$xAxlu5ECg(Yma8Lp#E}EN&|zG&+J1M*E6a792^;&uOSPWw zs$r$xML0xF1P*tzUAbab`Au+HUL-#FK&Ckj6)%sqRo6&TEFp#9MK3JTZX{QPTwLZK zI!!CduXX#uRyB-sj0jzN23_!61AhrxT6&b-8vsm`-p`_X0i1;+QUg-Kg`#ZkH74JrZr`p;K|ago^K^qh&ZZAqb=#n22-QT2&M)P_nc zi;GbHiR)+R4;9v7vJ>CYvUz5Y_I}v@sR+yqp6+|SznC7mpI7-$A@ZG4a9aRV|E@ETD z%Km=**Vs+T=mGDe3vE>YVE!c6hB_&o0DGX22Ikqkkm1UR=Hn=A)KJ;Kr^2m3^od`l z2ZttL6eCVNF;l+STvJ|#OUH8`_Aqp|@{q!%%g#nrc%BL;tyd_?rsuOe2w2%Qa^oo% zE*lIV|0(cT#Sj2+mL&WR1fWGuCA-|`tsfS4q)ne2f#5~R6gy6w$q$P|#>r@R(_mK# z-NwsZgc&cS?t9zeXRHwp1`^ae3H_#xi6)~r>m*xg7(J6N-_sH$NW?DiO#eIro%q8?X*D&R zoAX`GJH5t8IXr6O;JP}#tLtn1jVm8D@Mw&FvWXFb%7+}X7O-ka7ke{=W5+=U--?sn zS-hb=zwpiA9i_@_!6ti3gcL?iRSVwN?xFU@;pN*_{l65Ph!_$w;XjqUUJ9IYY|wNl zpggzLWs*IXxBLj)uZ@BKzEvv)yhZ&<5CW`mVckET%gSajWNb28uIAWTZO~a<+*s|J zZt*H4lpoc9GzwsdlA&$&_Ef~a^2s=4y#!|Ap=}QP!+<~);vGfNEyOq)up!%z{U_-* zLG?Urnj9@_X{QJ6AwlvnY=gHbQw*JjVqb9d6W5T z%Q}jZj73fw$BEj!DnawKkNdw)^NN_$JJh6a@qF5kW@Ab~vvoL9TA;+9nUEYY+(dVt zaySV{=qms1Msimq@tX0i4C6@m=9}jsShPzfXjFfPTSZvCJP&^Hf99rjo(Jp=7M&Gd z9l79uQDB&L+ZrK#eYiOPrGG5U8glvQPWRWhSIXSZmEIAP;W`pFTV*_pAYBj(zfP9W zuU1a-TT(}g7{d+gN75nTMzsM$os_GqYlLG!_YJJn-$$zYF5m@JIBeCi;kmhOi<2@a9Tjy@9xg@{7RlU z`{wbqBH{RZhj&yWFItQ^$fqf~urb^`*`nT6%`|$@&;y6tC@lB|jlz`(V*7d^tL&d* zoT>BfQ{a>kxDoStvPGljBL~|fp5k(Jkd8nOEYTIBS50!&(bBTIh38v?yMScb%o?~` zRPveg{Z7}3!JXb(?fP9EeQm{^(s3^Ra}mc!=uZKqI8qrh3j9!_%Z{FRz=@U&p=MCY(mF&7s8v?2c-=jR_6&S1TAv4G zsz>v^87MlR74xJN#b=Uv?2Odl`Zt|_ZjHp_pFRNcYQjr4)kew@;tCY& zI9h39&IlJwzDnz(ZB_6k&rk%PJIxnvZOynig}IYjQQ_9LRVNufgPaBs(-U!o)f~zW zr*>H8QIs4nBFni{%uD9jW$^DcS|!V`a)-bTEZv)?W-wk52TgNgm$jcQH-o$%DkZ%H0fcDm>oT z*!-s&vSg9#cU5*-CkoBhzgDop5B4MG$#chYeDU@s1Bl>RvYQZT%r5C*N3mq*$R_OI ztIWuvzQQ0gk?>bpCQt2X^{#zzI2!<}o<3I@QH`e#lei}i^GSEzMVpY^ZWcF@wEPcWNcvhfc1mtYgTZ@aSq=Q${nIh1s4fEI}sM`TecgZYLQ?k*ha zF9NreA(%o_WH{Q{3OgRuhEX})ltj@8@BsCmjxB>!qq@y<7&j|7*Vx>mk5MD(Xg~2I zdJl*ue%TPIqKZv?DjQ?+t=l;u6nq+a@yzmhdAm1ChVdpg>8Kqv)423lW#!X|s=MDg zo&Q0{;d5rDkT8zk`5Qrm>0rJ^Vst5gvioS^1}f7rI6iAP_A@$#1#_6j?Zf%E+Q`v# zdEAWBF`|DPEN2htwf!dd7^bKEr2o!UUJ4C7OdS~{LnH=`{sDO=Jyd_X1o__wxShu7 zY=VMF2I{a-k^5iiiC#4yR3v$~JfdH0iZVrWCKHi!VjIf2aqn}auSlSi>LPO^1Mxoc z&!2}CT|fJq6aBNduLMY=+)7w~;`%_%2$nU^HuSU_YqVv(eQDs{RP2AVsa&=w1(g*}qkx)){Z#zwiu~9KzuH?L@ zr^&;#1KS4@sE5knwv6o37q1hBup33>n^#?p5m;Uf%W^+kO}wi3`E@2HK;?nP*dQh| zZM9Wji1$O+z^p>BbjCZgt@U+HnhALmMGPWzQ>u$6rsw41kn|cXxVj#t>{HPv54&+V zJXhzS228a^jkLP-nldEkQe?b%BY8ph*CO9~8w?QSojG`#)c-k(js1`3Y*R!CiJ z2h)Asp9YZP(m3UM>2NoCIG@nK0dlNi0L?i9D~5GzJ=lXwT8u#Gc*scBc|3`8WX(Ko zo%jdIsxkLP_-C`B0p-tHK&xfpA}@VDnaI3MDu>E$7_NG?VD37q(!Zs$kfa=%DUG_k zy+z$?tp1JVQ4lUL0SSz9ld4~u#n;h+qSW0 z$OVP>z#vJ>#-=Qf%Ic9U@skMwsrm9xBG#zO>Wv)x<)K7)wjw4nmLCSTFzIsP$MhFB z6&RrFSw8c)&MBc2=;$qVU+>pKZnFrMl$FK=YCYmt z8DN0{7SOG7cY%ODSx8o4SON2ou5r$%>f}Y-EkaPGlIxy_QMty`Rf|q&(xwIO9P-_f7<9e=%%nbW zm2vY(&E}AXbbkWbJya{;>O~a+^>Fq-^z&EgnPvH=(dEVltk~|KQ=~nxmxj6|`sIqCDx0PHr9wT}qQo)F!8Da1SFiJIB zY|j~#K1W4x4ox47s==+jC*(YsL60KX`t0`Z$2+SZ%BGO$eA#(K$9KN#g@ zR1-@_n90lQ_jOxE#ujTJI13o_ni$mCg#a%!2}osU1|#7VbjsK5o`a-V23^K()V}Z= ztbVlbp|^pPdVxQw7nVo)dOxt>h>R(j*6VbDtb8=K_|+5Og|zA_*MT}8G2Ao=GBSQz z?EOK)*ICRrm|nqT`oUE}W--f$G(0k31Psg!YC&L;&sB0M5|CNJ59wNkM1~^nNrXIHlI(Xz z1_=nbkUrwCk3r#rj&O=aGCGg@yu4i>a@zKaS-8sS^OXgUq-Qcv7_OAW;xeezN|)|! zttB&ozi(F+{vO6c&gp%!KVPfYGO(RT1;`$-~Q-Yxfn?&0m%-0o9Xy0m6w!;E6 zU=GiIz{tvFG}5rT;6G~r6fl#t8HTR}zQKhIUuR4nvZLAnzgt8@gLg#5Rd_CrE4uVf zFruCqEt*GBccIRErvVoKxAF>v=3OtCz|sZ$_H+;c6+jmUsBpCn(bz#f=cde%o%$wN zpf@Uy?pS|+plI^-dx?OY8JH+3aJ`Pwh>px*F1>dXJMu2UKed<~ zLk-*6?}5=8q=3N%m)GRd(6CTyXG9prJ}kN6e{!5h0i{rtUKAXP9>SC8#_!f_726P| z!0FDc#Ax{=U``n@jA|n%C--Qri2o+&x+3{+bBLE1N^qRp3eCGqDQqhUD|*h9CLb9~ zjS`u>eVUx{9y>_lq{y|Q%C&^CtAjm4(jbdVlz@!)mn(eqJ@dz7HMIAtDj_%BFMD?a zxH%26ic17$pp!xKr~VwjnN zsmDj7>etE;o4(Y+xqrYi+vvU-t1MlSA8VQY_Bcp?V}_gwGWnjf8``FK#bHxYLQVT! z+iq4-dHTEV`uO8C{T9d=_3Ji4-s*YyEH6!>eTbt27%6F&5;~_&B-;}kzNO`(M^wrsaRy<(|1L( zBo`RIHpK&aY=KG=0%3{)cA7w}lrKpD`LSlfN)$u*dQSq$1|deycX=BfEcchza9&2$ zTT#_1j>g5e`v;u@YwFsy(&PO9Ji9p&Wpc= zoc!P@zGIS-4JAF80v&9>?lVr}6Yz!d<0FpxBn@w{1N$6?5#Ct86cG`r(YGXp-xhMi zlI+qRc%U5ktr(t=Z#LErdvkvkI-Gc+i*C+>o zP5BOiA~dXzgMyfW8g;|+;sM-sS-d&zL}1RL(&}3%zV-?njE%{z^1fM(Tp|5%NpT~( zjN}q?P;xwjEpxTyB(@QHoJl|4u?BaG^SRDFD@ugr)Zu70IIe6WTTy(E-1dmuqu|JY z^#3NCZS6T^C8gha5?C}i6z-6sk-i;x21C!AMq|%Xa9(YP7HVIG=wJss>hU1pje+Qj z$*btMuJ)f~J4fVTvn`AppJ?3G33zt@vC7s=PH$5W8fqVk-vfKEr17JZLpRjRJ!q{) zKl+)@sWip$4IcC_PQQRk#QM#etL%7YLf9)ZpIGR$R2!>!X~)eTYf`1YUdAos^2_KG zzA}1O%{UC)kFWNZ)8G@M6XPStFD017mzd!qH}4O}Qd~h^p1(&Cq&vvsJ0I0{;9y?? zK^e)1`#Y6GU4J1ga&JPJlC;*%kPH>k2>nXf?a3lV_?31*WY%I# zX`^`$vKpTe-pKpmy#)B#v}MF{6^gVh&L`oo>s3C+7)2?3u)iJ=Wu`*i?l!c3eE|g6 z7g5Xaq>f)#3<2yNKhDl#Q!Sq(TfW4Utr62i)5d(xr8Qkq$I$y}x7UB4_hxUp7wGrM z`o=4FD#BoYEjg46$RHLCY)Ygoxx?G>XR_TWwYZvDVFQF+#XlxKBa;%2FfET~^4fA) z55IR1S^e(yyntGFqwB1<0Iaz1H6|0YFz^T8NPb(dzuMLBQ3v|_*8t-7olX1 z!I}Wv%Wby};Hk@k%~^`Z+lRdWqa`$xHlPo=d#XX7i+7`UK)G`feEE3=)yW+zg^Y#X zjVDB=bnnF$;l3y#7TXTnNcE@|k!wU)X72xp62|3X(YH}LG&JG)h1R@=oWj z)X9|Y-Ct?%+H&K^%29m}9b~#75J>U$^?j^!1x_e>q(?nfXZjrLlT%?IM^m5eDdehQ+WgEvE;+4C?vfb^4v+9!DE4DF;?F&`MrQ!(HSk?E?Zb|& zVi!x26s4}>`|3OyGknHCWKbMTOa*aQ*x{X`NUfKtPGtSN@1YM1L3mfUcjB4dS> zDb!DY@u0$RG7Zj{;TAGjBpt^n+hNPg%X_PmEieSLabEalouOO+5BV2dY$*O*s=<7x z;)}Av_kp1Mc7)uho5A1y4&lmVsyRX-pm|i=>D@TcJn#!JOF(<-AgmU|oa_KXBlX$F zN5-}H3eVQo4k zC?9-0TKE8`F87;OSiYfgQ)W!5;F6q%7lPJrKdEB(@GtYIz4@BOiCONSnZrA037R#;(IX8M1c*92tYJC0ior}8hE#)m%EXnE|f8#ZMc8k`3ApEv} zMZ`E}5?qmq_^VE%5GhFFkb9k$!3-Iq5}t5EWn&kWU1bju>3wh{60j8p8ML|u^y=+q zw?KhV#@HO2&0!GO0+(|tYH%0?_bFEIR0*CCe`sv2fW>SFo@@arznF3FSnEWJ`nl*e z?U&8C+T@2V_t!# zc3O9iGJ!~ov~LRzv#GpgEtjfpBMS8zy?5^bAv_=}Xy^`!G+MM=a^g6a+(p}~llW^| zC)hJ_pFT}E{dds}PY$awI^SHob)q|ZA$==|qR7{Q#xG4AJvi#=kzQxuCFaUt_6qoC zGqX!E>prQ($epLh3=-RLe90@uy*I!y%HB(AbovHEgF}Rru_Ich2=fjJ^LwrCWkTok z@6Q>puC9JQ!OW9x!*8Tb$)qGiz?0R`>6+6Z}$y zvlU0g-tB+Xrh6o_jKiVf!?!cDM9#9<36ll?jABHbC! zS;5b444JsWUgM(UVyNa1pg52FFIEb>0&F@-WySBq-8@y}DWBbw=&>`;$L) zQ~MYj3;BfuE|S00YFGVF)E|ud^dS8ZiGYawlZ!qtOCOcb9w8O>_xDE+;?fz1Ml{TYdw|76s0@ z>2myD7UbiG$rWW^L ze0%j{eEqcVJOqVu0-TSO3&x;>s%hc8JeKF@Y_?O3BlzoBtP!7=+oEHATZ(YsZNFWS zVxd?hi0dtv7P`dfpUOlSTxutY6SS7$Es#Ldl|zYx_eo!IpMy1=B3-LhAs~W+ z=-VuI@k8_PlZ}4Me*f&%U$qiq{D;S`bDi~JSK_JMoZj(4uFL6CG^V@ zUU^_kz^7Ea3Bs5iBS8etJBbN+y8h2lvk{XltX!P2AJSaBIv{j_h<3~EcW!5yeocYj zsMLvjtMqg_QCMgvo6Md{ zdLu@4&(lDLZqxtb*ZBOSsRfM3){Oi6ka`7WkcE(YIQ1taWXt#+)&>3(#vW&SUTH%I zNmOh>_M)YjuF$^1zb5h8;yXuQ+4)&nB`n>u7!L-3dsyopFUSUBjVcslwK=9p@b@^x zpplWzNhQ3<_6g*fq0FQaf*dQ7`@3-Dwa~r1%WqETr})|;E0Ao zql2HyD-K7F@*GPoK6g6~5-r`(*L$D1kM29zLfcw@XfWpx8AZ$Ifo$>D)>6@h>PqY> z!<%?My&g@gSMs5mm6e0!y9vHpKT94*q>Ae)r}w~FESvX5Z^XE=8!z)x79n5V zT#sKXu1@psZR8i149A~52t2HF4)xZbb~eJIb^Dl;8#v7mBnQAls)#4B7Y4A@&pSsr>bu^k0Ls)NNWD6CRCwDm#;@su)E+Op^GM4WUh| z;~a;TjSZZQE&eWuWnvYd5S5^?3cNj@ZgYAW@Hd{y;FidTuc(`|&<2kEDqH36BwT)_ z9J;+@uEziQCnR@hYz&$H0VOTM=oTEX?S9gw#rUal_02P`rRMTB*D zc2=24e_Ck8*){a0W*$eS{r8#~Y43|rqKk(y_muLz;QR9%&>8<9x5jEPY3hw4mMmm< zk#}(HSyZ;lg5<8=Y5YsW4zlgQ`|$gN-M%JLqZVx0zgvvQ2bXs{Y|x3Q^M#txR9EM@ zFGkt7lP0JWys41U$3!pnA84TctQb|<@(sCHUVCtMP+e$=qac&#rz%>Gr^e4E)OS4$ zf+T?XQ@OwfdY-tG^}Da$IX!roIIf7_J&MiVAA?7u4d+&)bn-X%Fu*h)nT%xdqt6JB zE^u(=>5sxnS0s$6HZ`Nx7Oi?eqQHMpD@d0pEEy_4sCzB>AE8(`+y8FnuK?kuEs7w( zS;Q+?G^?(zR{N|q6`yRPSjNIKPC0RY8UBd_Vc}BF((|vJH1AdnGkz;qkPMp*d&^tj zN+fAug&^%#p7564yid`-(7gY(HJa@Pgu+{J!$PJvb4 zlCM~LwaK0v?S;C|#p@%_p66cAB`crtVxVa-K_Z^wOlAj0 z_`ceqR$+eEdXOA_Esw6cdfNGD5fBINb0m~r3T1OVApB@<=PAQRm^n8Li znx&}6b|xP+0zD-Etm~H0BYFQrXh|4e#CDA}8j8?AY0Vfjc|^L^eeep#6w)~y)`6+L zm%$rh`i&xY%lEm_r%N3wPg3WKb!sy*5R!_%dS3ziKr=$JZN?9em|tJ48g(UDw#@Hj zhFz3qf1XPq7kq5@TLHu!0J8GZr)q>J%igrvF##hhoe1Qum4<#&!p~H71+j1X8*0Gg zdUh}A0NHx8A4>o3qFq`MSHM(Oiv7<^+o|)Wlsg>iRW?CEVvw(5&#lw>AaasZr2w_Xv6-%{lUPh z;1`M3HbicR5fTh^@Tg6=NLtZw$y!eV9O6efZvyRl1z+p-$Nt{wL z2>hcdf9rlbzwYbhH~*Zgkky86QCgy&KPjb!lB9hDbRjX|i1vIf*A&lAdkoTO4lbmd zCm;r@H>?#OeP_mOfj3=$9QA5qm^k@e5e}tVDXD|9+G>0{EtjD$YCQ9IuI=AGowOLW z{r&}=^dupa{AXz=S^k&%2p>;h3^k7{Zemrbwb*%F4;wlK9>uU@zhDmUxfi~x>U$-G#`AfOnW%o@66J{{CqE=y0Y z5bMeNLLqzkIj7sz03~R|ln*`cd?^+c5*plxgEOAlS5TMuHX~yRkecEtgr~$A*lOz( zp?U#+;3kxctyDX#KUlB_32|w^mdC$7SszIM++Qlk>la}*683j><3?!X-&JwnZt>3! zQ{TL`#Gc7E^yYt}mfQVo;TXsAntx4pPXEyCzYq@}5%o7;D;QJsDz>=IXGV49em?^=7~B%5;r| zw#szB(pLsWs`J2F{k#)U;6@mae$|)hKW!u0q1uWf-&S4q(ZQlPbbtPL1vle<-|Dqc zHinF)cU^m}kLsyD4i5B7oTPAw%*=ttm}4pIF{K0+{-tm6E4-fnFqDqMQ6jHz`IOaK}qzt z)V7%j^;)Hec}CNU*HX6OM5kJ!~b zhvms}JIs-Em{O28s5jYGDqzWfujXNF&+AHf=9HC`dcb+;%d^hZ&!#qF*_e!isv~}K zxP!jmtJXe79BUO(mxwr)+o#pcMR%eivU>R1)56}zK2dtFM)1MTH%6?NLqFOMEtr5? zJGgn!_L>?7gYe5q4(hAm)`gvsu5}v06u06~A+w>K#@@e?{GKvJaWKGeLIAqNQ3G^v z#DJpU0)QDvg_Nu~k*!UmuSf`~eP0ztQtsK0_suv9N!Jj?kd0vBA8)#)3q ztLAVNKb;MTOM%~c6p_y9-%33{3CHhnHob#j!R73VZbBKZAo-FpfB*HM+0|TPbacz@ zUUup7Gp1Hzx;M3ozkTerSRm&)>OupHXgpfx9&#f&S|__z_s>=~S5e0=hh_ehi`9R@ zY003bhUsA;3<=j#q+!;40*)Balz5$}Bgl%kHbHI6`mTl^S~@$(wEzN`3sY6r<@MoD zzWcZSJ8TgNNcJ_{jN;D1gxiYdWp)rmO^{>*AcM ziwAq5CEh&KJmhi2Hch>#p(fab#O+ znOB1_wd&-(!lk8#xFUg#>*2*=d9$8KOubqgOr{&D>LSrYF8h)6K&-?M1Hg~-ZGGoq z8_CDUh?wHe@7e6p=s;EBn`WQdBb=b#wtF6(UaIbTf4{B<<54^8He9+St%MJ7%jpur z^G^<0>?U=XONiRJ{DH)dYX8pypckI%V#fcYkvqBkNv&de#)XTb;||&X@~H2mrGI5q zfAuuj=$<{jI8|~5G*LIa@xRy^jfrvX=8*q7zHIwsf!Jq(&m`$_+{s zm5q>ZpJPTkZr<;+a6|0ju%Chuyji>Dza5M7wXi+F2*V{H(0U~*%mn*^0s(3Q#8EnC zo)c1%f))^bd-QPc!u6xRMklEW3q9RMYxM5qW?Jk!v4?3w??`&l$tfdC&PlZ-VTJY+ z6kAo<(agjoGWqdWi;e10+yGG0^n=WizCZU}Jkl8vt6KnlQl-}DC*=fism68vl@#2H z(nhFXOep!{1&evgER_sLc?dAI36~B%Pv-t7hL87h96@hmftsTfh7k*$G|&)t-NJp1M0aTBe`O zQiL=%3gwy#fOJQAM1*#eOswO^=Ev29j_1V%wggMQ%~gpxh)UcpFYv^|x^c)}Qw7|$ zbk0-{|6Ude!G*Evlus;);njY@QSeGh#S61Os+3DxGq1SoUJdMjlK*KNE<2Omogijq z`Z~C!j45piVzi_O4FPd=GPXr=c|tuweUo7gv41ZRN+bk{mcAOHpN%S`Y-)o?JX>V2 z&lnrBbm$D zg&)j&A`5vO;?>OKl^(pgQ(kU%%`jq2=f#G4OG39g@I#zZ>Qrap%*0%j!%_;R?wkDX z8olM^W#8XO72JTY2Di9)y8_Bf1Wm4oLouP-lmrM0eQl5cCHe#5S0XGi-0MZBa01pp zY8zJnJs(r$CHI9c#=gjo)HJa5ueC|`7AGHj4|iS8#2xQW%ipKbC1F?tapOTRC1bP1d>5q}Ly@mmOqMeyWSq)$;1;!gaOmgT@c10Vf!~BL zvRjUy$uaaKwXs(GY--3FBA4of!??Znv#egrnJwNK#9!q&GSLgQ&nm)lnv7!VNq+ET0)i`SMj zbRcZi;Pu9Am(tLn194$1_I&g_qHNpM+cDbjLu&udTNhNZJk3t~_q^$|~k z4AHgT&ToEKJm|4H5L?;hS4@2s9lD(frDGM?67?m6M?UXb7RL51@j7LBeAP)yc9S_m zO?!kh3#A|FaymF=dcT@hM+|K&vSAa3{Y5)ko5dSv06iNHMx{M9K@=2}T-N0Ld~89% znA+-5(E$G+;H+>3LM@Y@RH<5zyU`o4wKk1kVPS07B!AuG&O{UN$Ig#n-sP>fNJN+p z6`SOtMspFT(4jRC17kr*lL&Y#$%6Be(p$BdfL?2o)g>03`@SCBr#VUQ8cEQ{kSvFT zKqc*{n4qORTFo!$(#^=rXTP5&xtBZ%5l?Z{!LypTWQOiM`?nfJH31^t-5fJ>qf*xc zSEeImkz&o%=i-b`raagunH)Q1_w7bjL(3Y+N)lf2!AZQLz_q5y$W0C=bSB=cF!<3| z2!da~Lhn_QWF?faSC2NUjvQ~ea$@Z%Q7qN z5fKbas!OeF%RjMxqd$MmlefS1RdDwT`^S!ew<#CLYqm1D@SDCe05PtC6lZq*Qrwpj z_5&GGG9jP#W@3vZ8uuX0eCu8HO|eo36Z8vpc3sHy$=3Rk^a!xlDdc;_fS<0V^+1kM zg`S(p$vTjlTdFe)2lxgUVU1o>i0dEk!aM>hco3HI@`%7G**E>lJ zkI~S_G=bEVCavQ}oIOkY$>t2JR^Gh!$b%--NITs=Dz&N~_CV&@=CS?qVE(^=9iRcH z1TrTl%6Sk&-Yv@tC4Lzf{whZBlsciPtFu&U?kxDJbk{j6LMPIHhdRABwz zkR~Ph;oLFQ1X0Kv9Ik`{uDAf}Y#y15JQGlVg57rk-(zMqRy&v6gW(=zuXQi@>`fD4 zTmzecJ=_U!ZN~sVSPW9dL{AgUziY4=<$j1j&&e^yCE>=@jMS}+7M8ImePz!_b}~qt zSWxGB+&ZGic26xI!%*r(u`StJ<0`T^&Y~VcA~+HxJ)_P#%JI>%&-PDJ&SwR^Wteg{ zfmdybzI}pqKDC-ZEa2hpuLyRV>|uq5z0vU$k8#-1<;+tqMbzE+ixjnmg^WQ_sRBPR zE|*ch!dzpkcv%T{f^mt5*2^6J=odqDWE2qKl3`9sNvZ3U)@B&^r3-XoZDF}zv$T-g zXK?P~2EY^0@KdI*N7~aPOXH_ zb3%2cdAX1BhhP*07^>LVK8^m}ZMm#Q#y@;^s7J;%c{X3VmKN4l?Mn^eN)@=nT&p)8 z83XrVZW!N((x#2(h567c;EVZ+mt63yB+PJR{42%vFcFQLTaIQSt#S1F)=CH-sIj<7 zRsFHt4oU18Jv~uNjCnrN(fVPnJ(#u{9shnGy|%;j7-Mk+lg@L1$6{G9`ZYrf8DXbo zR#H`+`!F)KR5MkgV{9SsLOPK3?p+wT-F7duKq|B0HhgmvyBT@HC~^4d<|bbdjeZH! z_|AFJ5tCCGie86X9-bwC41Y%C$VP<}Vjv$}xLQ$BA+kO9`_5*?q5@$kHpBOAPJ(@5 z-WMD{1d5mRozax8OoKmS6FBO{ICf2N>~_HK!g0sD@%=SmZyV$DR$O_3wCa1J8YP@6 z4k+Melxz-N|Na`!O{H==$`Yn^_sv{IF6HS~+M~xxq@(K1bFIr6d_25!z1`%^ztvxe zu6kKv-vfvi#tEsfs^>h%^rR|q=N096SS>kqm@Y)e3#YlgHAq+AYPS{C5kuL6pS&O? z?thicYwOLVh}`&lB+IFH{6z+@D5V`Cg9$IX8HX_plJHQXY*xhyEgezdua1s}{mW_` znYF=nb#p_Ad&w)DTPQ$C8lw+_8FHf=KF?@6o2@ZE+oJm40lRQ0iww zeDfF7WVk)HEKKPRi|54OeL9`E&YRt%K)H(Y>YBv#uL@Fy{E7-M4pY)>taFdBjmvi% zJ)=>M+cDT}|5!+^MhvTx9ReTM_zGi{-EKj`O{D)+Ma>p# zc~|H5Y{up>X)!*XO&SCa^g$7%3LIbxWBN14HP^Gt0cQzs1|Qj4&Tlw)5dO)mfLLR1 z2qH?IyQO}u%WQdO1us8nEw9NMy!VqeJ6dY{g*emTV#TB-)Os_ zmOQ$AOhACHwd%ow_{@ds^V**1t4jT2cCIf+wMy!h`Y+kG0x??e1ALOtHp0MWr4=6> z&aV@%9%kP~r60Ma_Cyv83v$rMqWV8s)fHO$UhDmspcSUAyr-rk567&~A1@c+#&(T# zsi^Y($X@iNj}8w{Sz42#u*`VwOcbuYdsWY6@lQ89*=-T2vh^>e5$~M&*7DriCTHvM zP0tsT_s^jwnOPqxjoww;#h`C>2yM=0p337?2|!841}QG!Y1xsZnRyv^Yoot{6+0P@ zoQN=x!|nseMC^lCwcf&=XhcxH-b{(gRj^P&5n7}q2QzaQ9qt?70Qk1w#h;3n!Gv-fRXJ*( zU;x@{k3g|?QnU0yV3Yp4K`oBU2mfv#7IfistIhL=jw^%j(@UyxsU=Ru zuj2QEemjLQSig1!MfBL&@ehV>!?yrSi9-o1n(I7$T%`}Xw^?+X9b;jzkJe@C3bZ^o*Xrq<1mngy38s!-R5`~?Rx zko5ljB3IBUG>u%x%4j=wmW6TGe(yjx%&UvZ$sF&R9w7JaKTY*WXxj79JgePkd;AI_ zBhc8UN)5>`P=I!wY(W>~xGNwoVqzlr4e7bWRx((&u~pwl1^0abm?TUVhsemtH6XJ8 zpcmb<<=Zs90OCZJB)D-l7#yzys{`9Zc+K0q1SyET*|2NS6>>Q9=>-*^>t18 zG*W|;au+I@2^-8;$(;Cki4eKRmE(50G34smrq4F@?Gs%wF;tP-6`=J2eh7~b%-2vT z^u1lrQ!Uq4_f51nH@l3-E@73oM)O6pAH7&aU1%hIZ#(V|T*z$O4IC4Mm;PCQPcsK} za1lW_fZzkpp}ViXS{EuNUO*qrwfD+G?9R_ilq5+``rO_0-nJ{K+o|>JR!_k!omBaB zwgiW1)B}0W3@ZA;vMc3)X;q@-ePo0OaajjM_x$3WOS{T%C(?3lYHDFy9XE0|37B{Q z2BjDY@GsW|Y)=(ao5{hroZfD%Yz)0KOW+0f>=`Z99=$9@vZP<@ko%LpR?#{i;I+pybDPqvO4@%C)zm zhox@YSR4+ktb#;J_|n|Y1@^cjgot5AI0H+D*OvRoWTx^WIM6*pgm>HVYZeU;uZ*}h zGhIgl9Fc6sUP=y1nhFsTlr{(IE4<{l>)sM!QhZPL=VENA&zTT`S=zbGyCTP}67+*e zBt1@uoS#xD@Ssjq2(vThSFeEMfWfxUwwIy~oTQ5Nr)@-Tlk<=4UZcEeIU%nw?kI`V zH*NFN_MaSykW;>!;2rQYA|S;@Qov`4sH78p96sS>0LBnB8K!d{Bln$I*&R^h%+7Zv z3y3>YzTJJz)JrZDvs$+`zd^kJf>VjhYGN1}{Gik4OE1uuqNZee5&eXikK~Kd?GfX4 zC)&X?*7n;?(bpn2swo<{7e3!NrUV`r81i=CkC@MxnNb1OugmoQlvqea(Jgt3D<$w- z!V}cTL|<}FPPq($O0E5LGv=XJ3%RrLLJ?TI%$`Z$)ee)jIbwLX0(E(8J7*x#Hb{0X zVu6&9fXe*m#$#LDqg&_O0`lBjtBfTc3uRB4iZ6!6lkXHYE`gWF5-hu=Z2`TH0&4IM zV#G3Gy4MoPNiSQt|BGE?$6J%Zj4x47zMp`(0votmS?0Tdj`CEH`Z;Q_kWfo^OWdc8 ztW?<-moNyB69)A^7S`NHmecpHwLX%tC!1=2OjO^CGSHT@K}JX|8!e=zL({~zeMh+0 z|K{w)Q$7{@BHPR2Z;U5!Z>(-}2W)yW_my9YzSIDMl^Gy(ioDKPuR@_6qG2v3qebEa z&w0z#X@;(n_#n^;1c$@xgkI!goXaDSJo~fZM710x(xt^=!4K*(#`-uSk%${|daCuR z8L)OgxvQUoWg++94hIFgX6CA^hf!5j^J_tJ`iA@1F(PUTeV1fm=q8iSqzRbFK8csW=pEx65DO$c=01QL!|KsYbg7Rvj zWr0A@;7)K0?(R+;cXtWy?(Xgchv0-@!JS~iJ-EBO-1+`{Z`G;umWNdF?LD(+*6Qxn z?Rk5uK^`(qTwM4!A|j$2a;J&~yf!y5iE45k4k4$D2n!oOf1)zM6w=$X@xL^!`p@wW z*y~bHeRsf2HdyX|Pl)RtCG_vDp6ut4$$=);9z(N*2To zv6@d%9S|f&N}`uQ!wj3RcXPL@4&xZAX~Y~0IW`)+`CT{gBZWn`4>-%jrYDzP%*eu| zy7Znl1snCpkj@rFTBKekt6RQl0+W<`CrB5sNL^Qskrc55q44`U2ANF!bYSBzXHP&A{G?QdDqC;eZuw3*oc{6ItJ>}J{@3W%aObq;e zD=0f`IfKWHOieB8B!mCLU7}Xgv&e$Jt}Z40&J?&l_4sYR;@^xZ8`|iLs$Ga}AN1x? zGx5#-39j-;FjDgBHA>0h52uA_w-8W8zHt@Fc3nq2`dl#yJphVK0WseX^-3K~26wLu zx=E|rd8NX(GcpDnhLwY6S)j8U5HjxU+*kk_vPBN?A3?`&tN7h0ZI9<4A7_1@Qr<+W z1iwQ_1m6>M1pdiOqlh z7WTZ*WPfxuh~5W=IW5Jn!Gs7l-MO}hYK6vMZ0fM6sA-*#?hGGs=t?wFCNuzMI4I_e zOjhI`*WjH%?6tN+kIKgbHJdS50GBdRv#yJbV}2o(h+b=to+RNfIu9XNh=|HW6ml%W z%e9&?K}V=Tv#<}(_zD|#P}FysHh5cM*iT{P)je8?yxl50v!Z1)Na+gi40xSynNm5| zYRUsZ{}3<|vDg0D5@+-ssx&YGr7<4P_t;J|EoTpmYx1Tt*6JkXvoBDIVGu^UV+3!g zEdahQMtvcCgBN5#qe;BZ&Q5ML#KPwrDqMG{_Y@@FxB6vSlR!V}XlGK& z${sjQc=i`m$X%?F*a&E7{juZ{<+xu7Qpv{TFm8AAV;o2N+q@od)AH6j_{%l1Vi*fm z_Ddzj?xWvEoIzMqGufFFG`1? z1p=19Sh=toRUA>-7k-P3GFX1el*3--rAIr{YV`zpM`J)8XjsMf zMCOUl7%>`l{EoU5SLn(pO$9rdOilzr4l${;1P6)q-+tK6U*4l|%Dp zDLA(SxfWJOHul*PuBiVGzqZLnQKuBOc@vR--n=wkoA15rY;U0^&V@P~qCwlQ&NUmkT0>%R2-fJ)UH(?1n0X+r=HvIkoT@Ip=#lfZAwwwkf}x3}L&>2lU5yC6K_YXG*Sjn9=Nq3FJP3cf3Wg%)nHd+3 zL9Mr-C~Yt3e5iJLI6s;9H2aGqI;J2J%@@3^ZcrUK)tseYGwdLP19Sw#*2$?CNHRa3 zyqonke$fcL{e-hPH%L19J>&IPL;lF7(k6$!;sG6SSiBJBnYaNT82-*Q zx61tOT7rMN@{kaJyKdjQM!c!;i2dJpumwSi-`bWRp(9s31Ch5YFo>+MGGiwZ%MpiV z)@dmbGI{LJLJEHJe^7S)B4L5P#G@9n9Y@v*(zy6_FG0Cjc|{k08JjA<3kg80EuDbH zCfC;?5i$XW9n*D#$Bi4&8vT;Y5IRy>rel7-sUWj$zpqXPw&<&^OxH6N{(7>(sLogA zSbq3F1bnA&D+V%SYfA%|Oy{^@#7DnvUMp7oH%AUa=&X!-*guJ_a>Fn_biCzoANBY5 zKa5UIi*01t6<%uee)ZlXfs=&st*&O1Gd79=j1MHs55%9$m-eWrp+)COOXp+c=H{jf zd;LbFa093gGy_0XqO|myl@C5eO?OUX`E@r%NhP1>G0ExtGE%Nu;29uwQ-3|(wHR(n zC4mK7js})+#^vh=;&I?(-yz5mjsj?Hz~z9Xrp~cCl7Ak%geLXe-XEh=$p75k-SGkl zDs%`;jxf#(0j}xCdjiWo$lyvT5+_(xA_3>aF%rpmcErE+yA>&;uzngczMF?#LCDkx zWI7+ZCw!QsCw{;*x(4YjC@QWFQB2*{2~#!sl8vo`d)wqygX7iybb8_&eAbTF(hL&V zu;CQdz`sO}xLUFQ3j#7SgZtPQ5dicefVpiDY4Pf%jrLQjoFUb_^E2T7768{u391K~ z(!MN>oB&{MFNK_UxYVRrCa&OuUF~v#qCoypBFz+! zXETefV|P8Q{PTFn*t=T+Jd%VL$oJ3-LH_Xb-L1r+{Qwv)%WK%W4AKr z!9rQVv(I*9o%(+S%Spl^fDD(jh_h_@98m zMf17sCLIGB?%$-LTJrxo$2c-(DlB4|_J_&hAGh;r54bK^!v$Hc(*3h$C{ zHbVT5R=VR4z)Jvglw^GQE^SJdn^1T>Hi3d271dJNYv^C<80+c|A#wtCNZIw8b*4I_ z2V&9~($GLPsnPuu%ck@O+S}*-Sjph-c&_)pyRub}(@J_BlEmv^GGTHZmY(X@8pe?Z zq*TV2vJ;fbEA1HVPuD~sI)jt08JN(Eiz|)(`DJbf%30$gN-F?Kbx4DsVmPX9u3{?@ z7Jz-1z#=*^OG?q}!t5EWshayR(Z})tX?p>DQnwUlu%Y)3Um^kjp6b7&6A(d@T&)lg zn<>M_a-(YK>@_UxE-QJZbiyhKO|2dtszsdxb>piUCPN){u%Cy%Y2v}-{~lmd+|36% z0u5iu^FRAAZcV|EnPfpCx(bX27!mJ zT0ORz%J-A3O}`W;?xB}$d7Fse-({C*bkvhEK^SQ9(elG*MT5nHvbA# zf4n}PCPG=nD#M4~PM7C21Z)pLFqZ%>(| z0rEKlLD{?R^StA;|5+|b=O>7mC@3zrlzPA2=mgBVED{oC!1pkwht3*TyN-3PNC7(FjE<>LgU*|kGuN_ zgbvYY33mjo00|5ucME7qAhEpIP3UF(Feq?^n3O`Xpf8xPh*Ul)bT>VHX7h|E>Q>mh z1DTIB3jkMbMcrZq4`UV!NYp0D3Jxwspyi}*l)w8U=!r4$he$`eWp#d>D7zAKbdIbY zf&GVu1bFooVw!c)?}q^rLAfTb9Pn8~69cBasW zE1wU851L%UBAr15~j z#uJb;lT)c~HWu+k$<#1c2W&PKjGO}g3XhoMdVaVvd>7Ez{Mvnu%r}e6$AOQ+YrE>Z zT=Eki8#@q$zvKbyClx;rW^u)%ghXc*?A|T^`O+U*^vgK=(j?75^tGsDBSNa&8cW< z4}_h(GtT2jvV}CMPKnl2WVfjhrYK;H<7+iqion9mVSx{3|IV5t6B6(M?Q_@z3#}>T zOK!*cy;2zR5QHwO-z(EIF=o?aIgmW$tm53h4bvIUt%GU{;=)^&UWFnJp5Pli^fr!z z-_BqX!@5;N)*=mM9XZ$w(;Fk<(a|$}CXD#=ml{hl%~8x1+J%kJeeFb)u6=ak#N>)+w;m=s7|(|X{?l+gJUkhx2`CC+ z6$oF0Ur*TQm8h;p3;xCEs{5%f%fP@OHZf6YpPu_>vbhh(8geFL4YPmAkeSMLo!u96*(eZydHV)*`_LUoG&>sOt@uUjpsd{V??|2SZtn zm{hNzCp!1w?UBm-?pQj6_J+d+&ETW)P@(uJ%P6nYo=Droe-mA|JK2)M>1z0qqCLN8 z*Z!dlh-8FwSvs#iI_Z=l@?W_@8Tt!M?{@WEfxXLL&PG9zC++p;5BRx2p@szDaCM~j zdot+n!bF2CC#b*12^o{yu5|@bgrugngOh7GgXe1qC0k}(P*H13OZcw@P@hy#h^~uT zf_8MI$kzNjjmdI1NpE?vseWHRx$W;#K?Bt!qKdZ{sK~QD2rk^r`>pglnG>j4Ul&6X zR6hzR^a<~LLD*YayY3{KSHo(3_4zlNSWmUE+2SqXmt)PC+uFehl~){VgDaR2%mSPe zaNv}Pl&<3kEfN9@t_-;S;mDbeLc;5UAYr{AI1L#9CH(-rWOS9<`+#1e3#%p*0rA); zd_$${mSp}}DKy91YAfXI#-(d7vi9C#{>35CaN}o;%KpHems*pdNTn)UbjOc*E0KRm z!`PcHq%i}XLTmpjNNJ>SllupT7`|(%aJF$*Alq@RX~}y4@(KZ5jM|W^pII*1a*hE; z+4F6UYAQXC4XzR6P~&-4Mn(k4>RAVY$xOLzDl=|Bbs=JwPq{4>$EE5RSAC_@giIS5 z#-aoGfj7rc{T|&Idj{wD+7kQQW`_g3Rf|nGZ-eauo<3Y9XMK(6O(!NGW3qQV2s<+j z$zhU`SAMsCzguOe5k#z`sKZX|T!OtXqLWne5b5=z-FQVDawcXq)H0KFvT-(lkIiXk z9?c;A+=_?!YnwP^RlaJ`=*_5=vYyFERIWr6A(ubmPWsI|A@D?C13SQ`UZhuH2Ht&B zOyZB^YX5mjhQR3w+v8x8>j8u&A_pNQ=f4@=y@Oix0Nsp$p!^*koG0YZ1(I2vET!rO?rLtMQrZ^5l72wo52pm8loerW5 z7UX}m@oGtayH06$3qCycF47J2tKc;(#Y6K-+pmDGj>Fl@!LP;I!;&2HvbmyoX57YgxbbB{i&h2|WubE-`K8XzorBt6F zv?_(EHPPeTwOjPQfkf0ES(-Z^S<1d282fIhQUU8d5@Ma^fgwV|7xvAuBmcSL%I<`G zgSEg*nExF)TA^yy4c|nw^OIxREIZYas@2n2m0|AJ@(PC5AiS$4sTUWLnw8AJo9Z7y zceYY!DtB0aSnRgx>+FV#lzuk=-Z@|qjA_-_ZAe4J?Eo97xV)Q?gMfl6~%w-fD8oTwFt^ za(1K9WvEgJZ?G%WQ|0~L{6hBTqU}X~>rBDu)1L#&$!z+I%*unt^RhwBW*7XTvd(XgtFjK#QG6dju9Pryf0Lr`$HKyuOm`2?^#p%vYT(+6zYS zhk8P$vW)x@I8=X+0Vq*@|BI}MQxKF;$1jjYSR0(u65PWgB4LnA6S_Zy0ZmDR?-Z9w8gvGQ6o#3!fq7h#Yy-?+RB=9UhNkW z9>tkU*D9l9i9vM^i@w5A_9`dr5v?gZjeJgLifj&KC^{gr=tXDMykK(fkrtW2 zlFI9$QVxc;t4l+pTlzZ90jN4iMXf|=(BcTl&AOe^qRA%^kiakv*WY0UxE_i!UAmrv zxH^iCb~=rG0gJZ2$rkEf(NTku2`Mvc1#!pgzSM_@UCTg8>D5PCHr|RrQI#V46^#dP zddG1h8D)%pC1Pzi>;o6)j@!-+3|nhzh0zlKF?x|VBmDZ08>PH;=Ur04T3rjbD2ERw zQTw&j!JpHRTu}ej1yHyV$VNV+9Qzrf|EP4TNwnnadh=wjLe6FgzpSj8%eQ`>HreQy z0)3!J;E7n!(~n1@@M3d}<@aUSxXdr@lxs~}UKNC6&4Da=kSXCv@(omm!a>O5Ad|La z*YW?|J%!*SI0S;;BnyTpNKt-{FvrQ4@7UU@*YPKTw$q?-etv#BKC=jZ=-)qL5)u*! z``6Oi%H>uj7+I2-PAGxrakU==Ku$@l?$S#ZD^T7^bR-(yLBKgzGpkp9deU-OTMr6B z#Vk8(?vygW9J#UD7OeVOYO-gt@nT0Bbqv zNLmBVyZ$F5QB4F&(U$Xhg~>P83*|os#q1;vlVrN4O@%jESt8LGRehR}ggeHlzp^a4 z;lT8If1Z)=Rd+gB)((=mv;1kYpgUvfX8-d`X|N*bfm!X=C2#NVlc$3Y@nF9pLA&Rg z&sJvqY_ni}l%_J_lq#9B_@+Ly)MP(#8C{x1P}lq~>vwd%1D%LRL#0o%x%#ZdL?m>g zlPQhoXV;9%%%r3sh+J5qZ-op zrB@~Dby!Pm(&PqI53w; z7o>mSRT;u4iZQtcKnC(HtPbc4YkWrOkjV$iA*UWtR&T)(bAGzJS@ zCeu+mK+r41{cR4)eb@}R>!}18cyvup*6R2fF%v0|HfHgAfvjtDjnKdiV^nouk?LrM zH$4V2EPcUv{?vi%);cIA$vfb^gP}NnRW{xjH3`2p~a@&xCi=g|vM|?~nGi{vF zEf2kvdeID|x)nMf*)ZZct&1G)bi~z1PC{GoU=PutktD+2g+BdrNwYhmkYo7|Oo#Am zAbm1dbuk2mAWs6$(xtR+Q9#H$n=9Lp@*@Ty^a@(Mrv#Da&wvKx8AeXnxrh zs!jrpRf%%NJVJjURrW2419-*T8hXh%81i8ys#UlIC~%q<;_0au zeHAVz%MJI17Mmcu3gUhJl%AYiq)SXpytcNM`<_U*Dee@49Ef&H_-1n;T7GlAQ8rO( zN0_2x;J00cN@i5gGyU{c@w>3szRPu;?G>1O*T1b^TcB+8xUq(0&jH;1*%z$cOxJY5 zN3on|j9oAuc%3Q#C((e!TX|_MW?;gwW_4ijB)lMks8{Bb2`6EvoDC+|_BL?NJvgUCndMx0 zhh+TGgtr=%En`xCY>-&!b+o8!jJV%h&GdjsW%Lq6nDQg~?Amm)cRNTh)EFgL_Q|QK zt`Vthk9~_n&6~1>t0cHJCZT`(QloA`euv|^r>v~(tIQvt{$yk5d1D5Vv$qfRbojkG zB0!25&2pUx-|Kjcv=Z4NcUrP@YQcV*o}Plj4~HDY>tB9K+hm&8 zsuT`vqHXKh|YR5}($b z96<>Prm0QKp-l$a49}E2C{2opI^RafW8_!sZ@dN$-Y?LkA5x1cX%MD(V6a`unhb7< zcm|6(QU*FZ#n6uKuMU1QxoA#5vkE@$!Q{fK zh)Ychilw8P@LZ;{02}+pSer)}A-*7OF!eNPW$pK`mP5sRd-7K{Wg5SK%U||DZLpvs z>>Who*!02>?8)B0Esfe%br!O10V< zSAr;}ko1*=%bj}pJpzP2VDSLr>nj0sGSdmbV7^3;;iLXJA#VU`kOS3V@%VzcQ2YEY zV=Il$H@)?%Fjdz<5%?`C?jO?c+h1v=(}z3rs<*lT$)OG9@JMJO#}1jf&kU1&y$jwV zKe?kBIolB5+kjrn2|tzP@YFYfArjE%i4C8-h;cp{s6>G(sjN+iotH=|XJ%j?$Y0C5 z?Qd6S^F7drg8F}UA8#+d5qWv!022kGp4lG9d_y{-?pWXrL44loX3H8M-PN^gOlDZx z9~CxP3l}^tccBM=kP6%5x4K``+iM9ck@$g6!s0|uf}TU+ZeDdMJ`Nrwp9?Yi^g%Yy zgcbkN<^a6i&V`Ru=HP;@THZaYD`@(ae=5VyK8oG35&_GEoZ&)%(}WaIRg1xZY47X2 zhG4g$d=XpRxb)eSGxmG%?Wbe8wP+c`YP~xRr+V7H5mz`2Xo`2@Jv{*JCO55js4^<3 z4-l=0tp!OcA4Cf5z{FbnbA5m`apW(X?`@5)XL*$Y*P#4c&kZ3d4JaF=hV}c2p%=;| zq@P4>+UsF}unBw{oEn+`sA~YXzS?y%qwkvpK)gugeKz-{~mJJa4EE)m&P=2F?4 z6K5hd4eI`n|0|?cqrPsB+>#9b*k~Nm;%=u+#+IAYqf=QDOf1-MaM049O2py5Y5xl_~m%)%C(H*RDwRvD^+p`7Oe10E)w7yGTz@T4+b%4?h9f*Ls zvCG&|h4Do$hB+EwY4Imyw^}J>Vse^wZ%Q4ht#Dw5=kwpaKA_MDIt_|iP6CX|F2$Tg zFv>W`>Iu^Y9q>5IZQnszq<+8Ax3U@@?wRsB;H3NF%w&a=ig+~e;dIZ{!ImuZ{3phR ze5A#3RE-23HcSCE)sGhs13ebJCqBAL)=j&qDb^mJ77V`ruVQyAoN<$HhM!e!>4?Sc^mcxi*uiY@UPZ5$~Y za$no4@PGQ*fQpghzY8kJQpwRqAlIJ!8bD3?iQAwD;WJPyQEUzee%R7Qqaq!PO8ZW4 z!Uu~#GccmG?+2S|6lIhO*G!Q7G2UcBOB*>zfSwS#SDE583kuz_iZS~tNuJ=14b32^4@JljL>%q?Lug5Hm#9SJ6dZe`jYk* zBz;d;PLi%kEEo#5+yrbPeq&;OwsxFRA9UdS7Nj~PM=fsXCz{FhL=ZTiGuS_VPWdy> z$ok={oStY-;cu(hCszHepdSS8ia7->?_8L`QHlrSsrIiOY+q&-6BO)6AhmGEAaKl< zQI^?Oo(T9d-S4qfA$r}yj`h=@8#LwoC^0(2poe0>m+vNqvE*=bb@jC<1Uj3^7n8Ug zd5n1dyUS-2prxxJN{%lnCp1!2wSf&m=}wgoSUZoPhp+wy7tgZUIn4sws)0x5wZs4I zi5ot9t-YJ|`^@g4^P7W>nC{bj4(T_$p2)V{sy2Fi*$%r;V<&80U6Q|E^o4}AJ)i?f zZ98Rrp5U)_+fa9KD{Tw!L({mNoyEQ^Q9N#ljL)SzVy9U1cDA+(WHPp)SUStQS>7O= z0z%YPp$W`XqQhE5`}TVO1TN7s#9%5W5&8*!%G1bs?3En_U%nux@;qQ5ZQyH@=`@gG zWTM}8LiA02AqE6cB_lJXa@?%3; zIRk~f>;+KN5UyiaxPK`>>9-oo5p&Nh{*pBz$;8GcAHl=cGc`MVGo!L*L`bJlAZQ`~ zx9l?<6JcU}{+b#LH?7g0Vie!JMHSE6mOg{Te(7(+Y9Y5D^%2Z_yZJ)rW8qS~H&z|U zQ5{TJg4t4h5Mia1`cf+6 za3r;O|A!k|mj8T$I2C97X}J1Z2m|s~VQ*{{qZ*%YvC%F+IG!rtPp$zDrRZOnvjt$k z7rK)0RapJ({e(Dr2@Q;Dr4}?|T3BMuGfC?b%ID1hx*&_~zu$GOPcF#>L4xADs#t!Z zH=wY5CE@&jLOS}by7hN^bfEfcxGuornYqUV)yl5&h6xhORvuhy=Vd8GnUr3@w%`|7 zvA!eKR$PUG? zN~9zTBKLG;-yYG9Ors-k-HI~*jW#U`W@zwXfY^>Hch$0Hw2)G)0~MoMR*e!l5MnkY zqJ%=kKa8qh{+xJ6)~BrTyzzG_i`!~JshxvQ5&d0o2Z&Y95mLk6%jJJ3(E#YYQ!u~h zYk;VvROpHpRrVdF!titLL}Uar5ZbgPH7IHjO$uKRq1V{N_(UW#X&|k!LMsk3IDEgR zIv&p+0t0J#VCt)^4P*6$-RFB+ZC^W)aOC+)E3m}=w$ernxa{dVXo8tusRsLsuybpt zvw&g;L%HCk++OX*GY8b4(Z@Z1OuPw7j3@uU7XZSwRn+j69q)31P?twsG~KjU@rGM3e9V>_DR;I9qXR<9iLFtudQKeU!h@di%=6^snHgF)jc1z~UT2ub8R7_bIuomHEEgt~-kW9%@=BTw*_#VQ z#Ht2-{JUhgSk{;9$fRscwiFI9iku=NnOKr446G1#PAEVucB{JbLX}3LU?k@fhkxw6_6Z&z@klR=7tc6^vMfEQSLpI?KZCaFPN8)}j{cZMr2kNJ|IzoX3+J zV-XT=f2yRU9+7uw>06`t6-bKUNi{O5|J+P2{teWUt16YMBl2_{udz!dUkhI$<+X)M z51uhnk=o1j=&nfT^nX^_K1B4{I$VCf;hQ^VLpl+(Qi$L*s2VlDkX#|5-b-Qj*S4)i zTMST_{??1YTDqR1>s_;sF$gf8Me&@z&2M1#+`aEv=+len94^!h`$G1!P#r)1SpjQ6 zDw!NE%%e;!dBBov->vAt65dQ-I=gW=Dhu7gQuwt4Tq0T=Hc~E|QqcJ=XmxS2H1_R; zt4)lJ{ko6<29EIN4O3l~&~ZWW7HGcVg?2D71|29&E)Zfp0d97Y98T9Ap0@F4OeUV| z*^2FNLexV0Y z|Jg2yM<*Vgc8#FB%Z*i1BHo;`##FxbdzeQ>DmvY!nE83no@0i*BZ<8yRW$qAE!jo7>y zSTbu$Hy^WTGH1t8U?+Kcl*O|`(56uymhsUSZ9*h9pf^l9P>72ZgV>?kY31P6|TEOS}?^~J3Nwe6F{EV zlZ4i5a2)|9W$+GLpFZ(md%NfY2gj@N&B#V1zFL%``UzwQ9#C@HS?2FTIq=ef z;AHoNJOk|!)3fzn+*a&ATZ&(Wf;8Qs%sY7W=6xenYo{iueTO@53!)1 zy&ODtb3>4XhQdpFflKq#&B`oQ)LI;sWqON16TO9T&ox&Kf?^LNeMh7(pS$6`PdiL^ zE)~_cj~#s}*-A}j%cn{`gQ;oJi%V9fO(uT-rYBU+hkBKT1|d-r$erGQGKwLS1Fh5NQ8kV*H@e7- zl;46}O>hl=t)@$HZqbKM>ww`o2Bp+*U^aZh0(C(e%OZz4zyBUL{I6gIAMp<{?o zSqD&`blj&v%n-=PA^HdLoQ2KEHGEF`?l5&!ztJpyOF9S4mC}J^O>5YRghC8GYnqE5 z*}hLifAB2*&T=Di1BOLv;0>ckfUfuS#OFnmfp@>!3`cr?>AjJu|%_fSdU;Ct1*D z&PN2Lj7<6ZjG6ITL#8()!p&PEZH%%X=}bVUc}JWxn_zTUKp)YeYE5OJHGt2m2jvZv z+^Z}+bt#4i1YcvwAi|C49@ZpbC#Z(7fy(mWo3g5xuOaQwZ z$NE>1@;&AF%l$!>(_K0k+YBK3IIlD)*k^D(z{&XdK+!0K6&{%s5mzZrQal{?o8adz zl^R^b&49!(C$4kNDB#kXDJ}!`-<}68-noD@r*zxty&r zy0Foxz{qU&WC=0?h@}IFlevm2_esYkB~PMxIZtWV+nXrlO>~zHIr|x{0bGk2y({kb zSy@=nfC#7Cg|>ZiZ4e?l?pUxf0Ao@ZtbZ;=uICIj?&2h)#03DXI8GHMc?1A5<_3?I zke!W@Hgj42OvIZtU|q2)+PH-IWa}$~r@CpS8N8dVczS!xuf04o?v zFY4_eYlJ!)jFP-jTdH|}EPQL$(k=!ehe_n7p31rNS+kJKv6d(B@6JIO9aLsuGdhiZ zP0re*)kQ4ey!v4o=V|N_37k|G@ z(Av#b6_sOOEPS382DP+{504%)NZ-MZpcIigE+E?6tbNDe_&bjJc?Vdf&F6<|-$S!k zX3Kc4zjqCBqMx|vol?tebN_r~869dKo{B?jBc-8v17GPGpE-_2xmoUgJPoAfwm`wy z8W)?!y-egT(kO3{T@^+6Ps6$Ld*{Nba?|>UY4P=20InD)9?}?P4R&=vzp_#=$mSDA zndZbfE81}c>^HY(h|zAnuV{nyzm6!$DDpEZ3t+4nd_5Omz}8>$lR_e!(q!#3i>}mQ zVx7a}B&2mdRoUXIdhbD@D zRkI-DJDC3n?$Y(q8>+rU{*$rXfYFClk7r6H>SlSUjo!CL#s!_bgpugq*{5bor_8%8 zA(W=2`q^}uswE3lkm5bu&7?nFJ^Ny}3@Rm{h*3;8U5vW|5r4}yRz&Ld&9kDPUR=E5 zNnXvdRkgQgSG!EtpP@2eQY+18bWrX&tZy<{;HaV;D_;-LpEcQHaOj#_3pzq+zA4>i zMmVC~Jmj{%zWO3)dhOJ1TwfOh*~iY#^(5;5=!fa5QXM`z%9Xhk|IL(HZ~G?s8t>WA zc1uzaWi%_X;gAz$rz#`2NOrN0OZLwoeC1B}cm*v9z5whoaHibuHp_gjLwu|0Vfff% z%PEW>pnUe4*BP}lQ69Q65jTQMfIVk)Y9~r{r*8UVn90A{iAC;;#og@Byt5O=hn0&! zqjTUc-jhV0UvvcOQ8A#NxRPE&MXHI7RjwOz0A?2Iv~O7Pv(Sa*gc?iMt3T>$1{OHD z(SLde>+l`dKxH{)ba9agxER0yJtSn@0<;pZWMYV=zxU&Jxsu&Mc>_E^ z2*rJHmkkRGcAp43Si>QM->HN}`6%@(n_k4H-iMVD4`v7uaaNiR6uw#T01TV;|9w%F);a0~-$aXw}2% z40W8F2krb_v<0-xRSV&VUJrI`t|%I1jK7`)P)-peB!>bMN)wAVM#^+0)=Oh*PD)9$ zQ`t~PstBv?%~A4J+WmiUP_RD7p|>kPohPix#8!B&R-3yl`q-BC1hs@ z)u$+)E&n7|YpSF+k$OEfF&yqZr2M&3GE2m#Z5R4Zi4GQDqYvKL8q!V@AriDV6uFCE zhRc_xe;-OLsGOQsx|?B&s4;W;sR6o&)d;%3Ul{}yaYC}{SFLVat{B;+r8_g|%}kPA zK2UF&Zgxoh(SdhIu;UQJ!obLlu(6q|h6WCwoje(Byk3X=+4Yddpb zDI?_brNd<7_oN5Ql*#qZ(?6%N!p1uTaC$Vx9fAF2Ij zAbj5xUK(`$4ke(xewMd z#=xF}85JL`fC}c{16^K`thRXY@jGcm?A2LMGhMCYUU8Q6d1{Lbt07#by4?>}I^8o5 z+c&X+L!Ol69HoKI+rc$CJi2#s;+TDZU6$-ZdW(-Lu>UR}w-R&gRQO6Xv>}|)fB5zI z;AZ%5*)b48Y&C z8`J#BMXc{I!otGRb3nRoosLEKf9$~fE46zzZN}6w_F~hXP>kQ9jc>%6BZ=PL_W|sD?D7-Np010HT|cL3$ZM1i+KHG{HX0< zJxkGnR;$rf%ALzx2ocu$okl}bEYC?~8Ox*c zoTa@h7}Tr?93J+RxsHgug)UN!GE8mt4KtcfyS)CVad4N}DF+Y8sLxy+(nMMB4v_ zUi)rbW1DWH#0yr9IoQ|;2<*jjwh@%lGUWyRW`6xZ<*m9yp#K*zFQ-XYrMf6dmCdtS zQo}?+YEj|-hlNU>=fUCIw2IfLoQ21r*Y5zpOJ5DFkpUS+Hi&}y{ z^2EEoP%H`X71Z~a&Z;Dh-)^t=engL_=P33k%f1&1kD28ZlME$2k2w(?&P{fuD5;jx zeq0G;>81ZRZtvi5CBf8f0(y*Ob4r4ZrFSQv^a&;zNWCAf4vKGpYG(2LLT+_esar4< z245jQ)>vuYh^Ykwx2L)rrsvs`B#&bnk1a*rggI1+eR7Qi^>lC3e@>B)*Qru;D>m&% zf5{SuZiw-~?}7nU2IbA^A?UMyYmd1lFE3>g_37+Vp)A=hE~5eT*0>JEsOX8cy}dpD zrLh)Q0SBHv?Jdh%laWgsTvD>#D}(d(Kt)Y~wGQMmrs=ev56qWY; zYH=&~-Qg;R6Ck3H^p#p~l27+p5#IWtLKGyKs`@^@ui5~}P~HO4YujwVLoCMxvVGH# z-XxcuD%G+j2|i57OZqgunx~gL+x&@U%Vdvq|Lf$Hi0P}P2S*s|%>J3bvl}Jnf>5gH z&EKf+h491l6Z*+)?%X9`9t<5V&@z>9pn?Sn)!#wcU(j25yw17H?hCK}=j`N&FGvEt zqe8LuiGHU1gR)UIR-XuoPHMCBMazOW+nOblJ`B~Cr|*{Zy77Yd^POp`jc3PJUx-HV z@UL(43hm2aXoMd-IXA=;j5fiW4r!p~z0|2;d}rL`gEx47;UUiE5&Dj!T}(!cY`#K- zU1Q%F3J;?!tp;uI{LHw%7}JYTj9spl9oBKvB9GpHFm5Vwy4+PWAkc#H_nCua{G$PS zn;xU!6Smu8QslRaQL}89?E;~1TB078 zpP~|Jj=@jVq;$eLJ=jW9WWDgAw~gWNbPCI^S5|_{d~H(D0(kk0uI^S%Vl<*b|I?+92>c|o;k;ZkT5yd*!B zzHFGM@(QbH_l4$IubrMGR@s}JUQQ@}nJ+CUY7WFsZ#z$RgOO=+&A}$}dQ2+*ru=rM z>SM4j3y9Z|NTDfET z6hr5v+8JYV2%tvW7?=}fXZGzDoFGoZ%qCoFZPMr(Lus<}IP{(`57KU0pKKLN4L7m} zI(8e@r<&*)$5+tE>0cOB{;blv-jg4;k%!8iByHzmUf}x@3@SQb$qU1q(;z>#G*cij z#I`UI+7+P(5?q8BehBYsv0_eTr+I1BMAkJH?jv{m1M?|{r)%f9CiO~A-;nu*_wHl_ zYn1}pXQ#=wgO$l#TohqR&<{{AyIQ{hI|s-MK-%b!Q{dF(iBFi9j9)O0G$DlU3^yG& z4A)yACNA>U3KvY`$ErBNmU|MY^j2fg7&zqHee>E%Az!(e{(bhW#v`1l5w%xJWX^R} z9-bTR>W%5{Er2nZveWU?fgJZ6a%Ty%YBziWk7;MsN2rO0LNX?0w?_XTWXYA&=uF*3 zt62}8u{fkbsv=>%!wvsfU$(LlI(cM^G)#53t}HN0qKX(3;u~*x45r$Px^BO}E1yda zp8h`Ex9=ozChlQuN30vgMF0rj0fq<<7k+1_?=T|yc+$`2P&6+dTzNDxeF|SG83z|l z>+zL!7N*KRM82l{KTN%4R94;h{VgHgh;)fG(k--1WRPQ~nVSx4>l z=pVD5mi9~WgHF$wf$#}L%`}3rfer+B5BhtrVmWhl^Nh@POccktP(1|-<&@$67xYAC z)2P>9Bh8Ih+U9o;>ItF)7}tkkkq13M!tA=#akjo4J1J;e#5w)Zdv#z4lpGm?UAEDmuio0AUi0iiFy$53qTMQb1?$W*Ne) z%!kM@tYM=4hFgBpOgJHiJMFaGs++4n!CEKZCX90#cZJ^bB_9;2;Xv*9)x|fnH4!EL zds4o*L>5eo<{=fQ_`sD5EeefFVuZ&MXP$)U^4zK##?zM+#hxsPH#-h5K)3QUAwB;f zy6l@8w{u*4wx7TYVY%~Yp1@x}MK!HFZpkkx2VXy|_OMo*{b0aRJDB4}Zg{CyeS4{E znD=^{B*H(%D2>;sG^In~Pl`W7GqYRb?#9q<1NmFWOW4t4o=wWKCdqgND>X@fZ>uz|eyNHR8IC^<(sQrbv~<_z`rzR- zI8u(p#ywne=w>S~MpeVVrPkoisAy0OZbY`4Bq^Dl1(JUx5$GQxw;dB3Fstn8xXZy) zBPMh^s@|%U>vbYe1|t^p-%3< z%_kkz;0&GEA0kAFX~>fVz(xCZfJJu)WT7BytxA*}q8gfKO4`8#J;)urTP1TNLRdP} zETwD*;Ct70@0*Se%V_!Z1}2~A2?*}>u~TbuZ0IO#{}p*KKU8$o&YB*#A`x&RZhCsq zuEUYOcaWFdJGC$}txcG8zvU4-UM`413{b*xzH#Cr|1j(yk1a{&RIjZ0Xv+JoT@&N# zA>Q-o{(1QW@_|uF zl^RdWznGbPAp7xg)iHzBN2vIc+5G|MZjY|hX1^8|ntifdO|L1K9yZ8wlB^K0~U znaY!$!g`5sYnh*fjVh`?>Rtu2+_^R(bzHPy5E8vuPma^J=iiUT|SX(nOdpB=9eXz0r+g%uIOG^j!<`gG4JA_i|Wb}EI;sKvu* zypKMr0RdaZrneSG&yV+M9OnLXtVE83%$pcRi-J%UpSzx>#LUTHk8)q(x&h8o6hv7I zWQp_Ev3^g726dnJmBFZ{@0oh9hz(Su3ZK}^@q!RhldM)8pI}=TtkPSj_oE> zZL21;6^XOS|~!kxMs~W=&N*&i#uh;9P-*!v3GPrn5^D z{w`e5gBqr{Pu}BjK4dBR*{nfjF$}9o7_DP&PUQ<+2oPepQ2Uo-5bmJ>3kSD!KAz>U zXPm{e%Abm_K^%aDZ!yn(zDXT($TU+o0-Sh3F%ab^rupD2$6 z;cQl#GS+J#s0Is#L_tK|KZQO5H8i7ci+>vc@^39U%Oir^mVZCuDJ&Q3i&MxL*W(CJ zSTiTML9}{(((XRMi?*$w&-wY|hL-BqZBc$ryBB6Kk3tNydQw(n;PxDJ@|Nn|b)W`FC86OB7OOhfxb7M077C2$pd zFJU;p9G*dtp}PQuDAf`Tv!a@wpn{P{^a3bVS3_~bASZM!)1eD0n%!R6aD~$H@D4d7e8>vd?e!iEEUxt^O(%zzL$3rjh^@@Dz+J!!#;}s>uz*nLOh$-^-6+b z+_)}Db2gELo5T$eOVu~_=$h%fbK=tqz&**M1ONJy+Dvj+h|o1=o+3gsFcRPceotTX zs&54mqQoLOpw7O{G(;8l`78VCEH2a7jelF1KK+l*o%aI@V6A zU0#~3)Hfbbkqv3NVy7PRutLGbtF@euLFPiBxme*y>b#)_4b~Re%fU9c(-`1*QSj=j z_SNpnU0zIigsyxolRT`|V7||H45VIoz~q63 zg^it!96YWL-qo;ojw(axYPRpLQ&qzPl~od@KTYgPRYOE@75?n?bTfBCO900fkwv!) z`D3!L2WYMz%~(a&2%z849V7c; z8=y{lY${BMKLs{eFXK1Ml;YNtJfbq(@aP*tCVQ5TpNF zPck_tOA|i-v1>L0l!msQ#R>G0d(XW3jmfR-Hhm(Gw+;|6NHOxLwn7YgA#i8YtweX= zLZbOF|W6x%+U<^RpsJujPA4-z?6v;yIOfr~J zfwhGjD?n$~df0*MBl$eB7SLR$*qyFaJxqMr{k#jYI{(h9#mB_N#N+*JS$6r-{qi7q z^tb)CO(r^NYL~RIKMbAX{d=)6E}7m*<@izfFJff$0!VCvUxV&P(@^oQj^3jna_TRt z%%G##)bA7}U9}#8x~<0pq4secDAF;3LiR;HG%U3j>=P}-z{w~hO**dv zMtNEC?Q0wL-|M>Q)H+%XPuJI>3a1ewn$FS({`}FIEv(AhQbSnw7b?cHc&yK4w@P>( z{x_SbAgz0gnuq(G)@qt7=2Ot!Xg-_5)_`zUws26>;`@!Y_}uNE~x>?XUf z0;QILfdPJu3>hP%JfYQNyDUXCa>nEe#FW{!0`%Dh*RZ8O^g7OYf{^vSzrTfQp4Zzx zr%e85bm9Qwz}FaJhmXi?lujP4 zYQZqpPeGgwiN9lIWtBmhYXfeUbKp?HY2|&bV_E7{h#PIKzVV4ZP-^8jcfbI;{y^X| zR2mDpNV!B> z#bq?*RVj2w8bgQ3=t>lFyI5U4dow&a=SY}iB}ne|S#JM09#%E$^L#5J-TPGd1K;JT zzM;X0*QyUBz(DZ1^kHnD3c(_@2i{Z7#Q*M+2BK<)(-0%AiEN?Kk2vzP^P5&rocl7d z{{*BO27Vy`*bz|QZD#ifBLWk>iyI@eovLSQT9mTJRL11_$1)GX-D%4q0892&y)-s- zlN{N3&?2)S2Z>R&xFr6=iP#ZRPM{ONEY~Dn-vB%;qrVh*&x8@B*`%-^{!?+xCdtPywypiN zcY`u8(G^Pb62(PewSNfBkLSzDM`oXCsjCLRAd~=HTay%-*|aGGv!vnZX1-(4>u*6{ zHQU_DvDg^!SWE=^Y7)N;{iI|xtz{2^v_pb>q7iC$_8?n>15`1kBC0nP9ZD!HU(ba<%X7Z?nRNQb4g#qv0? z{{)M!>k;=UKG#qJXumXxk|84xbw>m*7f@Ufa-$`ZAknoo{B92phCTYztU%v{^B}c? z1O@5lQ1&FfB4A}Fvt)KG+)Lr-WZYBkh^D2n1%3-jFlY3&Z&9lDQkzP(Zfn0inx{;! z%#UTCp-C9}@y<%5XAwcQ&1q;Z7HG$;W6@rW?(g-P)K(WAcc)TM9syw4s`Wub-=kPA zr+P*zo7)Nc4VF<$4;bK7Z8pn(ypebSma?j7c26OE{hsK2)R|!aDhw&v2yx{DrJgF5 z#~@M6*>~_xUdtG7HQNpuz}y%(3QEu3g;$O14S`KZXaYkDL>@>G9LBEzhwYM7yV8o< zL{#He#Z1p5pxg8gYKGFm!NV5bU zURA7K-<;7kcuTQZ+TM0$(;I6QS))SSBKsFad3LZ!C=mU3-E$<0;=#@6 zn9IP>OqbCU{V2GtST}&aPL+H6wCTUMNXP?`nK}XvwNS+}k3}SWg(P%kv7S15)$0Yo z8b7mRi{jlk`zQ{OvD;X;uatt9go2q_(d))C4`N{h9OKq^V+Jc;)WgV7Uhft6bJ*yE zL4$3*qNZyG{TiMBE>1Ou=~cs4CS@DkBJhLy1F7O_#F)7OEm>cYQK;C@#Q6#hCYC0< z8+)FM9tKU`W1h+%G*&Ud9pQikZJm6D-;?|s4D&bdcXOHUUoFx@c6=ImC`Nr&zBqjk z&zTk_?Uj^_Yy2n^V*&ivTt}ZCBg&Myo=c znU;fiA6`Q5)1&Bwl?U{rg^+wwpDU&Ip5DIrapA(S*!37vzekqI9R3PXR(hS05k~(X zWH#YKc4@zS2hLMqn2i+y64H-}#QRJMMVT&IaLAl&?PTSQPW*3rca84x7fz2Of;J^* zE%LQ9*t_Of*0W_KuNm#{)~>}T8lQv>_cj7wBRA$DqDrK|?Y&)^*ohjhKQppp_oT*e zb=kN>%uXG$kP!p{QyK{jWA0mcTC&uy=!l!P@gbnRD3=p@Zi*SajOa#Ff*| z4o**FcR^cvKGd!MoO#yYi1-cjdigqpkR<*V97L#Rnz@Ro;Q@QyMV>??RGciY6*r0m zZnpC~r|xp*hUpRR?+YF3s~aXQ`CN2*pbh$^zdDK+cJ<%siVUhEejez#?23E3DI)o=so zkZl^}{pU7lPvt49OcW)#o&RDSW<_=@kAAfBN~k8j05&zPVP#ywA(BH7(BZVC1#DfYXFL$RF?vG+BQzkGZ1-xqcfy`;|Pjk())-wex z(O02l&q;)ck@~AVCCxy365ac?@`Q#)R6`50G4V|`i`TW}e>2eI4 z;m4=Uo@0EO?gwu$35wq2n`DDhq9-0fwRLwKDbj-!kmz$5eaGF`17P6h-EEs(ZPl(v zgO?3%o(uYspMeI|n&Mk|wHfl0a(3bnjmGzu3~>hnR1Q#%C?jl98Q<(uMR{7kLMh%} z*`+k;#X^=qMQ?SME-!!DX7ZaRd8Gfi4G-CEgmAD82QUW1HElfruv-mFdxE!k{Ix~< z>B>VERrkx@bYb{CqXg3(I>5~@7a>APk}Gb5)qsZT8(6l~{G?gnj}`xsv)@{y?J)3Y zn>g)(x!NI5lfx>>RT@u_JKSa;zL$an!Q~8KyT7ahk)h|wxK<(+tbIgJ4VCE*ySS84 zVZmQrxbc!t%-djhv^&(GYjN zR8Y_cRRH}msT%UC$1btY^Hb1C&Bf0VB=p7z1@?3~&40(rX%ps;CvM%pBKy_6rT6s_ zRCxldFSBrR5IgA^ayo#soOnlygF~iiA-GLx-RbZ66oj$KIcPUiVS-W^5f!DXyo$ae zm-@x;)`&Z3r3u$uiLuFeZSCRc@#ea_nxZOC6< zJn~zEI+}~YeJ8~yNLvHQbNTrBzi+TmghFCe)l6F*L9o*p2T|yi($){-I?HKH3r01- zYlo{b*O!w11m*Hmb288B*SGBrT6knA9STK?A$)RX;U-|3hHTUIHRBT-K`Mn3gCZ%m z(VB>xM!GyExwIx@L>1G8$xavq+I{hp?G>QKvy}MbVGMp75Um5@K8TCkuh1wAcPzZ^ z+ro^|@*|9DLu!bu``~OM*#B=##<IBx5*^Y!V2zFjM%)`0zK6bmTGN#gjx-p29N>epPI#4z?RkChnsJL!t6oeHhQn$G z*6hi*K=&COnO#w5@_Og4q$I zijZK2jeE(vLq7zHMQ!e&3i75En(0p9|FxC>9R*IWc?R9SZJ}Bo8ojnYImtW%)kHIn z+XwRi?%=9Vpz(s2IK{kx^nID#yUv;(nuIu-ne_1V6V<%;cg+3kKNMRj*LPD3(%gp| zP=Yl!s2na51kUz0u+2JjpIv#%mJnveOGt$2!ZClyXh(4AX9s=QUp&Z079sUOtBJu! zVGi5zTh!Pn#jkL|_u8#TW0i$e=x;_Fm@= z3c$Zi!!6$0bi;#I${RkQ>{EYumcp z%xK$9XId|$dic(KOGevq)m<}-Z50P?h zIEQ&*t{KhU(>vpMm>|Q-6N`Co_?qUj1mT+tuluYo{0{$fp!bsFG#wUEhf#zaKSkiRhS52R#gM+5MkH47$zx!LoBr44q~#<^m*{}4WMKw=4v zxCV>kewPfwY`C_GmU%tkj#cV3%eR+!@R81!9Y`^$x%$#7L4`}eAX{1xPt1r~V2Xvl zt4&F59HkVhc^_?Hk0RRw&PPHkT(+$vX>1>m%oQl=m46wk-%D=llZ53R731;Sy^ewz zHW?Dt6=yS=+#8YV_Tn=~oib39eX-~L#w4wgi_h|95+%50<;>by=o5B}t?>K`Y^7Eg ziBnmJ8|Z-I#60_Wz5DU7wDk{k5-rQj=!!r^3X09&yioHJ5aiHjeP1!LU`|e&>?!dh zMsht{pF0lTuEs|8Dj1>DP4Ma3d}eTVB3HzO=CsMuqE1XpE5*Uk8tz-tZn7-X=+W_P zo=x7UdZ0hN4&H}Fvml!1Xk$gQ(A{qW1ae-eR9D#|wGU7PgO#7eDWcaQl@gtEq4^|d z?}o8|Z?--Q+go{0%(_M4G+sZV($&MIJhq7Vk(QR$#yJNiHvi`x>gkFh3{Z*^9P)95jwVw&e0@g(WR~JL`?guU9!Tt?F~t0# zm$;J#y?x9y^E(G1G^%Sd8GS2^j#5ZC4J3&PEd=Jr-+;YS7PsYOwfn^t*}cu<4PPT^ z^x(K3qnI`$#!ZRR60@lNyB*s8@#3)`-u-eDWH-mJ&ph3UP%(O)%0Oe-^rpH(_sRYtY8i>$;UCdg$hW`wOol(4Tdxh5;Qz78#b z_?8kbq%h`QK@JYJFd`DrYW8Za9KJ}N+px-OT{(zYa31wHnPdoKj29ilOXJhDW*TN-tKX&QmD{qK+&&Z6>r4IgXfd<3vIfje z!ceEFScYlP@|^)phIZ1xvn@Tw0hy0Km?_V=&la_7pE>%UG5dT#qO{3z%?Tk(?R7tS-y7^SMv zUrfLmg`@ZY81sCJvH1FvTj+9%)n{mDdvBAzO-xJ-E-p8;DN@*2*C{=PBgL_|fxI;N zx5ev{$)qxNvKN*{GfkIlr-acio|E&HT0x-CqC;r4a9!j9Yq&MWW;i0>8%UcN@3C)M5PmAPXT;8PIkAwj6r z?*yppNk+G&en&g&DAS=NJjNqdj=(+_&~f0$sFmHUk84u`#(6)@ww;R#oIi)$|i{uPg8@#2tSSLLQ;Nkgjf){J!Bxdi~qd zrXVY|LyVx0@FptDR3Dg!AK5@PLV~~@?4v`QbZ%E>!KD?+kuuCZ*aiZ z?&6RFIR3m2^U*t*M{AOuDp|=M6O&8o$_{4~OZMZ4-m6_@e|-}cKYN4(!R+ui+e*lu zw(p@MJj{>6k~X8+_i~tvY7G4MUu0?6TSNSBUyXb6U{R`-n{+{4%KqPr7V;`2>Nl?| zgU`AaDcK8c@+;iqX8S)#^`;~b;6`d6)LjH%)Z##2{IH^T6B$w>hkh(DlLvPlsaGzW z7YEp5X7Z~j-AY9^NC?92UB2~5;*`tp3a39LyUBgu)Y{`+Czi_}U7emwYnG;c@d!3j zH8|kgX)fr)p6|TSu7Z`5ISL6)9~C6L+TxbWWL;k1ip^4I#f$OWs>^9Oz~Ls38TqxR30 z((a{+7)~VaS~OJ=wh*Gcr0|_)%Cr6N73tPKsv@?ES;ksCze2$)?lAu)s7}chhzx5G z5goPC0ATY;(j(u0mt8^$K!Ors)(gOPm7_z z*gu56tPV-5Mq!gVo66;fQ8AzNX8|}@d-_FLl_ghGzio_Knq3XWnw$`nhDYB`0RyB& zWjlr{X-g9;Q`2tnku{0=>*s*1RcXnf+PM10ObVfMt`u=zfCS+fUEx}2$oc?V9_o*0 zq1LbFS!Dzbn1&E!jK0S<4)KN*#YDNM+QHA+?$9j`T=aPc>-rNaN(< zx53gTl_dBeXpn>K{1UHt#ZvgFL@9hMk3FyPfXwjhR{VncR~@?mCT-bB_=7zD)TShb zbtZm3Ka319bie*S52#)tXC5=?%3aU>8OlbxU-KzpRvIP^_3w4SgPsTDaPvbqsUbHN zSk1ggkaITxp_FM%=zH!{f^@aABmq>sv++0Mxjbn3<-yuBF$}I*ug%TeT0ifn8~o7D zwd$?byUL)Qlicn`JYyxdUWyDKq51n!WAwF|n>YdOogyzy8#yGmwR#eUwa3+r)^=VjFHv zWlcQ9I6FV@1x3E=`b9FmMqmg(?Z_&qlWe9g5R~s_8{!hPPf-gKOoXVrD~5@q#Ve7# z*sqY(^W$jK8Z>G7{z<&+`vc@lJBE?@OcH-_KY#lDcMffqywOYm! z4&Bpe8RNVqVa{M2Lt@TZh4}#|BBXT5A{LhXeDk%;`7|oUFv`!!F1z(tuY_JniwUdp z2VY%FZY1!eJ^8fxGQE_EK#%?$9pNPBcSTO|t?saNkRKZGV;ZO)PY{($cLM`foE7wa zZE%Y-sp3*nLO~O%zr7e~HBP+@wxjbh{o6M>C0g(vgd&2g4Rwe?sX9QYb>wKiyL0yj z5ubS$(XPL=n`}KM#8LHzjAs^U#&cLPskT44T%v2-Ta;rs-&pfC^S}=UY+`=n=imwd zXVDlDfYMv)g_K10NsD=bvu{uMC9`q#o#9A$Z{)8oC5elZ*5S zqh2|#SnA(QmqF|H&>nCxIVJN7x;HtwF!AFsN)ZCU2qBPg`_^J2BLu(|ttg_=FG4a6 zR^!uVn$JimN$O3bS<#@AihR2e#OH63F-n|s`(D_wBwQ^wN)lO^M*J21a5cT(G2e8} z4AMp1Fr3}I1E9^%mt%zZq7qqwg8oq9A)Ud@-C$gXpx)9yKdipQfO28kk?%lpj$t(On|^&_T^`x3T%__A9XnITT_Pp`Fkef+`|D2L^(XyDxo#FC?^`IY-7*GY4@$=uIeI%y*|K9fh7>H z2PzK)s6S*qeZR(I3pO0ZwX>;u@dWBgb(7ub(5uew(k#x8PUkpx(8Ez|Lf9-KUx>?t zZhZdb;e6n+x2-0zTrHpsJZ-Ti%aM9Ugip4pvV8G%zxc{ZV1CpbK>F{q(Px?aN9a$1 zaCruVp68=(BurWzEDJ|qeE|NGIn#J5SlgtYiI;DG_6AHfMe;gefph?v9KP1pG7k(4 zSW-aPF8_R_eO~jiB8pBZY2KZfj*l>xiFz~N+&?a}mA+dLF28Z@E1AaQVZ&nVTF1l6 zs(GI#TBD%HgcmtCB>%`L@ij`Ur{?!_LiO>{gRVCPM#R^&yXDWY_t5+4T0cfUoTYi7 zDN?!o;0R=3P@Pm$X-HVz>P-3Hf=mg`jweTjo#FM@TdJ_BVfHqgK6UMy)tlJe_2?_} zZAA_{|Lxdg2h?&B?%^o)cY^$>S|g3=tUJU=PFr!Y9zBhl?OVTXyD~xDMqM%D)QZ>) zRD!ei+jrLVP$d_QOB(GoQ&tN^e_E7GOwG*Jfx@Pij$7X)?g#^#LoP4D_pgonTW1e) z#jmiXao6K)Q)|GY*9-7lpc@n2*wfQfpiyQf7JLyUUfTK9V4}azJ&_f|zj8uJ>Votu zri|44^&m}#@Y9N@CQRm%A;S$Fdc&gvl&~n%vt5h9hUkM6d-k{V~Dw7#@NyXxEAp&)X6UBz>YP6!R`Cl zLLw^~8=B1ijkm=bCR=zsReJB%vOBPAip+lG-FxDypnu$9E~Bg!HMt0vj?6rWz_B(k zE%9_Fx4Lw<6*=)QYmT%l)?6>lSaX;Vo$0icbuYzskkU;fYivYx*o#y=mopkkeq40M zZCdjSnQUhbOm}U5_+Hj&@h|cyLREyqe*@pOBvA-w;6IIGv3hKh?^9IT#u9&|8q&f~ zA2FI!5Q=Izuwc|^#D)egG5Dca(pWHJWYF)x13Jzibr0K>$0fCixUivjTjU&d9i|;E z+)xy|QNfwFQo&yD-`S`BgP9dTO}EaKTAEkphA&~p&<(=oYJ7suDMN+Qyv+XVnjE*{h*z6R(6C1sr6@xpgbpj zYu*$pYoEw_WBDMVCS}qy?J~>jTBBE3rM!7UOqg^V*t2-(xpsbAuo^j37I{#f%2W>h zd)IMKj6~N;wzW~jG+0m|f+t)cHdew*;)||Kd+*srEx9M#WMfQEi?A;@(MM8Qb^F0= zy83~CKRvjc@!`;IVvCY(;@{#%x0p|DFkbR*=YI@apNbVtWc$b>XbtoBgPp%qd@omv z{-Pi!`;RGVL5~a5MTG>45Frli+UJjDJ7c5U!TwJefGF~sYhE&w#RTpjPy&Ie^oZUs z-ACq&=T`&XM9AI4J{OnMNyo`8?+MIP701)CzkWEK7#JutKfZTjqkV}L+!j?OKWtXt zb=%FKC9c`m{PKp{(0(l_>)NH3i~_6hq{(n{)#9Dj_Dky>`rt?GcQRKwTG0&~J|kXW z7YYV_mD-(wwuO?{s)$wk^uey^^D7M%+?| ztsPS7Fs=G`cDM(>(8B*u^Q646k77}`HRcD#2>p5NvW~KgxMFVm&mv*$tdH@bxi|EJ znw?7;C~Vt>{cB6_an4G$=MHF@Q?d0Y`g>r$XFrxb)=3cse^et8o$^tfDd<*-ZMx~UjU`T|$i znvB=4Wa=1mO=WY*CG}Y?Kfl~ocp09_x}V{3QQDHI8$Fwls+}-}B(qB9!tNMz`rdT*fEnE3FYLJeFzy zEP8l2nqU01HrIxqkm4Pa9lg{ad87C|i-e}J!lR$@>$3wi`n9t}y78{BTBuh$hP;2}9Uz)6|Rf~Uhtb1>o=Ss788A@;(@EUEjAe$9*f&qfmc8~2qI%DZY)KqnaSY9JX}z@ zR97aKS^j!@c($)<7uGyT{()51swCA$FqwIg28Hqy!f!h8e%M}5{%Eny5CVFh^Af=) zL@*ob2gBF^fV#q%H$Aa|KPfoq39cxr7n93jMX%U2N$%UUW%xAlvWanf0*#l1Amz`x zR?1~btNxAtykgPaeo3TgHv{A0E7DhQ7xMbj+U>20!Gyq4@rpV{4>C1Jn!d1*cOKK9 z@0nuc3=n?P1Q_^DG#esS5UN|2(xE6lbS1$bMM%m$O>iel z&kd?lHadOGD2+k4*9^?2Yd$ize=AP|Lv!j#fR?Ld@g|sMzK+0VDKRBXg<6690}rYP z<&7U^$>$fVGsot&-~$8cfVJ85eyJrd`A)V?(8SA{pg&0dnB1n~F>z2hrPo|IcRXsz z*A1V`u;|}UEl+kFX34NDh|Dp@g4jNZdnuCPQ29L|+Do*-$j9Pf;RHQyco4?N3hdYa2s`JF9el_RGU~M&?xM#+a537YWm?$^Y=Cv zmD3fToY@C3L z@7_?>eNJ9{Jn-H!ua9_&hDkqHQnCPN;g=`6=$TV7S$ZqBY}fCyJ`x?H-zqS1x0l)j zN3(2wyd2H>=r_WJNbkzk=wAlLQq4jd)|$p5QL$RKx#nygby?q&8@j=kHeLfIzcJ91 zx~~PmH%_ZBy=TC83SUVGXjH31xcj4Ei_`9e zp;7)08PBtk7rX8^KDgXe)DCeR#ur#mw6<1+#XO5L7ey+R@lnZVg@e&tzL;>T@>Bvk zsNpc6)nwNI8*jQ;hlKDPkATZw_dD*dP6M%(ds)Y-5|JmoIwj{G;b-ooh{AHs6jFQ5 zp?E#Mn#od)H5kR~NyX9?{KH(mHciK4v&HKup;?dS2ge_KxjrMC<;EbgTtxKS&MsEzRi-z`%(o4 z-W$D=oyq`TL_b zON&t&oMUz)jD{|e@Xfu)eooBvaq0a%+>J~->yfcnKP}?us7q*1JqZ>n#wg7*X;H5J zkPURP@4;9cUM`0Z9|{WU4vde!0{OG{Tr@3##njh_YvKU6ohg4m`L><7lq3gJ(4>ER zX%VXunx3%s@)>Prwgf6RN<^slU4fM!64oYmyg6M;);BMwZ!c+e0-@uo_kjz$CPS-3 z6^hf2;~D(OL!V^hb!BFeqAhLs$t?;fU2wp1+JvY%NXO94`{3p^_m|qHI$ca&mDpte zeh2h2zFeXd!;>q%3-CH=g@?ee98Z>c zjvf{_oi2{=neB2!=rzP(cB!S3i+GIoTP)2u68ax@yCdzl(m7 zr`lwOxNex6m8p!!U_r`TSohPh1MloPNd1}()wbGnwR-!izt{aek%pu@icnFv44mTk zj`<>OLsjzFCFWn$?Y*TUq$Ue&jZ;y@4ijRZmDR2O@@_qc?+*7_{Na;(q){iV*Z(lv zA6-&Te(T&NtTR(&m6ff?IsrP-|25Kj`YHUx_W@(K336$XkbC$4T^F}Ynhq^?9XvFO zYmqV;GI{sKD7};%11-ZeDmi*||09nI%a4z!vbXdvTF)<-7<+E68Lf1;47iRiCRELY z-u$05pPnnyo~@SWOo*PSV=?5?1#u~S)PxF=oKWxtU@l8yKWuO`*y{LuoL^OMH&cDe zkeW=#3GD!1kRcoF-QR&Pyt|DPQDC~8;KD1o@^^G;dWZVb2?ZK!e(yh@k|b=4@h#9L zo9xP~jhT&L+2sN8&XM-MsRk2@hp%$)I0|!l6y53Da!zewS=|oB1#v>{xCV2hYwIb{ zI~7ag6~VVQpe)zsgjw6}k!9xjQ=pn3K#sSV>Y~nRYmX+kNM+_-rjeZLHk%aEIPvwz z{#%qQ)$d!Xh1wJv3h8*hK>2#$b3yulhuAeY>=3#Zcj_u@1kFWHk1{@c(BzSTH2Vs< zBtp0EV0phBA=rZ#?$_%|tGw&e)$XLP^x}O+0%_0lBCv9Mv6Zf)gpEA#0x-kmBJ|gp zbvCK^i~+CfpHk-@)TYB%T(DFO2-a)iGBU#mH&(L3O|LT&C$Ls1n;+$3Dsr9XlJcY9 zmG~upeS8m5DqfR%j29|6*cc z6;R$LCM28zvr7Tc#V(Am?siG1t#=YklZy8iM0|+NVn#B0o)=^`Pg4sAEk8BnUaCAkD>`)10Wk>j>{J0w{n@`uU}Kips8i( z$sUfZ+^7`KMBPJy3}?p!#yZDQl@LiJ~>}w;O80!dFw!<77F??$Kh`q z2WZE=YFMniEVMP$L}LlQy84_wjEm6|Q2^GRV=UmjJ6<8gPfx@~Ktk$+=(3)up zxIJVjH+E{?AlKlTAJi_hzpvo#imQgA!=***oq}*ONh92non(Ww* z4Kq`T+mXeOZvPP1@Eg)0`^|wT!t|KFHyn7aSKB{}Csg^YBBJbAu=c#Odj2ZC^oFR%Jrmh1Z)K1<~;XcVbaU zld0*qNF;&yE@8U2>|T#Qf4jWz>&^}j`IXb3V!w`Xp|libJ;-AxNtD1=x@IZ1^(3w# zQx-UcV@<~?(@E&namZ>;zSaWE>vmG9=KX7C=4ZYS{BX7<$E2;nl7J6KJrGOSW((PU zXMM@2LRTK5hRw=;Wm5>Ko+=C7`GyEK> av+5$&yqIm)uSV$Qbn$2devIv)rZ9mxxiGFfr^N_foZCK=r<&?4AqbC{M7GXMb|hD^CYrZhM91rxzC3ZfYe{vCgE?kBR*;bK zOc`Zy2}+G6h4$`@*vi1k0YiLQR{_cW@7qYE9|VyhuXK6rD;&b^yA9;vFR0>xpjvU- zhvr@kB#nVUT$~TY+joCYeRuuYl4yAdz(eC%B2K>N<_q{^-;SD}pWouR4h^BpbRzu* zPBN_BWAiKFIHa#A7Y?ZXRg@$f%pX5VvuK`mTMjs^1n7-;Iqj%Z1VZZ++S9V}x$K~z zk?@s@O33nvefjTNtbN%5do*e9NZ9qCtg(qDYVY+Z-d=!U3dlfO-zeC_xOJMnU+M3WGMB-!=E(M}W3lMpfE z!@&M&=_^M%=r;{_z&;rc5O5n{%Gn2;t&zal%Yy?`{2Wl6OWiZ*_lhd|E5n;MtG+;K zWL3Rp4A^8v-X7~LmU%2u$e6a&K%I^5@D_ zBqIaDZ!2NB|L<_t^ZI=S#w@7ktiAbeIA6WJkpGh#3UCBMsszW!Cp@?AgWN7B4@?bi zWYFNp4E?S%nFgAP$3ue`5jnXin28fl6(yOd#eFGGJ^rDD?9jbt{Q)i+d^!I14~9vyF(4w;)-I&7w%^D;pp+I(K|Vq$Ys zguW{H4iu=7xJ>i;F|m9XKU{H$k4b|F-{m6o42%?F9@{JOMw@SK?cf&OvD&TDn%TU(bta|rvDE^MFK z=M?DWud=31s6tAJcba%7#KdG)t?a+c%>p}*L4X7SvKBfBxzEQ;yB#)KPq>y+Dj+N0CjC*{7?%@8y zTEJBy*0@ZBv#03{QldRyfBg8deGA641}sD(d4aHZ*?#~-9w`TPwn_(ALGXzk3?4p{ zTkl}2^z1=mH5_weeBTw4Tc+LL-+wjQ+2i^XuuIKjf#&UEW-XG(902el@5snr0W|=$ z#%GXD90UAdZv+NT{3e0C@il3+P@4>o%Q;%_Z$YuXJQDLHQ>n5lU^n6A>h13@oHf~k z0gohDNF3qJ>m~?Y;3tpCHcvOuKO-O_(iX7(csB|TE&cO$IsiUYh8lNqwZAuO*#G{nM-{9RyYX9#EUdNGDS53m!pE4o&udwFC5R&)*;$uMkCRtWI%_ z_YIHtET;~?D@w=Il0KIj`QS6IzFB!iv4#ELWeg(x^paT5zu*jIQywpK-0WI?I&t${ z01C+k9?K9xx1C%j{SE|Hqmvb21w?zU0`uylWFb2>!hH@e;tp^P2muiQg*gFOfChd~ z-_a6{0)EXT4bXiFIS{DeT)No{yIDDDap%MxIUlR_)quOPe?*~A{59Qp*onYA%jl#i zM_@rC2SPYx9(5t*Fd3|U=quF5mKN?0cS3AsU&4tXdB2&zetHSuhezGg33!gCt$ zz;dA~h43*D-f%V;(1bU1zs-uhFXy$8K*u#LPEB!zvnx_|*7|Rq&7(sA;oQiEW>({D zg#o>T4Kqvpx6488r(J~9a@1#kM^9xwj$7TRB!i6VPqI5~S1Le)=?0zPVt(qTIK%{N zANWc`7oV!ZZ0s#aIz|T#zJ^t`;W3_Y9iots!vrIxxye8{3FCoUM?4#?xI_)ld3@{} zd?ZmNjFFG+Uk#{u)WEI9LS*)Sgh+P1!&f;LY)(#2+yTV3#x49giboA--Uh+z8w|kp zVq#*;XGD^Gh9E)f_jfN`Zyz5P&nrvGtb*|oM9A$xbZC>)LU=>{^Sg~k2WXUm2ri5j zpbRJa_@(lWyy-=N4f9;uYLwZ8txsV)Q_J~oGVrQX%hSQ?1xVvtm0_D2P5u*8h~)9X zg8ecyoy{UJCmX_t&`xwcNa~`RbR0y)rb@cUg3w}rf*|vvV}*WzA~h43QbJp9v7%@? z{f)MRD)?~J(3S%PA)j{Emtl)0(_L2m(mOF)LVL>9Dj?D~K8WpNx^S2y_s?U@*jv-% zPZIyHy{~MjvWeQIyE_GGNu@#QmPTnQ=|;LcL_)fxQMyyQq#H?T5Rj7Y*k`tnzUTdL ze!}sSB75I+&#YN_t!sJTc>^8^llT&77i-4Nnm) z5~^m1PTffZWv8WhYvv=tu*$vJ_4w>J!tH+U-X7%EMSu~Aa+$659Z)`2s1GL>M)rNL z?l!iw{ASj&#zY`3l0tI}mD*YL|DJ&B}z=hdVFvq*BbJ@m+a%qh zP*UuIeNG9%{~%9;^8V5jk)&K_aM(hcQ2vJs_n{A$Qh8D>dmoW6r`*iCYn_dPMEK$|2Aq#QVR>~dktPKb;f)H0SD1d?HLsqduAjFMw4|-y*BsWEh-R&KQJS| z{n8hyihagT`P>(y)$6=%-p+RY&Nh4jba;yVs#3W#`8A%y`;4757|K8YHeDT2ediSl zKTZIkbu0@$2hCv{VE$NBhdCIVX5$cwgn-=i*vN_V&6`C-i!J1KB7KJ;B~kHW?3S0$ zhD-5{P#9ay-==Ga_d|ht^tf8>zCr+;Uw{cl4|x_2b6(H1r3^KDo?B&j^?KKiW<1vw zY1)|uL3A$Y5m>!E2X7MN!H+N0?b+40u$_Nf9*(8JsL1TcukSUqJ6ll!c^J250L_)0 zumb{4Kbu)VAtF)1zY*_F;`(aiqjh5C%ijBrJsPj%}y0f*-BN4M%erRc)0PE5LTGLs2tTntT67~&h zEpRPls}YfqXlku365q%hWXC~H!C=-MJPuvoAGuywSXjMzABBsZcA2l$CNpp<1>n{N zY8rKE;PK$<h}n||?w=6wLyH&=r= z5)HzB7)(T#FF?dxozEJ$;O>DQ#D#CGBXa0R>$j-%kv+)9=1OAkWl*UIe@NBUf{sxA z(+(3=$GZeuvr+izPADFCbq+y+;X7eoWT+VE64DEM0ot|kSTstpUf zo3ql>BPpM|Q9pmK3vpCkH91=CdnikupGSlGMw?3g{K5W|c!oZeJk~Q7@FO}y z1=0nvlo7;?Y#{~}Dpw@r-OZ`Q7}6Yx_C~MUYRMY_tbI4E60}V^VfPa3CivS({)eCj}-m?}_=A%@J&>8BGWog<7#5P z2&T-(N7J*6d-|=OIe@a`2t@mm`<#rARm{MhO(adEU;@sOCBju-CK4HXi7?9GSs)CXs1B>-jDe@-JLPc*yPx60|iy0)iopDp?J}?*=a5KJg#?ZclQGb zj$Y5MJlS!Y2Okt2!OP};9>r-V%Cj*RiIT}^^%!jO@JV1NG1K?$|U4fmQ-PLIpBoe6a`=-8uFz6~c0-!Kgaub4E z!{fJh!ybR3#F|i1*~^H3hYtIkzS3?<@F{T=QW{$@`EkzgD8A)!x!+$Nm#LQ!f%!f{ zM}Jrsc;x+a34r70X+2Y#dXcc?v7N#5oT;(tPXHB%^8THM9Q79{?0Znj-^E$@`X~Oq ziYdC|Z9-HgkF`b-Vj&Q(51^u#=o9Gp8GI;F$$y0$3c6uW86KCiORz}(1F~|U11l4l zTuNv}fqhORs=WnBVYK8qzDH<$XSRaUTc-;+mCH&HOV5KS{{@(Y7veTB27OU@*w9B1 zU?zVsHE$;Aj_1i>w?4yu{a<Uk-PE_}5cc190JB(UwGPIQsBe_44jbe+$QdEZ zkVS!DvyxrQT1LyXP`slYwUU<YXH_B`9K%_sxXwRWok4LRfz>K;#^AUM)!_sW}#&vf{9y@6Y=Rc$hmNM=C*@39s_Os#gqN5hzf9`K2_LkgG77?ibo z!QhkeRxh_!9*gnSEy1Lk5UB6uDk-{~K0h%MmxBf?L*KDpsjv?LGc+Q=2*zB7({X^U zL7#bf12Styyr0e`avK^hd42(UB)&??4k%j&7$KW3>_Y|&z<244p0}`@*H5U|*Vh99 z(?b^+%#IWU3lbzqdmL4z{wR@eg4A949|r&qFsqCC9^KiKWs)USmk6#hNfenRO z7l>bh1LtWI3UfMck*$jMNxwFYTOT@vm2G0AN`*BTX%4jPz=i5o<*P|cBLS6I`^+yC zv1upv^z^hFnp(ze_;A6vjSA~U(}9RXW(=HLFt3p&Wp*G6o#emUH>w~S$z|Sl%jx#m zFb{!<{mpxLWGo9@EYB&cV#SoFVqT-aHeV_$b8uqhO}q!~J$b+yeJ$&>b*MOuZaUxX zI@i#gDt|>@XIJzkici@60nFR@ktaR&Bell7Hu+#`;1$1-pz3F3&tIT+@*a3(@``md zCO@O{aAd)pR-}2L%5ccYiZM9{=$#UpSx%wJ08XeZlM;NvhTgc04p3980(3qHVtJiv zdQl!6r8(c!eLVY1%YaXJWVaIq2w(PahQV*?iWDF10cf)`?EWxqD!=zt<#K68)hZ8_ z{u5&T3VoqJh*wt`|C~XP6;0K6J&&=C{*Xi5K) zeZVh(`@0?Ddj^>K#eEXaKZDLEYF=I`0lYl`uY(P7PLI@I0BEVyGrv8nWTUX6{8_6{bp)EV`T7LCe23v`7&u;PYn4ySTm z32d70gQ9R~jlZU+ znZ_6Z-?C9)Lm_Nhm7?2rfP1KTvMYy#cOYu;14hH{Vo{B6HfU=jcaLvjAzf&&pKY|F zz!C}v9OC|%kqV*I2-)55fQ<$xhxTindV>M)>SW;$?*NksFxG=(3Zu}Y3`Oz2KcAgz zbYir~PNcIpm@%rtzUXma0kmfK)TAIMTUszX_YQD$bSUfkO|H`ryz~H}be+3w6s9cu zel3EQU?37HpQndqWR=$0$X&CuP73Eca5llBkHKaU0#}+c1!x=qti_L?A{AhFbv8~{d?3~`-CL3DmNSH6zP@)35juoL|)=6q( zxo~#9CX1j_z!;_Op}hlAD8TuA4K~ZEc!4dR5H4EG--8XqiGtfgB{f8xDFgz>rmusl zzQt4_u4l-3pil(9%F;W~S(Bo`pvQ3#&X_sQ^POS?Ylzh*TvSahj(5-^#btL&j@*=c z*s2;UhwV7F3OoXcSv>Ij66IdqKo8^xiVD+D5IF_6w~IUi6nlI6$su;yi)oFfV4~cw zg@TH1_q0OxMlO$nObB#*6Cl=llN{pBgfJ-Xk4_D|RL?xCCsINdG1CRBFxsxS$c6Hb zjvNqNQEKDo6Ma&kivNd;^*L$~GBy$)pde^LP*YPA9|jmq32uOG5Zxg4~i zd(_ZTHYD4^#Zda|NuwngQ;v-v`AhIA9PA@0kt8?pMDW@%z>VO#R)jWN{YHKIF>>t# zXzo3X=pP1oEfU~jx9T5If#E-(gVOTSiOm*HraId29MFk+U6huGYY>_uzih7GZ!mxZ zLppU(6Oc#=<%5EP&SwDSw&}8XM<+_DPTk8{=DdKJ zd_{6^z@yQW?F?o;;=VILAmg?9Wysa$D)@RN;+h?awUCmCC^VKGqu#kv~Osb#;eIA7Ci^RJyQ*aoIJq)lb3c8C#aiKTJz5=*&bz zLo*K4FDHb&=wL0(7alq9;&-B~D&k_48)jgZSI+#*ao2S)M`a%u#oAKWc=PqU()pzD z=b**9RmOJe+y;AgX{gfJUhnJ?L10seQXj@jPa6#G0%xuw}MjBm8&nxva8Bf!jzMlgPSe%=G- zt2=Dhx;}-}JR1)?}Z36}#OA@NQUz^!jSQJW-?oF3$i`KB*zWyb4 z3%U4>$h6BcMtsHhHUdsu<#kLLim*xt>y;?H^bw&8&#ytcG`ZnXtxwPR-dQnUn!SqO zTPmWn`~CT%oTpBSx%`qL@vE_dS$|w9?K6@MbiAVg)I!|4*yO-Kn$WP|-o?}BiZY6# z%^v3)kbC92s3i!*!#g_hcw1oG%|u!fFQ;FadtScQuoLbUeKo`G$@tvd`}Os=J}S)= zwZcYS2pKmIu`-XUIEwo8JeS^RMhQlsKJFCr^wSo7Q~mNl_5Fu#4-0+Ls09crorMmw z!r?){wHZw=@;Ad?Nop<;?CExeDS2x6(30j>pWp6V?CPg1Xe$12p?HQ4W`%2J=1*1| z;W*`Mjr*{G`V!)zmZymFzP`D7JJB6+wC3kqh<%WhDk+fnA=ws8>j#jC}~Xsh{CQf<))?F#a?Yop8unaR_V z40``^F=$%@H;-J9I(9Rv1x9=FiRJiMx)8h^+%umWXA&3QiTH;X>CfG;00H8ZSVPMY zKNyjT?>M>ybOO$!N6{cF>QXY~@}-CAMbbxnOfeFwb@`k zO1ZSJ>$qX8Zs~%_?=uV{lc59bXgcW~HcYcJtTIl_a3m;$N&pM6Y`=->vs)$%G8IIi zP|^jO2a33~$_?7v8a|i_+|!LuPUc=>5AGj@<1;#MsR}6E1A^bfX_3Pa`}W}Bdb-a@ z>Af=Zn6ipmWf%4u+2z-4Eitn&uPpFp?gUB0-%T`ai`A&%o@J(04GjiguWe7`Nwdg0 z$uYR9BdwV(*rcVI%D=9$Axy@v@>EAk=DPTvMXn(Mi}tkuCWz~jg0DUej^G#0T(et+ z5-R)rwwXefSh5mAzG6nGJkQLgeKoS;sQ1R^4>)3WE&L|gnGq1ifxD#9Zo;wl9+%d0 z`D(`Mr*9jThlkm)yB+*wAWJ1j?s6k_=GKVhQnOKT&L;n z-J)l%<5IyOJWUBZP<#!5S-)s;=&0l6;i^Fw|BK5-rx@0BJ-rbZvwesOc1#PBd~TCI zJJThhcWjv2E6$lw-XbBB5$2d;(NAdOlN!?0;v>#JXUfXPEBzLdeMORw0x);S;da^i_Oj5H zSCGEvS;sy*kN!R(_$2 z@!yF?*8Mb5Do#&j-{HM)yzlNs%pCK6c3G>Snyvn^Q+G|jrBWvqZ&`ryy`_nnY2NCb zC3mg1y!L}gmdu)nz1mozU{{_|@v}TEx!vo!T@SAc-gIG&NdItV-HikD-1%Gn!#wB( zpbjX9JQ)>5*=I&%TJ?o<42?%H%gK`+)u2$YjzfKoMF0ZV_2@lV>GhH67cK@^i zcfLDrCO1sOf!YtsB)ItaK^~`DJM+&5xOaa|zgan!lzD3E)mFxh+(F}mR!I=J#t`pSU&Q7Q_0 zZ0r^yNf*tx`l-V;O-<>)ee6u7b&OtNnU^q?9=?eC%!3y>U=8r8m&?yxU$H%Z@dd7^ z<;Ck1v(+Ah{QE*&Obd%T->H~(D#BfgXLTs5h#Dd8JC|ND4?n14a`PL8vuu0!{MbA4 zMM5{Cw#}$T5zQMt3aU)hF=)&fx*3NeTWASobumoZZ{S|~w!)$Z?y0CA2K@|YMmJWW znYrgc(iG|1wtoU7=PVp1s7BbxG>0Ht)Bvqt?ECguR$uTjen;(fwA@E5(y}1<)YfY- z=@AMM6V!k?{H?YM;wM3mFuJ1{e}#ugrF4GP_MeN-2GV!>)!!W%o0M$&_t3lSKI78o zL|m;~NM`3vQ(a3A-h5}~?fi=yE4lXZ+g#KUbL>3WXR`ZRZX`kmBcYT&8W}wGGL+=^ zWNKXjdSO#*6U_Xls2CG9hJ@OmDvKV&qnq6tp8DB{NxkMyYB}K$Nm!F@_Tcy<{LTjc zo!XZNS9Cyj=wJuNnKZ@41LJj8m!}yR=yt!jWcgf>2Gu;m+5{vCE}~{$$uKrxES#sA z0$dF&yx_@@TVCe-scEeW{pXqLa)MLUJxvn2N1h!K?#H;+%wBa`s%#`@V&aZ*Xrrz< z)D*Tz0}~gdu`m3MJ?vK;#S2IRyKq3quyarYONpNoRA}aeDCAUhR{tmE~>--@3jP)h-mV7@-kYA&)iLDj!DW>%x!4)kR5aO8jp#&E`S;`W>Ail-+1F{4z!I}1jb zX>%LJ=qFN0a?E=D-y=_B96ylN9kRezw5Nr4UHXPmirE|1p>_RmrisC!Q$7x8EYxDW zk^T6xZzXiwQ1gMvCj3V>TqDYBzc%C%D-Bjvb9oH$Ccy>PR%F|sdC;w88s~$<7`^FD z{G#R$=R-}sohFymt4b^Lu0k8zM?zfuOxqmHn2~gG;TaQpl?f)bi&5pVpQ3c{+=5FwmZd(IH z?rzXB?;CvPF6}w8#Z7f1HN(_~5>wLN+wQ)oUK9$E)vPhyfqaFvg}9+RE-$<Qw_Afzw%I{Q%Yvjcw8o-W-MI~I?{)vREYRKy<BOo1O;pd^*}7SJlJ?VAG`u$RADRw0jD{Zb z$ZJ%XHN(Njl;XLxobOn&zNsVQ5SjL0DT<6ROT-ef@aMU2Q~vBS|28O%&8wacch5jO zEOJ|4Lb~1AKk?7XMD4-K_Soc-fZac9?0~xFlyP5THOCN5DiSDOvfYlp5aN&5el}mi z#G$LgP!opd561q!ov$_};jwx>rh2F?PL3wxjh-4dEjG4$SM|QgQmfSyRRf~KrhPZ7 za5bzX9X$9LugV!#AiF-eD z$b~E9CP4I=ftEo_ctZ*nDx<%dfQ(YgMA_#1!y=l5Ypbgh_C}I^XT0^skVUh%x2H_+ zFIAP2LZqQNd!lWpJh8YKd2-@N`F?(xJ~eEgI3hA~DwSZfl_Sn=5uM}mBgd-oD?$B- z6>FmhXLMfkgRb65-a8{>!O1Qzmk+m1>c=Q*^Z4<1uGDT2quLSWuqXV4a#xtHKfllz zJ0j!~i+pq$VNUseEkjf3x?z1;oVN1n&mohefI|)vdy5bB?&`O2{4}u#wULrb1e!j{ zU3#DOz39$ViZj-LFD-O9@oob9+BYG#iX)ovK1l1fNLpy8SA} zk*2j9t5Q9RkKYpC)c(4nK0<|VUpX7tzPi4!GGL1Z5q^%FG}3el8M_wb=_>ivAeyW< zf2M5={!TtE1y4p!uBxr=(I=n8@>0-irw?En07n~5aA>HjYT&7ehK%IKRgu}ZKDj2Z z#D3$DG{T{k{3L!}S`#Z~?+0(4k))!k82DTt6A^8$`qmq-;ZGvy9BmIsG;n2J!)M)h zA%FDa3nl-~)IQ87&n02U2>1a?57c)Yz^u>9mLNVh3tPmNFf9naMI$Ok%eHnW5x1Uw zSVg!t&R9k+~$I9sisC%!4*BQ-Jt~28L&1gVkphjA0Bbtqgr_t=`w1 zfFD0Qxq8r119gzD&PPit1*>=QG0t9TAD+(UgO;5SGYkx2F_a6yMP%lib`7g|5=UBn z&hG^?Y85uk*DJ(+7VW@iKmNV9=nvAF-{A!a6ed0Vr zk7%NmgKTI@tV>{bn5Aym_Tc+_4H+wKs|Yas8UE|C>f4`Zgjz`ifRf~SCK@VMC!kJs z$;FT(U&+#Z^(r2#%S1T`!MHv-D=RA#Hr^1*Y|Elq?hKLWYsIfJ`5?z;dxxTX!T2la zGq?jLmI_g*V!AuR2yA*m$>xlYgSkxZulr(Lps(XV3jAhvE&>}ApkEXH5zHNV9C%M^x(t`~rd4Ixgw+DHf99&E@KzIgP?_ia?AK4A7Y^xvq)-luM0kwK z8B@1)H_SlCa0nf?bsYdV&~DnTLSS?il64A>biL1Pon)eOvex3VSVKq}$kyEH*z z&}TmG$r=@I*-Ro6JteW03nCD$h=4sKcz1U4cN7exArW^aU7XMbQUN4$bkS zOnMiKvWnSFjMwrAn`9*Xq%1_KRp%^;0gruC9!gIQTGkG}v{@eB={5_O2?btaQ}o28 z?6@x3H4m8gmr?hqy^30uQhscj-u~eETLNFTx~5oWBrecqfYW5AxFDHsqp+x`6QrBZ zn(tTxQV`7RgDN(K7#O6HO#qMjp=ILl^E;S<(yxRid&R1=a0rnYV*dCVgH6O_y7FMf z9sIzwMRLvTKLKvO=tCe!@_y9rz~-j0VwXy_7kNxI20DZRV|jy!hPGo;wG>gxi}PLf zXAOw3$>0lCKMTi#8q>NSfqp?1xKyW8tjs{d9jB+_W))Lv-!f9OvR@)P5}hX>_U}Yg zvEsU426^k5jY7QeJGqjKd!}&pW~N_f0>tP?V?%!qFj`^CX$HnNyO>cbnWnvV-NF~u z+c=rJ3Ax5hP0i3SyJg4lCB)&Q0iC*!tu330K?;IVz1y`1ln9`ua&^_Pp9L_cx0AD@ zG>7FVtDvO(*m@*;2ZG6LpuI`bNgc-LP0_x764leo5RSL*$MKqlcU|rd=zNkwtQ*qG zXssGol7!$(7*4-4gd@}?=wh_ofQd|oSNYJW{#CyP|M1aV#7A!#YU5&;{6P&XYs=^~ zMC-2N?HS{5=QK7;t}usw`ybE!h$DyHT)VtR$BrY@!nqE8U$NyaqMjz4_iJ(D+M6dE zjgDpYnuv6?93G7BePHfhV1D|kdf#%GbR%llIJN(7vc7xxM_}=KYrn9=AQ_3Ldda={ z#MhdT`lKJ1?AsaPW&8`}p`l3kUe^k6RSO2y5alHCANq0Ww1D*s*)+3>Vq&Z7PvG#! z?*=wjUN1=z*w_#LbR{tgQLXP3m2^X%;YS;9`)%fG;Mv*u;iC@CP@6gnMW-id7kjrr z#Bg=mYs!sUeD&b+79^<9qqh@(<(v2&2_NZ|xM&+~sJ{`^4z&ti+oqJK%iCt{OCQ$= zmQ&cF-=mpk{WR=Yr|F`d+_+lU@^-q`%t1;eP?9boSER#jp@g!hF6$Ws` zUT6Uwj*QLTEDgNuEvgPAq+#dgfws5}4 zqarS9X%4jkaTDd_mlYKiuqP#~`n7ro7B@-zkmv0si=sPBVNa-og-6rY0onN-eAbj=FwSHt|)-Ofp7e5AimN!P3-(qyJXJTk; zSh8|o+*scuFAV5Z2H-_|Y0*#>0Phj17&=s8?`N@OsMhbH?{On98pzeyf_%ws9f)`h zX{5ZKNvBq$!6Py;%+-G~k2NI9Ata*l%5uq`p_2%ycD-LL(Ma{)-C8q^xCOar#h`Eo zHbK~2VM*6TVdR@f5pR8Rp7BtcCz=JbP~ZknLx|al@>{(b7c>czz;VF=hL7Dt7s?j( zZudn8#C}&L7hbn#Q%EHHwwS#?DFl$rcn#YAxDsGpnjnUNc-HWO1&hJXuh1nAOm&wM zTM5f6qOA2_u)Z#mq`a{z>QTQIpvGy-e;QoWYN*Iq6YFwBV6|5g^Qn=r?~`fLV3N3B zHrfsnjbmh*wzWWrTMbtBOEyGqzu557?veK&vr5rkiLD|Z(m>u24jXJ1`j~B^JbEJb zX*i&6eK)b>X-QC-cvGRZ%@vA^J!$m?!H$1r@ypx+jdKM;Rx8r2mM7Lmwc5uKG~O~kB&tA5fZT91t`up_Ek!js4PGaGmVG)vsxw$S$Eiem&;f*ACqZ& zU_KJVtX}?sVW7((*>1tA%dIDp8CQi+2Z@a;&TVyXmmQG}8GS8JocHb}rMTV{Q$u>! zXpoWC@i-dY8(*qE4uZ$>0Kpg|TYBZ?!e5uxGbmfsC(}sJU;SKu>F}e6b*A|HJ1#Ai z6tt6#L2f>p(e>nE0 zB^5!asozQs*Qg+K2Motv`!wJ%oxFIm0u8I7&hszjfZH}rO+StPCa7fb)2OJb27r1l z4M-?Eecuv1`K5f=Y8hbKAg%!XX)cGTz z$H`STe%UtnICs%qYV;PUUIngJHz>0$HImyr|9hp&fLUogKdL$=cp55dXh=;)?$RV_ z>^z6sN?`DA>%SNRJp2^61Oj83JYct6(GU<3#YR`w_fs1{eRNxdowg)!=R z0n>)ByVj__@WysDbK%uRHw zmtushasTelbFkd`fN#1$xVF&%HWbNmS=&Xi&WU_Ir*3v?N)#&#(5{}0KdRRKyX;FG zLx-(L8{ah4I*#D*85Be%B;Y}phM9ZBGQt&@W$^BNS3QzSM89D2aHLwB=--Y z=;(fieP(HW%^i{RPq4_rGnxW=WuR}4LdU?c`(Z3oU@slXU8~G|OotLQLF^g{8M{Gm z9W*+80>T@izNoT%|Hp`CV2s?YPt(uZ$G0mB8TA4HLDt$3VdnAgWSu2|V&xmZ0>17l zMF1|XH0YLpJ*$w!bCUbdXk3F8rHdfhz=DtYcK|Hj z`BD$#2TJ!34G&tYlEVC;sF?39%PvWp>bBs^zmtXi8uN{luK3dgt zLm?p2S=t)Q^6T5a!~A!8qlDnl-}uca!J;`!P=hD{5DNV4~N;w7oUbRQ@h zozg1>dcuDeI9>o==O!UJ1pL5PtUn?EwHV)i*`upjf2+Z(AIWrhDpKS4Yp$j z8?1;NJ(BM~foO6#4+NsJjzacFq!F0!?N0H=NV<==9Lp z;Ly;!t4bG<>7}fv%MAe2D7AB~)~_}Wz&>Qy{z89M*;kMx<>Dh{;*AGdU1t?kS!93~ zp%dsAIBhJq=U(N5CQXyC4c-FiG7jVaXo;`o;A69t0wVg}*yW;1CKvYF=2Q1R|AL#J9%ARRE)`#Y(jQ;LJw{ z8yZ`YpU-e`1aKm26EalO@?=r_o&Q{eMKK^D&m-{`WPyax12#G&f%cM|9N6Y+x129u zddjz79OTc;Xe>c)ZaiPy9d)OO__Y=j60wp48IR~(hh8p?F4%MK(0iAG(gJGDbM1a2 z0Fx5k6G5~W{5WHAOO=V`-M@Y*zW0_Wj+qOsF8fl1aJ; z_p-KwG0Ph|<>%f>$BGPqs$M8(5s*ri)fdByg=U;`ZCH^j+7G!6*x|nJ{ z5C*qb9uLrE_+iY$Lmk5aP!RS8vkYVC$d;FvQ-o~=@$m2{SieB*A-j7O z4nO;G!MqFj&dyFj#Ro94WgLQO1QgPLI_-ZI2K=9+?Ek2f&q8%Q-BP@|y`^2V$9rlz zNRVvZgb;7wb52;!^X9uPfJl-4?D-$hZVcGzE$xkgWgrPyXj8MZ#-Pm+aNB43PMW{R z1`_=YkcJ{4AnRC~h;t5iuiO=Pngl#^JJGC6txEE`(x+9T(8 z$R1^X;xzz7I-84nsB z_xV}e)B_I>&sQxUT=hit!X}k-iCAb90gzR(>FN6#sPewQVEq2>p-P7laBZ;SkD+k| z6I@)m{)HzYA>sYgc+)+v@gr&sa$yWG@=N3{?52BmR&%TzlZ;yy*$y;fP=PimFL2+5 z@Mj(K5ET^_uP$yHzPcWemA`3)OD$`@k4qICpnftSw)LzO0}RdS;(5xngKRtz+{rk3Scq>JNu!jwdE=R7`tzc zPaC!RAHyyN%^C4C+2H-NLw}xO_#yzdcaPHxIB(zx|2?|bmx z`~B}2?-<8(_TFo+xn?|bK64VHC@=K{i4X|}2Ih(MOL1iwm7^NI`;3eX4f}=|=#XB!E5Cmqrfk^O**?_X zN!Df9O_r9RtGk4czSIf9@^0>Ff0V_t^LFsn$+hRVyjt&DT<-G3*XmUK4Zr+h-CtQd zu|Ie~`7kOqML8t}*>nSDmj5e)1eM4c3Ykv*=Ny`-*W@*Gm zzFgMlBAMMWNqB@PuOFfhuzIjIu+p+7aNx6QvTw5U#^tDgf?)G+;Hl!8S?d=L3{A7i!LCcUjPx&*9I}jZUYH36muEX;=1R>dxI2nTX>@rUN+W>?^l2RR1-dU zaP$TEQ%;uO#KDf)$kf5u zjM>A^5u6hYjDQC}_-JS5VnpU)XKU}w?;%L>uPgY$=ew_2D9HYGiHnUOg{GV$nV7?C zGcqn_PG(jLAtW*~GJ)5o=KRXylKEhzZ&%)yF?#}Ga!R+wbf`yHbkB^0w zorRs930%SC>}l^}0KidKuWV!o=g^ih&<^I{=R)M>3`4z1^%xpEqt?bO~oxvJHe5~vO|GNJF z{PK6j|F~1@@14ACZ2x)ZfBf>#odPU(8~Be6{aaf9`WA>w2uXnDUi3mppM8Fi!@!8Z zNQ;ZAdcf{3KGsy*XymzWgQIvy_6P~-QPvCel!u%J_*il;Q{-h$wsGW!tc=TIPC2*u z?Alla4ZMD@pK{)m@!ZHKf6H+oOiozu%IPAfmXr>|>LE}?7lr>Qs)!CxATHA8D%^mM zLfc6)HFwqadT-aN)M9bO!Or!^RpRf(32w)17zA1oSR_RV9CpUPKIY?W9@KDIbkqIU zw@8X2k6GE)9{NKs`}ZA?7-Uv9mt!=TVuk;F2KX_AT=>C1PlP@^MHdp18RG||=>z@d zpOs}%E(!j<`27z{YjCnCsn-wMw^i=n>|Z|`8uyL-?<>>b85(b{k;15_|M%O$V2Sx6 z{`c;XcpJgDG=E?!e*e$gCEy=({Lhx#zak{y^ItxMOy>XhlI>kl>Ho7|ICtO^Otj_DQ z;*yfu&Hao(fj0ACusZEuIPu);qM^FKL)#5*v-^RhsHlI@+h^~_yE$H~J_~w6WFBL! z*Wd^Xw%8|_JRkQg;^}kTkdTlBe%B&7<0WZY5&trvLT+}Q?tkT6i13Vjq0;2xD;XIz zLDNA4Sy>c{4$56^7J?BE`khaNBeebj!NDfW9f1k#W*_ucSH99M4c=M9<#zQxRB3s0 zMAX%B>+Bbcv^R6!Mx)5%3yTgBTIV3e$Hp#Sgsk=r4XGJPrvMdEkD0=!lka%}Yk>}| z1Vtm!HRm5?Zl{AcrASy?Gu>Tm)|k|23YAY~pLBe@oQfxg*+nT8S>x)eS78Vn$70~S zVec&ui=?RpgpT8+#d&|?6|o|3-tY_EUYMw=s~3qonqSLKN_<$<-^ zx8=@YlkN{V?;|4W#K#!l!C^N90||S<9NGPqu#0~<5s&fJ*&h9%l}bLjkkI!{a(9As z#&;i+l0IP3C=~d_G3$NE$;n|p-yp)yfSAE2;A{7M%>8Ru$;cB^Q^t!;uGUN)Us~Ty zqNHbLW^zY%64HDkkaa(pEuDIq!fi)5RigC(4-d~K<%1cpf}}eSf%5lg9wU7C@ZoUy z`GH8!#@9Cxv|&H=2Z$)FMsiK4m)3h~y9i06^*|)%z;$z!oLJ~J{yW4c9ic3( z1={Fx`cy#*uwuYHb58s;WQA&oVup?X?yl|iwBJqqDPY;zVUr}RkI&>ruu-_l9Bfoz~$BWN5XY=yO*!>9c_SMzqLmiXF z8fqGI1Mw`+7dId{d63$>qmYUF3B>u&(#VfrQar;NnwXw8aqFsjghe{lgM=9Uc%lV&zg$HTgi+e*XhlLcksSf}kDBeqK}C=2FppT`jg5`s zpZtU9940xCFL`GS+g9->4K=kfB-w;X-{CQDx~S00l;naBzb{rJnUtOlWI(iO^p%8ejuFRj{W%2f3-JKXBC?<1`d@8C~PJR3xm`@Q4lf<@3uRW z`67+mHEH%mz1QcaM0?9~2tEvg>@#qwI9_NbR6mwx;KsE*w#KQtdU`rWMn)kuD9l>W?*HJSswo>A8)EpVfYDY^Q=un?MGkgFI#}Rp_GAz$SpbJ7 zCMTDH$`$3ZGJfLtc=pSLoTCXuAM{|salwj*t%v=nwpl_vw00Knb3!>-d zB}!tPdb%c(k^avXbelR{;v-<^rNOzUE_rH06`M8#Ifd8hg&;8w-z&e6b06%eS!uIx zc2}CjX!(%NI{{tD3?f10YG`^FMNQ4kJARCi!ivF#5f>F2BR_efwkX{lM}$D@=m3uT zoO^}jehcl^aI>?s4W1{|FFIe8x}jPyX-0Q~;5#h8T|x>o)hu;qFJ0ZKQ0V}-i}2dn zS#bgWo;fG?;)kUmJ<7_NudXVKGsXzjWJtUjkm3hZA!=FofGH27Z)8NH5)t9y zEX_I*7jFX#7Mj)Fghqt&&I2a zp)Z>tno&`K-rCxFczReP)9`bPPFr~3*%S8m=4-lScw>o6NBBZ zQJ4>3U2y)ksXIaS*7|x+Sx)NAAr;aBU3=G|Jcba|wjglam<=vlm}Pp+K@gZLIerxt zt?bDuoPGP!SX5#`QfUm4FP@%HX@%7sx8YupKIbj)9=+f_Nm0#=;@J^ z!5skR=LqM1OW@vsRP_S-){5-~aCj{Gs+pIUm$OXb3M^KIjJ_Y zYnc4vU?D@yojJ2387*)XETx^o?0%gxXwx(Ei1Y#_apFkx`Ln>cMf2NJZtvDZ=nNf! zq9L}4phQA_N-s`9QIT>i5vOI;4gKs7(vx)G#{-yIs_tV=(JtN>`KH*MH9>1xgqOM* zxxo+}^!;>!Cr)Cu+&$HWeKxQQdNtE6Vo|JYx@RIC(a#3`T)5(f9*G8YU_WoIArf48vvx$U@9nmqt<=YH<&cbX(r2P4<8LCWJL2lB}7RSLa${*w=;Nu{`_p z{c)^p1vZ`bz>+&pY4X-1yxmL3r>Odt{tC@c36pb)-7gjhi)Z$knuR9pTy2^WJ7ZMZ z+ksI$@pi%|gvPB1CPj^D{^|VYX6wjmA2SDRlW$(hwHQJ5+>wTinyneNu8Mx8H$IaX zCG&64g&r4ISPtt36#Qh&`KtKCRyase*jeNJ!Q#WB`t6;a=DEOf5G?)|mZZX$8MKuR zGs@7=N6cm_jDGU{Jejhy9BpQxz;ou)839GOD(VNWw|oh1&nNZ2(VKr z;*jEFqOPqOe)~O?P;&?izbfzn&9?ihb@~0cIqMJ|-&==>>CSvcsXaf(Rm27s{fHy~ zvQ5X`EHxjrdOWJrgCBFKKv6}ySGE1xx!R1M;BfJl@Wn!7szmOc_Nbsg>;uGtr^!h*<~;W5 z7^8N3++QNqth-;(8F~WCh~qH}fv&BXp#!I{uW#LE6TW9|yEXN3p|BaQskGHr!aZ`; z7u%5=TtDpAtz`gxItpb5i|t4*c8R;Riwv$?y1M{p)b1e~iR}bB0p9;bn@eGEu&}U2bCXvJKjN zX&D)7wmnlS4*`t0`2D+zp^!u0d#bVbE-K3QJot+uf5I;;ET9t+AtwsP#EfbVt3`L% zOqbE=48-z|&_RhdKICPCbAr7MJf{PVRyjJWMJ>&dY;6OC+w;k6+FIRXk9 zzzi^W{^Zb9U}*s^hDJdhrm?Yc_=(Y%xs9|TkVNyG=FGp9sHR2Y%>f=S_;X1KHdHd^ zn1{#59TSBrYVOJrT)S7X17E)?ExycGNdJD`p$UhOa+f*B=vu1BK;5$m@?a8|3_^k{ z|G$lRJCqaW%w-zCLdwL{T& zNmn^6Apw6_a?VSVWGevdIrT8Bx*vKIz6u!|n-YTCO4nl(M9a4Y@+r2p-cuCTK{f+{&J%wU=zkytRQgty6gi z5<%#$5wSEI95X*`0zifsI(V_XY2K81L}?y?%^CqNUDZXFd~xqdREv$o#oovMNo+a48tHOBofZf94^J(p?E|B z+CfsVCII4duDcqkLO04=2^myO0{NOuJo7S8cX`IKmZJQ9g>H~G9UXR}Ia81Ce{^C& z@|FS$?f&xOKhmGJ_T!%eY#xV(cj;YsclQ}uUj{~spcgIy0l}lkkG}|CcM9Wkz6z3n zT?N>W^Onmv;C|IiNPa$zdWmMHb*tC8y>6TD;$xhB{hvumgu8PsGJ82Xi#Ly2Wq@0y z0&bOopcN$Z|0vilBdh1JDWhfujG9JK#Yu9iTSSjXK3)va;KI0$N|Rnf77p08@6-|(vXTn)PBv}M5WJZp5 zABxOj5O%35%!de3h`3RD$0L?6j#kf*I$1XaJr2p_Q~3jxvL%-v3Sametr(MGvsQz{ z<1i5Hfxa(9|gsO(b2@Dd~e|W{rxYN(fr=u-dwuY zanQ6%`llfsDkv*-HwZ&D1-s&%h@~a1zQ-~GD3cjZm+8qT62DTWU9|G?_O5W-(?%g+ z??Tmho1X(QK{{6(rs?h)bsT$q>&Nh*eYkDV$Ext7=s-gii%r(4``X zm86$JR{s9*@KFA2MFu-V!~r;%GxdmGXdWX1`~30R+S*cS{r49>K7yv}<5w1o4*4%_ zowXA!B{5eZ5SU48`qF%mdcqC>?W!rEk%59Z09G+*>FGP3AAEjSRmBc!R>rpvPL}ug z?PHwhx1VX0>#Gg8ywo$xA|O!d+t02W-|$S41J+)x=QI@$-9a!$L1}5X!Jpq6rlzLt z)6;k-m>XY|w7(r6A5X0Odh+B+f{;&Bl8 zng0jyGHe{ac|*f?TUcLbzE0c}@_HWU(7&0jGV@sbD8=YQ7zZ-M1yJ5NL`4?@Rl|Rr zra5-huO~dVGm!rNS6sLX(PGtdqpzvYQSOR&Kxf17+29m}E}vy(XAgPs;d3C!(tx*c zi4TMQ3r&U*?jZX=#j8D5^Fe&sRCYD052>U^VHz?rGES#6O4!)g1rS_DO~}uIcp^$( zNl{oD3{V4mhLsab#;!(5Jx$K3UaPUVd<$~lk+BQ4n2+XTkH`h~9~g4xw}v*%!GZLPd~ zd2wMlxXEUn7;4lRH2Z8V8kwqZWFR<<^3yLovrv$t8yiz#y-3!D1;N@Klyp7ZN$<@3 zAKR8?iAqc?G-vYJt?ZpxV+QIe>g-%y6hA^n8W0p%^i}`uySL=h*;I858f79qsJQXi zVpHTL#l%rAD>%V$X6elrDVI{?M;`b-{Kws2=eYO7WD-h;zON~Vml#R zbi>UWjqsP9@-|fI-~98hU%#G!XWbZ1Raw0?ZG4~18@vfA(ff!5FfR^uuy%>s%-K(V1BuYD|AiVd7SbIc3A`mkRy$*s}@KY+_uQc}v) zhRNkp>_K=HO~({i)!5Y3@$WKw(N*EIcK}31v4w)d+<52;2uy~U0ESREDx%y6TBK9S zrI|{T1VJyiNK{a`4TEeR@NagT)zTUofkkl<&7A&VSYPt&$g#7*3|3j`U@0YGTZ?((^R z2S^Bf1gUTtZY_^e9|~|b6?dNhxdBXC+P&k^)CR6BS3-*fqypg6s)5N=6!b?LOi=I+ z1Tj!D41Za?)?AHF#J?4I&)s6O$iHWch{@};0^j_RaPti-Hd2BFc#2jT_A1mDfMA6l z)#V7xe{m+J`ObzKK%MKb4uSNq^HxFbv&|tPA;A~&ZYbROvIMkp9z^4s7QT^}_jXr8 z;ds0z9Y!kro!+ck5lOMV5(EMMvtMR=(EU1601?Z(RBEXqY2G2>Kv6AXsB^@w2o^Ci z%H(IltE~?f8j*%QxeNDyxJ;iw@6Lc>XJ<#p!;^Y^cLFV#Ai^?mlZfB1A4Uc2{%hB$ zW_-YyV!JAkwVZ1sKI8oY7iA)zo_s|`MZj$)9JGGbxO=dR7%0f{L^UAaU&dMmqk|mo z?TSx?FnCv?CY}yay2r`>%m~lehsAat%(ah`-Md&;Zkg0;+=Fzkfgc^{YFITnwZ| z1!?Cy(<5(S`5nK43Xz3=wX&w_%2CsF=ywGQ?_`4<+{{rX+U3^Sd#4dbE5M__w>$* zU>qkBe^9tz?FzvHar#-#bC;{3fdLsRw|5WG)K@KjHuj33q2RN^R6Pr=L3~wg46rUJ zJfLdzR+p8@Q^jZ$m5cNb4-TsG&}p2J0_fKhNgDYWi@JSer2J;ur`c%85G$1)U6bp}1@-0T~BkBFwe9dGA7_^T&jQM`&nhG>wGr<<$SHgS`HrO8@z4zxm=Pi=S1)3_d!7xJZ-7 z{!HZq1D{{9Nt`|1-3Y73J%#%tAbF`JQU(<{)A2kNt$fvZ)x>u5FK-dcbxWYT0;rgOK;!X%MI(@^k@25G_tBD*9(h_r5;mh zI7}hMVNL?RD-DFjv@ufxXEP>6IF%Mue8b;?GHqyAQQs?5NPGJ2Da~LCA65*t-0Nt< z>LhdYs96af`-K8?{8(v&Jh{{$I+eTvX_GL*Hd#YwfSHJ&yU7Ofy6tKT++M6&UzaJF z)T!&rqLfk%yoMG5G9a<=rnbutz9&Q;Z{BEcp)kh531ZDunIX4ctRTBaF=k>4Q|b@V z&s8h$*4KymeF|}UJ+fXz7RRXdR*KYT!WVXSc~G~k19ek?>fYIGl99!9*)TCK&gQ6@ zcfH4uMQVQ6?Nh?+2$%xz!!Hl}Ko-mKVOTz)bPBEJcLV%5uY5eCPN%j4zjbrBZeu zDqPGh+IHW2kA_J=~3F<$_oSvh>Et*xyA z-*_#`RBxo?js-{}{Cl@QkCN6vq5> z;8z}aP&%EQoP0F$=X&Jg=D;r!_CZaaq&>)lAVO)1Lhq_3#Kb)Uo@P}H(d9n3>eryN zw6anvJ(ZT0uH&nGI^4k)?Mw7B)5xxEKC!5ei6JSwwx;GAWYv$~ z>;G2NQKr2bh@a@|dr2L5eK8|J`XA~jj+If5D<{Oq z=Ql;yc_pIJ8M?yyTp~I+IP~E+gMJLHn-ocbyA}-+xi{Dk9-;=g6bia*MqzaBy2=up z%-Hag{Pm{4X_uacymZLO`vF5NKl@hf`RSGgOuf=TWrp5SGt+x-F{KL#OY5$>D%DbGRO{oR1s29G0()|)e(FwoKg zcyD%R*LGYn6$2qeD8PYOAy}U%V*JAXetmLqw5izcevKmjTTgQC3kqkW1Vx6PzNWG= zT;fG%Z*OnSq}kFsMpQ1r?d^G6u~VS)`XHOalG}Ti;azpoqoy~%ik?zbEYKz5YY#_L z-J?x^d*lhcwG0xuhVhkNE+F!R-W0U zbS(F$aJv$k)+uXEtqvw~0v-^1XTqP?3?4&`C(_*SFc-VjlIqIIEINXIp%RK}0zSqJ4pgi?E6KWHl|QpbUn3 z-KRx>l3ys0bTcR4?ss4Q^~2$vjv^`4+KmJtJc_v4528L?8d$2*{5~W2& zNaW*J5CkgMe%wCI{c|dl!!=EIGHP#U^+E5Y zy$0pT#kSiBiR;hOTak*wl!6?1#{B72);wG+1Ifg(m_~SEtzi2~LjJ<$|?eKxXW; z9Zo#rLr2sX(FCCpJpBh(KKYW-_+^@vE6>YM5Rf!qWqkfj+#5+6%-BMPJms3)#YsjS3uDVT zhHhSH&~kj_zny)BaeZU3T=feKIFpc&01;!Qh^QWuii(PsnOX9p&(Xuf1D{6`OJaR>^+Do$ zYGGm0aXB7V)=})S>h4kO_z+Pjj#y%a%ar!8(t5^haMZCDXmdn5q98SQi~ae=uMK&m z@M-h0v@t$mKbrxn^WGE9kAs7vfhm^fqAvcft|Aemreh;NSPTJT~Jx3QIKS+SPm@#PLqV-kuo|a87?_If%AL zH}?_Fzn@vyT8dW(Kj&AlPg2tO2FF6`o~##5E*8{2KTl*aoKiW|hzMuO<{B-*w z54_20kIDGOOOIMA|L~d1@BF4u@FO?UQbS2{9k@JSeouHiu8sFh@QRq?0G~Rso`HZa z|JB$oW*`>}BSd4QczlNvxVevB+<5E}Dm}!6J~qw7l4zff?p-si1!h#&D2f4f>Q)l3 zww_tI>}=4Jr{+gRjfircNjOK4spt*NkB~`)vYsRhb;IqKWP+|lQ#V^aDJA0yI}wLY z%kPu$XRC7*QO`rV7U;<5-VM(;Q*2n-1iq>&pejf`AP>~EAmXMMYiWeA z-D`yl+;~4aCWV1Vqf$FR+%Z866IsUUjCyG5(7XcGn=Kcu4 zp5u4gm$qHFuN))smO{J}b*oH6zD=d$)VGRgX(jT9w6(Rx%1>qW?5vmThOw3F5(x|24y zvKP^`Eg9x_AwBrCwDv(b%@ zT)w4gdYqiwgv+_tIUeD%0L+g|W9ARFO~nicI8e_nC@br!Fp4`^nXd2isG)q^N9a}M zmf7qTDfvq)fJ~r-%gUz2L0m?L@_0DT#?H6&=?fxc)D=@tGIgOAIGeN-`1m(kKdLdj zqS3ObZDFSbS`Y_%8kg42vI}hRjJ&4#ip z=c+mTSH;X!P_^jM#d*=jj5IVg71g3JX%zi>Wo!(NBkn(PKq|-O4ulAn-B@C@?GO<) zwb&%SYMNe6YXiK6$r>{TKZ~0WLQ1Ejt7p0G1#90IWywG>Z zd2N7VYE_+bEy=c**0FEYIhm?j-IKf;(=l9@ox0raQ1ib61;r)|P$9cGTyEj})O*KX zgX%~3P%_W*7TxNJZCh-*&0f#;{v^LbL9;BnGXt9oDHq-m1_G~#`VN25VisnciGMoY zTgtalty^L#r;(x+^>u!BR}IOTTmsf#v)sP*u>ZDl=i2+B9;u_4KNPji|3*k%nE0K! zop<)DppqHY^{t;FMGR_|wi8W*bD3sm&*9xYzkEP(=VSQsx8n$VQ#qri=2M7KJim7* zCcvqssjLrCi7i7`-l$93k({mdU3RirsoL_q+aFVhNr*Wf)Jttp|Tkj&aZt-r0{o0*_KrEOx@ND}mA@u{YWc7-V@#dnl2H0hY6 z67m!XcZW-2r^|IWo!42FdhWkBxP1gSnfxYRQDaYy$*FkgOWg$AMOaD@PN+$Z#`c)9 z&I3C_!o3@Q)hTRBK@H4-H`wvmtJWquq^f&d8SEQ5>aBR|^2lRpwJh^E%alGfRGd|Z zw|RoJQ$3pNKFMKn!8;f(y}yHt$dIoU&L>-#6mup&7-}dNk5w5$BQfphb66`jQ{$QE z&q*c){o~@MO#8cYT}uqkBerk#hkkBd9Ij4GReJHwA5Lj$aMr){%6R^wr`yD;gY%*| zrG`MOnVV9^6?v#*y@gzpbNne+e%;Cq!?I&JqvR5=9n*_8vQLOj6fJ9HLb{~y6ABDV z=_+C7TaUZ8{T_){gvQCXUimF$yRwxQ{`#^&qd~6AV9+n*S-e}SJv&hIlbu>`E+f_% zGg(Ne&{gnBZ)rVB@^Y2Q*&OLI9IY)WLeE<~dsbWea6GPcX^Q@CLID0=l7?ik zS|^OOw0$BNY}iuRqdHag3k|g$pJ5rcw)ZCOr^cpWe&r|%HpgObXEs}p{G3W1(qov% zrOVte_Hw7;_S)-Y3p$J5@cbKhY3};Uz}bW#8&s7*gy>$WsKl5dn_E4FcliObdo2f) zRyH=y3j|{is5j(|2O9X=$ZMl%{j8w0RW^`-TB0&IGIzM}YWsT}{WRfEP{t0TAAq6+ zVC&dXxMI{nEnTZdi@5lpd24POWq{VDSs8JmGP6~5=z4qEDA!7XNz(#Q4qc{7(3KQ3 z%0bEbioS2PnR2wv&#w<&s7mqXe2w7mUs~Mk>Jg&=2n~NgiT1rXH`jdir9-V=+6lPysIr_D+o14}|AsYm@XUHm z0iZ{gUqVky`U-hdFOGqMVUINIfuy3ETCR2ObE1|HvMAxcf%v4*y3;>BnziKg{9sN1 zrx!4VPcS005$?F2q8xC8@Bg_$QD1);S1iAA?yTGrbjTvDtmKBCq4mI0HmLeaPttR8 zPGHxX7+ZcA=puujmm?zJNBCMt3Ce2Nw(&7AGT}Z_DK}{812?5Q^s?x=&MitD6Tt6g zf?D@|W!Rgw5Ens?0dbs`vS(ebkW(%0KjP$CJfLN6?>F65ejQ?<7v0Cbb z16_nsS}3!|n|&Iy9KyP1sWGJ;6M94F)m?pEql_9KobK-!y}RT0F=5kd&{`BQm_~<{*N2k3@P>s#?7oUvMcYgSoALKma_Bp}Bcp0=sr?jUq`uh4re0_yX*_^aiOBx#qcpSdlHh&Gv0g%~bMEQW}Zp;K8 z5s~6VHSf3g_p3+yPWh1Q2FgVzsW5!l>Ts4%O%30*$p51=6}zGYG?QFR={OiIwfh$> zfdeV{Dx|BYH)#VHsJpJ!I)@)+amDxo9;^a_g3CXDPA;d(Xlb4`Rn$blf%LH#OsGV~ zr3H00hCKbTR_%rKKG}A~vggij$_Y*K{&{ym5Mf|ofDRM?*1ANJ&$2cKU^GU!8;sk` zlQcIriemQO-dq(=Nl0V*UCsZfYZ-ZnT~X8i08Ib_#uE_7o1Q*rj7aPax5;=Y1E z$z2N*^4QpzwZJZDU3%mFnS&%MHZ~BxiZb)$KVf6cO9s)RNs?B)l397 z8)*TQT}yRJ zCHD!?@Ij5pN@!ujkRDd+ke`vUWS$L;h+DRn-R%ly|0OK)V@9nictG<=y1CUQh4sAX z=2vCg>iu>{U37JIEq#*0KCjplaNp?KN4n!6CD(QKtJv!@w`M z11BAL<0Pun=AN%zhh+lZ_vKe4O?|`fpFQ2>P|Z3Bw)%TP5ghk@WMr>Nq+n37M!9kC z*;8vdIj>&Rk&^*afq8dZDIcE}LJeNCer&HGlQCxwwi;R*KDgd+_ImWJ@O8Ri^Gl5amiw!M0toEyp`=L!7WOr0H3|G)sbUdK}-T_Z1V z>YzQYi-dde&4>U}3k}d_2Kg39j~=zB@HzAQM6xKi>%%Ds-_&e>_rAUMw7v0Wu>bzq zs$6mR_4oC4bG3t&Xye+l%?~&XyO_aT3jJN9NllFRlNEr>k4i|mNL3e==lzC&Yrq}Q z?mQwGd^gsXkpZJ4G{3@WL;R{F{Bs;lpeZ0| zuQ{~8;~a}xwjFd7G&^y+XF`17r^@t-qba4Mx$WoPgVvfWc;oEqyC>!k_S5e~30>`0 zbMsg4DRg#?o~;W|LR}uVA{@KCRXYTByRx~tnNa5?A>e66qA4$T)m2r=F29+Afj}a` zHT~{*FK&&=a!^mZYhu}+EcQ~1&_V@i%zqVHc>TA&<%13(B5R7IT$%We>FMd^?LH*F zJV;n&j4QWU+%QX>kH1(j8lS2}o8TeSoewiN^4TuTKLm#JT=j;(ILyX^<(Lb)T~M1I zK-!FP?HddNCFgDAT3JhStT9y$4Y4VnPb{-N_-{ zb})FR<~}g{xSAY`Hqo^i_{+20eHGWoC@2B4O;}H#+D&br((e1t87L2PJ*~ED3>ufwXjVq9D;tTG8(@kyiGomuvd;s*2X2Ecgjg5Lkhdkx_Orf$_Us z#KEkL$Q1AOW_G0G>829S3oyAPo6J3=zkK@p^Fy?!T^}i>w0AN}f6y`o0{7j*JYxHh zosDxI$K*2WV0jiSMbsPJxCyB#iSR}3f$FoAvH;)edJ2~JHyBu0@1mm6S(SE0x@g>RU&_gK=coG?`CcEMpJ;r| zB%oI5A0F;mrVLgaXmQzyv(G-#Hr}))!VX3Ao zN+NET1nbL_^YeqF3sJh}sv^vI2If`eORCB38KK!vapGG1cl>OTnvyWj0iB@&?4{Bw zm_F*Da=DZfP&r` z7^>gT4h$ftJaMipecdmObt+**JgAQ! zKc-hQWiTc}CY@0d5z1!wl+o5s`tYJjvNs|$kn!8fwso78{9QM__OD#mKd*n9;Uv3w z`CKdqV-ONrOR-(0gT(Eo9F`I~R@(jyZd~pZG#5!qN(OzH>Fs?urZk4n;f9j4lfuN4 zSU}CggEz}JOUuNhmQTWF44*S8Hl<_o)^ecui?b_LRD66e2$GWF!k;SZnVFfbONRl` zK+4d1AMkc<7L#Om$M;t)zGnF0h@CBoVea~d8 zzKY9Gc1lt_g7hEhI&**B-7tHv|Kvg$~+CMt(#jI(blJp~sp(L~ISrw+A6RpHh z@9D@3oA{ZWhDJ&bo*elwP1sLgZhv(8!z*{J%Rr@8?EdyQiK0b`5Sx%$s$Z7eMnh4-g)OO>#M^jf_MaDL)g zHcVyUg@RHe88M@Ezox%7yTaGPiX#UJ#`ZCxY z=-&dsPn8wXUj6w{ck4BMx<;a*G-g{Fw;GPG-^pby#3e|67>;*i7D+4vhdK%ly%&vdp-dXS1JNznc0~b!2ta9SJJCsj6a| z52qB`@7h~GwgY{A^T>Tkw#{yP$~0;0W=Q5kDP~t>jE<6*UQU1Nuhfb^Fpl2<%9vDC zR1}vsQ9==v8>8`GOT>!aG{M>ahKokOYtjH3wQV|{s?vihZuzZy-kG7C zws))KTc5z>T)0~6dnVlmMY%w`5xS;5s}5(Cb`-er8*eaBTx9kl&MzUJcO;WKkstJr zo$b#TGk33ahaSzCL?$j*v4V7{T@oCJ9sJO@yT&1`sI6JEBVB?RvSgRYBwnMeA?=6F zI#>kh9>-nMIiw0257x{EJF_H0C$vo#nciK2qH-O^OtU-7?OA@nj6%@i4q2yrBp!R21T1u*nc?a}txv1PVhGjO~t!v<4Qo?9EQz0sML4noZCyow= zHqV|E1zW_6o<2k+u_QviY&q~jLle3%M3Y$t)9Mf^1Q6gOz;yQu>dGkBKc-Av7Bi-F z>2?P8Dm%?z*Z4<-|9s|9O}-mS-=_fGcFerqp>UwV7Q#r+wDbNewVosvCZ??5{s!nZ zwoZvG`NHYCt;&Vb1qL<4{fB62m&Db{%5o3dVA=McZrl_IC- zY@ZPJNLcUqfpmJf+<7rzaQpUd4v6daZ4loEChGBq;Yr+(HD>wc$;Rqensm?l={l<+u zG=BBaZ2M7C#m>9>kG|5y0dX!8LUp(@#%;}RW^)hAjeN|x6dA5Q2gH+9%GIxZNKy5! zu@_`7fMAse8pa+hrBn8B{P)6?@`eRz6EeI=fL*Q=zH zD@W`~1C(gOS-z78$1qrJ7`V83+G13&adApo85;VlQ#!DC;$7X#)^FI)%;PD}WhvK% zu^-l-bx-Ckhr7FFooQ39q}{q-e|u1u4By8=hSD03w$#qYBXBjK5yi@lsDee(krm*$ zwm%!19qgWMeIYB0|D8E;QrpGe`shcgnaVuEjcK{>jkZlu#Mbn0RT7(Wn>P4Wa}%r@ z0`)mJn~@d9$eo>?$sa6LX9YD;;Ut1{T1DT@1b+q>VsuI^0V?~hjeWa?6=?cDe{jGA zse#|TR+9m!Pzl_|NP2IwLu&|_mE~G32=j6}Wn$)*rwV3|Q^uW&W3B=0zSF6-PL1#Q z(=sNf?Sl|Q1t!?&Qfs_uj(9`0X7$d`4t1h7464a4O7?Aq`?=wB4Eyz1m2XOMa?Ae6 zH9UFLWmu1dtzQ&e9QV%i$+u_$J7LSmT2}JZuWC`VL~n(xXR)Ql9rPKxH4MB1tEdgS zhTovabbdCqC?!`hE|%J&B9lL8*~AOhxV59Z-TE^dHsPvMVcK7PIC<9OXp1Z~5Ie?N=qlPb=BNJ^{*1^7WZ9QTW;ck*&-D`Cfu>d1c*mQV`+W>nEm^m@*p( zWOf?#uWFC8li8!xb5iFW5Pgr*M<`0##8xMM4^jyL;6;xi+QpFhg z9ChQNXB%9v;DOf5En|mPKzQ(GL1J#Jm)^%myJkTy5twxwAb)NT{-{eY1JO^KK3XqV zJGVyXqgM`hem$n`Y|2!7b9L^1AR_f+XB{WUCpGLBX-RL~qE$gm9*12q%YbBmQ2o(a ze&NfT-AIqi?)anM51VT~dX?%pPN0ZKM5;3d782FfwxBZk%pZFqV3H(fy4hNv%YUCf zSn1v{=os<+70oTSI^c1>BT%bwmgau6Jz~&ZoStJfQe1h}$#Ymdt=}m%j-xj3c5{Gt zG?(Y{$*S~I+nZk+eFc;HC9gV*h2K1pXw6>I`S_vU;9LK5^~|&BalXCQ#}c`3-n`lQ znIZn{;;YVV-?x6+8|~*K>F!43CvEKqx4-CAi5J2QZZ@A_Q{YQ;_ zm!}L8MjvS~@m|?Dg5L(cnq z5p>}@?ar`G(z9?7Iq%T2(i>x&%cjE;Jh&N?B6anS839-!`0h{T9L6W$g9a`wYZ(uBbtNAlWF>|zQm+Mq4u$IY2sN$OsRSo-HOD(A_B>x=65-`vy| zl@=*0t+i~Fx2XTT8f3kv+C0?~j&$%fp-hezi|}cwyo==` zcWG)x@_e{J$)9ILX-CBsWU4lkC<$Q(pUM6oQ&$}ng%_=11tgV5Kq;xETaZraSh~AY zkZzFf29fSkI;A^AKuTH^knXN`_xI+_ynl2U$K~#g6W{sHcecLI?&zbhLgm=RG3ft{ z&XQwqGACc6AI})b2sPZkG-UFmc!8*b7Cx=REok|*|770inI#lZVt;CT9dm1`A zk)R_*B4#U#-||a=+UWXuyepQuUK8~MNKNWIu9*N8{r{;Dl;FnyLSzN$O{@Db?z8dM zVWQf?cZq7}<|kP$TjP9;$Wtq~uTSce_f`${f|}=%ubx*`6j4h#`lsj7*s>aDH>Fl8 zXh#_eWPL2OOfCIb(vVpkZ*3o_&<%(io+GRqN%lK_i=kY7RnlT2ZK-10vn>j8=)bc9 zKI-c8(|y$|YG^|Yy^<5D2{em~~;UoUndryp@&PR^T>zRU5 z3GdafXnYwNLtmeFog6X`s@fiC4z{>vq%?RJ}RN$lJm+wY19H@d66Xb$ZHZ z+RN1E5^g-p(dkWB{qQXgq<)u%VVAVF*gVszIY+*(;+}0S+^H(xL7=4~c z)y7ECorBcjSya3czCTk1ZP^XGudbhgWI!4CepVklT=?`-abiTSsz0b2Qit^D&~M`WzI-KDk;0rNE=eb zC6mEj&QGNfO>CvIul#*rwpSiGa3bT-G;keZW6M#zzwmySJXCsnR@*R7=lbops#nhZ zA*cQ2hMhsfy(qay^F$u^Tf&mE32vXGA4%C?jglM2ZY)%$VUXzs^wo5fcI3I9Mcdnp z?sr!eDagsl+*2pR>grsunjg15blpTAedw zgIjTiO(B9T%TySfW$rpkZVB%%AGd6Gys~szF=UA-lYkUsrz|XKh`#!-{^ePSPeu zs4g32D*A=g>tgY|v6WTV$HR4`;khqJ;94Y7P(Kg%VMCi#B@2?}Tc-V!r_$QH?>#e8 zmN(2;BsoR4Es)8y&axPNHS5qdu6nvMr{?7#9n|c8XwYD%d?>e`075ElIIDj{mR%99 zM)4sjnO)dnA#LXR`Ivhb$wigy<&RcFg+xkIUluBQ*jkO$2^*$ZPLyy@|cl+*dO&crej^d&Uqv@t}qnUb0rg6m{{27+&?{+e)u4` za@CY}eD`?nZFUhgP@eNhCX?sP^M<Tr~{$^ZJZP{kyzGwN+EVA`ptCRmpo6u4KioNJ%E$c&N zgWb;eu-cM84kv$-KB&h1Ol2nq;Ljh8X>@KtG5o~f3XGlxeU>DEz@_lkAeT~YRMBA& zMe3uR$Lky$%w_TCHq5V@X#Y9o@3>c#gW{_W)iDknbt#T>FXw({ev(0cPv-gNo$>zH z9Avl7z$SOk^&;Urh;fVV(WtObAPO_H{LSPK>`!%M{))qM^5Vzy>(YD6wHVwBiGC^= zv+F5?bOmDk7c;?0M_V~R#J3;fjt1VfsT7=0y@~#G%xj>{`P|d6BWBf6G3?zCeKWz< zYoqR{t6zVrs$6FHpdOcwh6Sb8sAQfdZzG@KQ!=pQB$-00Z?lA{_F;G=U)~rE%|AWf z`+2G}$WK^Ad0p{gy6xHoW`av=n>3VMTw>%;%+@EKC~S>2qr><&0A;Sy{I7`s>9nz_|A-fan@I4C#VO$h74l0Jkyba~i(i)TlHSqU z-P2b!k%SdL0o;0gKK@nxBiRr*!k<1vPPFOpkzN(%);nZaylpIMPyUQyo$BIEbJupIhUwi9`&(O zM7%zwTKss7H>QpFM1t9t^ckSMT3cJkcea@e@B$ULE_UQIf7(}dZj}TegOv^hy`JUG z>wo^RJFSU`4_MXbp7l4=Qf9x6F)IrW$|NT zv-lSp#o(q_8EFmL7ek|B5JBReif0s+x51>13vfCtMC8nDiNJZ_fP~K?^UbvV zs-N9)ZdVMnGlUFuMEjf`?ylRjrNujzsth{~Xx^l;=>RH5vy@G>-1N+MpEr%&t|1E`9*WEWt_WRE~ znatCVN^q0ydP`n7Zj zkROHP=wjmHIAml8=_&OlRn#~cT-LZ9pyc3ou=J}!qc=|Y;RWCYDG!Hy)X~zi>JPs) zsHc`aq3QhcMg%-eHsfB!x!bL;{dD7d9um2vsmw+daD}XYCvNbTS5)vXjoLaVNhmwi zex-h$=u@vGOWl%0PW+c!@O^@}jPhsCq>mboID1jSdgLjX zq9WLrgt$1R#dd*Ijkb%+MrjI84i2>$^L2cOR2)<+Gmr!r31;3W=iDEjBq^L#D)XP+RVu;Sg$)#J^5;?Qn*{E@jh! z-N}<+>Ju;6SGxP!=5KnA_fHYF-73;5R&TGbMLUNZ4sAyZ&gA1~zvt%W9()}4ZH2M{ z70aO$KF~;K1IuwB8(97iI0cQB!yzW=_-t(fLO2p*zU&rXBbWcgMMS7XEThM%%SZ(xA);FD{A5qxMTg1!9NmK&)Y>g z`+HCb@C5POj$4zC3`XI@lfb1>OUYEx34f9J%MDH{<{sxHj>Xl{=g97IFHcWTXW}S8 zUBMkA`0!jzL?nLaIkpkkSjmNsV&qaT!Zrn~)+VkEmxA7Kdk6ASrmZZki~%yd?drn$@9 znFXQWW3*Sbm8@8IZ4KbZA9OgA3i)`HAdVEiJ$WsDy9i*aMZbXfRQc;lSGOec*MCo$ zbTv2Jyf#A4r}@INkEU2thw z)J0CC-LPw}wl2vuBO5BaDlWG0+v>U_?*|=w#728rQ3LMyvptvavwxmAsc*$v-9{B2 z#^Gv&yx9OQ9&9VF#6iZSqcB`Di+V`_I{d~+;QjgjDd_J~TOS zpzOJK-e%oU3E9oFr69?sDaoL048%aBaO2s-op&p5>*_f=9&UDRGH|T>8~>MTrg?3& zoYRyy{u-Sz#}I*)IbyNSoH5pJHQ=O$v4(!U^N`Xb?6t84>PaKfWjhl_GKD{#v?KC0 zPFcm@Wk`uJ4v1`DfxEDPm<6OM>d*lATKhf%eN^9bn}aT?Ub-UBM>L7m_OShu%q*XH z`{7rT!hoG1DEY=m(148`gCw%aGG12a_1bA*5%DGQzpN_U5(rk9C5sNGdOZ&d#@)b{}hdClI8p9m4_cDX_UKh9&$lSR}Jw97jz=ul) zC{#rs+uRQO%_!Q(kVe{2(CP*6=*9CIx~j}};7EublOO4;E^%I3wSt_yjwNBV)rzg4`e>u6~UBv;W&J&}A> zf{Z)kCF{La+fL`_V)_lqzdpT8C0mUAJ~&DlRg0vc{jo42o3|NCyqzNCVK&3@QTO_* z_OeEkM#{z`9QnZhx84K|D8PjohNwaDqTxs`rR36R zw~0>0MG)QAU}Au5nW6K{jsey&A|7-hS^YIhTKWx(Z&k}ywzr}JWlm~paci2armA-G zx3)~btkyQ+%YpzLWV36GUuL)@nGSOm@g1h5Hub}nY*dJ>%$d_}H=DJuhZ4;WyEN>E z5J+^*=&H<*ZC(!ye($drVV<<1f45^SLtpetS?<7E*(QiMbberPhXqE&7B>^J5z>9?#4zKG@pk#BG3Rpb-y51N^qx5fDWvh3T5v z?ygqyYq)4&uENpLI^?Q=*t1QjuTk~fYCnAj+5S<4$%C4#f7ji{u(ox+gr9Ylex}2t z!K!oI{V^)-(f87H;rivCz}6H`yU)r>mGpPk*$#n5zhArKWrq!M&9a*M#FuqO-4It> zJ3BQQ6DcVaGoKRb@TpJqU)>vObhR`&e3s=Q4GmSDFFifxJ$4l1YP4Dya5Ep1qo)#s zrI{l{d%|ahx0qwjN%9yszv{)LU&y(a3+euL;%h|P<+#Hk%BKmZsdapXE?b|C!d&Iw zDFdx4`@7HhTZn-yfrW2~KQ(qlgedtsc94TqU3-9fv{-jFHZl%eh&qgF#6n`{QHd{T!KB)v4O&Ecy01#qw@0A+suRxcH(!V zt%~Jug_{_C1o$5=2>C2=G=%XB_E&DL4g@s2;t|fLLNJIHRuX4>j{gjrZj7W{eRuQ= zhWBDC2^=E<63EkQ7+n?AoduhPInKH2KRYzVtizMZ8ufE3JoTYltHbA$o7?_jv;t|o z`|cqvk*3nQj&;a|d$`YTqHYwA_jX>;V@`WrU{v|S&EtA|=uHp}lraCt24{N{EUcoR~8i}b>2p;RPg zv!CeBdA9;hgpFA8pGV|!WSX#Vry$h3Zc|M+tNp&YCjJzQDxy?EI#1qCbWuQ;ca9l3 z6^O%d*I(PP(3#U-uQ0dA9bZDjGlI}kg6_*CqfN#10DIirzdzM<_|b{Vyi$sfJZ+HZ z$9l4YFzvLP^!8RqKV~IGVdBIsW`r*#BVEdHN;riPhXK`$>-1`|{eSSsRy!lg3*HBd2V-l3N zQLzneMn=8+_P4G^M;ps0;mSsvwAtDr7L6?sh1w{erUB(8 zrTcfJbdg{jiYQ6~`|2PswDoDcBnwxm9fukv)<5XuC4D9{tZ}cYttd`ZJBe5=-Q&Oc zzE+GRs=(D}B7gI7OGCTB*j1E5G5gA(&+k&y(K^630_O*E?oS1}<9v;ibo@|6Z5Fj( zb695RjDcC+2(3fyLJE@?#>dVcMFs_5jxCsVnRiJXOO*rj-&`E78a}8s_Zemc^y5r0 zd`u4N|0gMs6jYpO>#?!U?S}qsBt=>p)==mVwE1C zjRW*=i<1Xe9y&TL^^1OnPwctthWK50M;lXQxX{6dON z;y&~fHLORKg%d<6GwbS!O5rJu0TzriGs!*1hTKdokC){VBK&Q<&h>TW&<g(&RTG>w%L=~8jV~kJejH_8Lbfd&qYL)3LKprOHaG-08bRc{?9i%BtQu8&gV+&A>2iJfY|BfVH}%_Uxb5eV75d1!m8E~#!xi;7PRDIVmO{UjjK6+E0n8{8Cj3Rfr?~?9eyut z(3K)*5;w79-+b=$)HtM&hsEemtCm@$uRvLwaDU@zPn(*Gi6=eFw@_P6j`xc-SqB}- z`jax1ORLw`!s}BXAOE$JHh)mpB0V&~;Yg6IE1Z|X4_)+-{^Ej!h zX#dc&>X0Tu*D!G0c>FS~diy8apL6KH>#pH$Gx7F&T^TM|6=~b>+s#$)1@EP7H!O-<3z=-5q_1qQ;6KVMx$$-->?Pw=F~`cyz|`mRD4|AZ!r;9 z=CCuVg2uzyj`0hP*N_rKRvt(${Mx@#^Ge(Z?YX~V4HmJ(Pk0AK?UPZdOTvUuUO-G< zhU)(czu)4(X`{h&+Jow;J{_64VkuAM%}R(q&(xDJ%Ai2P$5%U$lOR`*dlkkwxfoyL zUvPz^0JBz!#t=3XJ&dgql{svp{hV6zhexs?Cr6ma(Kla***;sRHa2I)b>_lanIN`g>kqg5n-lVhH= z3z6HiDr3!oEz$cvq>r8vK4CpCEDv+Bh+o5WkQBPZo$>PcQIViY?e982u6!~{fQkK; z!}qn0@|wmUJ0f7I&>1Zm?~t$$V|j7u{324aE1b5cU$s$LYJf*D)Hw3$cMn*@d(?XC zpHe(Ne>R^mw!inPUDj}1q5sAI#oWG)+T*W$*A1$G$7xMR&2cz59xEfrf}Y9cLF$0! z*E)$pd{|z%UzPc^(kq2Y?pGy@S>+67FB@&$?_C}g@&Rbc1N)}<;BPj>V7v(wscU`H_CN~E zXH}_Ki7;s#NJFKqC%>j8L_;!HyUxODv61G@?B9qHm(7(B6z7F@*;JwRid>b$u6a zoe5=9iD|$e8VaGewA@13&fzx)&(8x%i=B&1*C7LKUI0-xTQNix`r=SibhJ^uLSta} zqpk&nP$Cxu46p^cPJ>#CGG=i#bOBY0AV+Ir-Oyy|<-ow4DYGW5Idmy)t;HMYqRIvILhyqoN8 z0@h*%UkkC#K{(nx8DbF(`c#2&OfOn|%}Z1!9*f!b&sk3u2jVE@M3wHF?Hv!$C^?F= zAVxjIvA@#Z79u@n!huMuObYD!Y$)Lz_-?rxNJA;fi4>;>IXcD_>Kk{q*R?{2mt_Pz zU+gxJ0BjKfx+&I9Qbsj|U_YN7Ztdk1O)VF^d;ZgHteqX~GcNe3Pe`2ZJxF$GB0!|= z{pqH{QU^1~lnXm=;I|6TT7~s75bOowLcoxfleLn{oEH&+sCS9CQ9h}c2g{2h%+8$P zgwjb7H88*1OKFkO8kr-#bw6Nw2Pc#2{2C(hr5d494qc*p+3^xKjcC1<2H)>)5 z=`b>JX46e@@kP)OWmnht_nA+L4Ze9K)Vv7j0zDBE`H~oC`-^i3RqyfOD|-!J*?SEw zT3RDXBzkP56bvFh?2maR>Z8d|L|=SjYO=&aLcx4N5q4~P6F1BD8ReBYpujFYV*{`` zm(6sksQK^l3}DvCz~{sarh>~NFM^$W=cV7~)(?2!yg7M+{;$DA8S^Lr`=dByWST-! zl>+q078`8C$o=ndLYic!TD2ZZNi2{`{CH{j{njJsNTd)BQotig!ZzxkpiXbWqcvJY z@;cw4LA?8r4OX@DCh&wPkrBFK=lw4)40DF5sTZj-vJ2>}Zg{})NcBG)8%DJ*wW8M; z(A+q;AI;F)tq!>DeFYY&F!%}FG#M<~?c)1oOv zLmW>0uU|a^#n|S1(sxo{M*GNw{q$C(nErcz%ix;UA6#!tvLn_}tnKWer^tq!3g@K# z9{(;zk3qCP_KVXQ=_P7U4_#va8wSJ>u|T+>DPU$u^QD?hcU0CKgT9|h^B!e#;?~eyxDjK3aMfh3thom#h?^((_&ll>h0i`_Oez9>@K#hzPuk=kI z;1Kr?52HcLpd7N+?WFa2C>@Uv-h2z}rUUa6rJkkoU2bsm+vf7)wIzg>hRte%;@2uD z6Dcfw<#xO1-6sEAT`yntK3V{rJv{_)^9EFv1Zgtmgg)DqT2ytsa_($D+ohSvCwtml zd$YZX$bl#mKp_4g33u7lNH1T$WHTGWUIH3C+uL^Bm5gV)O%6bb$u9s_CIKoVy9=1B zgGhFf2!h_yEqU!NdkxB`(?4Bq*7Aa_rV7y@PoCIz1lq!l83BAVBd!06vLTLAtnKbH zA`ANEQ>O=-OWPa3Y<|6`Kt26Llsa&SCU82&2^;F`lbC>Dq?M!jq+Mk#rSSn6umx4! zRiOiJ?76|)nD}IoG}wbQ_89>=kCJgNg;8_fMk}l(AD;kVQdY~Y+(0rzC5-Tjkk+UT zsrAEl5J*}_CH8Nh?=|UTb4LrDdoa}2)|ypk0IL41awaP$Sfc`bjm5wI*03RBP#pbD zQj&$nkqO!$PJK!XUnOToLlW)gvRcI_0?*Ut9hf^Nf?`jV2J=Hvw{QnWDqgYyaJqq5 z!56*2AMmd{gLL)1r@SQX=^)Zr9D+91Fc!z7mr#9V?6r)wAMSnPEe2c!7g&S<&0~;C zYf+6-!u5HrU4@fZLR zFMu{1C^;m2{yg^k&-v#)KrR^m)EA2Z?6oW1M6m+tx4Vi0IyOUI~TQRLC`ruCL+{os(cYhWW4aZ z-XLlbPQqKzJy$zjla~lcZ0J1d!D#lrWN|xKh`77+!8^ChUWW6$g-rkB58j{s+=Nog z^@B5;gx}H+eg6fq1ZdPWG{&G3_;0@EH4r|tBvvjmdG%l*7J&Opb>5@48U-1doe=CY zri9UawJ`_5lNM!%)tXm8<>-y_XzC26Hace=$0U%shRKzi#{?9?9h@?JSMX&V!hoeF zLA$$HbPSJO>rQiOl|CV&YBpny?B z6WU=)a8D)-0bFtfNk(RR`mWjZDxgJVa!*np&=wUHg>$NaIiAhW_!IUqB;-99l#>32 zFahmFrbtp}gvp*_voF{qyUebO{Y4y7Qbv8s2AP8fiJ`3Ii)-aR1y~RmZd0FTcf)z@ z!~rZO8W`CJZ&E4NK3dBNs6m@>d6%a=)Y(G*(?3~7ZjoV;q-_z!I_G34ahIv3Of^1Z z&d@(Ar3WPg?I0N~ojg#A3U`Klla5fX5B>zPn|-c1cOqxC^Ns>66X#t0ZtEX^o^IVb94^Whq01=ro5ujZ}Q? zhy;#3z(CJxW97%(*J9nulbdaFCAp%B@$qM0+b>8MLp1nC1?XK~J8g}>iib7ZFY-W1 zis}I$y8CPAL!_~Im`szqOHvEZ$%Nm=lhs!upnv4s{`FSQJITYCh5A8P;Pa{#A$IKb z^gjplfRFjhQ3W4ZI;##a6l7aGvREuU+qf4g0-UJ;*=Kv|GBmEB9otTSDq;L?$y zHy>N{GRd?s{`h6U;AhJy^Ex$1;gYpW=@h4wCcfDS(G4Iq41M-nc)z)|WePeoIle#3 z@|L(qwR$uLB$NjAv7ICVRv1iG_OutsJrdx1rsObMa4Rrj;<3rWW4Mj_YGwH@XVeLs zTL4FjCLBmZ_a^d!yFWhu@P7yf{RCvOv9T}sObSI8dD7$0t-=k|hF!ThIq}BF$8&vC zQ%zz@2dl3&XpLYHA{IRnF);`*&L3$NJ_VeVX{cNIp90az=k<2KYGEnlepiV6ZhKuo zk?SwF^C0zThb`TltSjkyACSOIaAQ!A(xAbh7kmT73d_l{KbUg}=UWrX2q?k>o@>f7 ze!~)iB^_W84;DCC@D!+elA75M=v<63)ia&iyBP4i?w`DH6NHy+#zx}hcOGiMnM z^rugssz!C+vm;i}INe>n-&<)X1-0)PhG28Vc!DF)21oA=RIQmcCSBr|R?6ya=M;7R zw7T!EP-J5PcOr9$?i^JlhECbidvkj`H=Je2)AMKwt`#kslNv1Zvl|%~Ua4^4)s=^` zpI9v}jD^XRz&S8qFU-*AI0O@0bhyM9<#sJEjEqAgP0I0djq>+fuf6m=%dvF!sqdrz zvYemFccGxhd(#VMS~*f^|8;GN(g+JAY$<1)Cfi?pT@;_sY}oEaK&SW_p=dKt2=wR$ zebH}~9rfRxt*WpsAD{Dkymv3#o60_@%&C=4F{lkqgaI2$KHFm7kpK+9A}xRg7qk7z z1o29_5xt-hk&%!V<^-5kxi8P(a@mSAe6FB;HzD?aXe~cVW}jGfky=SUDxXlohYfDh z7a|b(0uwC~(QFIPtcGTi{OIo`>@oIPeYT6z);5Y383Qi_z8bWXfRc4$YV)r;%h7KY zXLk8#0`>Fa7@7-W*YEW8^!{=tIwv;1w+RL=#hP{ZT&AqpI?mL2wDPv^o$6v9KQbmwnfxzvPrd;_NAZ~euj3&3E>DiA;{x*({fy-vDTe{yj+U-v@F7Gpg&_ zIqO5JmguH>N3^z0H>+Z+&;bRKf`qHM3uK!ED_Q0&&;H|4Z-Is$C8bNnB)b#S4URN6$ti()xq zzVSr~5LFdrRv0R@r}Rq~@3a9B$VyI%mEkK~<4t~^0uPJiMGS$l*pZ9eg~J*m74v2^ z)HHOwyjrbN85roYOLzNCtEr*pjz5cI>Mf>hyrMR-(pJ{@TIzDm&hd>x(c33fD8SHx z+f0nNsljMkf{4z$dM(aHOHZX`brj425g=W_)7hGP#6=oI*_=RZUlKMBs3{fdm<_Is zep8(1z%)(Kq~L2-Ei&%oP*>0tZBi1c)XL5l#|un1HY)2DDtDaE>SE#Izhivr4*~=u zK6qIAxy3{#BVZV9!iEnTlEv@NqHtyBOnLS%USk7Kg7*fD8JJp#6F5PgcCcybQKbAA zqdyc9ttSbERm?6&GX(=hTuvJ&GSj%IMN3Gke82;t3|}Rb0}3@Q#J|{`Q+l`udKzj6 zyFVY8HO)j0avyIy9~qZ_F&fy0ADT^qDkBTMukx>{=J%%YG|3m@iMxXEC^%ij6|Ai? z_L-q6ghxIgxe|+%M>@pmXsw%2G$^Q5`3na+lBk33!urlmq~a_76qF#Vh584?4Co7_ zb5RX6ukCO5i}t@>wHqaxI|12)sT4WJC-AR4;e;e@O|?;$zh0OUUg?rty#6ixti@ru zb*lf>{&{3h{qy6T$2-gZpI8_7X@-8?pg~J?c;A&EF)g_l#MIlZV_P7-GJ+%mH+2^U zUndO^Q>PRZ)IXprJlajwqE~*l!CufVW&Rp@N^58%*^j>7n=wqI+R-|0)9rhnRk29Y zfP&fd&$i2h_1H~C8H(^l$&;cJsqUCW)IiNs+2zhsL9cDl7ER=Jd0_uO0Vd+iS-d5nm*tffa1(gBf!6-QVjJ2v#cl^HC3AH`Mcj|ct3aoI z<&Ryh_V3vAfDL$FbFvvtV*!zREkgLF7B?|-`t*meBex3YaH~7uUl5}P;TxbMsJO8z z+6TfSpV@-}!XDzR$#AHfO&9`15jaA60qDRI@>(f;XlTe8ga1R|H`IR_xrZ)+L2W)~ zlkY4zjjF=JzTC%~hZL#_<8y6+6q(WcQ`~PzU&1x*g6f-HIU|Y-JI`=mf_PY zM{*NH?!6NR4qglfOY~8pzSM$YL*KnX4$4h&^D1ivIRL$K2oE-rBub$OHc$zTw+l`d zQ{B_9r+pA0q@BP<0z@nzVJ^v%ydcK1l57}H6=PFnTgYVnq|0h|=1e;YqVe-Sv_moB zQ-yO9^CFSpBIx7_CNlKF0o6r`UvdCM2j3BXo^s$k5!?<}Vtj8eeuoel#Iq#KMt{T? z^#>7jEg;V`yVSUp$vopFQZVv?tZe0fu;& zsw*JZGOE514KKjF%+}YLpV(dFs)`iSmvGUPppf?I1HOw(*V}G#G3M{r3D}Y<7 z+#Nj4e|N;5!BAz1v1!hns4?yGoy-l+bs9v|7;0k0DBIh*7j61_*`tv1Ece4E*P>m{ubCZs+mK4=U;n($U^plZoC}1`trZ%7a{$1f9!w0nZO#m!%-~7Q0 z0)Fj3YH(_bOg@QdTF(Ok=TWXGUZX1vlh{P?Nky;pP|Wn?WX&`86K41dNfBWa(opJ4 zrCP$-rg#&{*`BqvHD`qcIjEp8wA1#(2QK=IZ!OSJ9Z#KlE6S>19nDFQunnn6)PMa< z(Chqj#1QQ31PDmPUnyVV<5d$vl5?B@sN;OT*xTEy6RN)TzPi79{NI%nt&tm&G}Laj zqg`zQlX7XcN>}v9aJHcLj$JH%A5b8G>*kh~mA&2N>XY&MlCV(^Mrv`SYxP^Ur=<~@ zn3|erFNDU&-GXljqH~bL@VWFSas=w&faE^-Lj?Ns=TEChTmmr#dcPFjWp*Jk<#Yjk z62o{`yQxAM6yh-uWCI;KV=%ZVci@6PXdCoK+g4hZql4`7!(_QL`&&M~VV{`7vBbn7 zcu}5KnE>$=y+S=D*(V$P*p3EQ>3;-byym_m8~w7spL_tR(qF;Q=T1jBc-7L7`hSvT z)COMLG{M82=A(qxo*=?6>R#Y77gvXd=nz}w6!K~FH8>yBflIh43AXF>)AQ6&wCN}~ z)OzIqceoPY%E%rBnj-<6XG`^)Vz;lo0cx5eZc9cN2Yzkf4e%n>es@<6v_^zTphqPX zE{hLF5CwsG+}cBCv0?zIuQjoX#p~r@4@q}A`rCL{~AJQ zjq;EH;KFc;2Q%vciojj4VA^zaVQ9#+XU~dvVsGJZ4FO5oQ%+t!SvHT-C4O^bBd{_A z3=ifCNKTzZqG`9R$TJ@!kQ+uKiG-)8lG(RlB@|A;ZI2H zYS~QCd5Q=_9oCV?FqAEt$_>t^;7N{~n3*>uDgV~>Z6d~rnE)_Nc8E1`&qO_6F^HfH<66j zNu*U3n4*Q;eH}4VGxo8ep?HV$R0`J7&qvu*|EC22a`FIcI7FtivIKBnqCfyS8H052 zrCISad~Np1CtMvNXj}+^K-^0D%@2Ugyvlm(%2A30dz?5ufxWvilDkAYXEYWLvK~JpU1n=ok=GDH)Jmw!lamz4NFa-b;NT5x@ z=387WgQM<2{x3@e(|tBA8TlIs)?kgNlENSE34C?eE<>+@`E7Dsl>!s_krNJ)MvDjvxL_AObyS3)(F6h~CIN^Y27*wz^34 z6eB?9gdwE|1cbysM?f48z{25YzKhNm##x=c@QA_-CK4pt$q^J{*ds0dCt_>7t3{1`8b8OX&$C^C}(jYV-=M4Bb<-si^z$#{$v{ zUvDNxjyZ&u?r8P#fe6I9PoN?83-#=tkFzVk0pL17C~li$MwTOq%@zWarl9Xv0(G)u zAo-(-^Cy9#-MOV`@uwuX^|2P&I*qcx{*Ez^xSWlo2a)|T_pa7 z_D(j^7>o^2n1AgKkYo`_$lq4!gMxv2d{_>^6nXt01lr;?J_m5DLVcej-x|A5aVTHK zqs{Xo`aNklHbfxv>xEAYN}*f;0wRF9JyG4GrF_^@b}TLZT?`Rlx#jX*mrB^kxO*(^V;TDBLl zMV6!F)eY4G&mRVm2fIB2W7?$q19-n>L6{GS)SFU1zp+RnQnyRQ8l%a~Yu0@0) zv9dH1YASioh$eG`PhwlELew${deMij81dK;;S_pPkol%_+oobVn~br1HSA=$^#Y*G zB!Fm4-OOF>>rAX%RY#tfKwN3Nz7)Dj4Zc};hKRMU!P*ZE!Id0*W;%GG>fxxst7n6@ z2J)&@l@n>`hf1l4GV?kfb$vk}{MxPY-p{5hFD4|%-Wl+~Vv7o-k@C>|X6j@K0YiqL zK93WAJLJXTkT-m63O#~6crP(4P-t$w=AsrKPK;0h%3gq;%=WdkiXNZ8!xKd$K-C%x z0Qbp)4UH)%18;~wy5c|;=b%DDfD1Jp8yLQ^X|84u&mM#4$DLpb%TlW=Mj^iQTXewy zb@l#X(x?@*fhm7UN)~b5VVnof%^sx}cohu)=e&FIc%9Y|KsE+V5lfLZzk0?{FWs$5 ze?yP_`8Vthq)_=0jIIQSg+;#Fk5m~7mZ8C!B7kj*L*M#AMypUSzLp&Y6*Z(uRYnO} zn^^hm_+vRq*j}6ap$Rw~ju$;=+dpGy^1s(t4F3oj`2PG<^F~xvi-I{M_SwY{fvgu| zvbq$FdH;lkpQJ}Fmb}qwhc6I=OFJk4c8{x-3xEF%WGEh6S?NsX^-UQ z?4G)|)>aeHH?%-{T}Or#S!+^5L3p(LCp@1Di;ILE@e|~wqGIp-wzl{b?AV3dwP0OIATT~fc@T;oxA?{fJEl`7Y7KY1*bDu zv%Z0!vmG39;NjaB_y_n9koM4ug!s=`IqVEb!n=VX6_%#k@yAE|8_}wZuj9jyQOm`k z-<&rh)zELxwtFLXZ9i7|BZp)`B>5ud6I8FW>tAP}dg!>XaKJDLA|pj@8JfB+!4oVc z4cuRB3dWsJg3T8zWVUj{M%$#JXP1{@HK$awFs7&~MGd%vaWT-3$`U2~j5eJE7Ikhp z#H>Yth~>;`y|%uG8rR)Dr1L7m?xP4d$4j}-rZsmztHQ4o3#G9Sq7U!+M7DgtMa zOTT_)y}P^nF#9{n^>yy#eD7h=H7eO(s>%?0l?$$4T4WvgHmu9Cl;;wWl!yD3q%Iv% zXwwn_L1S=UIzvctBx);Aej@IR!WU-=r4)%Yc6R1q3=Qobt^(mPwet4^`ly%BF}@@1 zNK%-e3oz+*lD71iNYfZ4Ht5!93a>QyJC|3LR3&3RaCR#MFI=YZ{6petA=oP5# zD3s&V($c~wK*DjlW#lb>MjVP!Mr}-MC7%C|FbT|~w|80boPLXnZ%#w?jt^ny^D8av zjwg|BE9q}N+3-tnmDzYpN4jViil?Tg3UB|7vUZL|>^3N4-j@xFPKR;Uv}G04LlS9} zRkf&1`XM8~)d$09`s2ZrqHIiuAG2sazyjsAPvhISyf=NtXfB12oRJX$O7mdy!2{=` z0%`i!tgKgFXa6WudI!n8$GWOqT!5q0f&fR!Y@Dhkm4sdGekz)q`On4#o69pJe3dx3 z;ZcVr<(U~0)qMMopv}8DvXtQzadGht;YwPZ`C|T>u!3G+%_%o@Ubo%2_y{s}T+04< z1FvRyS?vV`-#%j`nwS&wXD(Tl@<5m>=_ zPhU!9hR8~pM;~?J$cLx*PawblY@&w1upWnhuRk49jFE&X(yo2cJ(iZ0g&-RM<4d@- zBwhE{gbRZH6cgQ#{OgsVo&piFwER#=++pxFRe!Zi679@VFWkROgdr5IX#>50Xvoca z6zh&lRQFCi?BD)=3=r6PQK{fiDu7t~0<@(z-N}GwnjY{Sn=R%r?EBi^Kpm36rH}M| zW}?l0ULxZB#veQ10R=xV=~{|5Gkl7{)YLK!wT}>-=0ZD_I7~^cit-mYn-gfNMJ-?M`mF3`-wfM<&0BHn) z8s*H%!Hs&!7E@6%MN#}E+FV>whdW3MFmmwqGvT2?BRt(+{Nzab%APD=qkr_9d(@j& z(+BE9+-Hg`A_FFsg0%T@7wW0C$DgsoRq13qHQru(djC^s*gC zXtYBD$}}dqlu9q*pJuR-=e4GV6m_ktUVV{hVpU!mQPgvUh*u4PBohX~&IAB)fuesR zIixZfRAHV-dbxpF<>qrd%S6!7$Lk;dW_{XEpWMM zvDA{1#lX;2Vy6KS1!qn#T#5}gLBV#>O?@St780;JDT;}I&sCx)Sl$dHjYS2_r#1RS zp>&k1F|B@nHrUav#j&h3lAeJbu|eARAb$M4BkL)oB&_>K!?a4xhnJx6hwQN05d%j! z&t8(Jr+2l>GhyjtR>e%bH(~>$S}NEFsW)_Ym)qU|9Whgrz!rX5qYR|Pq$KkW5w8`& zn|}qgdZD?m(h%%5Osnxa4wxMWkNt{R)1w>VpTKR#kEFdlp|1>rTZa zz!2VBzkmSFnsbhE@7Z`ey!4=q#H%kUp0MSoplCFm#4>-cz@ujR(fO*9E>cm-^rF+G zsECb~?yIaPeFA5QjQ>W8W_e{Vk^H+{3d1KUX~I2(U;0KQjg^UtjE9oM!Hj^Lr_Xu>5!C=Zjc5kmF@=V zZvH*r^PlIOdoF<4v-etWGN}o`p_h9CiUe5`eNe*_@VM`nbVOyCnxt42U_zeEio0|> ze7Z)^wTP&!k~6q0&Es3SKu5q$9DGj*edi8W|KNgWyZVg8hLP|JOXl@l)5`hQq8ipe z1d;~0o$0L_<&75mc{pKFI%wnzs7lg6y5KTQrqh?0HeW#$EaLEZWzA!<hEA`68^>W zU^jcrLu&^p1UiMFL%e@u@(>DKm-4uPcW!7WP-D3O5hVajsdR4$!7-eY4>D4yaMf}2 zR1V*b0?)kV9s3aeb9CJp8rO1-?QY*Mv_`Lk6Zn~XT$66m3Q?Y+x#H<|Y zEMX;_E~zuL144onfH>t9_yRv&=MioqtGUlz1Zs#@jxXs^$=CIpui_gtbVFmfc;F<1 zMCf;#gaEmU?AkIqIGDhzoX(2`e%rO@JP7c>eNNk84uZe=;v@c3x)H+GDI}}IpZth~ zc3~qKG4b8}&<9##V@>#AO|+;RLA(UAHI&O^mj=1-i)Oi`(Z%__vn~N%Vy)~f;pCrW zncTd*ykynX;@?_s8{5fscHt@dBkjw$`6uie7gXXtO?;!GvZ7}!^ZMoL$YffE7%lCj zrubUodngvs4)BsvXt07hDhLREb@XH#aayPyi*L1t(cxsM1N(@U!LY?k7~IfUu}KsT z-0)JY@x5IHt49b*%b$9SuYOiLK;oI9Q3_~iQA)(bQf6|>tmnZEq3BD8n}y&8PILH1RXdfM>Y4faYr56uSu}*$B4E&&o)kj|B|kdS(q5Cdkwszi?MPhy?lm3JQ;}c|aeO z_;1I);AHc@k@m8KoF*Zx7|}8I0z`l*xf=*0E$**R1W`vqA`H_$T?Z%(46z4w8;3vA zBdn-f$Fj$6fC$;oP2)4<>Is;E4chPk|N5***Mx>mBi7o*9dABYW2FQFe}&R87hL}$ z!YH7S5ctci5^_8fGoYsq;)u`@$gCGjgD==ukiz~t5f~+8xG#eHIED*-8Wyo#dV?sw zjW5Q=MN`a{lXo8mlF;2g2fX_JRTnp=V-FHf5IIXDJ#1UUn5r4y=*)hgAuxTr94(3Jpi|hvpYH&yHVGx=zgG)}sn4g` z{G-`ybC7LFE_JmRrCf;ARS5Ku~ab@5w3k4be=(IS>jd z#O)4AQ+%G*gYk=$h<~W-MY2>4VU33E$SjZPJfuoZF_(GCfE+uJOMvpNNeQaNIu9@5 zqY7k4x&5%+HjYl83Cx@ZOcKx0aX|lj!;+tpY9X%taw93vw!pwk;>hsTVHw+Rx2cHL z%Xc7<*yrM}u4Ig$;m8-DpB(%8++sJ!4Le^O8%E(m)3gZC^@vM3zLr9SK@$GViJF4Fx){Ic}hS z0TZ62q9>8xjU^CVlZn}|Bf_dpEv>8+3gg}Urt}W2AZww%QLM*cya9RSr`*N-Z}qOt zZaP&2>NRt_fqt~gewwrOa?T#ePCdiR?*CGvc^oZe61{T&N}BQkjWqaKOCmWOo6y@A z@UvvIYE5M;yJdZ+-l_B+yS&AkNLg-f{S%R2g6oKrfY17iaAp25E&Q zx+oduSd&wgGK#T!F6`}eS7&qczUGf6{T6d52GuL0_qngLBYsa#N}#iW!F?@n zjKi=W6E)tPl!C$@Ik5^A_HX__N<&j0cqI9I>z5jQDl0wODCfJPs9rYpbZ$SLHiSGo z7b$l2?v`HIzRM0ZX*h*b*pd0LD2F9ZUt~8?xU42@sL*?Oq!E9kDR}Dic4bG57{mH5 zMmf287@$j007Z3Ob6E7bE>FgXH64H(#{vl6cd1qwt=2{E&#LgbR08lU+A?Upwy`Oj zC)FFBFl0unDQ@eUqh>02X8iAYo6j#IYSP~yvw**a7t{u{E$Us^r`wZ#32Hn3BM%q@ z1J0QtdxyCNF#=mXAn0?@dtC6RLfc^K>3;9SEk$`KL|Y$s)6s^{`EuI)AVr=b`uE;y zE-HyX#xSvCmQ>xFUx)ctU3!12MemD7BA17|Wg2tj3(8ywKO6??csKhnVH1gfsNbEXAt?=iTZUsRqbFqt~S2Ds|?4}P#0)7aQp0EbH) zX;{~Wb|ceNn5~VcQ3)O!gG(b2uT%fIFkvp4J8rcZ-IOsEMy0W_Uaj7f^f?HiNueeO z?XNpPvHPpW1?vZc#5^a6-~zgD`;^maHQ3RL2=Wq2F?+MFlNOYKM!5N*%`&ez3p3)@ zO$5^MF|yC|nbcBRT+{D|`1YeH6*QJ()C1Q2KP3drmD36XZYo6;Cean$pZJXsiHAmp zqz^X@H;C*^k2c`LQ8h}-_#Nj6y^ogudVUxKVmSz`Z&{5k3dFlUBz3n+L%&i4*OvQ~ zFQ&0v5%}Tg(5>I?Yj7@UcIObc)Ct5M9(zp(IMVE`m$nyJ z+WvDDI>AJthc|Vl6IRU9rmGQzm_ECr%2fLyE8fylXKwUbrk%YDZZ@?h04?f;*| z$rtU-CRUW7&l}XpNf-EW9ykFgWYA&uVij7yggBrmPu$$F{GI|RC)7S&74Clwck_9+ zRe2)m)%^x^8}#f4@Q~gm((r#%m83U3*?gnaj-0=wkL=t~>UE-XxCa>7Ney|OZajKf zt?!*S(CB9e93F0N-C)q3C{!YeNz4of8fl3@nDA*V+Fp<$*dJ-!2hrm3th0NJ7F4P7oJdhOF4SI@Z&!ZLQPG9|RlA7| zv8jP?yM2p$F{?8XhM#@~zasfwM#awO-%N{vwm8gFs0CBRv0e%5Xvd8i5X04wYv9rW z+{1oJ@6G?ZejIV(eZQ?G{M$!SwV7yhXCY>tiZ(^Xs6HPx42#qH8R*8n)~*mbCY`OQ z&dRF%vJEN^Z78B}$bZrujWD)C@xE_%us|*qpkP;IU@3jxuqz54p41mmh z82!8}So*Z|R_c+E-cBHvf^#15KM8oD7 z-^g~fh5^10X&Aw>fl=n1-BpyzIgsGIuRhQxZuE3=Mkx|5HlsRD`A^+nwNI*?w?E|Q zn;p{?R!iwOI8m|e3~UU3W;a}|Oi`Bjj2E8%^a69Nox1zi>w9KF;*@<3!l_tw$z!kY ztrN#!mcItv9xPB;iTcIxDB=(v2F5I`M25#gA26)@2$*9!)k^H9q*b0b7s*8}IkJBmlJ7VlB&eV;k3 zmTXXeToC>{t?nI^$5D@Mn|vP(qiYY+Y@TBlwI>1@KMPUstWniEb0 zJXAWbUigHRZju=58(q89L>A53AzfqN!bt%!vsFZCt;rrOyx{uEM@eqLrm`9309~%- zYN*Xgx&&G<2yCI{w$#A&Ev)LO|3jM^%Z!FA6 z_D@{!NbT-CPj5Xx9qg7$_@WOwW|Fom*nbZ^Rfa8aE}r9xB-Fwrw1 z#5)Tc;(rESd9C}h?oGD?-k*3 zb{#%lfI!q&AqrQ08Hi1$ylxKWR0*NeTrC=41g#6znDvIm^*t_86V1b4o}l36G=7*x zOGp#)79{>26&zJn`2!||;UwH?9birm3dhT_p)AC{2Dy7QJNblb8qh>;bUJ#`aQkAK z?$qznYR>)zj>fS`_laHeEq?S4yX0Y56MHf!0SE7FYFuqxqh;xwYjO#|Mr&!%vb zyft`?Uf8h#>!OG%CdhYVB#^aNW};z=jVZ|5-4~z>cO8i0d>$^Q*^Sl-rX(dkr+yld zBHHSpfhTceUyMhM(zeT9lP+J}4R3-Zn)gaY(;@Y&bR$C3pCP5wk=y{SxO`!i>pHJ} z3q(S9?b4QkQUS&4LcQ4+;-Y~n624l*B1ADumb52lUzgike>0FFh@+s7bj(RmH8@e? z<&XZsMp_z?#cuO#EM~{luslOT`wsKqWwYdFDNDzgiI2DR6Ft0)ip6ktgPoL!Fq7#l zIF+^gH$QxK&opF~%Cp4OW*2FO+LWaakh_<$*`K?rHi`zc;u?s*!MsAP?sqwTSN@fD zikga2sk=Cpt_nB`fb!B5XzQ&;Pjmm`*&aeJlaW6xr$fq(sJJ4SFSFaZbV>vuH1mN2 z5d+b=e}6s4kuO4{EgYYpZ_!XoT_});68pDkAq0#EZm<~s5)ScMb*;YNFuVSx7e*_W zOGVsg=9pM1f1$ayCKo6a8ZMQlrdn^w%20R=lyAj0O7B`2ae<}3P^LudE3!l~AH|u@ zcBhyylYY(?&&&`L|aZiWMiFpx8h`sgL>GDE#jO+ssH1%&i9NGy;jbmW+kmn`gb| zIH`QZ6^0SVgOHq$mEs{XJrl`CA4zn`vDS1sI?cY(BDS!*T>tRZYv+62ULF(4p<<+C zmZZCR&UqVBC<=od%L(@|M$q@vlbnZaGSwP47|j2rP{}hCs;#1CZ*N0p zUaPmW{de~O?|JXI!?(UUq~TXdQEY5$KTS_$y+^V=mU|U8=to{Q5uf;q9slNZz#xRH zM54PxJMyr& z^NtSY!TvrtONS;HWmJo?R4*meJz2%+nog2a)*Q+oB%rZ|$RWhPDmF^LxnOiAHpECH zE(`ZKs1o!m<>XOia^g(YvWLLId_5F8cJ3pED)(Jx+EYuDA@VQQD6 zFOkJ&p&B&evrHvzq}LIk=U$6`Ld`~LxJv2jI2B*k=?zB#HpyTK0zAKK=P7Pn>d->G zs>t8Y-xB=Js83ZGSH~|v(2-p0A0bDa-tb|$?r+$UhWvD7m?`S z8j*pz>SW$kGkuC+;IA5d2m?BfME|0f9y@AIQLJc@t80IWY~nIy_LhF^Jomz#<7d-F zdYvRXv2iWy6vLzqo2RG?<>j$nEVVdhO5Q-J?UQEt_fXm)?X?&~Opop>La&byKt1 zkQ5^$(G`TeL@%VBX%Bb_H2>iM`q%kQU{b>dZ%(s!TGcC`|BJm_CC`IRYGI4Nr`K=2 zt*|>;q2F}YychvJo7Vuo0P{T%1@A00xhGE~x5=dx?5dBk;667ZsJ=ep(Yu-MRP=9J zXhzeWIAS>-d6?8gL@lxCKFQ0=o7}h<6FK`>FY{Q-18>Q*P$QF;$xe8eWz6!1D7b>m zLlUmA45hWDiPqmlB)VCvic_aCwF9OXr-r?>alTcrS--x^88tyyM(cm~O9Q%qA3n>@ zuLn8BM;?6>5_E59xlN*w%4`)*k`B}=u%ZXHa6rP=zJ4$GVlU(mVhN$22w8O1hi;_j z0vgiau|k}uG^7tlh}0F>%OVO$HH?SFAFmNUk!y38&9KN(*ZblN13F&|y&oh2iHmoT zhkKl(OJ0AT)w?~wuC|#ciea*+k4sr7Wu5GNZ~CP8y+}aNFjq=aGWesI;x13t``cY~ z-J3EFyd1;iTIVms+yicgZ}yXpJ+6Kh>zglo&(6S&HCtHQB2H$#K_08wR&w5e87fA? zpP^*)q^4J?KbIBV4Ov{ad?g^u5;^7<{Z4Ko?V(byNjx`44X*ABIZ<-=51X+ykn7fE zU)hE%BqT(^Xw&j@v?Yxl`RD^~E@2|QVS728$s_`ZMM$4-o9FLYYSL%5SXkB!%+a3Dz z!^JPj-7oNJ+rR8AFO`>-9v=Nb$qQ2j8Rm z*l_aNM4R$MIzP_e4Xm zi#NXp3-Rg)<}8-T9Qm!3Pa#s8F5C>sx7}5ON5o`&_0!)38jgx^W+aJ_)0TR0Kw|cjd(AFYE z_;2{QXO0jEUr*f3%^IDtq1ypAM(?;>M7`JlN1=gdM-Q+YAK9J*3;4YGKz@aU1Y9<& z(#nK5-fXIX+BtexO(7j5M|Ku9#y3iJAT+zuN1@3?kLOR~imE$Zd?Lh9G`ujLxMcwH zO_fzR1F`>Gk(8&e5JW&MjfSq3BMe1$t37ml& z`Oz|s4dJ%YZMMZJ%H z;*`To@V)?uOo|U`v^1k5^y*2HqFFsH2x;&ihLGYKr`w zFnOqGXi!YL{Mdlh)T~E=&1Q7q_AXgPMXBU>;SIgQH`MolzY7Kbjx8RXdb)5b)<|^d z!>|TT}$_eIZUOFj$?lRTLx{kTmsr31QgW?&q!r}*lVBtX!Z6ctV*4QV-N z%s=IbMF<$qtZitp(VcXYzE*mLOY3dzMpeiHWY(h`HjWdFs)yCK(lkRdlAsoHDFdU! z$=Lj9%8bQO8%INPqnBPC8voI5KcrJ3IsbIeuA$T&W@~kDfLf1bVJRx9xwA^#3$-M; zPT?J)VdV3&BlC$bL?f9rnN9Sd5Z`6Yuac+8N_;clp*^*-gH&@H-p9URm3j6Y$0thW zV~`DzF#Zo>S7wY2?&JXfzxF5HlX@^2rv^+m&dO)i<+f#QB{gyQ-b4K*PS_J4;_wqM z4tYZ5_|f|U`{dNP(d3_8?blMs7-;DDWpRdSK@9Pcbx$nn=*do5YTkr8gyDG$IBuHp zdXjI!m;bcN)eIg>Cg^fMK|`K61yCe^pb;}ML;-)u%zh|l1XGDo-rE`jr5Z~AXj+T} zmsR*3(60fpxdh-5+|!WQQ|R7p+62Vvpi8cYF$I6WS2Sr}O?9dA(A0vdJHoh>#* z$=#ue*7hSh`KDyA}C<_J;gM@ zvVKmVs{U|&Y49DR8_5MY3nov*w=8OtY$s2yrzCnD%del{!iEJRQ_c7_KgJqUB?Yoky zy_RW*fbv|;c85O10lRCM*(&wexOu>+m5}v@@tY`97^EO8hGg^QiZcu{j`=V5puDfi z$bd0Na_$7mel=e#Al9fUP;GGMj!x$ojJ@(u3-ZF zq>eak0Z`A9VH4zB!?JMZyxcX{U3ChsPA*S-g3KOVTy>%K2Tf7$E$(vv0=kd$Q}=5d z=8XW7&=@BOO1Z%oyZN$RU{EAE0g{__(9YA_^8upl@w!l$YIyk_4O;q*n%5CR-6Zu8 z(>FYS_3-y>L&?RsL*_zXXQ*QkI_kR{PhL$J-&eR*JHekDih3VXQv2^$qaZjI^o+aa z^X%{l%+)!4__{$V?Q~B*?{(0-i;wYcvre4ckl+IAV%}% z@!XlXLW&d6sYkmZbmgIho9d5IcD~)_>KiMXiOa{2jxZ-e(}+5QJTOA0A=(C>1Mk2| zHwIS@km?F}B?FB~Aq9S}D`}97(l_)-b@fwCZW|{=^o7u>Eyuu>#3;gBN%D{-w9q*F z#m`fkzs2v5L+=e}!##J#b3IDuG~$zE;h9NJeEM?(rULD9kO;aRnDE zHy-c#dnh$&T`V_cI_zGq#1Jd3fD-4Zs3-{;iqImpjkF3}t5y~+f)95DxOWGu_%RJq z;qh0}VW+o_admoLP+51%eNWs!^tt5HsE;%6TCYuHo+I z#&nnX5H2G74z_OpHAaJf1F~=70n`-+y7M{<%-wA2Uj-4C*ku16Utt?0OD9vyb`ne+U=$k+1pLC`rj8v;ecT@1V7(AH3kkEA~Br&mN~k0 z7knqw2B$H$#b(EIlJ1Cz*GpoAgY^IgDa`jGbn3GgdxURU|LnrgLAA}b0eO>&p3jU;BsBe_O%Hio&XvheCL5M3kr5as863cvXqoPy( zTHtBgrO@X421X}dR;>uxBgp%CJoz`fZ zR_OUh5ue5%=GV_CX?TJo7}XvUt$z;nwHe^Tz0Qr2(#7<#Gr^Yr-aUR5!fm6^#xKRA zg;uDh`ycNyFap#8o<>{l$}3`U{u@@(=3BKpdmmWv z;M}MdtIQBs)(%V_Pf66ikJJ>>(i3Am))B$QOeHy%jUUJpj>nU27~8@QLxU;TZF=x(DO z%Y2-Z7n0nK!hIcumWBjWKEKS#p+=~3jV;y|H!TT1&aMNYscwZr$Uau7&8OOwj7YY$ zWn(~dY422g1RkalR$Seg$X`9;3i-LiqVT0*h?asUyQlLgvoq1g%#50sYa4Qv zHIwPRoUbp+i%GZ3hv&&cpn2!_^KMt|BtO6FO%{YiTmwDe-8*g&iKj{o|<6k0(|D+$Os~%)%WMwUk~qYpairq zU%gu;b(57&>SNDt+>emvIsfIMNEoNojbo*eqSGE~s#gVD?VyQc@-tCQ)?#&#N+w}UHaA)DmU^oF zL3dxwo2)mmHl*3WTi9;YTvwT``4iQfP4_qc;;@U{)DM2HzjfDti@G8vXc8p`^2g1J z+suYxGEo%o+%0NUTXN{g6U_c2S)vpINVZj+@*)|{1Zv&R1%eqB$iR&drGOdyq@-plSV*^sff&@{O+RvQ6$lb-be#w}I4Lk+W9a?x#&NS@ z`qKMs-g!~b@kb~XfBqk>AwFUTpJzlk@z`W`X?cQ0q`BuE;zHQi1L}h9BTkyK{+(ZFKym!^^$`(x0?fIyOxo~OAMe&tJKSEqaR_5KFlU z@aSj}_hW)sA}&2eDg6bpTJavn9g+^Tv}VxO2aZc24d5RP<4q@>yf~D zj2LR3-U#2~NkX=H=fUGaHVzZ=zclb#n5tPKAH)J~3AtVvBom;2y{^$fe~ohyjori# zS98TWt*MR~qd!PNXUc`xll;f{yR>K;3dU*;^uHq712+wv6sS@yez0|W8&A0bhM|Zn zX2j80UXD87H8M{BXVupsHb`+Twj2d7-^$BJC0jEszMu#OfaUI77!+M~`U?0L7i)Sx za;2EBQp~H(^wDIt{W5}n$z72bsd;4Iw)4ZFnx#t?d~=eb7> zs{_^a!t3Hx9cRyj!w%5&Dz|3G7m51o`&P|&5T);wDxMRl`nDxMJ44!^Z>W?WEU^~; zU}PhLhi9pQsZBf?hEsinE zoIZi|2GP8GLr*%AaI;T|c zAk1S(bo70YZq=mj$({{&1$f_~$fsGG>|T)1X6HpK7f_ic7f};+SDMKFoY_6bFwH2W z+H}z2deV*qf*2NLgohiGYh;AIor(FG)?x%8a{Vt^jsEwG zwmv=>8XDfK`$i9Ve!6cv)NmTJfQuf5+~Sy2+j;6YiKM8u&hC=vU#x(2gWDu3sT_Vd z5)u;bKncKijqF(pzIrOACCqyH!D6<_rRgWdVz-=W-)GJ7LuEEhthR~M%Sq8e8*n2y zDCEF@V>eJy>hofP2QjHU8T@ZUfaUYW?;1-EZ22IvGf)ognNGMg5QQ2%i8z`q9tXmD zcNambs9c#&g`yA-&@B;`eTzOH(JA!XYEOzFQ!;*aSVW}PPDkYWlo99YD59P-RJ>8V zV?T#`cDjIjk`~>9&et@Z?y38!p)3 zI#Fh6kArh@HQq;IP5~X15!22Lii?Yj(}gJP5Z+1Wp~3V@%7BpT6qtZzjN&?_d^Kw`^A6R*}Ks;3Sv# z+5SmqbQDA*o8d$&C@2WRsnJV~zd$QvWILI_z_BW2;5_kh&n7AZnZ8GCI)L>q;Dok90@g(SKV&t!3V*1!YYsDxwCzG`w?{MLzI;l~k z%x!UWyA-x18u0tttDxagxK$+Z-V3fMwvJ|laCmeQ@Ic=-jc%r+LkzP|0|lKfnsQgV z2ZJ>st&dAF=krx!UMhFqZHvSC_iSZ0ED){SThv0@!)q6$(G5~KuH@ApDT-#GwA}0P z_O6lVf3g4;NuIxcQX&pSPcO{5!t=Vd1GCy%5GY zvoD739Py#8N|D5vJXs-#_!ZMz-qes0&o`_dn&Aiex89N2aa`}_*@kOdYSc(F8j`N0 zBv5A%;6CuwMwYFFxdRE1@Z;U`(;oCamx>yOen@9|frlfo8&_DoHng@Uf`{gw z0h6z7s(Rn9_Wlby!SDqtAM-9ZO8Mm&M~Z0PPxV@}VxlcLKmTH3RBtx8gn6quxpfH0 z_8YIFUOG8=xm_%S?S>3icfPat!hQ-`>%82asDiGhHiOHdeW`hML=eAD5*%yyLTH`x zYHd8Z-u5#^T(3z(&+wRv#DTTmpvOklZ-SMhJ(%;dXM@=IMc8$Z6!dzV^8E6wH#3Xc z_T5W*d-p1jRdNsNNzHa|m8W0vNH6fg(WBb6dp=;u1!|{3P_g;x#vm#>x?8;dF#kUc zIBO=W6vM!$FG#8XhUcs@8PNa<3THo64o5J;%`@fLkiI9Jy>v(}#N>ItA-An{2~#Plxu4M?~ftT^S!r zGQyQo9U`A3wB^(b8{TfGgnObaPKf6eGep>on>N0`WAr*${AA(%u`6$O!p+_I7BakT z2Lg#N=TCkKE$;+gff~-zG3Vl%b3qZxNTNIER|XB)39X{kClqVPC+OOJoA|gQcHAOm zmo~ZwHv?$k^VP}@YE0D0PXDTo21l$%E#kx7-QCUhmNhb5Vv)cW(3pB>uL~aWxsy3o zfBk#9AA1Tq?A1;VDfiU7pOF14LdqpR`U9Tls245m!jLj6(4;9|jIh*j1AS%fFlMF+ z16N%sZWH(w?WQMKHH(t;U!A1*ZWo!a-WQfW3ulwcWkXTUB#!Akq+hA0L-W15WtRa? zn`B;X#;$bre+SYt|C@l6oQ8c ztXmh4h)DZDC2fF_i;JrAo*o-X)coXjT_#}T&>;3^SYw5PIDDXL2^I4YQ+3+T+vFX* zD3{K6!;*?LE%dVd)|$`zwLaxO_8c|I*gLaaIMhhVgdGV(d| zxcFM&!NWXK;N^?}dCY`Ysj2mps_iC0WU8OS12M+(uDjRrPi0Y2;rt=A#IO9D$+5*q znyq-StpUD@-HxB$&PQMivSxs8ZNxsQZc#A^EmEh_xFV@p1eSI4hNq}+T8TZqt+!pDDf&MEB9>#ZJzhv#OZ|>!+D}WdhwT$4{RF| zrIu9TZd68(fl3K(yf|5K>S>R9(EC^BRVx>y8)}m80y#7|5&96CqN22zMv$EpS)tAz zvs{RFDnaQA1Ox<19xKGWL>Px7p~GCySzOc$p`#4uCmrj5USXIh``BSa6q8k0U29;M zWsV9OfAQ}LOYU!so$$k;ku#6*#Q~95R`^76(o`BC(SP|8GqRV&KMTwz<}^fIsujEV zqxn|@-MyZ(cXCl;B$NPmKP85$EOlit&^bbG#$$wf@54s{47^lp2`?Q%euL!h;a$DQOY0XQ84A>EDZTV7aa849^&NaIHt&`%px>G?dqJK;X;ZO7xw1>weq<7RkqL`=wsDv*L8x}C7G=s2 zAH?SrBT6S%>&x;Rlzhb+yd6!#F5O>}9_}eaK49KS$j*)fDqeb8+8k-2zE}Ua?MLyi z7bq$S76@rt)1vnUd4~29L$8e__J36VYwTK3t`qNQBx+5T;VY66oJ`(SlSb8xc@}7Z zp{2Z9sKoZ;qr%*E=d^PYTO2;d@^1xs?F`eEq}?bo>KM~2k&X1Z!7^N2ctK{`?*)P# zh_Rw@%{FaMk9RLLjW>6HQ~6i$`u8(Su$!r<#Kz8>zqtTWWN0>{UndU3IXO9B2u28P z85^(7Z|vy46cB*fh|mBh5Zdu@;4EM;x@kx{x2C*|h~C_%XWsg?&>nceNg*)QAL|U%^lKp63fzPA(D7E^vpmkf)4yb9k#0=x z$(z&tXloiMyr5s=dZ0Mx+JPq2=0#P6?|0=;|3rfgt*)DyS(BAg*d@o3Hn}on5idt3 zUXj*W&GwU7k;qh#h2jNpNNFGOW%FOD7t>;s^AFs|w|djUx5fhaGKU#K>E)kN%h)f` zy_z?osrTL}#+u*Ln9yiQqZ=9CpGwzipo4bP$Lsu9{{o^fv+4jFH!5YP7Yxjbj`Gjv zzI<;&#MSt{pf0{*@d0lqBa+aY%ZG%yMSbcgA_8)3>#WgUscw}=3!M6*^WkT2sZMf4 z<--DKWnQlUA^lu=XStlvzc}g#bG*{8F_S2xoq3TDGOV?IGVx;J5?E+a=6bJO+{iup zM2JJJAGq-7OjQwKa|^gC@_-lj4Vq<&$mg^Up##3aLJ$OQ)!oYHYw`^dn^HI@8AOYj zihzQ_oNjIQx42QeL!Re1f7!JSSGA$nnwTQmsk@O{_^EhZb$hp9?w=(71r|#Z00KAG zaV4u+54QGpCdou?sb4&A2#?c{QHqJIA?$-(G$rr#y`=6MicW9iqUsAONi!;EAzm5T zHM}mRI7Kt(fMThM8KiX@c`w2g9jQA4=Lyrt-JM#=oh3*Mrz#d_Bxn-u7u@;M>^HPv zm#6@n6S{#3910aIQ&!kx2RG~8sRD{ou=0F5S`|arT%CPgjIHPNAZdEw{W{HG(bseH z@y{$W#6U+qEMxU*IZzx3h|MNuN$WZL*Z#7;P>A@_!YpM`WU=S+ME*(g%Br~$xcx+q zI}uohk;J*|`BK`cD)Bvvci6AgTC04JTgQqKsZWgcj-yIH>!lcubPnN6(+McC1_fAf z2e|$Dlo4(vkP;x(>4yl@MP_=hr06*&fAUy$XZQH@>q~x%X_Dd`|CH*&7iZoC@bP+q zyQh}QKawh4brYN#3z#oCrx$#sXXt|gckm2E-M>he^W5s6^Qx|{{`*{JZVp_ram9p_ zLody`5gi%pr#wzMx4V9|ZJ}9%$mGK0?%Y?A=Ae&uQ|#A4P1b{n%+8O%+FghFtdSOpEoIP~)FT`8SUR8&SB$V_5ab8HU zf#tAP%Jwb0nZ8n4iH1Bo-FC*8FEOA&rE9qZ-#%Xhoi!_gkDK0}Gli+L^FEaG*mgbQ zPo+0)-Jgw_ISyL#v#cz&h_bgOeW&WEdg;#=Pk=nc^J7E96_(A!FmeG~;kfq(rE=Uk z5<13;8!wuyN2HQu%Bb?5*`}`oY-H9_ks$mzM5Yi!fe0%%PnHtfJ)Em6bHRi6QZ)s) zTr=XcjuvgaJ{rY$X4>+z^;q^r8WZNwRrfK`Z|e$osS8Z*4PzqCcG}9K06ljoNoT z9*?UC4HURa3iTSS>*q-B+YIm=c|C7z;kG8jajOu6BLa2JKg1T`Y%_Oa7K~(&5D={2 z$0QTIKmw~4PF0R50o{0832&|jDK$0dO@Ho04_~e%FXTmBo(=4$JBj-L5czc8U{r*j ze_!!F0=K7Z$NPs;5slGYt$oq-$XM?>t?foy;TpbJhneuUmc(&E2>F2s`Oo}t=67En zKH9fmnfWQFvR!dtg-aOWnv&&!fL=1A=Ct-Nzs7S1d3plQ85jIza`N4RA2)l5_eD2D zTvbP<)M6-jBEn@!RW|brZ)0O%kM0#BLtoWl`pb$D1QKE^#WOH4)e?B(aE# zt2^1ex<8(OjFNSzrYuMgL8MIR1pP!Fi&9gc*j3RnFeX4T`6>4BdkQ=d_n)UzGnyq@ zX7L399^Qff^HAik(a|Y)s}e$MO9zk@w8>1=z-`{YZ4mD}g22^!!zA)!%4|w}YVAh# z;rR5Sl=`bGrg2(Ig;R!RwPiXAvB=B1Yh_=0#QjgtjO5cl=Y$!m^rt{gMtm=24^)$( ztVha%n8=}z0i$AOL93SEHHhgwM$CTw7D*=6{%_#Hg3lw>LC^_s7^1nv5x-Xxzi3&H zeYr;uum6JpH>ZV;fa&2Z`w2Q-nB+9+AxczD17Ch2H#~p5`qQ2;TQ`;c=KgF9WbTh&sGuQ)p?2;meq{wppCXU!5d>NvQb)vRxL~@OzaT123AYxVr zH;m?~*f~6`#B5-`Aoy?4PJOCtUm)g0F@ozO^N$37^<)cHC9Pj7uf%W-4I_V)%hsrZ z@!B3Yp7fQynrEU|;N5``Opc(OflExh#ZZ=L^+y-}qkxCE_q1w}Qa{CMU-ST}tR1i!W?;xvr%r195UVELtLf={0S@H^rlGiQXlv1!#~@5R1o zN=&PXmX1atix=j8gmD1!+F?uoO8Z@r>12Jmp8r zLFiMeO1W{RsoI~vEfaaLM}`IcLOpKznx2lfRCTOSui&G}H7oy`-{J1Im&61X)jD@7 z2>OZVRf<9;cGsFg?RHh#72wDwfD#k^O=5E`Gdi3Oj4kMUES@~VsV^N!Hu}X1Jp3kc&Ozd*ABk+D1dVn2 zDZXSg9s%U3m>nGCDWbP1rwvx$AR0MAx*EIJQSS&-+ zE$Hd#PeA+5UX5^l`s?0&YEeHe_DZn2RH$&egHxjOPRdtqtcMzj{TvCi?z8j8rqFkYg` zd{8RCIo{ig1kw<#>TQZ9LBH1u05IX71h0q201CzRqiw&O8ZIU08elotZd+w-Q}G*h zCLk#ZCPSu*dEvgLTnlz(zZe1g%+${n(Z6ld4_qbB*D-L>=@V;`Ffwb35mmp2PG?hN zxEAsKWN@a6me41DI4md=Kc@^r^ce(n1_@xM!+lmz)guO_^B7>xl)O0Y0r9sj z1(TUoufGVI`g!$D!m=Kry0jW%aBuKPv*kuXWPOEecgm?;)j|UJ88eXM2Gqoe#_vvK z7QE&i>kb!sUx|9Ow2}>fhVTUB?81FzMvm~M4Q^{d?VDMxqn3RE_83Of8dBbb#a|s1 zILZMK@*l=VAN1W5ae26Loj@Hqr;VTn4tZ_O&T|$W-o&}{p~01l^dQ2<$}!!JKXcRN z`uWZgzzMjV`T{}|9^{T^x(IDn{`T#geQ(wI9;y2b$iU!#yCB{2_4LdU%ju_cc*pA8 zg?-N5Iyg{u7vmxyK9$hCVbFJuLvv*KUGhP3f}NNUIl-x{LB^JanyQGVH$=PUexPFH>#%4f&Ded^ z!kG}gZQ4%6_fn7!DGPw^(z%M?b@>n#b~-Y?80HGJOO{$%KK8va@u!bqndtI6`Q|&* z>Oi-4Nfe+fGIwp!L;nNsfZ3|;;Kcv`QT0_(Rd#K=l(cjsE!`+m64DLQ-HoJlqjV#k z(%s!%QqmwucXtcyx!(Uf+2;(#dTP#jUrF(`gb+Hh?O?u=479VkTO$Oz4+)qFRY-r> z+yI2vcn6lY8zc1}D!7AqFp)WjEeRkPm$TtLKZAWIB8ntmJkeW^Iz?3ukb9YquV0yezOWHtnSI9KhSDnf9Y}s{{;zeDxTvMvm4rk$)%$CTPy&e-M9d9v=Lj08qlq?zI1HR^PDX}XGU$=z*$p@toV^j8-i+*`(C=Axxv$puUi5sWyP zFj4Y;EPhVf!@qt_YJS?UA(&$9t0)s3OXHfKa~~i+S?wfyRJyg~k`YiP#s7#>TSg<9_VEKYC&qAO?iGo!)O43>Z!aIGB^_>K?``X z&wgiqmUDS6Y99`_g$I+##c_rQnKFG@)sT(Jjl@6zddV9+m^mu)u<(o`JRmb5GURlR9Ft!D>+!U+GEQGM znXUUzBF$m31YGj|57Sq4-j7#eptz&AGIRUA3pKu`KylY!psaUkEdc)9gPzmT57LdF zJ-2>lWR%BYOo~wQ@)CiQX0>)E2Rp@D8QcmeR??lW2P6Y^qQycZ+Tq+=N^R5aYZWAm zhp0E0Uq2eS~ahqLbH5BqXEK{&uB75?nPnW_nAVL>RKL2bMC zS9s)hb7h1<;;#cMD=+gHxV<0VyBsa}hYRY?R73&IC<1f%&0E>CTEkxW3{{{n#@6@~ zSEYh@STpr7{QiFQnE}Ff+90xRvev$+rJ9to;h(=~hSY1WyLmg5#g|u&ZU6Xu$OG*_ zT`r*yCBSKJgc!s6-rV3j%(2URR zaG{#RKkLNU%sz6GbPS*pu$JzenEs^ngiwp5V6iP9oU8CKt4RU%C0TXRow{;LG*ocI z6hfwF`Y#Xope6wmkI1ai6k|BWC(;$2^dmtLZn(zWf&WG(yYiow%L zOuS#f$nzQcC7pMe7amx^hT*pK6&Yb0f=@L2?jfZ+Kof3*J{`-0=x>GDWWWBc0q0`i zTMF-E;BD#Lebfqfn_ZJjaGv800FV#w*7#1_rVmDPdc^W$q)Z{H^sNjp-2q$?y?OaD z$Q0I?E8y^>W=;tU8I!#sNmh-CWs1vgF45f)p(~P$X)q!^j-$)RrD4mvl$`JIJ8}~! z>B9`K4Mzxtvl1a#HnotaV20+s!2K5XXd2h=l|qI~Y?ww2154ZioGBxagrIgBFN__t zV%YxKtsR5DRr!51=qG}o3oAEqWS|L$6x==k+Z9|=H6okyG=X8xodiuIlAr~uH$YMk z@>q;voM^Xu+c&g=tSVSCI}l06ZJ25juI;mHNDDGw!SsHMO|nsB{&(yq#=%LkmJWWC z)A*rQTM5X5A&6i!5k|qJShIjjWYtG&L2)^q)19Yts=L>A>~Mnp{rmUeGwkCh7GUIS zFu8^J4b>1eu^j=R<)ZrU2_)ZigW0ZrJ03Z^sZSfEr-@K5vT35qT8}LJ3l@OY@3EB;P;Ac zr$-|o0+4);@r5st3LFGM4nn=n;}V87VE_sZN3qO^OM+(pAcPdZ~~E)QHuLH!V4fW;~&@>5o+NVwKO6G*x4~E*suJ^)1k6_u8OXQ2NkBt(&2e7Z0f@I9)UQFV|r$|pocc9RH0O(n%5tjYMhI98!y z{8Msfds3Jn&m`xtBW&`c}6E|=Rd46nIY~TAZSWhYG6T|(2f6Urj!ES?L!kTEI|$Y zEmKqq17etMhM*+n^I;h&#KpRtRti)qlFb$H8GPZ)g zg0r%4mJc?a0=pBVWbeN}jBD#Z5OSMI7btOJm-}H$e0;4!F^bG{4y<9+OIY!~$6?+=`z z??kMjYo!5KuB)FpOe>LZ4|a`>9913$OT$7q4drO|8mCa45v4I!B_POhN>sQ2tc=qb zU&%%a0y^nTb!3Dc>g(72RffG2U;|*tWF7+}SCJi@1^Y+v>6DMSTnhE={YkdBJ1ZYF zi3WGsSsGj`Z@BnuwZk5d4P$I*~cc>2PK2bCLo6<;hBUtNd9H<@uZv>T7sd zaQ%f`X4tP$PT9SRb!_ z0%V*qUy|5H&I&+m3`zJ+wwJ zBuJ%az$;9_+7MV!b#!zXzAz#L`n6-P!J>uX@HZYM987e4S3~{PSZ8zxl1%)O-90>b z|JUnjVR{DoEMah^Ap+e1KTf1%rl7euA`f6?%i4k5bRNF&jCxpjXZ<=knHbqOBj+G5@w?Xecc1Sn;XuUH?>KeSl1fA!< zF%Gk8R5#>$N1fF}!1Ja|Zx;fsV zSCq_#)f{qs_6#%p8T{{yXPGRL1sHY|sJHb}x|uX9Q|ulLdV{T%dDN=))Ht@9z^V zXvqTnaMu2kjC zR($(9% zqmAJJ4#=ng#6A>4;n|Z#nGvCoB_av3+k78Unu~sG9lX7>vjHkoxFE_Ukkz)(Eo+J_ z+{FQ>K^r{<#&j*`=~QpaK-vsBKOhg$HFD|TY?~m@85^(urg&< zBRKiqeM%UeEkziJCiyle`&{~0aDLF+UwL@;GPMTHN?k$q)kb@Cob%7LRS#G_??FQ^ zjYG%5mJ4r60iB;g;-qp8{X?C%l-pI7j}&ebrv*8Pw{ydU4L8EB5<9EE`dCCviAz8*S z-1;PA;jR^=3GWum>4I5?F1@)_B6l$Z4X&14JrT1FA|`iNepGzj_2m}HKO9FI{uu*M z$7Z}Xk_cOG{sz@QhQ*6sK3~*+U_4yKjZi-+sx3r=R{6VH;%w>XcpahaP5A}OO#WwR zviw9vW{@=r8wHJ!^LiKs`gq?RhFv^gqNA&~HhmzpF=;;)I{!DMr*=EE;~awICuDsd zZ&CV!inH4f%6N7S6?3gSKo?M#=26cZGw|Qr#fY(Hr>vQ&q(&g!0{Mhf;rfg;C#_%JR~eyfk5)knj`_S5IM$qpH!Ip55X zufr|)FYCmJgzs=MU3L31d+Wxjjb{fDVqZ$fPrd=juh@HRY@j2kE>HG}=Xq1LtMYz= znN`0lI#_`m;rPtJUdH>c?+`btWm>cggJnbsQub~0REgV@O0zMc(oIRjr@i@(A-sjf zIJGI1hrd5w_=3muGyPtwEXB^FsnOLQv2pDjcc$Id)nyhaO8H;$d;;GOg*^&%OWcKP zQ$sdfqZ1P!Wc}v^!h)@_< z!#G72R5f|nP`^y!Hhr)QE3gq^b2r17YA6#n+M&*~krV$(Pa=dn@p6KYkgq`htt222 z&nEj_^m=O)V`m9>zkDy-@d^2!68@K>MZ$)*;`kO9E?tE*w_FMP&s;nFYg#@$#=6}N z+r#D89(u9iCA{^L{LBZNJ1>{jm&>Y6IJ~6OEvby{-JJ@h-;f>alZ-Ps^0+}>0ZG$tmd8L_hMpvn(d+dA49S=(uj0E(D* zyt9^kJ`#D0zy0&GcQd6)9~_*8_a$!yr&ZDMH*@(Fdy?%+-S1vU_g3f@5^aQm3m6qv z4D<547Ta>IE+q|xH}ohbBBqwD&-}IlbO-l(zZSpOy)(0-AgfH^o5Os6t~fY`wfz1) zf7HA7l3oO^4g`@$0$Jh-`Dp)eo+ia+>`$MU9G~qrtT%M!Ymr^212fRGeV$Y;$Nz7= zL%{%GG7LsO3WWhRSYou0zNogg+pi1P+qPJ|GGKTBt=Z9#Oo7QV&Ds()>SG}K$6#xg zbhv)Qa@H<^^4{4p*rg=#F`@Td<8l@=z10ml)WMFD zxiVk+$%sLDQnEO%#KpkVG{4mY?zGtBTj6-d_w_XHvOgKYYis1QDM~_uKIp#&6}qc@ z->pQwkm}hKu}$41MWgJpcEx;#I&~a4*vmB{ul(7S%8XFY;cwJO0 z{}?cM(RM%t+ORO|(f|?pB($u(X^I5hmD1`ep~>%S)M5Um<-Bhyzxkb?`oVky^qZ5 zh&6JV;xSt@B^K%x-Vl~jaLjzd1KKP}Il0+j zx%{uAx)S$7Vdy=g|GgrK7q6fZZCKn$@rP#--r&JaSRf(Wfn|L^c3}xdDh{Ktx4lszd>@-Zx=T<)`u1^Kzek>XUt783J+gM~e$5 zwxyJBOA>tTRgXt!hSQdd9dwpQ?wXlMU(BX22CQZR49kt^}!$?O5GiQj4Gi=N&2`MK7gBS@*l^&tW)YbA## zo2kWn!J&n$AO_rLU^&DfB((IATtR^jrWH#164tCbypC@gjKX$KZTBotJtGGiiaRKB z(~5rOX#ANeCYamds;PP2ZUUd@HN=E>edsY_MH23(%n$?ykpY0JTt~+2J5G#ESLXhd zR-0U*w7igex(Es-ek=riudG_^d8!vp>)b`;j)#-E2=M5nW%_cv7gFAqT&x1ir>mWU z0BsVOFruS`OOJ#)Jp6puNNL0mL({?9e)S_Nq2QZ(9O_UGj@BuW)NkP|0jw+|jX9C? z2axo`1VlQ1%Je~AlpUaW6cRP~wZPK?Xnrs!sI$fo+|KvCgoON7?)oy>KT5odvT{{! zNmufJ188v2C~m0N8zT)-LlrNJgKEK}V^7LUOc*>WA!}|sREDpHbqjvibyOGB*0w5$ zKwE*8(G)v3%^Jg`ji?a{bsDD;or3f%-P)pUDY>r(zCH#ld*uF@P!j;VKxrYhRH>!Q-vsI7z|eintZ~jtD{^PRej8G@k1OBY;ttAM{*G$Q1+J zQ3O=N>lWSVjOx1CzvBf5Rnvy3=-4LJWn-YsIh!B<;&SYORn5WF$)=Fe$Nl6?^+-Gw z2^!2_VMuJfe7?C`-OB7(5c$$KKM}^Dx*>0%-U8u!twB;;lT{OJK*TV|vX-jQQ<#C? z#14DVG2;z-t|y3prZej_DIQ>^_Wrn)SMZzjVh9p^x{8yp+PXri*nBl57p}1RE5-3Q z|LtDzXELudXh01(^JJFDhD!7C6YKdtIeU|~6BUuC>fL9iq}=n-K-dV3A_i!Npnn-y z{Z%D>c^Mc*#4u>31O@r|hSvwP2F(a&Cr9`z=%7`igxIQ{Adu^Gu0*6ba0C5N{&D?} z5LPst0(&+D(f>in&le&75~G$ zI2&D_a<*eazuZB>Y>yVIMLKuoGNq(Uj98;a)ISqQ>4}2JH3fH&QN?+ z{`uxbXmFUDX@$R8Nj7~9AtW?(vU*;%nsmy@!XxLmhM0$O{C4ao#<%ShP3!}Y5OJ?& zYD!ZOG9}k^vlr`0?~c0Y3X1=OgrC&VTWGnb){`2eN7^;TFi|U}GOkasABq9` zyXj~4Bta1^UT<%&*s6D*TKj4S7JAVIb&g=vo>LqFGQ;8z=JLH?(+_Wr++k=coGMvm zO04+HtX_*KC@9z)KdF5+{6(I4zU~6d$~NU&e~aEKS#q{t@MB{^CM`ul#1K$?t7ER2 zzlmv2&>!=Mf7RoVnIx`{DHnhsui_}Vzn>vJY)Qpcdk?xCXI>98Rzj4U5JOPV_AjD< zE@rrsrcE@GLh-;)eHU;;2@#Ak1R{qN*oKoHLkM<|B1m>Z8ibO_o&f?0+jfs@hLfZ} zpTPze2-&A3S@({`c|)A6TR;`GwiLWzJhQVAf7Nt@ZUQ6-cqAoaUu|`awGTyIDOBJ z)9lF9hz4^6gZpxmkVJ53u~-$erA`oJTQ4*@P=Uj}3gyiN5hv=s5Rli!%`Uu`Xz5fk z{OEr1_KtW+ogr2QBgGKZbWWFqfVD76&M%C@IN?9p0xOJ5B>~X0q5zLpQ~^Xt2IuT- z%ScpP(!%9DxWk-Q9~i&CjqsYtM1PeiB@&|w3Rk zaR3s%@wBKRZLpdho++QzFFtcH&LNH?h!me_&y=P#uHM|(a0(@B8A`R2721(B0`%;; zHizOw5AiZWl#tVHj)`nk9eheDM$v2NIa|DNLmR~5X5lVgxFaogS$TODHRSz^tgUD%;7 z^5Uaz8EqB2iQ}$goDDS}C+0TR*R$shJMb97nVfr=0qsEMbC-?An3W^Rm}gl#A5m0n z_q%_}ger$r+b{X1S%c%n{fLww0p;X%8G)ZaE4DEEcK`6Z+NdA7 zoQ^ja#iqTbNWMfSA{qx>haqU~#gi@3&X^=<9Q!OPrs}o|#d}7@KOV`-Yue zGM&%HN2kjH$}QAN7lfX7fW{m5`T6FSEozu@*`#J_U3FHmg9$OV3eB*(+E-_&#NoxU&vbNfIIhRu5N4#oLu*L^caZ9* z%Mes{ZXQN&Vz~2AfRow*7g0=z`wsL4+5Xq>eHIkrph+p*H3ye08xTgKi`HB%3T-?H zFubc7h{pPGh1~%OSB!={d&69b=zch|p0AjIqd6X!G&)OCR?QCNnLcCBk|y+AAg26P z9GjP;jzy-d)u^?lvJ);QPnCXkp0K*!65o~{=g}16&sE##;lbV=1wEjzgE2Wz%&5PN zYGOvdjh&SR72WZo7_DB#82V7pw>3`kam zRmJ&kSCJqL7+kD1IXBRR;6O}+yl%z<*`mhZjt29~R`n4(bSOFe0|9Q`i*8P|{HYE& zPDwlFv8mXQ{cYvd`uaNOX`j?MFozJokYrE1N>~-KJ)EzcuXGa3|0YSDO#H4{OBoqC zp#TB>uAbS$ucw-0YHIdHcWqLmI5=2o)|0ERw__gqOUoK=iI&-dmK^R8DW$b3md*v`PH!t@6Y9&2m;eJq4+xnRz7A7Ey}3f4r+ z2L3dIU*AN6(X-8?GpVCSf=jTu9k)Un!g!SFf0k5sTqIsK85j{OI}-bl6~84yg@0Ol z0FhMw^|M9fAecEj~XFIff-1$^>_=wZVT4PG`XVY)al%aIx>@hEkn;sN*) zV*CE)EogXVYkgQzQ{L$#|C_V-@?ovZKl>`+CRYN$D0U4-;IOjGygT9h90XI}<-~ zOU8K0JBe0FWp-J?WR@^POUcJ%rq=?s7{MC3pv6YZM3Hl|uW7ww%bSVB1 zdNgp8jCG!Hv*3zfEJ8Ce*!Cnmd&5I)x8gfZoe&HLj9iSYLd3YE^VMizqT(K|GtCt4 z+DBT5G?K)pqj|lCy_Ee~B!kpT4o{K=7%XPbIojw8^TmLpol3DUcuPwHgSI(vK>Qic%1`#yaRaGa&x^FcdCQ7F&bouOwIZQ(IpndHK z1wA1`FaE;P_qZUA?DfsXR6)B{`G>RTR~j>1Ahfb(jo%9%?=@PWwjSEXYNYzpo$-+( zO+NHgoYKsTH+xlK@N@bP#Vs>lD=i$X)uoWX#}yhn!6p^r4p(76rJu!A0KvqGg!UL;C45GM&p&nb0tfO88OKoIw{9O40?H z8vkf_mh?+;1mXL7%>6M6VZe0nP{kB3B_2VLjVnTvgV{eJntxCUiyx4NVZsjPxL=r+jp~Fm(;8!J8Xp8!x`+5)yMOt37-~R9rG&m? z_*Y+FJVL*J-vW6&kMbDv|{J!7jS=oS)zZ!gG%x@n5|TjgnBy}i%+ByZGVeG zG3m*oXjn)V<+r+)JSV^L7dzcQQOQ!8W6y5`W_4WX6}r>qR&`(eQVs-~q!e~z@lYk$ zkI{c*2qk2GLz{86k1U}{qo|mEa+%lx()Y#d*tsR3<={(>y_EN_pZD_e>MCmk!rSQ= zv(G#xJIX=A69*Y_akp67Ekd;QMOCHAAfO(I=V^n>PCx{qVed z@shwR8e!$>P_X)T%DIW_GJy8oalK^!bu+H$s4*Hw zyNn>HmtzB2)^{op?}z~z+?VHCNSXJ62ATj5(S)y`w_^3BW(+p2NP&U*O*csJUc8Sm z^%xgjgUA`3_F`7taz{s_>$1*1iHY?j_kfx?ja8MT*{J%p zR4T$-%VzlVOcqBXQ521#j8cQZY;s!e%|ma$9h3O|m%f2fXUR5#z!1ET>EU;;`#+>1 zKtTyXNr`?^`E$0%g$BKg&E`6;M8JA!r$k+CxfZ<;+L7GKgx1`w&i)i_yzVGEi++8k zo+#E*QsPiwKaIE=(_xWN`|47)UGybencdtmpeYxtGNd0Vq<>;VGGBxORGEHIN9D5p zTZI4VRj~gMWde4VSYR8tHSianSCbNqf>K(D{zOM>~u=vw5z??|y$ZMilxQ;4I{} z%pCW(VX?KkE#L-|J9@Kx(~Us&fiix&uWe2Q3q32kN4q zrkGEcj8}8qYj<*vlDp#1hcy~?f3?>2-^5Cg(&q*q)o?;Tvs~V$u!qT7^b#9Bx8jqL zMT22{4HO@@+|cUo-*r_ATjrH{WPD#cT)~jNKAk#3!+A~*KcdoK7mXtiKxh!1dw_dm zJ%cyNUyNKbjAzTdKDY21fJrl^tPM8Hf&b8Y0cjfY!sLclB!^U0K&*WA)tLXIPXXmns+*T7-eg!ng;U!vX5E%= zTs?JWDLTgt2fa>TfP6{=2$WoW>Xnlas$3==rkkR!Jnk<{$AZ7ouwp*a3N;>9Rk5`@ zG(c1-zIG8Z5TSED%&$&P+tCQE>?ThtC-O!D8!1`AAEiuwD+B~PL06SY8^3BXL(m*W z4uVL|GB02X&{d=yEZ9^w)<65r_zI%;X8mzvE~DGD6rZwn?x^`7o)~t18zvOs`3B4N zG|0iRilK~x5{^kih$$%1n4TXAS_ZQ!#c0SEC&fO9a5ecsJMRJRAMe9H@$S>(?Iy?^ zSaei;(%Xu7koG%o_cD?(jj05WZRS^mg?Gu-KpjpUklb%R-LA4$Cnbsww0~|ci#pq9 zPatWDw_U9!xsB|sJZ`cy1PM7AgJ`d$4g&qj{n!7|lVsPe<+GskGh^xH^4*KxG z?~JXh3O^N-^Q9;|y4`OGv2$j>VSQW>`>D{2t(HUXjJQPcgX55s$n`;3*n-gCB*h~s3ab}4W+!{^J)mX$-C5rp}mj0&0T8gK<(?J3y%%y z#m?H82b0mpmv*OZGg;PUxnjBtyKskgukxn1LBV0n=}|=-p>ADrhpyckXJyg?##V7@ z-{MCOzU%A5tzzv|V$Dy)OU|oa|9|sjN$2T_7)xnE#g;0fN-)&RvOz@4e=n~{7fl$2 zon+jPA6H{k%Vwe=@qi^yR_IeEHjQiyQ4FWVe!Mw{XL-~g2St##D`0jIe!_ z*Fd4rLbzFMl}7z_vCi@#TjC+xDI+`H*6mvNhHN3a?#YtZ0qH)3n--d;V0|E%u?{s2 zOR%~QdlR$xEj$8(H6vi>bX1Vn#K2+vqjr9BGks77*I!AAfYrb5n;Gku`C67&h)6;M zOJA$s{|PA?#3L{8W=S0NC2f`X-TloIHi55HiCp*Znp*YXJ@eB@)Po#|d`Vozf~eAb z5En$*|CVpy-Wii~qA*{IWz7cLHg63RJH|f9_#uTR$M>lOECFxb;7^WTS~AM2sI2+` zJf;EZ>(f;-AS$f9W=lW3+MhlL`Txp-|Lkp!!cLZ3=3De7j{Vg1Jm4q9l63AEHvYl6 zW$c%XMfP+a1IXjJMJ_)UImePwj*8XhRi+me$>&Wf1C3hD?05JSB}0cQ^k3nbo?a3u zuk$|t+*xkOBQ8Eye1lhdfzQBzS?&F`>PnUBBm9`q7lK*o{|%v~m%$9Kv*FrjTQbD! z`}k2v=!2AXk;LTEM*AZaKV^V*p$Z4%o(}HF&rc3V1dlUcvIl3x3l^pY&Z0j?M~uA= zRas&`u)1Y1nv8WNdF>l}D-%lh1xCSWwsUxd3|Ix z$^It$9RsX(gYBfUsXI}kC`R#5xVzi2V{qVStSMrocDURbb2}_=totUJq{0yP(CwdA zsB{>yP;K@uCtitw*r?!N^MmogOB+2QTt{;}U9+QGT!?nTUJa&_05jrDXsU$e5o+O1y;lUQ%3tKZG3nwppa%^79B{Xhia zwbgOsR1E(PTw8m)GYrSTg>dv*JCTt9e0CHfl))I}|9tr; zVa_mHu{Z2I`)3;j(%u~$R$g%w<4ozQiz3F0Wr6G~gLGD|l`pjobd3svPdFekR^oRi z|FxR!1yTOn2l#-4a;Fzkn`9Fj+>}BTj3bF{He&4#HI^dg91IPW@v8xPv9MeUb&{`0 z;wB-TE57uMxM{yVa&u)^Sm;Tnd%50Z`xr^IL6@&|0B~|e&qK3`1iR>Ke@yW?{jvAb z5Wnu-HK6VO1^^(7BdD}CA$34&ze zCxw7&F8roy9hHPWbEcgL*})B8CjWWt*dd*I9Kzu-r-Q!>#I`vsXL-3{;f~t6AMhD( z>H9JE*>D)B?Z>>n1TaLO&sPu0X@!}R<(C*cl(dISYivdrN0%ay>Txm0s-{N2cSHul z3#YIVx^R0A80QLtspVLO&^;I`BOVy15z|mVJJlO0u%Rm|DrU)`RUJWbF?_GXsM$iq zg#;0*cdBW0jy@+@o)dE99stYQ_wA7+&~6W_m136B2g-Ley_mF}larEy!9&_?#a^|< zKZn4i1~wAla+P5Uln6k5V$8U`G@zxfwoRCyc&#B!l}FrvL$(#53e|zWq<1Q8TW3hHKuW3S}?^0l{30nOw%~664UPf5C_6PqT+j zL3knVZXEw)XfGJK134MzHX}MB^g?_LKK@JN+u>{f z^#e_0c0v*{vmr1p#U?K?=&|@T?vAAe1IHk~Y*XFArYZr4TZr=8TBD;qz+5>{pN4f9 z`rfSvU&H=n$cZ%*&QgM@`}j4D!xVY1I}RAyAcZxIp};q~rl$9=GgyHwXI3o{hae$g z!W>7>^CJuk?WXEgHYV{r0h{Of2%QJ2j$a-euJlF)N0K-^gN!k0TS9h>yMovOEhOab zjH3AecF-(&ToElXI@gBcyI*;>CZR9QZj7d9pT6ilf8eOh6fwF$x|49Go4gGCp5}0H zz*%2OZ&bjTUsboEkyG#OyN0dL5L71=YGjdSt*W6B0?eNKk;7S*oAueV!NemxXZ4@H zZwwwcq?Ex%57Ul~j!Q-T^u8hdV6zf%6!(XK+aOCM(ZN8LVA+_EN;bRS4Hy8>yaG)Q zqJ*+Q9^{%hb*v$x3YNb6Hm*%^!MCC0xe}VX3letX@P9A~!bN$JNqWys=KA7Q^zi47 zyjZ_sGDQ#=XjQ12LnT#sKV@~gp<8NPpZNj}Q82!4;+I>v0~PkXgaDF41=vwCH&}sJf z@mCaRcOBwqNqM~x0i&FSLf^><9JO$|W?}(Cx>$g_fZ+{RYzg?C7N`B>L5tV>;M{In zAN2`5)86#-H*)#5@8}ttCTxbXHMuIP^(eEYx7xb& z$M-Zjyq>DL^gq0}zdrEy96R)Sw&)QwPsz>KJplsN0wmF` zB)<)k@-0GDYYg$~Hl?ajATVHSq43CRu~Z5yOzNzIqc^{dw^oDO;b)OH6w~JgI)Q(n zm{J6!>&;c@hG*y>_r>N)VBirIBn*U&*Kf18M4rej2LlA?v%+^gVJ&39gOnppv}b_E z(`m5BQ=jKX3ruOs_4f4C{m@uHO)q5?Ze7(8lkah4QnStXWBo|{wG$HwX*jfQtSAky zKW%JVm!G2RbONCK@E(gH2y*<*do{AHw2Fmcklx;FGT&y-gtCCchsp}XkUf7HGCPs+ z%=RV>0eEKBne~+ncsNlxq02n%64}bb$jM5I`_0>`T`LTb6pSiYNmOO!{&8ca<(uU@ zW4331#I|Zm%rVt;G3(4pD#VIVcY@sbopTdYsuQsV&mz`0<_RoY{w{t425pzNX-*VXN z94awsW|U;+FghUh%@h{mXA~nVDCpl2!RUEorBE5K|Ni1@_QO?)+HYtyFj$(I?0`x- z7fDW578Mft1O)2_fX2RaoK9NgWNDIMYk_y+UY(Wp~W7fB!$b1t9?9-urH(i_Nlw!%%R@jlwD3M{)d>U~V zR!9EwGFYCIIyhN15gKZU^u6l_@}0KXufwD^FHw3Lkm_i^qR26(-00-<-`cznv5X7) zuFkjixpPkPdH6_JN67(Cx)M1gRPl4Lbm|=yZw`roidy$pnV0BeU{<3#1^5I8It}|6M{QVvPF3Jd*RsYi7$xO9X2av&GKi)W9)C*?R?OSk5cSZ z;3FB_VT_^M+uE)e?Lvy6fI+-*+3ei(v2@g@@_Nx<^HRMh-&$YvE14~xTT@z-fvL0~KRv~LKSXnb zx@{cw?!=0{Nm|5ThY2t#39v_<0V{F5MNjulIYxxU&O||$t_bU?C98SsADaHS>b8Bg zJ_x$R9|sYU`k%=HD}L_&{y9(lZrw2#cebv&4jLD6?rdXV3&pE^X(@14%>lv7@iT8I(oR6x;rpF@0n$O*OhqBsU)8HCcTp z!DA7cjBW4>ou;sPH`DWMR$bqp6Z-aOIhV_hRndWJcLyzP#f7)᧥gm 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 G1O6AxOJUgn literal 0 HcmV?d00001 diff --git a/docs/networking/site.yml b/docs/networking/site.yml new file mode 100644 index 0000000000..ab807f28d1 --- /dev/null +++ b/docs/networking/site.yml @@ -0,0 +1,83 @@ +diagram_id: 63 +finite_state_machine_id: 22 +name: site_fsm +states: +- id: 9 + label: ContextMenu + x: 887 + y: 1031 +- id: 2 + label: Disable + x: 760 + y: 468 +- id: 7 + label: EditLabel + x: 600 + y: 934 +- id: 8 + label: Move + x: -69 + y: 861 +- id: 1 + label: Ready + x: 532 + y: 560 +- id: 4 + label: Selected1 + x: 214 + y: 566 +- id: 5 + label: Selected2 + x: 220 + y: 810 +- id: 6 + label: Selected3 + x: 249 + y: 1047 +- id: 3 + label: Start + x: 582 + y: 334 +transitions: +- from_state: ContextMenu + label: onLabelEdit + to_state: EditLabel +- from_state: ContextMenu + label: onMouseDown + to_state: Ready +- from_state: EditLabel + label: onKeyDown + to_state: Selected2 +- from_state: EditLabel + label: onMouseDown + to_state: Ready +- from_state: Move + label: onMouseUp + to_state: Selected2 +- from_state: Ready + label: onMouseDown + to_state: Selected1 +- from_state: Selected1 + label: onMouseMove + to_state: Move +- from_state: Selected1 + label: onMouseUp + to_state: Selected2 +- from_state: Selected2 + label: onKeyDown + to_state: Ready +- from_state: Selected2 + label: onMouseDown + to_state: Ready +- from_state: Selected2 + label: onMouseDown + to_state: Selected3 +- from_state: Selected3 + label: onMouseMove + to_state: Move +- from_state: Selected3 + label: onMouseUp + to_state: ContextMenu +- from_state: Start + label: start + to_state: Ready diff --git a/docs/networking/stream.png b/docs/networking/stream.png new file mode 100644 index 0000000000000000000000000000000000000000..3a00d21e9768bcc37db1367b9b9ba482acd23726 GIT binary patch literal 76443 zcmd3ODX z#KaU|h>1}u+S|M_vowZ*p^q}u(_?(`n7%__Ur(>2mx%$*-sNRbP}obork3X3<`&9U zy*A3!1a0kEBJ9}~UtG_+pUo&4tQ&88qz^9L26HMruL-z|KiR9&@>d`E!MaGB+i}3d zKSchJlKe6`8Qo|FW{kfRQG!s}z$@MW_J zh^DuFOe7@6ut&!3W^-k$W}|0I;3Q&G=UC<7jmuJv@x|j`Ayg(X&M}Fh=Yyu5n0O}GnIg!3awd2Jk=Wh39nE@qJl^Zkj^zZ93eoeusdL~ZKmXv@#a>f++U;=;*dV{gLB&d0~c%ErOU!NCkh zFgv(eJLZdPf^f6@+}suW06KY^g48W@T*c0M-!Z=HwInXZ-*A=I@ICnW^!2 zChwCc|DE}tZ*FG_vO*5u=MLh_nH7y`4Jm>A*7?@THZ+J==xZ58+DmZK(0uJs(H0UQV;Dgh9{~;QHB6_LO zF>1>Am#*mL9M2sI7&roIm^~9J!_2(Dbiqbu{;v7|^+rUov56v^dy{yAMN(2yhK7eF zgoK2QswVQPs^Yh&>gY(GnEj?f!^C`))GL{uo{mROE`R_2eLQ@8iKwWkpB7KdF`!pB zdmr2#`S;Zn+41r5FGWQW#Kgp_U9n63Vw=+Pb!N}{c|4c$z8KLLjwcT>3X+v zz3*_+GBOXZBM_h)&|?7`@UAqmvSLmVbc<$WWc*T6VmCA{o5HVxIVPXP9ZsDisdzeM zi-vwL^TP*hzVhw{x2I2^;$dS`9?Z_oTwi;S$#jVQsIr;K+1|G1lkI6~;}NjNIeEPQ zZO=5=1A&v1(`@Z`c1(0M$G9;s=H0rKkagJzPIk$J++(`Bx)yD>cNt|<1vN0+AGOjc zPKNP@gobA_yvo67>UlV$foJbkq*Eu&+{--VnBBSdZU3uA8NGl&l4_x50Qu9%4^oeA zIcb&x5EfqaULRuMcisKGxkjR6X=(X&^R-N33QfSw?m^>aAKbU}MnCfn^K3R!o#RsD zZh9UbwPQ3)k-(ihc07Lsv$q0&;d#)@Q|@zfeZQqvFxUFJ{wyXjkulKGOA$XlbT)l1 zF_E~|cJD_JUy$JAdhaWD287prL_Bwra2f!FiLiu_knoj~(ytHGoT(qbyq6y6Wmg#w?v# zZJ~r7R@bS7iStinQt4cmdmiNTzNRw7U?kkDZ+`n0j*mx??>#eA^n4qEFuDzp$Fu6@ z^b22v^`#0GEO+VDYIK)owSRRV5Er0RNXgpY->1@Pd2E~Jw){X^UY=V?<`*6|3@qe_ z4H-5_1Z!*$%jXo{>u6Zb+d*RS-Jz(spU zX|hz${TF4cKE;E&okpg3=>;vfqwxBAK_G-K>~t2|)0303ZMuX7 zbJnA<%S-XPIkYly>hLg{sJGnB{}3?G+o#uuxgwosUR}(a!?oU1?n_LShS;-|BPM;R zO2w|*H7lkq@9(X<{AbzsFs(DZd^gu8O}PyoC;4i{-CBXuOtC*toq=2$v3|BcT8I>p zpSjw0i7g(zll@a)$TZ5E{CR&yo9_|4)LdI9Hge} zowqdI*Rtca`%zr(PAdyk{tW!LhNC}Pj`OXkZurG*b$ff^wXXwDVC8=Gn(45fb{97) zkDx8P22E7ijL2On-iPuACu~p?`_~Jbn`4_+O!DN#-^L1f>MYJPM6Q$kZS1hQQ39bY1kg|D}qd* zUJ<7h;UDG&{Hq8!kbwrNa&x$V*JW3c_2d*00Rf{9O;<=l?|0UV=)InIpE$bxk+E=! zKZf1u#aS|-Mu>|@+Z9k(e)N$c8bnaMcjxl0FYgxeMMOk=A~asI->#_OT?&#^sk_aW}EM;V3S}W@pK6p_>k8r!zSJCF*^N|4(`ugeR4%(Ka zg}PPKXxu#FBx3}djxB2k{UU0JwN#9e(zhJx8-c2>dC7oDZkbju*shovVP%|`Z|6!z z{#Gsek7^gY zfk|!9Bl z;(a!`W(8N@GacQA$!dvt*<%6o@K&qApvZ>#F14o3PYJl~8)-Ilv8P!YtlGx2qJGtE z2zl%O);KEUo|lGj7u@ZZ%do%2oC?^^`mQfVHE@u0S3Ou&BPGU>W-23d;2b+!*eGt| zR@_ojxfQDcELfS~CZGbO_h048XD@ZfZ?vO*>8wWis^0ka$B!S26(P*+P7hh2>zfUUB>r)%x@r}QyCjB}Wnui*`IzkSd#xX9n%{gK08xeejnzNzE0W{!EAo9ina zz7taB*R{%Yr%LzKP`_ULrV{1$(0)>_teTtX zYw+nh?>;&@YMpL+ekaunbb6?JYA;_>2Y<6wuXo)$U7MADjiX*~I`E$8d^kH;k>TGH zlHsy7oLX2}(QcnD#6+dSV(F6I5VdP@ksr$`D8$F7@!Gw$o~&k{*0LkJeMa?E-?g@x zsE1O@4|B7#^Dai&x&02G1fDLa+Rj?jKn;f9-hDv$m8ySGaPW6?@`VdIqIpK0qlf(b zntv+3e(k|_lBB)mK2a|gN-Nvs-PN^a`jpVMGnB+8UK8-Con>{Uk6n_te^+?Zr zFSqB&5F;-y4=cxy2}45EV(=rK0&)KKm&dpJI}P>CYtxmG2v{U0B$NX3dubTZiI78POYG@UBlYP)7$ zlmyjF6&01dUWLSztZl<~ZG`A(B&&joni|dEKTw^x0vQF|J)M_{f^Ho3oFCMj(uJzSxuN3U=LdSz;xkPR&;^sTk6ZWh*Wk`E!-dqbW0ANG&i#Fr+K%o4 z3v$wLw~`>Vf-O7(VqVvQsfHryaSeI|Q`ZE%r0q8=1s7ssVhoRa*hz~x2I4`5smXzMS`eG@)QuA!Q zL;(XUbjT|&!Fh^4dw{P-`5SfAm$JB!kZajtW_T|b0~9`&gEw6v6hy?0BXSkPOUmw9 zvG$dVO5M{!%G1Baf@R_rH-hb@B6Zccw#J90pHg*}zZm*n=HFF1PJR<;H8nLQ7qTU! z1H%Eesvoi0jQeLQEJm{2sBx56n|%zOfV{5xp=Rf%O?G&=9KdF67u14;Kdg=%0=|Fu+(@lJ_oV} z-^#_uT|TnBfkCN49u8cn;Zk@wGI=Eit8s{#hnSVGx+|>Fq&qUzbRSbv;gT_MhgJ%! ziK3g(DlXR^m7B@sFStGr>EKMW9~@?i6>zLmv9towVA-pe16Hq<*l!~Myi}yFo3(^!?P5prSgrfsiDm$^ySnWKj`((gGezvD4$pk{2?PFdvArz2sNYA zkMwF&n~I89Vl|#W52`J`NI8~f6c&DBDYIFxJ$~)q8JO7ePB-q~ATIz9e9my6@i{H0?^f$}Ll zx*}3rTj9!f)j{T54i`pS&u|!QRJy7e$KA7n7&QYjLVMG#NXkgXnigsuMkUC@$T=UI z6nhPi*XHK7TP#r;LWPYV@+JZ<+h4d*F)>w7jv5VRp5wRFOdYo68jHm_lW6YHqmd1i zUBQ>_5B1ee2q{mn&8{)lJCSwDJ2^!Jzx7*Zr)?KrClz1f{St<9g0(rhk=f|cb#y*0 z4`SIuN&&C`afn(zpO9Qu9v=D*(Oz^kn$(IvQ!k}$8fWAYNk(3-JCaoBFlJA+RZNni zX~n!ig}0xYn;MrpUN>Mw%gn->6TP?M?Z~twH((9YnW4jSmnL>E7^p%q)sqm-= z)(tD4&IwRInn^nJb38@K zh}nd5xAV#$PaX}6g{RW04cINn4hz>ZwQ|PfThyYc;2y^5?ah>fPd7ZTtuK#YwP;*|)w+Kw|{ zhb};Ew-dy6mzngdZUMk89~>zep&Vl0lM5vZpa&9yjOpy`WQ+x#b3Sl2>HG>tkgUE| zH1KJKszQ+s56HRzhcg4XT2N@m?>2LpC*87p3^j^E{ zN^cU~FP$3uw*DZc`YKdiSc_1ba%mBMzV7Thm|1+i9oQdp@Q!FM4q8#nA=h={fGn>RU@R~3IO-ud<$ z-e8Bh9{&s{alATr(l`Pz2T=*?7fz_pn+$y3ncvwmDxbNyxYmW8d!Bw?{{2}lo>}Wn zgk(DY?H*>0;c^Ik9F}{ijN1ay30Ykq(3YNX`Joe%axcwYWo6Om5=QPr?;fx>^Ytk8 z*x1-_HbFs@VqLLo$rRPvyu9F;BTsx1fd*{kyYTTnHgNW}D$HZYXY=zJ!J&UXvMpLq z9`_wCP%F6jD>6h$AaC;S;Pg-9xUnbr4mu>`- z9Rq0ZS;a|~`4)e%9sOWDC@8)LNOFjDiL|32DB@tus}PN6hnQKFM7QaY-|!k1VN9&ST>REO`unVmR^6 zypxmo2)#TDgvSrB6ZE{j8xh=qEbx2JSC4$AHr&DH^}gh_>u08Gi<@RZt%1hrZm8Xjy##*X2)jxe@@1I#r)xL|XU`HTB*;4_!_+kxcCCA0p`y;_~ zm`&Kc?uYas3hXS!CppiiriaFt81l{b^O63-teWM|6T=%MOV$IgeZJQxwy$yK8cU4T zxh!?Xo)*m=cXnO=WTNaq%J2Dnsy#v&m6ZR9>BGXULk+DVwA;Y-P_8+8 zo|Tq1fZKYKo_P=eae_BkFVYYq;>fb&3Ybgn1t*hYVzT-}GE|`=j|~@x=VO_)pL1(B=rqV>QoD91&is_R^q?CH49A);Yn#XB#~&78k?e>gET;8a{tkXhVx ztyxJ$P5o^A=fJ&ZOVD`N1i+#1o*Eb!tY*jSyp(&TtSr8@wFU6U)A!Bcl_bjLRnM8j ztZ>N43ccGg?>xo}Fbj!@1t0`YFg0Em++Jr(@#@#TS2H8k4zo)W+*G`cuMef#0 zB0;5aYazY~0!UF`#BaxI$D{mBFR@$snl!H0b$fqsO9Z7`AWW#mo1X8Le1)&;;u7Vi>KqS!w2-Uu@QKs7ttU)q<2K%bQ>oQE*{bBo(l+j|oSXXNIlrXMu+5*x;A z;tjRPG1&5*X8h6Dg75&s#L)j%2AzxQ< zzf`xp*$otNq(#?8zeCqx0V0Rxd;CYs=2tK--J%Nnpy6U-Gk^lHs?l@Xshf2gHYd}2 z_7=#SwkE0~ozVO4yjxcY0?R8381OHGOAj&v(QoL&7v!}$l408S7HnBuwkM-V_q?#r(9>T1VF!Q*6GrC2Av3N!t=GV#L1T&zG0OrIyR$I*`(L(ffHC?w_@ z0^n_Qni~L51CY`yYr4dB&p@-uy5XEmpbvGo{G|@h*``K&w3R1*`<@st;Yz(NRIck^ zz|8kc)Z*jgA8yy}yi`+*ujl0Ej;khQ6BeFsOa5xdC*Zgn?KV=Z8-H(^cEUA*5H`6` z->3Xvq53DI@kkGsfMciWMi}T}`33@t*K*S^_ss8MVdXAqbVHJ3c->t7?IL zZ}GE_RIoKEIr(d3NCv^3IcEf?zza6e#VrZ6ur9T@w8a1x&P%1j_-`KM4zs-~t!EMfuuPP=%6-JA_@lTy}Z zWr_EFTsX5IZ~dO}D*9CrVr-LGn=O{EmzdD-@E@g(BULLd21-ibIA*O!h!k*KSWo>~ zp$Z-a2%a|^E^_elZ2Klz!M*#L%Cx+^32%CnCH7GFo;<i~>O=y@!`dH72yPEbn4 z+RhGE^1glzJHNQ_QWYTZU}tByzn%j=BE66hnUJS&X5?F_@pl0m_H1{bq%B$X0BJfi zkeI*&5FnX^D{ox9%mS4rW|p8P)|@|@epGwIyT^y~3kyq^&$j0lQX9T}`GPzZad5|E zNOagnyXQ%z=Us`_o2aA@9x#wSF2A?6y88L}*qP6BVtAM~(9z8_UV;i;iojLyvfBaK z?Rmlq`aT=`N?Xg%dWkz=`r9MGuVmvXZ4~I0zr=|tD7KO@ll&R)G~a5FZ;ODa6#g@n z=a@T>XlP&%p}YZu;+hQ9RC`oy8r8|=o~PhnX9Roq381QgdnD=&lIOlbkMFoM83*JG zL)w~^p8g&G;Ggk$^9m+NPo1DwZv?L1@r@#c62dSvQu_#UmxG_4YFtt^ zm-U6cy`vipq1%zLxzs(~XnQ;mrKE^|tIF9R#K|7SfXzGwL+HPt-CjmQ7`TZDjIMc` z@p7{##$9-1!)Pkdb+FlRA=5N)53U@IFqEO`V_9jKc?GHW#(RNOx?~R!?uO%nD;kVa z0&WV!qeqfbT+dYQZm&lYGKIicVdl#hda_}ZyE)iwB#>jPmlqdDT2Iv??&acod?tpn zt15(DOEy6{gt6SfK`yTU->+UmzG8ojb!PwebTIg-;yvg|86fnC;@WhLjA%eQZZ%8t z3lj9@eIG!ct2G~7PB7KqDSZD-A(~g9doRibz4=AR%^T=BpEi2}dho;0@D>*roHE$% z7ub&2-|g7{vmLh3CbPkuwJh%cEU5umvbpqRXNC^rV!avr3$!Fg9VA!NFvEHpyp!2h z6p(+4;{T7O_4L5i6TzxL*A2x6>-wd;b-1h#o`U5{6IS@-^0=5k*Cn|vW+1T&Njds+H^Ybxmdh%Oe`0+EK z_50ty*&`$-mgY|C?dN*BcXujPki1+mVQLv`=|>WUeyqn1cJIy2n=Eu`34;esK@vdB zsEL2sX^SV=#NUM>1w&_JqpN2JWw45Zw~U|;;rg_D-WSJKYrAtR=AT_}&j14px#oki zbPsoTh?0iCqpu3eidIZ4%1+TyQTH5}?@0Oj{&TcnzyE#U^ItIZ#L%NX2S>(PkBJFGV;c-VL_nL|kJ6UylB)u4r89cep6K^TI-X$yr1& z%KGV6`iYK5z%>8#+>`j}afLOC>u#TH+>w;j_qoLmk9qI>-dPDS9M{jHNycSoGW+7B zF0Y+H8hu4P=<1?HSo|qEc_z{N=c*gqA7%`zXAU8Ve?#c{=ae0xZb{r5`Tai9A~*Re zRx`m(9^uZ2xQG*P8F&+1T2+Idt=Gzz4i=w>eemsOc-hcW0>8(DfztT*%qhToMZnJW zIx#uSoaI?vNlPQIu8Vi~c;VS9BLBNwMdRT+3f-UcSOp%uuj-q7+fBMSM*m&{V$X$nFh zwl%Ok`fHlfl_?n_K90VsSCJGi6GJ)a`8pho7I(kv@i4yAx_dX#Sy%+^7$&TaUV~f@ z7Se2f50Rq^wO>x{(pD_@Mf=las%_cth$920tbt{JVobG{U;iCFEq!UOk8_=2Mxw4O zCGf4^sY4%z)E(t|h+jljnVuITpvCL1d=oJ(Y@4{gv9Tt7fNySPm7Qe8lXvk4|NPLO zy4Jhjjmq}awh!@tOn11R)SGZf{}4h<`hfJTpJ%m$-mQ>a!XR_S22YRoZ^alFTxJJO zajZ#xm>8jmq!~Oj8cM?Bk+r1?cVv-Fo~ov%G=Lt3E=@^Fz2p3Q*Ob1gpoaC4(w|9~ z_19;(=X;r`5y?pJUCG#QiUxPCcYHE1=~@~gl5cUr|7}k9PSj?As5yLe*k%&R99ClY zufaIX-Z!TZGQoVgbr=wX+h$EAhTA%$NPdcR$GjvE=C#K}t5L|s+Rojm@?hW%igyXc zYjFDkuYNh``RyI?hWl#eM-IHbKz!lk=I>N@s}(~Eevqv$P|pJ?nwHd?T*5pPC)|Qu zOxa8bd_dXR6Sw_E_U+c&G%{q`?qgV9&+6|7hbv7Vf3P}R6KD52VQe9_(<)gWK0!<3 zr(B=^GsvkS7C=GRS>U19m`1W|#{_-j=65KcS8Se*lJW%8dNLf1rD5`V3=N9d-0&4% z)cIs-KiI8_N4uHORZ^OKCT&YyQnP|J-+%A48vj;#i0VBMco!siw60W7yzxJpD;Uno@tUw|rX4m)yFiQ85<{6u<2!I-XHpOLBaN*8RZ1 z?f?Vp;Jq>V5+LAiW@ z&#h?mR{i?|j#AGLb4I>V*(bCy&jMmFF){HAJP=-)Dk8tG3hiMf%VzWt&b~)-F43PB zI%~%VeQ#eWh)f5u^U7l^=v@1TL}2k+#&|#5iH(RwWj*8G7j*x>PucvfxFF2x#i9Sr znap`8{@`ND_qjnN)kBm_wi0C$))4}1?AcRU%*Unh75zICFU;uvn3Vd|HBM1@4Z;3XUb0V2|8z<#IzvHkUnd4rh85 z*{ZnPGkAz~v&39mw0M@re%NwzG-Ku#W3Ok6zQ@x6=W(~s#)q>;+=)AAJdQ6;67zCW z=$~A@IA`yN)gBeIxf%StEAzS}*&qPJ^CA;hJ}FpVH&@*j8!7_L-3S8*JW95PiL7(_6P;Ol$LPz)=j^X-PHblrpt^Kn~YgIio5L#Kn^Zl z9B;FkPR>5FbA9B0co3OS%Gb6uh^29Aw|*b?Gv9XF!o&xkUkCrU0JYADKMkb&wLiOY z*rekow@4ZLdG!4+-Af8BdUcc^+wc7kyMYW)Q@Eu(ONsGI6`@nKMY-Gh#y>aC``&wZ zkmC2z%^p+j5UrrQzsVqH@lC>?cI96pP5Z1v&1Yy>vq4Jxfu|&h7?`)t5{OS?MSZcU z;AK=?D@bLGrzeKR8X6jgCRl}EQ8PU9Tx{O4@jf7_bvt-;wKa2Hm_7Qr4A$FIZx{I2COf_qz$cVq@3jgiaphE?WH{MB2Wogkf1rKU2HG|%g>REBuGRp zc+UM!))gRr8bTZDqM!Z@s%Q)I-33@f)Cb92D~D+ROl$QV%iW9dAt$8qV)ccXocHGaMc9 zaE*edJ$#$#!6Zr8g{>CguyX4!LcLqcS%}BV$tcYGb3W4W>gf3R{PPpMJEsl?4inJy zKBxV5x_}U=EAjxQ4T{7|fQLHk+zmMu>rYF=@HM!7^5zV%(4~>9#AW(QzXFmdl!5e= zAcQ#Tsjq>mq=WygetUi2ATKBKK*w{x{MQCx7dJM#q#sCE}bi4G>O2x+zA67vG zM&eR`bmad@oNaUCe-lrQcR@!%5&d8yvL|)7ABa=yT`qWX4fYv{B>L<2o%NnVK7Ya= zD$;XykO`~!|LbWBoA=xn2#UDlDMrn3|=Xj+%Z!Mg#9w8@RVFu`p6iT-=o)A zj1>I>*`c+8bTLTTe%BEIY%-dgMK#JA8-!mEHX8#9EDs&LK~XLo zq=>HZe7)fQ4>=Fylf|0EYC_ws@rt79_ZxRYE~u9#+nX&&>(nJzXx~{A7c@p6>wNbO*K4k76EpoCzNe zh~d@}^4!5kzRji(QGB?NzVLXKgOihP2)^3=$Q)}JZtPAm`1<+-Zo~3M1=a7HUu0_d zm~?`-R{B1alzG}=K=l9&Amh195)>Mm$*ffw=COnou@$HzLP3Ni0gb#7Au`&r8Yy&4 zl%16|ko5vfvajUo_wV1H%A9!ccYI0wG_cP>0Fm`%GWb!!@FmCuPJ82k{b;bl`3MsnTb~pv}@!jq_GsEqM&@xx`c<51i-+G zp^VqX#7qvPHX0OcCCBxYT)9S2{tF5FULdHimNXX>7emUe&CSg-;^GL-Fy2J1$Q%43 zBq5Oj6}e9wmSv@7WwSu0_CXr{+X1rD>X8WqxYiq@2sKKZ_;;5oa3+1p>AIKmgzGNG zfir9XmzHQfdF8A3Pi_R4(stqJz5Jr0xU^70)|VC*1>H5@To?NNbJlsg6FiVWR$S_m zqyL~vgXkFs|HA;oYe*1Jw-1VPvcv*0JLeFy(MZPwB&eR%(~EzDq`yIx=WJ>NsEqvu z%Bb@yE1iduezdeO@H1*+PLw`JPa~GsPuI799v|pTr;sX3YAwU`#}y^P1J&*M>G9Tt zz3Zv!2y^YQTd1}3WEKE4$Le@B(>dStdALm^6seTzqOpP7z{It3)8etRll^FSzDFvWfS!h1WO`tv^FJh1i&fOMc=ZQ_)cO@jnT`r3EQ>E0aaE^T!^ zk=-U6a!=cv#1#g>@Z~S?Yq+axwKN5!0+=WOM&zP89#Sgo2r?B^!x14N8D|H}uguLE z+ge*O_;TY}1!c8gy%Hm2)y`UuTc#Q|$c=K^`19-hXoG#r1m%I}9s$r7Hc@0!jhAc% z`T5iYUZmW5b3XzgH3fV=S!HDo9iGi4rG~%S9n?-?J76=$v=UYlc5T~1i;qzP97;ADp~gP^y78Ieq5 zj}3T{;V3^N!)-PzsjEK+Ju|8WEf2{*$#9tM&9}Aokx0sKKDS@qe6W`Fm35yK zmz$MUf$tqNiYvs(O4(udhIt&H%EgM+?XFy&9f+|K9Wy_niJ_(rIQiuI*^W{vHQ{kX z|6985bQoi^YLAm0C7&7Jld;zVQ#D?JwH~$E2PhtCPSfk8aR&p%lO)xqzo^%|kowEg zEqCuBh&JCKxOz{TqhkvESQYzkK1G;)2koY;SrKZ4I1Wj55QmA9*ECZ zb%GSd8TQ;9Vya`$Q@ND30?;Ruh0mlq0Fow4o(~RD+<(Azz$1w2WguLiwu}7EP4mv5 z?(5Imj2+wOGnV|7%k8KaD1g=#5t|o9dAi$zB_ca>*5^Y1?F#qAF$dpS?&^x%hNkSOh5{VupG)y6MO z?cWgnf&uiML|@}~;YzM+XIFA*TDNibq`%N`r>w?dxr^3!@^M#Y)d7E8Jd19I3|IV@ zbUV`>8V{UfmjG~xA?2(4Y{uut>%q2k-et%V)3?@aI77Yef;i7t z`}g)@$mQ$3chOnv_1x?PeUXzOF0#XYVwZNGy*9#DoBaBr$Y>zHdl_xNquzS-g(bSK z1~l~Z(&us8QT_(Wxh^)2@r6-H&6Qs;wjOddnSk?`jqUx*1t+(S-3&f7*<&P@>ss{` zsXhlPUH3IwlqDtCx!qA9RX>+G7a#YpOn@X7w$0bmQ&-JO$4}bL9vy8+H$R>}=j(wB z%=wiAdbgsvt;XLGDQnV0maz?nDen9JL*WIflipSBk^Sq}OGK4@sJ1;VhV7AgbZ4I~ zdpP^Q%#^^xEdZY7!IZAoI|WE}Ag{fxxHEZbf{4#GuRYr~Ewh@|YU>`ZvO}Jv6+)HQ z!E$ec>sOrbpmL$v6&2%T*_f^@mPs|@#95mTCArBq71E5A*U~H~vJV=zkVvqJctGA9 z`7`nL=lM~O0@cFkr;-%51$puZY`amJQb8@F^ua6e)?=j-MJySlE&n*pk`H%5I<(8} zw`b}?oN($*HHM(b_z|x#zn?|n$YMU7wCyS|Ap-@)jUh{Ol2Y$EiGe3XOf>#FqQ4!B z(Ms=41Y`f(KzLRyt`tG1m+#Az9+4lE+5`7Zg7veC54I@}O4!F{kDUf7T3XtDbIhqy z(1q1H_~yeSdEZPq&$Hopj(3DBKU!9Nj?{c;2~iSUi>e3Fz(XXfpMeIF9FR}PY5%#< zs*n6o2zJ_OqiR4-R7DSqzb} zX!^0~FEc)vo0GnB;H@8pm>uXAb!I$xEJ&dfh?iiF-Q+g!ir zJtV?G;f27zq5&&%^#^?d9MfbJ^Z2}@7nYzZu&;V<6b2UaS?x`;x4E@-oRll?%Eio0 zqqZa22Otn%S5u-t6#N3_ z8x`H&vhLsm8yi?Du*9w)L6mAd)5pM!7zrY0{@J$iv=J;m1?d5l32M2@DW*=tmamGs-xLR`;FHMh(NYzVn;Gmd7+Nkz^jM+3C#qpakn>b-E74;79 ze{V!b71YB6W zOb%Rz=E!uAb}}P{6V=~TqXYA!h_F3b!)de>aoN7lK=qx-BAEp35T3Ey;Sb;YjQQQ~ z`p&NIxHu^E2#*=ABS`t4HC=7l;3Mt(;G~j6RzBjVKPV1U5ogV+k5-d;wR%((C&UTm#L88V&ATv~+ac1iNTVn_>P4aB=s2Ahj=Z z+A+QnoE*;3zXi}5DY%cqoX`Z*1jC9Olt@6EdB!9&zjxULoN~7sWg~zf%*w!xyESa= z|Dn=+%Q_dC`4#k=I$cvttb@9Vg=+NpgioLrkOJ!n$j7VkebCJIDSKEg)E!-?pK-!G5kF zqt>#fU1Kj%bheh3_*of*cna93n`Qk**!q1IyJyski&Mg~@WmU4U2zcsi) zdL`R{>@c|W5LCmyYnN%XGoT|A^zo9=@mWd$4T;_8?9Ru+hOj}4K5!cp8VX?FvG{YH z?VygIcJ5M_a|pC0>eVBlp`#DW@a0fMv!fUl_dKFSAe_J+v$O>CIx4g)Eg#rvE6^-p&o%`okDe_6 zyTBpert~WEKB#LJ@VTx(_g~Hqh_SYgR1%Z4#?@m_jg6%}n5&>`I-N(aIuJ(!L0}_Q zGkIe&96bD-=nBaqIWsLYbNHK{1gZlgt$pI==YOa{Vetv5qAO7Cd7V_SZ78LSFv=|* z3G0Eu*!^s+AnwaO@OW{e5)z1e=p?ycjWzIyaq@-2z5KBibsF79a z7}3>>wQHhXQ#5FPHx4 z6A{p8`E@dyR)+owUW4~}`Ov9(o1G1xle6>h5nUgl#w6QV29yNp%aWC74j27l^T&+V zF}n1{LY)mL$>rqlB=bbpQQ=gnYhi_UhJ_3jDG|4*(bWI0?hD#Slq z8bdhN|ea_Dh*`8@)W%|sEbXv?!Kc+u$+RjIm7k zv%GA9`&h(OSzOG)Bzw622jG@<>gX)r9RcD<|8AAVq& zkA9vwb8e+4Am_^$L3 zV$9s&PPV5$GKsJSoxXG3IjEj`9#D6zC?_?er{Q-$hG)e(yu~3fHM-$ z@;U>AmhN@W7kB93-fx`^y3@=dujlGU5{8C{1<{aSFJ)C!5Qd8X0Sc=H1>map(M9C} z@_`|+u~s})z9ZryGe;=E8d%v_AD%k%QWQOV7Z-=6HMS9rB%7r_Gt9MJs8Up6M5VJ| z-fByFCu>3dvz6$!+_7f5^c?w2Ujn zSJ&`J@g65-R5bkNw(J#a{xBcu69c;PKd274(|>AzY>JBeT-y0DU+cw6U&_KgfUo&t zp+f9l9LN!h4(2MHn0Hf!Ra9azGaf`~+llgt98RjwoS(PzZ%o(pg-R%^TjhQm zh!XPr^^9JMmw?H>7+Ykra8#qDfg<0R+Do)K*4vRn*~C*qSxv2ga6g~?`7#YQh~D+} z_3f?cCN4oJ%w;0>ixGQ(!4Cj=8fBo6%K&z@7ti$7HR)KM(Tc!O)frz`Di>P|s}byy zai!81`%X~_^9Vwvyfs)KT3V-5;9b&EPD@hUS7_yydPD13>!}q*p?eez7HSQ(m=lr(yk^z5HRIG3X2U@{ zj``M-;nSEiTNYYgNt!knf!J+#e>5z$SM&K5F9l1~Y)YzgS0_hP8;)~u+!q?2wG z)K5Mxb#i)XCBfcs;Cw&N1!|&%qZ!T&Cgy=&gd%+Up|nc^;Q*Xo_pL{I{m$&gP_CHvW79QSJ1>G_ta@MvUWx z{BhA6#<7OLM6y9fwi`6I8rwF$lXu*2 zjQivM>940b=d8W=+6!~dX@hJR?8n?ePTY%`{aZijeoN)|`HNrqqdK+1{jS5)hH7$B zd{a6bEq12kqK6k!a=6TJC0&hTwUgj@VHn?A#+`Pf;;lc5M!F9FR19s%>xyJ>+us4G zKUN;EEqLzqhBB0|PyY;luU8ju&Cr<7p!~c*qel~DjDpwk30Ej0j4qBUm~)UZ7<&|5 zzEDHl)hbKT>G+uH)y>(Wq&dXh5aQ{ewm3yr6eW@{x8|r*mWrsr1qVyrVu_ zSFf#X%c;)l$0Nad-Zl5Gj92UTPkT^z>>!rr~P0a&-6)V=zQEA^UTyYII_f^bXzfO@dfVwR4xTp~M zr`KL2c0f`hO%MSAk_1LasBl$2ZQBVga{l-l%i?qCD(+9oaQ`Bu6#`MvCL7Mhpxn5J z7cQ@}+(|=I6YAiU2r~hAUw9YLe3*l;wGXrnI1=DH6CRBscp%W?rrruF!LA8R&y>Xe z>rlG&_p6duZQ@ggWLZ5`9LL*!5bZ5J-i%CjHWmPGsu<&1x zsPNcm60b~K)`iQicP<*R60oy{)3E3pG+$I_tV(8ER{v6jB)9PJ;`q-kYdqs(=_q@# zvw@$}AR}E*g}j8N<8N=S{u`6I=DYsY)DwJdQ1q~ka^m8nhK<evdE$mh#5UM-qP+e(mO2b^#W zh4nYGNWF`#3WQ;MQ>!R9PRt1-)3V~J&7Elqk4>*^ax#SwSWCsxzJ=g6s>{bQG~a?C zYG5uuBbl|l^wx-{p>h806Sw-Ot1CY6J}Tq^NGhfFkWc~ZkGuEgAOsBUit*#ORth^{@8LAhAIBR^(=ZVM=zb^Ooe zriu`lo?im8EM%1AtaX{KeL0N9slsXI6-K5sm`)@d)^MSzIt<60q&QryBnh|(YA+}0 zsf+NIuO^2c=LFcqx-_oCnUA!z37=-a9qN#rK3QW|J)|_t3S<7#r7g%kcggMNk!2q8w5XDPs|aoV0x!` zDpEUN^l&pVnm-B^>L#U@Ccwr-QQKJd(@JJb#oXcYvD)AmC@M+}BIT%pK?nFj!*PgG z;(b9vRQYGsAuL!qR7)9kg03rP{$st=SXd09<rw z@9S{@UI4BAo1fni<~W8gEoxu|WLZ;k#M+AVSUlA?v7iR~LK*6pUvW6erll8PYp>OI zlWBWtYcqV4@1^r9Zs#Mjb28OGihIWWGhW+*FCudT2-;<8y8h&-nkORr1gd^knux?5 zSX*BA!;BP~_aWT)yPhbP;R^JH(q)Z|3dOFjRmii@iQkmum>XDU!&#}4#iWqw&~z-Z z1Fy}^xpge{$L3-rx`_Z`!o_Zy)Y4w>Y<3T zLg3$ljDgqV$;IJms$S+o-wN%YsW-aX>cAP57=1G%$_K(i1*r?1vnga*lOzfNGWPGY6oTSFh`+2f%6(@ z-QDb2rvxt#j+pQ; zNfw7*3RCG+{RM6uI<%PJFjh*87k18C6|uf0GGFUiJ#&Duh(7w#;1)Q_?9Nq5TNt-J zW&_lM0l~E_Vx8=03q;HaBm(EcT=Ck`6~)IRWGgu@#E$wYneSbdae{x>rl<#$C0)R+ zHRwFl)q= zvF@2zy>C2OW-xf2{+DLyk=z+`NAX!T7&1Slw9$>id$KdUF@oq^D$n*u|8{neX_z*Y zyGz&~njV*Z>5ae#>g zH<e`oE`Euz%(>mBoTmU1+ zOinhrr>aX(Bs!JUWc+>CsZqW}PQ{G`KM$ zo0r!XA=WO5$M(DNcdFY>GIx?#t_t0f0|B&N2DUmsHq*gK{wW^j&r)4(Dl02TrQ%^j zM|(D#(3R(1z9hJjBpxJsl$V2!ok@ka;tDZ`7Ju^6v6wxyQ>zY6CO6rTPI58oe=Hz^qJ z8`223}$I4+|cvStv+Y9PgmEqK!1DIME2<>l0X{PJ&SQ6&DjXai#>hrr z4=>O}A0Z&tarmH@1;B?%67O0ldp_%{U(fo^tvDaAhJ3(azLwyYa>hTh+%EFDj;nK# z;jG}_;LoqRe|8%%WE;M`&{HO;k&nlpD5>tJ4PsKphU-U)i&5CzFBxEG`D_R(kaM%o zf7!{)L_TllTnaqz&^I`2>9IW$X(`Et(kFAWNvi4)w~4YTSxbol4#;9j`0F#cEy ziO?NlEwPe4(J6)_s_Q9zVFGG&67?QSCv}R4;v2kMU4BGILl?MhN@%;cY`0ur=Lk{) z(_`=RsIsGzuA>y!-t&`9gn9S90;SZ*u^F|SPhe}K;r6Ds5?X8W)r{d`kB+5)5-L>^ zNhqHgr#Qh$HHk%qvQTs8`a3?WsLAB~7fz+tI$yOix z$-8v{b$&vx9BO!KfZgQ!_Bz&+rVPTqdHEA;!8Z5Io9lYF5Z7l(r+7PQT*Hc}+O1=` zvcr`kTt?&giF;IdwX;|wzs0?tY9d}$SCw64REktn38RA&B9_Y}5SC#p!GO97^IF*` zoJ~?aN!tBy!raxaj2*o3g#$1{DD0M+S8{VFUCZ7+abto#J6H)+93L$pd4D!^Jej9P z8<<|glg7j{1QW0{J9^p$Nk8LGA`#5?qe6Y2yDqW*wG~m#v)A=XII?zCuE@Y(oz=Uy z8|jzzxGd!0D^oiA6I&mBEmjog2e^_)3=!Q z06YVjd%bmgjNx#Jv1;>VT$tbXbI!!QK0HFpBPuthyexqQBd>@SFovB*D&OH~ zpSPmjtCw)gi2kOB96Y>H0Evm%wU?c>%=J__sAeq@k98HDI~FPIMVCb(azn-A$5lgL z4g;Y>(VcktLdUniP6%>R`f1fz!BCVVYjwhozN7&y_ZtFF~kjW+u%Oj8OF; z^rX4u&x-C!PODI&7NPd*N6!e!?z&nI?;YSxl3xdGgMI_Tb6t#gJfLB7TUI2>ahd?`NcInWL z@u!_=^AzyNMw4ff^hDrT=geQ1)~Isu6SjyEdJ$RsEP^fuI-=Tro3>60++IDpgp`G(;SQ-C{>2#4Q`#A3`)QD@qghF z!H#CITFzp5jUb@DGciz6vLIYX_%WN^!G?w_20ra1^M}514RiB8ex3F zA)HOLFG-ltRi|T18kS8Xa$B)Hv2Kh0>pE?%o8BhLIo0#?7rb>5b5rA&C~2II?uPvK_Dq=^UOH%g0Luzs6v`%60fHO_(I>sQ2N3qA-zMCz=;Z!!VevgO zy;8yntB?b;!MNS3am~F3cn!87zxnxy*HA4#20}f32!O_m zlhU-MySKA5X&B2xZrMX*T(8LZN1j#7fDz(@qK`G3-8Q?&BIc-7vfFM;_EN^u*4DM5 zs(CFJ3QcoxV8Hz}HdA0KcbSlIf0s(ADZ9Vc`B-JS(Hgf1V3}NEV`KZ^|G@;mgIDcs zr&asatraa~DT>TEo5@$rGhw^gFW{?=c2Q_V>WI3*h??m}8L&@ssZ|K1>yGR=RgnJj zumE~97r=@#zzQVTYmSqghW7#R zFnh#$9G4}x{1zQ+@Rz35>)T=T7Xeq2{qAoC_kVD0hb%ed=mzr)h=w$M;5PU)Mlgu< zMRWVXAwkW~$0=wov}b7XB_}jYGD|b5o_&vfU9P7Ym8jN70DkL`;r)tXH!>vwhT*ai zKuTaswKg`Ya7I-P!_LlgTKxr~qix#$CeP=@`6y+bco&oUV9}?=o|y-SVmDbCl65?6 zMfTI6+sVTCx(RaGa>CLfsoKf@`u&M9v`Q|B(6-?@LcBkJ7hc`UTiTDu^3ga%*UF7P zOJ(EIRwrF^5K$(g`-8D(b-#?kD@r6i!Hs8rdoY$pm!f`Gf9-<`L~sA3|h6S{L9a>QPy(dFZ@r zW1}Z{A4l_VG8lk&&JkI2OEs9Gzb>9N<<>OqYac(`eqV4oTcwh{oVG{y9jC;;UmVDY zwAjt!QJqU%Wcu>)I^+}D)3k5R`=Na(QTO2HI2G!pM5-A;e~R|*&y3>W0`~#enX5IN^~5;I`X=MCo}kCr!i28hySHJ^A`@Gj1pMGZ@G4{NmkghDpvpwLZ$HHAma_4W(rWrRTA z04|cbMR4ZtiIdxmKW(EwnMa=ik`!HdRn_G_M?~NIwsnc0O3KOtFD($VgA3UeTqW~1 z01dYH%~X)vo}$xEBTquQFA!IN|Bt<@tTGCrlk@M0rD-1z4pP7Xb5#5 zUEsj#Z+=1v>*p>NP@kl`hCGMY`a*}SWR~bk4B0%Ie~A{07U}oDi7SHyIx8uB%icbs zpZSV;Ci*n3*iSt+$43~aYcFZ9+1B)ROi%YP^I0wDhvZXRpSTW71LA&+i(? zUaYn_hVzNf6Y2pATdaT1g5k^kjRF&oK<%*)$D~e_W=SFt_5LZH%n9MXy_p)TVIE)l z8zSF$3^O8?Xt<~P~mAOQMRzi zf_{$M&TX8FoK@_HI~n-EVSsh?4q>-Y=gX!(FDddQie5?VX24>WzK-?U2yf`}7N#v? z<9H4L87+2aQd~AGvK}~uU()g+3Vx_d*bOJgZfJSQRWx|hZs#~Cwwb-lT@!*cvu^cz zyvn1v1c0!>l!VKZo*0?+mi}7K%MJzo#QDq~lb7%7)9q1SVu{R$+r|KQS>D7-%&cc) zRiTkv2}fb9eAxbU9T<&@^lC6B5#Ial9Y6-^GzRzmuoFiI;GO>OIUVQzk*%HYzUA6S z!P*Dr2?FHUMh5=U-)aj{C|Z9>&}+yKD@alG;>DDjFk!Hyi@XVT)nQaEMwz5&SOovb zG=L+kv_mW>={t$iUXt$kz5aaL`HTm%+5_%tci*z`7iRU}{`Fh?JNNp2=>Gm~lOYsd z1%dqE5A1wyH{$f0yF%%5sKQoNYwI#evIsXP-`QUtD6@0%p#;0v+l4k)P?N$J2p{P; z$|tG#Rw3sRO`lswD9WcOymtw_`^AaWff?J#ZfwwtL+_T|EQDgk1Y*g9bqOr;Z~@ro z2)Xy$8sF17z5@X49(PTYGYP58Q$ag@*xT$s2+AdlHPltF00`<5YUqCG5p6W_`qoP; zJyCLT;oLx70~iy-qdCQ0MJYY&yeE|(o)Urr_-NWg)6!6tOH!HNT{D?`NMG9N_|%3| zcRerl^u z-D-oy)o%+0q@bt<5e$7`FQOZ09p!!*2Pu!4yMg#96_YGQ| zEmI_WEVxH&DNGAEWQlwy;z#F6>L0IfqSY4OQ2OG)9HDo+Q>%m9MoD0fs`;*8y`m7* zdm5qwU|EL+=cD7RV%%T)upuLU!^@L!&C6y+Ct-sa=gtI{QY0s>>Pe(eL#!;!1c%b% zTm~QcJ)wq1S2PpS(kO=sqMHC-FVb1@&0?9^(_uUw4qkH-rnhB64@6l@qvZb)CO-S~t&(b7G)ayt0~cpg{C%{6 z%x&j#tSV*+v&Z%c4gBg^5r2~MG(~~9QkG&M&^4?Zz|%yJye9u16T#FDI<9W3?z{6+ z=zx9Tc`T@sKax_gKqhn2?$9h=Oa98|qm>e!kFHScPHi$LcH_HFx$ed*apN2nr zF8X(okN{f2AnX~tJ$0|gp^Thr)x#dVZ&yk>?2Hq~v=7;62SpU!psW(nc^N#RTGFVa z)$#ZJhb~;TM;7m5}MKl>C!ucr0|(79ko#p)a4WJXnq@D&*c&ILmR+ zEGz_G{T-})ij;0@A*HQroe#vuY;D^wMdgxl5`52IOa9^oTKwtGnHTje6jxA4xJuE5 zL_@j%?M4zwpA7_BeWCLsGjrXfQ z2BhShoL|JB6p=$>lsISK-`<(!rjBy$6R^@|pAE#YZi|O^^dfyED4kA(7!&eF+4YgWHg>us}Hiw6gX{U2Q^Y6Y=(3^+~7$mx@F?fGK;EYtpax8imky;6!wV12mgDIkmHgIt z7)wT8^V4z#h_{?i=T(S^P1Pr?*jCu+g2JHP1NV^Iy#L_X;FtAdK|qpr)XfX`s!Ibt zK?lvAV>^BuFLz*$4sJNOSQF8CFm;|4puBEJ7p2|{@ z>r3)98HDMl7VuVcsv!+#%vVG>ye`hb;#LhjPIg3fB-8Ev?I(TNU41nff}Zprz7dqK zVBwa8ulWgjMt4P$KXmb*QP3U)dfaF`1G|0SpLMO4T18Z@^Mc0jR~=)u4UfOq?WEt3 zLv$FOT)rg?x$5_afgE!02MCBHTxrpE;Xt&!ek031& zbXE%@x~Al%xF|Ur0m>%@(9os$9SP-{5@O&$)K$rqoRqat)gSHc9##39ke(Q#4x)dl z>Tb71+!^3`wuuIuN+Ip4XfqY3dI*M4QM+pN-yA|Fp(<%LVpqR!k>*vd=tQ+IRN^$g zgqo}{NM$g5GxW%Frjcy=nQj=&Ib1$AqkrUd#WPFtYNL5ry1}tSFJs5JogrUaD_7a^ zE4fhy6(@97gl*=wTb%S?T2Num_<+!WWgNEv6GSLnLK8gp^)`r|;tPpOYOW+L*V&p= zKkQi^HhSm|CtLoCnm>+tuQij)Vyd;L;=6uFm>Y&0-3b2`X@!r{cc!l`O@on?2aJWQ zSw>_F8CW~}MZ|y(*zq3<;&G3a+SK9S{-|;c*ZUJqs+f<1ZSSyVv|jexhdmlp&tjfM zDGuqKVGUiQ60AG1U?oFjDCLf=?;WpKJnvWDP&mBiV95t0M7n(C@;GwDqZ#hUBLfM2 zaq>yAXdu?~w@I8)e4)zzsHzGCc^LQ|wcut#8fM`bhsb&2`#fdwhIpdsga%9jyyr;2 zn9#lDhJcXM;6=Oc<&>*;4Mf&Ox~`QsC2G-GBJVQB#E1fY#YF3Cu+MFGk&c3bri8p{ zYihq7+vxaB^*$?*Ye+1`M1MahM7H-SU|f(97cT6}5+%rrVhwH)5Br6px>VnSNES=i z6T%~XRq*0GV$#YZAIK%K+CTNC5p+IeMB&asd=X+Cck z+?Fm{2icsE1}!a%N`sag0+n4H_R%6Ig?AV%das;SDybmcqzaIcS+*x*KSM}{j2#ll48FFpuF+SWWkazHFT_bA~1{2QygDnUYv$Y ztkyfdb75M&lb;zmU6>(fLSg0L*nbDb%W=*Eqk|1drhP)sC=lhuzEHFxIaN9x8U|AF z(njA~rL!iiygoPV?%f5_Kq9mYQ8jBdEZ%tAv5La6D`Q zO@65``4b{B#%9HGey}BLO|Bit4@?QRDpk1x(}cneqb}J@-ZPrekQ757(T0HzO@f{2 zi#p3stH;+-O7BWJD77$}PZlSyBl7}GYE%IfH!cw*<23&kVUuK#=fsuZqK&>bD*ucR zu`aD;Ax3pML+y0f%QA4NO(SAWn@^wnS}HiykJT2g#!imEWEotuHskJgo15%6cT$78 zQ$waKy}}wS2CHcE6EAr74_7<(AY4d1o#k&G+jxIU59zNC_{IDvbbHlm!NN>U8JHuL z(yOm@-{Nl#WDWY)e$|U^0MnbP8ssB$k87Uio8150!=juzQmbnAWV40+UP{+Jp6Qm1 zyL?{xJ%ws$qrixAxvnrlAR@)CljR14Pl4btNcc%DVD4?+NZ_rUR2p5AlX5Rds$B`3 zxVnIpQYPmN*S)A_?|w55^Ysi8XJ+DOEJm;hwzwB9yl3JoF^vKD>%B+uLyo$O*xyy6 zL~4z(f|#L`OaY5<(Hd=vcWiXFqoIg!S=yOdYUV*Ezlv4TcnS(mqA9y{9Tdq&N+%S1 zYc6s*ij;1VPb5$%K~t6}kRdm=EY!--ppN6hKTMy)S^DHC#MQ=V1}J~eh9@x{4&`V> z?h^C-c>TkT7FOLCY*IDQY}s4wJn(Ck=;fxk5wFOZd@V5|!pYLXf+QLLke~9;Vmc+1 z^I+Z2_@vZT__}iEbjrp;{oMzidDyKDHHnu}f-336#a_Ft+qFj~nG~Al&c&ebXU&iw z6YMX(P1jXfR{~bErSe&jLv-9Be(I}AjpvIt_t$N}JduF=s~Q$KH28aCDpiHqwWRN2 zvRU0f^tP#0+LQ0JyrE#H(ZuzpqlZ+eI@hYcqR-%%iM;$xqy z#iwIN^4{_mmN|S2F-yT4wyNy!U}1di)(1FckSp5hzrJDG_nOx1CRIhhdO(h_HNe3W zDcb#UNSbX-6C*JtmozZtB){?9>RLPz&YQyJ6EcHBpVI&Hjchh3#$2QvQ$RcilU!Aw z*M{u%7PeMehv8e3LVx{9UF!a*nMLE`STo&q$-KsBbC0M)>?!}=c2Ozn)bySSq>G$I zDkKIe#bCG7EZh4YwN*7+w_F<}u9b%Hb;;b~TM?Y;l z)oq|&YfBDTFxCbAUIIFS$plU+$zwrhLG(PcZ?u!O&`@BFP?pnTZ_84DNJ_7=EP=HHYqZdi1Sef!WXW$su(ymT)EJ;33U~@BljJ(k0t3%uEEUb11OH9&@s? zA}_Ew>XS^HKh)9^!$(iJlfpB7HuIgdd&LPI%JZRF^^aNHiIrur>+{$G=!gUUvMFya z4*rxUf1#F;dS=t>vs5-i!9x4tSj!!;wT`BDT@9ba3n?wvyMZt8u^Mh^?qghOcXeSanrIOfNJPU({>l?v?H3~@d`_WZI%j8otvMO5(O7NZUyK-jIE7(tY#Elyi*!d3o9SJW`#Zd6J?fYfx zko?2bv2FgHqyEucm=iV9-qz7(JvGtm4c6th*y8iKiCQO9FS8Gu0pByi2rmY?WnM6@ zpWz~j19EorgNN{NEez37t0m(_#V4<)T{yG)z+4_ayt=c|UhRla3Yg)R^hkg1Uqd#w z{*rI*o2NfKR^}M2PWofv+ka7*_Klf&=ZX?Y)%CBaZiZlDil4m04ay@C-kADj-+Z)8 zl}1cFZCEg95cK916pbI+j9V@C(y*}ogu5j|-yfNWfMb;9>-UZbFmmHOyua-8akyWR z^737A{?XzNx&G^Z=_!E2N9Nd@tf~_Z$O^7R7T7PmMe*}(^1m1GG2|As%uE9+j3@eM zbv|4Osag&aVyPFL3?%$PUfX|hPu;s!D%HRxQC3vMHRBQ!4Q&_hyj*z|x6T0q=lbud z9`6~7x0D!Ui{&v>a$G?O;ztrJY{ZB(1k&hluKB{6Hsmg0*9tRYY!ggy;@A|_8evZ4 zt$Ncs;R)sxs10rZWSSsqH_I89pHc8p(-_gn#}wTXGwA~ z)`p|Tl_pLThI}o>BK;RnjeSQGU2+p?o;b_UfuFXjv>$v`{s^GsOC7JC--L`v*e@)Kw2Tb~QoFlB0>C z_$2aUy01GTII&OYyQ7+m+mdnWX6N%+-7~MJ4ceIm9c*dF%M^M19s?hr?iuFmlvH-= z=Z?;QIiw9IB@;D<0Ki)Tk{`9GT`wtfC0TAcdq2l_ZSIhspzD6k)4Q`!eufAnutHg{ zC5VK{OfHE`{8sJaUhoK!BecQb`-rgSeXq^Q5HTc+){E zCRY`;OsQTawusCD&-`5SX<1If*F2+DWN%n{d{p{xzL?uyWn;(xc($gnz-g^=zXsDZLd0PXTh0#VXChOHF`V1+|O+5#mNK>($e)YF$fGq>?Hk zY3Jc`d$H5IZGBXm4z&m@HkS8EoIdo*=by`pwGCZ#=AUGsBdEPm2-9H3C^gNjsmC3$ z`ode?2;Tsc)By>x8B)Z$OTN;Cp+*?zByz6;4Wm!8Bw4<1k5|J^lZ5w~TV)Y2fO@!4 zQy`zuWu$HxiWcZM!P@{VBZPM-mT_5inOy#VmAJ?{BpMqHjPXk?B z^`s1pY(hm#pE4Oi`CLyQsx^SW+w2WZj`HO-t~7`rjbl*f7l5DPNg@RszEj}wZwFxf)8yDbWO%kSm5VGc7Vj_*41AIm?Hl? z<``XeUm%V=9!I5e9!I!a>-Je?X`$`7+`~no(D?LN#h~^JOi3|47<51l0WS77w8K9> zSO{G5KW*(!Dhwa)e%iK4OSZzh<<`G%^dX&0dDoi|`^1E2wnaduM;`8p`Ou&3ycKwp z=S%qWqFqWW3!$Mps1EMkMS+oQxI`(SZUNLg4P{zyHWU?{z3O11Cs77X1k=*9x7 zCO&D(V4DjzM96VFv2nx@Sg5EZkZ_dAuygOPFV_x>r9kbzFwMv7jQXeIebz=!mW=Ol0JFtk&frPt*WNA1Ptno&n=y+_Jd!WH$!lr zoNTz8xsc_&6ZXd&!`~9Cbi42gye|B$Bn$oC9PO!^$Kx!$vlZAUQOkSu;wTP88vx*l zmeZ6-6|Tp|48hw4OzKoup#&D64>v$|ll8#(?R~;9hdm62+Tkq*hoMGreHr6 zn1M3MUq^Df(VrF$eyJx@O?^&)OolPQqYSz_F38vc_%0e9!gR6y@rjTxc4P(42QYQ} z`Q9#(c1fz!U822E8VEMn=|N^zR#0SiD)3E!@`sA(wL489-{S?9vSz0AQSBgpB9(Dw zR)L`~7!?yWnGZS2L6v(ePR@S@8ib8&Nqo!U`s5C}4DV#dL8~tjMNxXL(J*HvvMwQZ zVkk0tP)hR)23?RsNWI}ruGSjD0P@Z!V#FlG_NNUCv0lh0}<%cs*2d5#2x^Y zpv>#!<`!rV9rySLo2!>TLM-h&2|w)AcZ_{7hn#Qz z7BFu^Df2jd)+>NDqZS(W@$S5RRou2pd*NJCjsPQP`LzWjE={T740KkET|6jKp=ODC zjVJi-7Zt0c@HkPFTEj;gZ;SCb?7)uatLl*4fue^qO><#+xugYn8Obz+0wMB$uYeEn zA*kSTx=hBx$}t{;KRu|!tYTu7TQAp1dE>gvcl#c%A;oO@9sZxta80b^9cO;;c}F-{ zVi)2hpbJc)Q9|xF28gmPafV?R}))Xb3`YJg)9O&#zNK;Lthq@+^-qiwJudv$?lH9O>$QT2j zGm+0#Vve5;tv{h~)SBlzPh7Yl=t?5*0AOk}m)C%0zI{7$?FyujYTW4(@AMPC2?tBC z1^s6Ya)_NeO?7oRW(398^F~2;O@`(Ti1eMTZviABu@@Q{6q2$+$_1U=cVsbaUk5Oy zb^Ww{EW?ilzWK<(Q%^tC0vD4%SRx<0yzl)#*CG?~aI>Pn^l)8JQ&UsruShTdT-WoS z0xdk(c%jPyNp=H(5yAUOxN0<5hf4A%gd;bZ6C?#4p$Lw`R|R7K9i#DHSVp-DI=27M z1pv_*31+@D1P@Fv_o#pqTboMZyyo=?3E&7aV=6KqI@a%tOY`lb6p0%=z035+`;_J* zKklm2Vo2-(y3p^#-)M!o4|^WEir*#I4F|ivSPzmyNFpU9{f#?{1{T0O8dz2O6EL2o zj<=^{LdRbPlfHX!@~BD$aD_^cFqViTxMka1;RoaCEMnxcF5K1g_dA-xq;c8f;b0C_ zWDRHEYJOP5B2yKhWGnp(B@Pb9x+|7@qJ$UCL!;Y4;4$bATf@s9G=SpzI8P&U+OnPiB{}&rHAt|85K0pn85S0Y!zinKB=>Y1s#^Xe2o?7Y zUf3%f>i5Tf@g?Hd;EF&i&Me#+5xTd7ZzZJDkqxp4L#-uIq&L4WEtdmi&qKc8&L`SD zYKV3p!!kY|gZB%6rd(JBq#8AD1&bkxrO>=Z*`ahXrmw!fP3U9uh#8;Z8Lu|mH=ng0 zFP=;L2`a-3VaCn%iyk_=JAI&B!0@@jKH{J&meD2%B}@puG%J zT(N!w@Bu zL)N?6-^?mDllO>lii%PWArvzp_A8*rGLGd{9CyD4q+ZoCI6u;EFQ1)jmGqrtd$DL7 zvY8^Obmrh36LdMP_JmJyBYpB2pQt-_qMrunL-zVU9-|LM`4cidfJ&AUfdrWnFxvIB zOrlG40)Lk-z?vKHZdQt0NPsnRx2;p1iQe$^naHpkRE3(H}eVof5$* z)1>O)`b63@68@cRFYw{`%zD4f_;v4-W-^kEAF%Bgy$DzM?!YN|p00PJkMdImj`6;- zTUA!#Ui1}|&&%Z?^HUV@6c_3=N`miju76-IrzVLa(#`n-!9l1?8BEw}%XYKWA;J)L z`jtLt^M|bLxfPCACnQuUJq6(J--^Y}i2Vx%j6dA|yMSLgl)TQwicQ;?FZmdYF_cFb zu-EY+7PI~izeqxjGChr|Gf$q~oVrCD-eAv>7ZXC4YilVfk^cPWyr50U!ti4PxfGzG zBaIX10d)h^arc!%of#kD?-&0Mzy+nYq{c)qEEGIMW=kWIONKc1tDHK4$dK@lGMIzp zZ^24138T#>b?O3#`pF>!WF?=fCj1L9Vpd0TU{kLPQxYR%>y)7CXa9N7N;-@ z>3ztn2tbz85S=&EKbUK<;`zr<=R^owI7ud9VB_Y5E9UNchPX*dCS5iv``(mqa-7ZNzz^V3@PMO1q%;4{w+PgqFgMzBYe)>Vdb;;v8?-ncL(i0pui zUy_dS(e>?6*0(p3p))KdqRZ39=qw!g6RQG#l|)YO1xH_jk_aJD_YQ-f3qbet*3>@L zqQOi?Z=+eWsI19*h7XbSw43dDCg9-kyFaDg5AEwH;94QPWBm-^>@n~b?(4R(86p1H zN1UvrzdTO(ra1e2w;bGW3jtt@0kaSaEnTMB?w^#Dffz*(>Inm!+}AsjFLv#JmcY~5 z=<3)}`LN(rm=YpQVf>dH26%bC17L}5Q@-bPT5{RP0W)2oxG0)y3mo1B_U7wSZ{X9r zI@ber(*0?C`Ah>*C3ax-Oo!a|u;Tfcp;3FtA;jNOfGRJ^&O}wc;DC_B z2^Dzhe^42Ld@v4Jn}gna?97FWICke{4tAUh*K7m847*ZU>73(4lV(W2_~ z)ieuonv#LP08k`A3{z5(>hMfY$Y?oFy9)4BjlYH`cInDyTa`&dCkGo|;zIy1zf_ZrzYJYkhx@GJ1v{3W;ho%!+Zc%kM;d}p;^F6;(SVLHt8_euUW`Q`;#%RCL1z%fDL`_-4IxU&5MfR$uCMZ|XyrdG+k= z2cx@x00xzsnp!C)^PHA}mscmlKin%3JFTF`+tk*L{0+F~`QF#~vc2y=A}8;Ymo3SN zX|m}_#VNCIzY#n-0Dp4{oFUAeA45QuSIc3TECK9ohk_4WMECvF*4L25?wo(3pcD%* z2lbkkjz%b~n+)0PCZpGH24q5lT7WCH@~;p;KT2>TuLi^&E+ed%ceb`}hRW&Bu_#gJ=70Q*k+=y}i9pCv`UO|8Y)KO$2sgN9(P} zI%5V!qj8|&33Hu@k+?7XX0P0hnODoiK!n!oulMV*sygn_05)`ped5xeaYV5Uj8{!%3QyJF63r__*dXr>`-s$GdI8#9B2NY|Kt+&Th$)w z0`h&>Z@{~iISt0*aRvbSTC7ct43V#mZub{&c<|{rV-pj}FB=~;f9#Nn^Qj-ME0csj zgOA*IymqtT2-rqBlJ}`zkFCVQZvgGpLh_I1DkNh5kH*oYiiuv#t}9oKlH$u0p`3L)CmPMZo*^W%bVH@fqz^aGOczi+Jd(0*P+i^8X6Hm!R7hA*9|(# z??2lMFt*Ti2?Fn^Yn|#!@z=+9ZJ>$4Lsn8!(8Ghv!qTz`<5yl@9z$`DAu|LokeBe~ ztYTyu|=TZe}GX`6V_OXfosp+Vc ze0W#1{BCo8A77v!>|eJ5rU6LD^c8)w1F+-|e|6siQguAS$GXAm-`kb;fnPhuAP^|? z0Y;ha8nmg2`{oOP(I0?<{l88ir^kf&(C6y(O9IPO0GCI{`iR$norGEXed5^UYH%I= zJB09?ILARBVmS4>+?MSK9Ea5+qxq+|LB6-V5$CIXEdTM3D*{{4OQ#BWnBwuci=npC zR@fCJ^W*U+Hy;-XJ3B_7+X`KMKw;Dk7$>=0=z5!*Q0bllf}0q?&e?`TJP#BQnLA5- z_hXiICS80r0>_D86Ojl(K9bPabE>+VDVjFmk1#+e?e7N`&sTkn3;TV{(kc?n@@<(I zWg~}ki~XV#4Dh{TK-IGI{mm2L;@boW-Qc;A-3GwJfuBJ*thi4>04984H46rd|DYt> z+!;=+in)eyCSi2g5&v-Y%^&_`R#Ddf6>P3!y`75&FxkUT ze?cKa7`?TgHm(#drM`d-f6mOz#Nz{6M88il!noISpsMvANN24(|t!9WpUYy=Rc)92qf`=H0#Tv zb>8X^pZ+YxNih6b${k!C3%mgc9b~P)j~v^7IQjp6&2d2D$JQ2DG=1*}A#k7%`~$dD zF0-)`Ae{UNcHU31Cl**}l+h5bSGi z1wg66%P)dp11Rd?F6LhRKc>DiAj&8Dnq8KXZlt?YO1eWtLImlM7Lb(gkS=MYOHx8Q zmjN-?%zhL*a!QDFg}z7%6Zf zE&ibS0}3JWclS`!>bAN*Hmwq6e%?f>GvwRxfcqaQ-&?pC)?CrE{4N)O2fusmDcb>Z z)UFlj1>C`A5QYQeF}YqJ@%pwRZ+bXi`w{7-k?9#Sm_%1C9KPB47*b6S&y=d}`JVlc z7jXo5y8d-}oBTUWK>WWplrl#<-~yx=M(O-cm z1i0DiaO=3#gYQDHYS*`aYTWBTAW?8obLJ+L)nepD3I6Hwohx0y_T$ck%@>*Buf?U} z7NIfUr+4u@Tt#1g4)=M7^_z_r;jjryW6>n&HQVuBsA~9RlD?uvBD_%Z0s^kikWuin;MQQd!!)Tffqzjli5-h<0b8{N0~n zQa~(I1 z2u26MIDt0Y-VARd$$R#Zg76yKJ>9RIkJr1&PoG6Mqb#5=y^i^)FZ#U)-3=Eqb(`ok zp>%b!S#gUn>H9Vh<6My>scN~(Z;(=QbOq#qi%BKm1H(OS41YeUXsVNb7*qUx#OKgD zAxnex>UWu)YnId#=x>I+1Sz$PLrJTjFR2hNH7MzANvwL`4z-hZBd4O`8ij9(O8W-_ zEw|mX)={gZcB{+mXGhh<`rWNo@TqyzKgjm0!STG%Y!KU3Yeh5%seo1eJQe$=uMQ-} z(0=$)$T!$Wnzd%<&6;=1qU~Thq!COm)WhZt-?F|#%}m3GqfcgdO7-Rgfmj|dW`32v zt@+rmgMEX{FaZJ9D2f!WUPcanVEM{Q4p&@(Z`a>_4$zD)v8FnmfkeZ>z3c?8mY}!D z51?K>2rj`jcM-0ea1l2wH-|~PFS4i1ws?xF^@1D17L_;@)FyFEEefSi8ao1|_@QHn zyPuL&vfLkWy#~a$b;u%?wK)Er+z{y^UOK-u4hg3&?P%94lCkWmpu}^ne8>4n@%0hcSUZ=oJmPC zrzskLP@~ou&xFCj8GkiSg-QYiH8s>?yq9N?lA@xuD2^{q*s>3-TT&MCD~@LxVuGk7 za>VR7A8XSJ0<;qSe6oBUJuA?|3|LR(!qaWd=Fcx&h@#1ouEm}tJP}lFbiTEGl9~6T z=?9Cb(E1f57#;{g-jawzmV{dLdS$r7P;Yf2?g+cgV2iYf;lp}8b}=VapnU8d1qZ@w z&LF4QK+p$46v9bK{Z4RM^vJ-2?@NX=3g-)K8%c&Q*-wy%o0h|J`;9q?eFu;!PLwfz z{rmCecNDZpHD2bh0fX${rhjtht>~EHjh6hreg!oC?tGkHK^5-}!-ZP-R!C479K;{L zJU%8Zpv`uo{e7!^XcCHpPkXi#7oZcKeez5Zjsc>#oAF|uj1f4IuDs>C z=uBr8U%`eE@tyv{0jo zKu;J?(*olaBEH=pL%VD>l6Z?I#NO^L1~Q+oa@~&YfpEb&Lxuw=4FFPaBUe^c9eF0- zd&`Fj6lmZ{l^@{?N|D-XQH?awKbUChBrIQEVOJdF z^@ARL0Od*kz27!*BO*Vr6TjA?fGHE23y_}zG3|PhUYf^{aat$P6dm>rBzh%}kMex&$bsCnqPb#R{+N zdgA#}>Qt$QPUg%ngw_qZXe@A^$tp221Oy2Z=DmuGbk^Q z)_$=b1L&N?M}=1pTYdb9-dwz3HR2)=qv3$PSTReG;uRrPIHV)fDhr5}ksp4T{fAXc$c(mK3 zdJpyfhwOd|{<#2SyHHzh{+^LWlMo6_pk#Tgp*b4q zli#ued*BlOO<_?xxUU`QrSAhQpa_xiS}=&<8LFI|k%G-`Lc0L=2P4b*lH91h9!kS& zCq6e}O9u1SQKNNLs>>zSPG@e_&h0|MdBeuXzv@1sGsTtM235I5c3?o_XE4#7;7UA! zBXZZ2i1^^$b6-bU`FoHIt@#jcJ}#q%us^ti*kFh@0cb1H3kni%4#c{){I1!HEH&z& z%#c5mwKl)2Ox>s3#=VNDAD0!-Oy5et%fu1XK9=6Yv@8|N50O-!MoWD&P!VrkV4FBW zsb&%Inu1mG&zsUJ*`s_pn1AdO)C=BR;n)l^n*NHiXkuuIyv+t;Kkt=pBk?svfF*xE!@lf0 zQ$AOc#qR?9V?ryfxzhzAqT<D8VI-0H3TuOL z)|fTCej+MT)3kr`wRO9MNFxgmm`;_VcXsI@~`A!e;4vnR3rSp}ymSt}R zCSuX$JmncLy2;Zi+}9G{6Tcge5)61J*N?3oid5nJsy<^y{~I{&iwEL|R#8X|*Q(5# zO%(IAV5yvX^nOS{v@pW4>yAWt*)8e2-J9*=`SA>?MMthZ=D1hlCNZT4A8YGaDd>(JSDVR|GE14Jtf|oRlQn}kITjfmZ1@@jL+VbYDj{3)f zpEvM|4;|5xwgTgE;OkoDpiPkbwrTb&=4OP+_{MO@dm0E6DJ@fQiD4j6a`yS0t^JNP zGjC0KBs({>FOthY@T^DcJu8uBx8e;Q<>Ro&=;oi4Kc`=6Bb#d_T`Y}of0J%b@_Y0t zCu%;8U(;_shIKG=oSS(hQ;HORVy4XiGJu(-gO+~2n{&$4pIYPidE$XY7`NXzs+F;6 zQNr99YcX{qVdG;3?%1sNFY;d`6)AbkG#V@x#0_k?8Y{vJWJao|vI(R_&dR(6B0Xt3 z`a51pP#zupy4(2eM*v|Ne<3NNp7V+Zd{K40aA3G&c;H9OlMq} zsQs_aeg-_;<*nhRxeNq;d}zYlBrMeY_lA$%0xL-|+5fvz@JHDb8{$U~x-p`fEa~zT zoXVjQsA>kOL9A#lHWJ^vd?Ck4el_@;@1}h%yk^t8Eei`C<(d+#Lb|*m({%@@Elzs6-M&&km7Y-4fJRYJR<1&6y1s=zN{lq| zV`tkOkKO&x-bjNoOpx7_*-3RYX0EN7mYp)33BR3Yt&Z)b;)hDe1iC<8((gX}>v~Aa z+z$Jc+JmNd5{_!Z*qnAzwpYSQRs*juZ zpNL_b58U*|HrE^3F?O<=iZ}zLmBccVYHCd)eOuMow6}b;h;j_x(qV>z%S5=f)0T02 zG%&;(Fn)#rjvy0qqhiVIhdjm;FG%SddG7X-swedBAKOVUR#F7VB<80`Jv;@j*LOBy zM#sLq`4@@eF?xy7^W9bgR1U$DHhrg?gOiL?a54()o+8H}GcDo`(x-H%4$RNC;>$^$ zQ3{3NCrbav*A@`I#+mc;(-mHxVIZR8+xTud%CYkX)nnDK>Ad@|odTE15?cnjeEgo) z#nEDB#dmxCZoJU>EX#c%;(X_i%1Wa6Qo=W*fwR(!Nt0#MR-T@*Z)>jq;d_ z{+XQKtJLhikf6JQfC>yAo@s8E^t>f*RU*Cr!p^vysf)0&5-l4W20&qbzIfo^xfxfLjm$C&QlT* zI#j+!*LC6I_bRtGV~=k5GdyX09C5vhIu$|K;MaldFCe+hxVHzYb|<8;FP(qiW<1)C zWXXX8QvK-=Lws}2-y!)Tbxx%{yFWu6llmy3`+eII#>^D~HY|(`exmsDdB0PjM8|{{ zGK7(*@3xa%C3ti9oH`|YQ@I((4n`{| zo$z>kn}0C}gU{E?fWx|uOQa3wHpy(?OT@v8(~Q}Zq?4?6g3zb~7k&;5kDK+SD^w)d zti=4Hzj0NYl}&se7mx%!G)|&6|Y5EjGu5p_z*zJ1TaidJRDg9e*I*+zZR|g9t zH>tmG{Dr*FG^b~gAeFko2Fp~VM%~U(Bt6<%VMPM(REK87ZSD5BL}hb|C}P<~FKb`x z$ZS=cbon1%&2iYA)2_(Va&ch;Y%n4*rgep^01hM-F3SiUby61amDo0^>YrK5XYgsV z$B85TDPUK=jNP&u*mw_1|VH#AC|>=9Bc|p33T? ztJ-Xq3Pli8Rm|RkDdPMfHk@v6GuEZRmj=}xi0<#YIegk4CD;kb>5^axF37FPc}~yB z*l~Ubt*c`76phL!hx~g1dCMvPNGqZG`tM_fx9PWUrn4ucX~-kD`X5I)3*fg=hN?Hm z29{1d`5nd9>}^G<*y_+#msgfgy^}*!SWg#FdE-jq^{$ivk-%Fp_}D$BJY6zSK7ZN~ z1W#tuvS3fwuF!b~P0z^Kd!tMBlIB~W<$J!fsWgVSb*0okr38OQdyt(*3@i1Ek{|OI zhDNNGkyRSz#^DK=$-SeikGGgWx;v=dsrBc zOw7F!3rc4KU%kI=*fq>8I zxdRXPS5g`)QlMgbgnsh1bkSqJ_h6I70vGw)GN||yb#(gQd1ri!Cty)if2`2L%OvD6=$#Mf3;pprvJ$mYM_YxZO53k+d?JaD_sogZ0f}n2UV7d+sC_; zg)93x|14pEoBZVAC3|`pCh5lB=iGq>>)ay?N&UXj=Y&#hrDa0sfAbag%oWf_N0gfL zJk1ycN<}r}jb@lb)de>32oeLLhm;04G+~vev+F~wBaO_9?z_uVxapXBo^X?P_P_@c$LYpnrvnk+UlZNY zj0#fvqowG;a}B>>qzwP)=x9s91fX?92}az#=fmYH8IM-#=Xm&hxtZ%-R2lv<8pV~ZMB1bw8NjDV&TkLXWXeA`55MZw;Vh&Na z_Y+E=X(d{z`yCTYaBaLj(n`@dRU_%lHkwd~$G2&Zz3BbDEr_aRen%NrdJA9>SELnQ zBqSlBXmSHrB^{)rBEbpMAK*+f)DH$tMY)%Vz(9{w z?#U!hWF5nc%#VFP|6W>Ln##^e_9SLroxFR1N;juN#^RBMb6B7x=cB~@xDr=vH~p^> z?Y~7GtVVM=l5d*H@lA831b>&4&dI9}GHzW7yA*v2D1YfD3Hj3$(N2eQfvo9Yz1@2^|Fw+lbiM_Yxm< zSO~>Fk+aLMbjj;c%&&NwGS(@)v{fp@)0Q)CKnd*Q{?)JKAi46XF-7U|=nNC< z+jPcT4yunLxYKmvqB8eIngqN1F7``xj|I^aibv!&^2P|lK+^fcRrI0aYN$3K?jm;32ic$j;G&9J79p75{= z4??ar>qXTg02MFWvCpeglzY(|HNHkDQC~3=JL$3c3f*`C;Nv_GZ&d0=)ws zGL!ZD4bj(^qzcvkcrkDZu|ph|6(2TM2*}9J*pcx;5k9E-K*I2=-Doav)$S-SjR3=T ziCfWFR8UC^6y?z=W}N&X89#edTHf0+_P(cl9T!e-eF2<_104^vp=lmL+5zk^JFenW zdQ<~$tkQ6FF)K-@@1kk;&^c*H-_7t#4rS7r4xPXakIKY8vyE=nO;i>9O+$f#(1m;^ zk*A&M0wJnM4fLN2aQa!j+TkLm+;N$>Gf~o?$n0z-4JeocxZxYUY$3O4$m7))s=ZXQl@%4779{=FtRtR`++iM$>B-X_-H;X?>{azg-N zBs>A|FFpWc`P^$OYo5?3d;3(x&X!efoy5*x`mWw>t2G~g4xLNqM(ev-yCCV~A4Y@< zEWIexbT1NcT1_Qn6?gOJ4){Q`H#9Wjf+u!Kqkedb8!43CSECqL<;n%%{ZWZ@+UMb* z$7r&4IJFGV)hLLo2*RtAwk2g72$A3gbbh^JN^5I3(=nhHBcp ztuVVCt#Xb+NOD_J+nQp5+*{*DElSWhXXEtM2t#zF)=Rv7p1Fm%rR+1=FJkQq6BoKs zF*P-)Y~8x${~^?mGn$!@A2e@#M<$ELVSST=e-Z#2h@*-|qw&Qa3AY@HEGMRH-#%pv3(~L?m%S-ZI4g~aOWRUN z<`%Pv#d7yoFr{9m`)DpkAXSBaflgY)>l^CCQ#+miv}s;nvzX9R_k0dR+E-r?5$XvV zEcjs3jnZJ;+-bs$#6)I7%R6SC%HQ0F>h$``-bTFsfsyn_BBQO5)a>T-*TnY$1hUuo zMO2}_@BsY02SOkqI8bN#{RChO)&W!ceG8e98+qL}nPz%6vXQ^!8_KDFk1L)ho~w#j zRqIB3`Y9Y6gz7rDq11J?FjiWJ*69$ zHKD)Jbfw%@21ek);r+tK;Ye8s+WAtYO_FFiFCCgc16Y3Fi_UR-a z?tYbuln|0j(fd~!abUer%pZMXHu&5}etA*Zr&Br&x%4)4VV+T^RN+jnB1=)yPIfa< z)Qex})g0#8oU_YK+--(74g(Jl98uXZrTAU;t2O4D2|L#TZ<)-@9sij_gD6*h!Sqba zV)q2@k9@O#sL6I#3?AK#`3)0|-YQqqx6L$)dDZxN?6ekti$&o#vK47bNt-SzW?DaL z{8gTmuPldr*d&o3|1Rt8_D?ztAt|GhA}t^)Z)_#o-4iw;&q(WM`=p1m-iRCR97f`RIg*zZcS{Xa}$;*rvT?XF>H$mt3J`A`?iU32oiXrC&nV4#se zsgJ@d=mJZP2pmMqE_ zIoo&6y_OPAb(fLdTJPq@mun8YMTcBAo)wOs6_>S6-(tteivGG-G78Sr8ndR%Ne-*D z2$ww^`*f~YV7oTs%MJ}DypD*!4fs;6((-dGF9>lpQvtDB z$L%-OpQ29JMUE2sk(ZIb^iWuRDxlgk+NJXi*!}eA7G~~2wEjgP>Mf`EbpqC>$3K7o z^}hHG;(YaV?Pw85%ar1?xvs=2T6W@FxiyD8$|Ye;NG4_kMHlv*p6Bj@bQTtaadWen z*TM{W)G$^I0b8ip*@J7N{wB4`AKA%M{dHv4VAxI5d*KQWDdrg#9|o@%kM&i!Wc+X4UI>%!fNF{S$`CsgKEi<0bDCs#laUjf7 ze2bi|rdhWx%3 z$fWyB<1p4o9de@i2;0yhjOUPHJl_%TdeqUM!4<--U#|@Cx^3FBj~F-v$j*^{>eST^oy`KMZ^ z;gKK9_cv?K;@)xwG!n6el}$Wa&AOxcl%KKDapOE56Ijh4{VXp9)WnD~@6}~~FtV?S z2WMJw=tZ`(QP}oaU__i*RHT`&6JTF-laR&heNW$G8B@3{kattH_NBrYAF00GQ%$C} zH(IF@lOVjQC|iFdzuX;HeM$rD{btk>T)9qy+MSGc5{$_$tuG^4AD@=jlOFLD%|voJowYAsQ;ONhIG@W~m5`6r zE|p1?#|yL-TTuz=2i+=Jn9h{$M?R}%2QO2u{=zr~07ysBD;jUQ8nm|l{F7)8^)b5xJgky$)CE)F%tQKna#HlLR<*1ze(vkye2Q>P^q+vU@G?YgE1o?lhFXH0 zjbmz(akT3;VVZ5LV2e>x)OA96r!QWVcP{UG4|OZ);29bf0f^G5hH#)=D)Cq1eIiDqlxO@H+E|u)b`WS3s_E z5>5=FroU!jv|#*zkw)l2{WRltL1s&s9g%>l2+uBkC+9Uq8WTO>)PRTcCmO4;gqDmi zJX@;$yMI@s`hGoYx%)lX0}ZkNkSES(ThA&DTL@ke%b&_={cH89>pT&8HsQ!7zqOH~ z+###cu2%0u6|3M$fkiI|!DPsLiMT%8%ze!DitY*najj)DJ5Owfm1A&16ghI>VRDV` zc{rk)%Y{~2_))92vc4N`b7Ko*qQA^zq><>H4yu~IJ(%E1UelIx^QKUl?;%0wlLk%0XZlD>fe_USeS=B6Gm)L1$X zEy|f^>v|9W_)L?g>v(#}AC6sUSC{>c;QD`ZauxeMIiMLcs?!^F!tjm!A9d_g)J_0qGqb7k6-@Kl;vx zZYAqZZ_AeQgMI!&bfNrMtXS<#Nqck)1Lm+WBfXt7=g5mV8M-kR`AiV${B@2s**Q7U zBnGHJb(Bx@ub(?r-FzM0jnUhhEth-oo|yeI*?f>D=84;*;i>zz5od>})QE05E`YUn=Q$e};Y>O&HTt0u`C|s8KQdLM8g)RPDSEQ{Bz} z+zx^UQ}>!*5H;Xf^qTQpa*u|z*=bF9_^C7oz*D(NCYh(O4wcIvQgI{-&15rTWqS;B z0n9ADbeTyhR9Q!d1Tpuvb>v;ubAEU6i_v~o3B1@{N00nIorBj>{1|lB`B#O%fQWI_7HG?li<-~&p zk(NDRgs>H^=xp&y{H1j$y~Qif&wcUS)@RmeiL0e}U=!JYPOs>{RFVuR$O%vXfWsEQ z&EQ{@v{+>QHq_Ofk9j#cg3MiVw~A}i`BgA{f2Kla+hhv%l3SXD1A6*gK_zQWrS_bm z@I(w}Fy>}hhu^7`*1yREo9XOdMfI`x(;~YL7du6`Fy5L}TW2kp0mRYkY^`rDxR&*PDS99{ZV|9hlv52}s{#uK5 zSx&=d*moL^J}WZ^r`LdG!~OE-7!3lQ7~i(d;QBk_*31GrE1n$Z6#KR3-5bbVZ$djQ zhS@FlMdI?7?KK0`ZthDzV0SYoN&nlt?clRM&mSEc@q}3(zv`Z_Z%j$8hfnKO?m`!axD2B&BK3Ezt;}ZXT52dcmAMs?Vp8_Ts zF5y-tCK-(5A@~3i#QHQSSgO3D0#waDAnDegGwG`iR#~m&=@zLu){2Ij;G>`u@}d+_ z5$qYumpM0u-rfHUj-eZkEI+LtR zedn@D`7D>xBt)xiQ~q9+nGhf^mVZ;yfC|TB$d{A#@wT}^SC%vDTHO=Cy($1LNk4H4 z>Ks5#p~OcFXq_JELeL&v{(w@tlX%tH6}kK}Xzt=XpGDzV>=Jq|H50msN~lUlfA?K} zkI%+YI!J(#==8Z{3TI59K1hQ<{r9Djy@Froi_@E#SM z1EcJH%!NLW*oa!;Nhy-WQw?fr68C$Kz|37iqfDN_=969C*u3#MKnLsuxv&A^MQrQ_ zpErVY))CqGZQvz3O_EfK11FD?Hu^}Gh?Y({f+qrsHh=@6-5~iox;7#!fGL1mp?G4y zg@NSjx%=VO4vzQFfmQH^JC0)8a_8Ct0ia+vtN}O(;JCmC(Zxq{IPg>*0yr21u$oo+ z*=oix=ofBUy~@>8s~vWIx9D!jAN0q?;lF;Uyz=`=Tb?**pw<6W!0HTviPXkM7}nR6 zjxM~g+gg6NWAkx_Cx9tndVhQK5s8G9bQ2Ux`hn0(+hW*ief6TQ6^sAh2wl}&%2?hG zoK`dM-SFK!+k;{kEAdq8L+4gW`T(VP>i6DpxKE(>Q3yv}7NJQOPLiUThvh&SCQxAS zy6)UFfuCuZ48{?B-c7D&`Efv_0`_2%*rvWO*fFM5F>DP=QF2FE>4MWU1 zY4E?fxj}@3t&<@Q>LUjz=>1pZec=s<*D&J%;2WXh!M|;SVo9 z4AH#4IqCA-e9RBExa`<1h&}@g@EKG<@K$D!p%blRW#erMUrNXKpAs_$kHwO>9aR{{78<$Tze38~(7@>e=b z-?~LO%>g8vOw^4P;AmB^s~!;1|HD;{`fKTydk|NAHrNd#p8r2u0Pqk17m{5AV|x{x zoNBO;M7+){;{TqdL>F2y%7%z;N$3RswK5>F&dgH2wtpg&f3JaE4&sI-j$&4?MsyK9 zOik}0`Y+Bl2Q_4m{vvpcRB|GIcCZS?_9A(NywS%t{;ND|>__B*H zM)^_w3Dgh4Ns?TJKI8x^La+}d81iTlC|7z32_phHpmFGn95z+A?h;jUWg-M}1gPr1uX11afYp|EQ|KuUOgHd%(5?^|v@|cwvXG*OJ0ux5gO-STd=3$C+iA9;-r0li^B0aB7KoK)Vp zy4lGM8R>ePS{Ke=oNMx(ydfegj>tdY$P$PP=E36!1_mx!ZqOOl!V%cF-WTV^Z2ULp z#mj3gyif_Hz=D9XTLW!v;(H*^aMj+svb+8YJ1Tc&-M5IJB>ht?ufTNvzBGz^d#xU- z*Q+S$gtHf7*(RG?thNvH$Kx2hzC%3treCW~-hDb&%^c1>K13N0#Xli}zPh@aHE`?) z!dRjB42rbe++$isK2s{fEI>xpS|l%& zTFDd)a;m@^JfeKk1F>8OS5(TwIUp>NUl`ZPodQeftmKLK-|>@+#C#s zHCq@7mwqVVnue4IUB4r+;i8vqh-eq2x#6fw=%hs%PNG-vPJ3#xOvWJKR~5PX;CO5V zFK8W;`28B9seuPeiHKT5;aMZ@R}M~7#)>2-OiJIL%?`b^cwB}a1f$7d6tu^(CI z^(N}B-fx$RY|RVHfQkS!AKfWQziE43fv183zUC_|7>r2fyy}7c(n=bhVHUW+|BwvQi@b>&A&rl6Y5jK24=p<$*)mXO8jK4JI5jD9^R3`65q&+|k)e=qCi zJXF(^LOIV5!}MPhkh0kK3tYNV`7%dMo<@5bLZ0eWVYdI;M*~Mk$YiIs;qf<7jlJG& zsm@=OU0q!W*&(;T&0$#Tx`{~rxx`ANA&aV~J2D~NB?;1E1k#6HQ&TU!z{G&1Q}!>f zsXSI)KT=az=->(1x68A7U2XHEeM^-Kds(&dcjk>x6Xu}B=y3pjb#?XX;Ohm5XeRH! z!3-%STfSTt!o1O3miRc7Ma5I=AelFW&*0dwn2fH6>-dL+uDc`0GvNHK`Lpho^t>{u zzShCFx5xi1W{z>bm0R%;v#3jDP8_m~W`_{s!}HT#W9Va2%?ARn2WC zNIX3}Ln@ve=Roko#<;in07;Ux0R|%r%Xcv0c}qRuZxW@He|y!;d)1pSDi>j4t(>=x09(Q}1*B3-3gy5SRo4JcV3rT*;rQXcdi1?sK6LC>&n$UOhEANhf@L3?M{r5cU#s-F9V9eA$S(S*C|Ankpt z`>(lDbDMK^#N5cS86JqGSbCW<-e1?VvvtU(TAzZa(8^pZHv@5e^MDwP8_#-j?iWgJSb&-1!XwUpr@=;Ak9 zNr)K`0N+}YoSN#b@qKY68D#stgoR5FmUhNp?R92ZIHjmS>x{bU)cMh9POdk4<0qIlMPL7Y-+4*?` zlAA$4wFbU~zl15?JS+`-xFseIMJ8|~eifO3HfR=IUPB z2c(j73pHYZJ!Y1^o)v)-1&p1(c}H-f04-FE{fKZ0kW3V9B6%YtkK7uyxF`UDuJ(Mf z!;pA<(^66z3MUVbw2kp)c|95oPnMvqEekdto-|NSl0HW91w=f(Rrb|r? z@AIp$*eeMrIM6!yJnZ7x?texlr6dNvCTs?8WC>=)oqF^Vu5c?-v|>lX0c{_ZM|n>SJyp z0Ozv4KQuj=@Iw3~esc_hllM?{i9gAvvsiSd+(xYydX!l=Uk@j4e0$mMeeXFYgphYrG;UFa-mP zO7s;b-0$C|Estr%pJKoyo$3k*5cSh_SerM_zurvCdjVs;Ukr^8dj{XZPz5ppB;1Cq zAk|89SZdUZ8w6^37dXtRc26k|e}E7AdS5TCctv$J@>^gtt{uRprluHyiR)Awa}CB( zeI<$mx%&4=LKe;$E1^{I`D-d0Lhv?1>*i*_G`yh*nPh3@D*Xl(F-=_*tVKgTJ+}V- zejVE01lnE@4vNZngVnc;A642+6!<4R&4%$=!5&$Cq ztWzN7VuHYXPNUbJ1c9FIew~E(g;NU-2_uAn77~K!?bRcRd!FRAgFPRxjSuKQTm>j{ ziOy_$S`0Gqc31?y?=u7OKSghh+q!W|Lm4NaLEt90gERwKl7%|U=a?Z&e_9g{0>F*uK*G%(sk?4JozFb0x(N9>7KM;1 zFk!b(lg7*hCvFH+yud$OyENOzb^daDdn?|YVo#2d5J-$+in|6fYFo(eo&j7zL1E7? zNFl}Oj<~QZ+w9Phrn>tPmmNXAnIy{fB$Svnk{7_ z&UBT~V=Csr3@Pp#(U|DRVDYjTk$Cjc;kUQG63Dw2r#Pc(r*rwuUbRAqPculHa08<< zq4vAXhKYVaI@|-HOi{Mf8f>GCjhkDI+qj(-Oq|PEly8iNsY2bZc{NYqdI&2gF!u*5 z{2KkI*G{&}&6y?DJqivn;OM_E&UEMEx0#^rAaL2AX2Zb6b)2)Sbs@0f0Nd1%l82kN zQ&)|PE7_(7rBX%2Xivao*pAzSvNl_%y{3>ZqV}xsU|$Rs2ZOV2Ha)=R=P_Ww?{VD& zosh~ZDh`I8i@9-eYP#l{glq6=fk5E;U<*=n_Dkg4`q!lS(lfX>Y|Rw)Ul$m4;Il>I zRPzr|5I>%sxg2y`M*BqpM^eXb@)7%G>k&=u=WHvm@)DzKb-m_;s65F(j7jD zL&Vv$N1AV&t_)AYm{^)iQX1@C`j8xsmj|a0SK|8362V&Cu>(FIG6o`W3cRCf)CobVEGx4e z^A5r2#yHY9Y6QEcU_+>|ej0G2skZ^YbzPVy+@_T3YQ}lKLV2~Hbt0tK6;^%29Ky;r; zLZU;f%#`QN{GHh)4r6IABaXJmdCq+S)0UlD@hRzqYp@t)RhR z%7DJ_9~&9TcYYQK6AcEBQ_{HXu_NOTAPO*k+I&cWp_>4YdCU(UBhT|7i{xIC*E4m@ zlIu%P<9Csmv);>TI`>70qvDgWj`tn3NW7*r?8E}(RxgR}2Ny{5rhc7K8~-aG<~^@@TQJD5ddKTe!-AvDR4(6Ub0Xc`$kZZH;C; za83oD__Z(H1jNTQUFe8ipbg|&t5L;-%+H42g_wBnG4b0!H#m^<8oO-?p+9i?zyAOq zp)tQb{f;T0Hb-Gj+f-$MOrqS{32^!(iQxlv^(EQ<~- zH5S*lAcpC7yg^&WQ%}Udcf;0RLC_L_rjTl*QQ??f?}? zC3bM4Bz4iO?|q#n7Ay&Bo(2IXs5-NHPUX?V7sHmR?G@AKB2QDAKjP7Y2%gx5a}x*n z@bA3ckoOboy=Z9JM$8Z%+LL+nc-#18u$XYJj%VuXX+ZI0uDBJPjy(gi??61agBsDP zgK}9=bba2SX&vwkE`|<6Mg0cI*qm)1$EhH~?BU9m^+Pb8w8khpy#IUzia1S%xhV;O zU%{!C#WZlMe+@yVlDxL0g%H%x2Z6G+EhJ*0i;zXFxfm1CI*Xx^GIqS$Rt`g_=#__N zD*$8rI%II&>U1gnrj!&Dm;RF)R8q}!wo(tuf`<;z%p`A`@B|EC%iW3mCgQ+(Tu(q3 zde_3tK?g1i5GTrx4t_TsCXL(}AM8Rul*G`@g3IAbFwRw*%t0rNjAZb3!7PPBZHVXj z_6t#;X3VDt&XK@Yp3nQXCD7eBA08~NB46gfD%FsLJZk2aK*89;(z2ykEPbX|$Y~y} zxfp2-6k+qOT`?VWHb6Qd^?LZmsRg8+LA6gBRo~Df;sOprG70Z`Fg8_s{GOPEq_vm> zFNLS;)+O3~GUAk!nD|$!T`d8DQy5Y}hf)6arX}q?a7(`|Xq!5)8ZH>#148%;Q(B(i zNvUVnAsuo!TeZYBqj|j?9Lge^j^uH@Yr3}LBoZa%NTzTZ1bit6DK+5XD&XNYCLAyg z76Z1<_!?7ufHune4n-ziK4%Mqrq))`7N_-Kz-9kmES+Omo_`z1mzJ?wHty_}jb$v` zTDF#L+spQ{?ONutZR5H6AJ5BPbRYM9T|b=oo}VwwSu=28D*(qdaKLkIfoCYiK;QX` zs~HT$IRG(HJ#WJoNpf0RX%Z3=GG=Clr@K>&Z~j$$vOroe*!ii_k&vB)XO21c6L5|< zqWB2-eF7L|?8bk83;v=&ak;SqiiNcRR7JZ=UUz0=q8e;$axxjfB8>vb(EQ?Jad>{x z&Q={_q|FFFMi!JplpF-kZTgn^(%`L0jqTkp6@d@Eh(H!1?gQBt+6B~6cix`Q7wWuG zd|sdLU8L@RoU=^wkbcC;EKVriC~m+dkOV{p!$8nAj@>p-kA;Uy#Zv`T#kr%*Ol6>^Tk}2RKdqvleV^p0NgDCi%E#VJsO)u^a-$bE=E`f zVt@LPj&9!1NNdBv?dCyU$X*u^Za|gTXgXhF++#!$I8Mm)tiYv`>F1EF)M}kfp8yId z?b6Z`Dga~N-Q68miKK*6%RNO~6YJ^e&3E}}(~brsl2!-H61LcjHQEn=LnC$gI$my# z+Y=%{+k2=c5QCAp6u_p~VYYMdY1_f;&rU`#5_5u(i%0RU>Ub*c8yIk&HFm=X78mLP zBRG&gFF5~NthE9dtX3`bDv}>ItslA2*4CEhk&t)_aC{H>aDP5X6@}@I60Zh{xKCvA z7bT~rPoU-^+WMseT?*+;uEZ&PE`uLj7dTpXrvi&>wp6P_F$br|hfgTwR`7e8 zAz`JaAKU?SAOIx$ey%HB^d)lO>V11GdpG!RGg6l#!h3bij*p?`To47{qMu&qC)l;0 zA;M6ts3%)s+-Q9jJ!sTfA@3K#{NI>$oGYQmWWa$ZK2LN3)c=RO{h@c)-%i)@FwCCs z&y&Z~+2Q-LJ;M&rh%AyBSi*vev-NJVtaR>mbVdU>U8eXh11Thg$ZJR!hiLUQ`8tp-8 zq5!ai321db)!{qN*^$+*FSud+(S*54xFIEOtF{-&wTCl>o>a8|dPI>&8*+$qrt4JY zV3FYFrCMNM!eYSG_E;M%{2@59Tx-5Y2;{0(X(t|)nwON9&gvMdF%L{LwGC=WR)ZwO z#s9Jk1O=LfqE5yGPsaC?<^6}U@o-mn+bigK8?Z(<25$Dq7Dl@s5p1AN)X~ZI)Frj1 z)#4~|w|io7lMXTS`|)~z6xeL-`N?bRh++KUkxZ618>~ULU^3ZVf6-}24LC7Yp!KO5 zY>ag6Kq5Y*YPQ$C3RnG7m`(x;ix9bkG8H;lF{wb&s` zD{L70f!G+g_OQ-Q9{f0h{w_go++0j>pQr56yAH{wCK&`t9-iDe?cIK1{PnE5QV$z9 zg%l9u28fCvY51kH#DEk0dO)P{=LfgmZd(-SxGLpg2%SVm5FY*=*ocI4zDlp^eA9qm zBo&MPwrme0+5>>#=H)`GxsCq2*W^eqfGU>RGhd#zutCreAnz`4Ibxi+RLXJmXq?xZ>JMxe(k?f(eD<9;4qATT@Up24FRBBQr;HecwEffMl9-#OlO1J`b*M{bOFF$ zJCGRmCXTD=_kofZ=j50b2n7df1-d(CQ5+_L$N1`Na3d=7h+FUR*~!1iBvq8$J)mxs zV_MCQK>sWNjzMe-Fjd_EE6^R_WNPV$naxN`Gmo8j*I}tynSDY$mMP+2tmp)&X&%+* zJ2|-z$N69%9r1FYR-&iq7l2K_7$@-ZXfL#JQ044V&>nz@1cP{;dF~M`7ycQgBkmLI zA9ygx@xirSQ6U)2K1ySAT(Fl3PaqhxXI^jMhS~b`f&0CIw8ouhODue_K;zqo@5QcI*^5YJ22geEvt!h0s9mKC z-NMkfhrq3JOn2SHV5=y3`ku8_^@@E=bF$JXx90V1HB$odxv!~PPfnz89#)U0HM7+@ zEfob5rNKw~zg?&TZaM+LptpJl(?jwyV<&f7s+N%;}$Q>&K``#lgq>b-Ysu;P^tbO zv5*GQ4;2;i_iO%l!=Ut*=gL;>m?kKdGrmX}1mMD%nd_;%a$kSWYGvqt(g1Ri1voqx zj~l?dcoeuY3{+Sx_GqzL%@ZE8prg1YxWaoJMX&3TxB@{3xN(FTX`o~0N#>#l-p z$*QyOvPOxr7|_5uViiIa6f!XmVriEB zt@2vz)$!&)MM?QLQSb2u0Qv2(NmY`bF2>0~D%3-YYn>3?zN;uKF)dZs{V*<8e4i_I zjUk!A7)uuPtw=Zwh&Gq=B;$YDEmHV*uw3dre7&1!I%DbCrcPP*=2BugT!tBXWa)AB zdiCHkL-mGZXYtNXqf#DIUQX*tOnYt6oLpiEOO!DI{r)0=@&gxE)^m|9y5tpjNA+li zedjBG%;GECRjYL3ymVV`PFET;EBD;ztQVUEtA$}udp^^u*9PbqVm#o?W{t#Tzg!73 zc^FM3fAgP`g7l ziSG5&UgRf=jz~|g(GPf+#os?d{t;obMD)nj9d*3}B%tZ9UbaoGrt`e`u1-3oRK9$G zv&!xTkp6)=l1W9x8X@x`!~vF->%R)&7&TPz?Jd>BvXj`pQpF?-phvvS?-Hbj?Wnhyq%c%xV%;bP9PKiZKY z^x9o>io25+y&yg??@1SKSmiTSllcIgpw>Dl?zaXM#DxdhQ7OE|`$S$9tI{VC;1}rx856lwXPJ=ju_@-~NnYsbzN-{$N(|B2zC4 z1O4%C>-!ER@jiSF>VJ4KsqwhkZ#3>*6RKG8dm4~>Qk;}n4hc$?t}`|cjH}9+LBp$- zY0Ib+UDIm+4mWCPj`Buy$93O(ugnp#pd0H7eZ*Wf>8}Z+SAw|(4vR#<PcCa4>h7@m+2ah**F#t$dv*IASufZeSY3pDcGDw)+0s zf3?vEQ_|rmJ^!*NR!dUUoH}|X3SRk0usAU&xLPP{%U^*11sv8?Pe2ZjIKM4HB z#y=`h{H=(-z}Pyh=oKw zT%%RLZOtA+_Kwf_KHojZpPc=^C$ki%NjYcAJ~ZilSGXgU5MUrwaaZf{kX{gHV>94? zotVS?I`vpsWHWyhAzJoZbAeiJ%;&2_wh|$E0o73*_Ep|GRV?Ojlm4U*(EICh)h==s zU-5Bkn?vNwj@`U4zuAouzU%n^wJb-v_bmcx=Pj zh_(B7O#-~)G~M+x&+|5VXbWNE%VYk;n^3fy{riPUnB3E#fB>-;!xEL*zC2m^ZPOtK zWU|FINa>jOm-|5wF`LeG)@uo^Y=gx5@@k|#%BiV+XcsWdsF#!{RubBiht%^cSVB@V z%o@#raXC6wjCB?sNBD9-NKmW8qZx1jP+s^3$lwGYz7$4*d^}^%Wm*>^9)oxBEtmRc zoxz{qZqh6{%W~(&wMaVO?u(2&P-Qz`@1N_3N++Iw=sa(vkkLNbJf6Zgq_b6WY))m5 z5PIDzKHgeHFEH%tB-sn&W6LNoo%!`B7`=r&ffl4j2I#7E5EDz5xa#2zR{enV9*e?G zCIF{&NbNDFlFF-ld(WN{PtvQ6NGH2$txU)*p+s1@{S+=)rp76}CUe9jq`~mKz0eW` z-U%OIGyR08f*3?&C8ZpBIvXc^{}GJ&>+KO!88`B7PfEXCJi?waBVNNXNf-)|C1@__ z=Lxpby3Rky)M}#ktQ?INXC>9;u&ei5NWE~+vsKPldcbOtghV4vioAA$JSHWEa`6rk zcEue>9-pgKd(GJsXo*D~0Fa(tz`Lg<3F!Vv^rSo$-LuV)ZcU)UAR=1rG~WX;_cX+! zTSTPPDo=3$iilis{`$(rKO`(B-7KjFIz=6w>>SfuKhek%-7Lh!VHd>OIkY4qSh}Sd zn(v?K;_nV#_gLB6l5?Vwz5LX>1exFI$DUY=ToZURt->us4`!;h!g~DUC>FvC%!j$> zh$_pRd8(K^qt(Zaz&98>s+4aXXyKt+n9#cvyuYl_8%E zN8f&wY~K8Axa*iZaX16!Z`9)o`gvJIaDyt^C-oA-5vuB2>VH(V`}ZWH)=k9c_>w6K z{(GL3hf~yt&deOcM-eQ3A1x#zBq0=y{3s}k!%he&E}uO{HmJd8HnVD!Tb!H8Q`imYD`$z#go}ZfHg7P)8W8zf7F{5`*f|BTx zb_EqzH)1nKRSIRN5Xhxz`;&@U&2$A>Ur-C?QcAzO?XgK}LrZ9_i<;v7QY_0*s-^EA zjefsKqILV$f1>$DVKdrcbF@!rvB_(>j;uMS>2)paR>!9WxU3Q%j2JgI$BS)~Yw=ZU zj+>S~w(S+(Y#do6o6F(V!d`SJYGi${AD!&|46c^=2qfY+p*h0A(_}?pk%NTYxUGPa zvn3NSiAWu6Trfw>e{itttM(rRn2e5xhb1%E)BWG0PL*kaJ~2ucI;>$}0U`N5EM$K~ z@dCweoT|^RZtGW!sdDF<(ai^a%a;5l_y%7VBT)wv^+I|Du!LUAIZT3K3w!)}G7s1y za3j40R0H#%zbRyvL$2!dxD*hUaI{^zoDDD5iO#FyP5Wk#rwZsA{Y^Tye$pj2>m9rN z$+Z$VJOe z502QHg!-N0H!(as1=`-8tBrm#yrKnnHTagad3iiEI?!#V>a4g(}+szTyiH8 zWhE*ZXc;==>=~p`J8DFs$|!72-p*FqracZ|0p3f3+LaX(^2CJAFTyH1i~^G7Hxn@C z_W5sf+M3(-@2XtOY6V5~jgwR6b=#@aIajo_n%iDS8;wWr5FY1fs)oCt)CaX~UbL22 zORw4!QY!v|Y;5TMXyZhYQ&v6sG)$^KUoR`vdzA8I>U!*oUFF(8xn740h3Zy|kYhDO zYR@%T-|7tJ!c;sgg9uPGENS_tWnWgPhL~Xzw50o+U@m))mS9e z|JrD-xP3-W#;?;(>gakJ1XZak4`g~7w3>c5 zF8W)M$Fo2R8LK^TE_wV`GY`8PD&Q_03E(CYq8}Yue^G|3D#0$n^rnng`mwv*(|#pC z&(uuih05!egaChwM#W8peS9aLO8o_eQ(7VDHtwZY6EF#}O0cgFoQchFK;_c&K?pkl zd3~`{NWE?Ts+CR@o}FH5iavT>yOYWOM(KW3bZG76Dbj$?NAX2?aOC%54f9y1fHPhz zSwD2cNNA}*b5b&~6u%OfW>4V|Aj}F0aF)kBLnVgjP<9p`WEdSUb+A9^Vk*I77Cyta zmAGP{j^1L*GhQ63VJ2&mW}ZKfb%1Uh-fu-3x|35G1}k7i!=O!SzWlO<$tGDSx-gW* z`>2pt+V0jaddsS$ko6c#o27ofkvl>97>F4ET!f_PMqO|s+59z|lbGTvyr9KBK{}O5 z6{GVCAA8zy{#>-i^35_@>0ac$Xms4hoXlhBXXH&S8|7MiXs(u17-ECInBIATJh8~L z1M-VsvM9wWLAU2`r`qji96j7G3*N8Ej)&miyLJ$KUu39VuiUAvvm^x}w#hauJDlgm z-S+r*56$Ei(igGR-FW+NIZq}m55yBK9gOKLv@m~i)68*z7sLSj^ z;NZ$@IJ*yjSpccuI>tmkmyN$=xpd4UniV~6zdL_iBt^9@y$RxLKOsZC6Kdi&P1GZo zOP`80G3*u4dq!Gfz1gX-4AZXxMiCRDq zOshIw;J(sBs*n9`>5;-I!B;efF9Q)-6*#xvyN{ZMd$h{gLdt3wjRk3*SMeYlYIR<5 z9k=Tu)JZ4odNH^I?mJ?zRAe$vuQV$eje>sS zb^;mN@*S)B2SfNGOtG3Dzk01wC_lRRufwYJMrbmcV3@9n1vPRHqfOH3&Q6_9mttBL zHv(T?_q(B}Om4?s9iZbXAuWB=CaO{X-J`+Lpo^KAd2CgYT#gAi<(41^$fM?@I(~u% zs@u?!Ra|Z%O~AY#M|D?~GfijnoCi`Et%8(Y9Y^0h9>wc7iCk-S5xln#H2Q(JVq^1aJLN{qViNTsvDKZz*&qI0H>EpgT5IxnQunCb*Guh2?d6PTeJ=RvZun zB{5&Sup{=qj>8>{b$(zfc4M$Vk+~No)uhUzo-Gi4#@mM_#i5y$l%&zAn7In^F5A&z zOy3C6qgeObD(z0Bygh1-PdMSg~y#X+nX>I>kjO&~u3ds0SU2&LW1uP)9 zFkh9{G-frBy!I%;cpQnli5YaDHchUOz(Y#)Etf)>2E5LsD?ikq5GS%(JXFoi&0PC{ zE{Ke_oUbbd!Z@|x0fy+IE*7B(f7?@2_lxXamG`R!=_WLTZHNpxAe4$C_Stv_9>o+U zE}y0ApjLwFAqw~uMDF;y&f+KL`ba==hPPqbDGa%V#H1eo!J_}uh-I?PSomQ+quIkx zl(h)(D|XUtX4z1jOH}%6d+SrI*dULO<*W*ceUApSs*af zz&;X?Mp`xLo5C1k>-SxJ*^3{v3cEB!1_42DF%)(HHjfWrM7hRR|CI#w8G1Xoqq$fD zpu<@!zGmrQYUa~iOhv|PeDARA~kS%EW z%Yn;w!{3lahWJ(4@8^Q=_5IEH3 z9>NszA&y8tN1QV8SX*}2%|7fA4kLz4?^q5O@d!#aC5^ts?kn*0O9`U1l*Pi?`eH)& z(3`S}zF!f80*w^qCJvPB)pCX{^Wq_^rQkc_fgZ5^#fBiz_IIiMY{Cxo&auffT>sVd zVrar_f}b^QM`74s{z-)C0wc9>Ptt00m0uS?MMcdpe*-$XH_Yd|qYYhnjWiJjx^f|s z>(jKE?I|o#r=}T<;RYdGFs;A@&~^K(8w?!zJ*v+Gl*3DN9OLm%7@_Sslpq=1OHVct zGA}J+o>-!d(|)3;^o~K7{JMDk<6vjkp3cUJz0Hb+!Nsj-SO?s(6!#vN$*^*&H-JL6 z7>(tuMY@g6{2N^<=?O;15XD;@=z9X#&UHXrQ)2qU=nu5w3wRMMW!KiWsw(OnQ7}?= zyJhBd#x{A@@~Wig$$V;#fhzr*Lj!b*L~nHYE8$r%qw!*+O*Ws+810t3_1IT73M9mu z154bDVegYgrcC_N=Zh25D_L-?6`%)wJfUCc0`!5`mZqG~(ED|1eCq1zZi|{y5eNk* zYgB%yACZpz0P3sKDACAhv1CFaX$_u0ime>8nkO!~9` zYep=GlxfU{g(fUkZVfAOV5*S2U5KHF>SdR|F=yK#LS8I_{?9fx3M_5b*se}fGS|Qs zjo>NSj$ia(5mm8_dtbdjBkt<}k7py0P>BcNc2hdSAIuBMW;Kw2xkhddfY}AvWpO!~ zhyU|&^=D@YNUnpu^8&1Ykk?b!dqF1+H$vKK)QSlT0#Ur-fmsj63Q$_raFo$ z-m7hLt&}ZI5LO&Vtgri%U8{SFP5X9>wuz3eY;MA{*#C=8;l#XTnWa^T1e!(>`K0U= z47P6UfgUjagYT(kY8D0ZH7M%~GG-H(ErV^1q5c^a(5FFU4jh3UL)aY9WpG8n#-gv0 z2Vur3ix4UnEF1MhthwyYrWh*hXhl$0uOeO4^X+kw?vx2RYBeet$NR=$OTBjN#W@NI zigyslx3v$e0(v0AdwW@89&-t%BCSz>8_S!b~+g+myTTJW}>aHpY z6lU)3Qu1`a`>t-LQH44c=dR=fl1wt@p6kT9-n-Xp{X?%mCqaaM^2iPzQ0~TX#TLx0 z@%VIaPkv6yXf%X!iXT#AJwfU+SE>x0nMrIec*FO%=HnyA=PdMW=HA=dzc(c?Uv3C1>jMg$Hp>)6{rQ&Tv-YGeQvY3*%a7XlM(~hu)yo zX>oF~z9709yT2Bz2NkQ%b&bHLg04+12{MgTkB1ifT{e;@gzj+w*XHNB9FJ1{;rjfX zhk9i|ww2f!W$N>)P$*OXkKcVV63L(%&e{_~bxF8iiluSm-s<#ob=ir{!ja9=-V$Cr zZlSIDNsZ4f-ZWzkf5Rg}zJ>R8By3IgZC-%=dq@*>0#u*AMYz*|t7h78x8j<>CvGpS z;ft?*g%?%rcWG>DjmRzrI}W?(Y}6@Jz)a#aLA}+JoqYiqtuVNb^d~>$y|3cc$d*zg zuO7nf7uU)8gC~L6+7+?e#`>l9T*W-3-y42%I1_4IGkS01aOR%i_=Bg>W;-J$483il z9_UMgvE>9QBAfW7W>9pccABJg5A=yoO(AA|AK>ix1q~L$xCpHe3(N9xCuIRxyW!JJ z;kIwIGK_$Vpeg&4$r)lge+YQ8;_jCvx5~91hHrx8tkU%V6pV)s$U@h=MS4y$h$LlA zq~?Jrg}RgYjTgM&{kMEvMjWmDElaRdVgazIejA-nCZ}*BOBzkd4)Z{!3wej8rjVO$ zA9-7t&s|-mlE$^(|DNp|OPC#kuhrMARHC%Q;G>k-n;@u4`i-m5Ut)Rd7L`C*bkvo# ziONo|u7HN~ZatT{I~s(T8mF7PNAvoU_#2mgxK05?-lrSo(IL&2{b$^8&*u5Z$gF~m z`q62?N+AKo`T)* za^jPauO#K1Rz+-Ec|9t#UMQK^y!hUTah!#wD=L*b#Sr#lukLPH{8k&SI_|VChD?py zH%!lZ(fKo)q@k%{X1Zb#=ER|qaV?rhOq1cdI)I<8l(9-X%Fmz6Gb}o^IyfM;^oB4z z!cB6l@^yIioS)d2cnb9-nL_#E=NUGcetWO6t!t#E*FGJm!g|!kmm}dsN&ETXBmD1A zYVBy~uaY9L+VY;o&1n=C{7CVjXwaNDecW@&mVJw8Zv394`8)how9`_Yq5>JGdIN1J zyiUw4Smo=yemU{m$>4e$BeJzr7wb`D#r!gM#H-rBE!O$3-;HA}*P7I$DDNK&sq6(J z9+EM{hd)hTm76b5$hzsq(UkH3qXH#1@jgpEIWT-$W-Hag@iIA(pzyJK1EvL*^4RJfU@y%2fPSH4W{HBaI{&?mc3c*X1c}<7@5ypMdFo5>Sdz!4)BGIU$ ztS{@Tc${0~cb{%VzL!g=2u_`yS1hx|rVuvzAp_Kk%4o|fDivp?$yCAvJne7;Y-fI8 zAyx#uI>jg(rg$Q1XtScIgn>Xux$lLdmHS!|Th0~CwS}kTI_7|1!vRXuwkj6t-ya8cJrOM{a zt`^04!D6+C-eUBhJ-0o{d#Pyaq{Q?&evy0v`8a07G=yjc_EZayz3qa@FrWHwez=Bt zw(6Ggir1>jTqWz`-4M2=NJ^bh(!ASvtIUq}2IudC{B?we9L66m3N4H#M;nlSG5oGr zj2T{9i1&VyOmzVN)pihfFazjuH-dbiX-J6~>ow!7az}kj&B;tL17gI-63AM)1Y+SBzb|~FV~zstAry|AsUvDC;H%)ZYY^yp5EMrgF#hWjYda;;+V<5(#WSnx_meU;h*B}}LKv=1_!NoE+)TrI z2=SY2XlELY@e_-2hN-*qdo&)K7I@-A*dO z7el*+TkZ;iUEW5^{?8KfEKYeiOMVJm7? zrGE2*aLgjHrxj^d@R%=tdRb+%@U21Fn(nd6rY_of#gFFqn~_}Gk};;N3bvoElyV)+ z!y**^HAplGz#{yRs;(DAEoyTPmtCf2htl-ypMs?_KdXq8DB+p4KAx+{ykHpsk76=! zU_rX~20aPXi!PbvvPXSU!A_UBGenHG3F|Z=s<$uQTv?PP*L{M~=Zp9{MbX1yxouJ6 zQM2ayD=ZHB6u3^uV_^^De%Y58Whu-&u`tS5-zpKOzx(b6>Y4r0b*wNwy>xfUBQBy<;d?5oq5tXGvNkD5K}H0FrnNcET%4tK60c-2hiAl1m@}zQ(0DDb zG=C#QhNxlv)~31Gt*5ioJXtqtXT3Nsf;HWED)(#2(8w(EITP9-yR=sdD1-Z8s$HKg z$E;jz-~4KPb>M!U-F;oUO8+G~fbgYZRs5u^lA~D>?uamggo5&%;Tbs{FFLKXaQnUt zq;SpCR?rbsE^Iy^IAuyBz{mx_uYJAqzA)Fnyf^^Tztd3KK7(_Kv3 z*X_Yk+WLHy>3Su$wswUpz4hF&43_t1QRg+7xP;(o5Y8PDdeFUkYTRl8ovqe1;m0ZKJ-$jiX~FHBIfr9#VCpR_ve*jc-=eI5S$A|J?(6 zEiqDmnvjjPN9TB!GY9Wu5E+=%Afvh+?H6^|cB@}M9fHX^m*nlx2->O|9s+EARyhN; z?3c+YnFI+~%5R6A-`}Wef6991Stktzp_hT=csWHug(Kx0-otMmbvwqU;%j`nR(A~W zWKZdq=(b9)+of{HN?ttrpGzMbaOyW=>YX!h-WLKCB4QZYb5eikJEi*LQmjrfu)c=$_vLpWglhl=1;sJH(eUKtf)I-4fPNuzoga!9 zO5%Y3cX;K?k3k%G$OzA>*F;416@}l@iuy|Sb~Xww6`Aj9q?_x?_gN-mFj8_RBO{Xz z#7~?xoqdOnfzX z>e3aB2#d?WAMrZ7F;Q=17ul2j^V%>=wcmtZicCxuPIe+1+me>{s;FnAQI_(%Y0jXDU1m$(A&to_3aAw2zIBM zyW{tgf3$eFY&1))vhbak@X6v~fJL^v5TBplIw+?=wkM#1!n2Cw*;USF8%UxQ+dO5TYX%;;N#D}uqj>!< zfoV@3s{l)WnhRhOa`sIb_p(lkaaQ zhe}G;!3x#uGOF0blqUYGu0hbsNe)O{Eg#_2G(XcyCGMMzAhj2bQY{nbp0)AiqW@7P zbssEl)QCexrAng6X~@GyuTXDT*mt5+qMB7ITN-?}ZYpIPjjK@5r@ir4eWY%IO|w)u zyksuDjShRurA<02BPwZMS6mUfyjXj9P-szO@DTbs$XV&w`2Z&&FH~lEUIHClw+vl9 zL|?NwMN9ooG)rT%hSL=|`C>0RMkcg*zlpJxuO1z9T=PF)vUNaAB;4P&ef4%;{}gb` z7!mA#dt!Vaii><1GI!B6ZQoySUU*9O#tAi&PnO;^oeGf)qGJ_JGQ|kdV$Ax+G!wip zx!azY8$if>hea!Y6j~2?N51?D$BB2zRu;GF-{5`0Ni8~K$Fo<`Nxk|bCgFfqkv)oZ zMZdRrnXNv@>aAvmj8#6L*0t%>MAZTdj0TzUtLpn-3)$V$^n6KkHoGP6V&Q@^6*KAWlG&v&dUA@D z9Jmyd-nMbE8K9MDc{D{c+zn;w^AYJ{Bg(;7hH)QjukOo~BRam=uqQ+I$_pCI7a`e6JhPu*oIKK@EmeKn_cw6b~sXDJ>b3c|04juWT&j$gD7( zjR(TEp-m*&#;7!SpnLZW%$EgnA&X~El=`$kSN9emPwPJ#iA(C<$f|a5sJLRV`|3nGOF(iDP{ySx|zk zAxrlweNN}c)Uv!RU1MP>NEbaqg91H-3uRC+R&r5Usw2x-^~(w|()d@}P?w_A^E7Ts zzPy6$2V`u95>g?{Q&nsT<<|o#Bk7FQCM7+ZWjgWt9=`fDisy_8ycPFIVj}@}V4ST1 zQ+-|v+?pIEwJL4r^*oxn0g1H`Cj?#=FEdXK z1*O3!=C=X_#U^XukzG(mBzmb zJ%9@NbNsZ*g73;kdNB!lSXmW*Sw|b4jjHN&)!`%!+mv))qTLDoy;SJPoRMXFV1v0? zTeBEji!ZJ)^7E?>hzR{vZ~3cDZD2vnv{SpCO*=4CKuPwX{b^^NLPMr=zZjd5)ie4z zLeXHlxFAr~DY;I97Jb+Ar`GiZmAM3V<+FwdSH++u6bb3<)xqB(6I^-6!nYb#UOJL{ zI^Ai4*Ofn7e{XVaPk_-t9^Ped=(N56RQr>pKJ9g}8}jF8J5@dPB4zwyihAPtBCV4K zLsO%Y;%e9T3eRva?vU%$2&c>sz4R+*p6N()BXhwp^cCPE(qZ+Sj6K&&;#0n+2@ho! z^}M2MNx*_y@q%qCc;Q)3@xf&qGS|vUTwiiWfcngqQ9l z1jpFjS4?K50^u@4GJS{D+TPqoq}BU}40H3H3h%$=J)Usm$*G{!A9>7{6o1I~HL=T~ zitG{O?e5bGU+9i!2~vL)G?Kq}JuTlGueW%(*NM-!wX2v%9z2<|e@!2B4V0d;L%}b*nAf`7wmN@ww~);;ivk8%@X>oM5Q@NCSW__@NB3`X($cb7yIq+T z-?83eoysnz5=*P;&Ec~f*VKuj-lkAh&jU36@E5IR^*HaQ)j>5}@j^>^2b;WFr*c~? ztw__!^_fv1*H^c>MH(#(hV~>%MOpt{fkhQ~Yw#M^}I_|h0k)UJ1rE9Lmqekmhu9m_1b)Ii!o#*9sHJtou z;yZJhd9&q&;vMM-rRyW^ck0jCVPk9j(l6F3qk$}LaTQy;iNzA_eK)4FYa!+oeVoJg zB7t@#iq=mSjr~I;t}kn}Nr;-3QK%9Q3Ra@+&RHsjGQ>QiM-(s?2$Rgv?c)PQWF^S2JPtpAceVKH^PL3coS%kil7V+_0la@lZt0?MeQm+lZ{NN~LO&gsgbzYB9as(sqA1lC~xfnp9^R;ps&w%VpS>np@EsKl( zw(`o$PH!|z0oI*2^@~8H{UjEvh41n~t5fOV;sSqWD}h&oDARNN$XPd~;gN zd{$BK3~IJi2#qqD>kymt5P6(6(tD7>RpW?H**8Z{wYavqR-KnF=tlLnbYsppmMM;d zI%nxYvs5TKj%n@AnT%e!PY5Wx;7fk0VT%kPIzEe-GM^n)#e}8Yk2_d=T8F?a`&puP zJ7<1P`h4!=76)B{dXT%iW94;KVR9Xb3_xkYlrk&RhE|4o;^{S-`YwDn1@8b0& zI9Rm$o=Vb%Dk^E4IJ$P)GG&iY65hsW3&f?bg;ygAS=Ngd92`p6rww{!KI45B3CU_P zSEv@1+N>ktErn%iP$x?dGihN8B|lmw*3_7Vnf5fA(M8BU1-yg)Z4||b$$9_nj^&i*NpRU#`p59ug^F0E# zm0j4n6k?^9RW)Ga-4#%NbQ=_LQM*p12Ul z?8nxJzkI3noE^|!eTf?)CpD>quVr{=mxUiTxdvQuM>{zNMS!{5M_~3Hx6Q6fN$2wA zb6ub{vEIOrGE1k{kqdztyaVTi>JB34q*EY7tZ#=KGGbQ;c(Ug=S-c>yW7KNhoD#}T zRv}*}_kpBF(JrjxkdTo1+RHi&pliD1%hcn!p>*mbB4=`i4jz;j{OZ!B`ZVDc2e45f zp`pX|ua)m)QWINy8n^|4e+3MZx#Yvt5rNwFXV}|iKzOzKHRz=pVjCiOz;(qN+T-`i z2??w#ly7k{<-cg4n^|>$%ss^g#&L(mq#jvg(+$IzMLNxx{ z=edyu2by#&R&OVr5Nk`CcVUoi(H}PHjz#pk9ke(4HyxK&%cK!?u@N=wYu9s3z!z}N z5K1ugi!Cj~SsjXW@VJ!)cMbc7+AD%Z_8bV`Ae%V%sGWI)&Xb-o93=jfT4h+bcbM+ePmW$GT8<`WNuzw|Ke^= zQ_~_hSHI35@ePklw^wybOUrP#2f<@N&5?GlNyNo8{dyZ(#D7r+t?oTM?m?ZFZN zgR5$OALiPQ+>@EAD_~4+bYQfPJ+ioFz`(f9%H-V{R=+M<575b7FfA=D8)Hz5r}a2C zh8!8O+$3ha({6FRtX%`R4I8+Sq7hg*+!_UU%timQ@1q` zjE;_e$YVGVVaCe9ZSC;2Rk=Ydp0Q_5#{wby7e#Z;uH%10X{zAk+~H2OZR5(rf+PQ_Q*|2k*zk;ej~h z+>;?4?RX&hfKa9W<4g6P=GpTFe9i{GqJ7JFv{*?|cwz0GydY|^-wnf>_%v>`O~pWD z`<6jx^vSOeCx@i%x8?~r>;oCtYAa$t^2CS*;Uzfr7k1 zefhdvUso?{!*>Bipveaw)2goZ&hU&C-$+cn3mTRTnD5l-HCYLP-uK69s~jD?ic0ll z9$4h*hA8@&r1D1?^&TLo3N(dJ|E~|&oh*Y;IqNfhpwOo01v2oh>sOG!D;Y zwn{7uet!P{e@-=NOkA8n2e<3NZ(uc8pmI1K#8|sFTL0UrY(1+rq2MW>`|3@H03-C- z@zlIGK0A9|h(4=<8Z6=v;Ji6qN2-ID-36YK1Dq)7Px=B8e`Nr%k6*AWIq*qWIG>A10LZLiiIakHiJhWE)b*k`cFTMo^&kH1%-5qUd?=6^G89Sl+kbi zAroq}1DK)i)OPkd%YnQj{E5YFD9!t)B*`2D&^7e3noX0v9s(d`8n?k~jp5^127TDA@Hd|f518O+5T=g68FzP=TGTk#=eRKCugyY#|uSrgq~yX z|9vYOgu!S+hIaS+dCn-nCyvFY0EWNz6WDW21~7{br7=bhd{~nZVCgA?*$c4_K-RQM z{EWF`y8zA0c-dqY?+VYJ;0o~C{{`i2st5ez1LoOX(pO&=;UJL+Im^>S^{RsmqtTbg z**?CM*r)q*K!Bo6=`Poxq*Y>D3BGss4Ls!7bn!{BuQxn>*b_KItb6SN7@Y}H@AeR| zq(;AV-3bg-(%l(aG)Gl;0}l!5OVj(?vlCy+`N?wqXb$t(Y%}D^ARISCK~a&5wSU{i z(O2(AY$YBmX+Slx&*k;fM1^Oq7(wVx>FeLzP>l=*24;W%&&B;G%@QrJv0c6h?0oKZ zLnbFIg2!qZ)wWzI%fjb&PO*F48&4msU|#|I)}IaW_#KzvW__lMcp*-5F`MC=#ohWG&BRiA5D>-nCUnwgaVj0-oXRLP)YVzzm z9@_jtspDucSo#Fjkt=|N>ljwX02heOxl+0eqj^nq>@4^R6zx~RQ_tW?r5`-bsB3VR*ov4#dtb6Ej=4%^>jX{=Jo9GZ?QNQ4dj z;SRZQCZ~Gfb8~aG4hyoL%C6S@04ErK6=0S-^0$bpoGF61<=$E*?S;^$aNzWqPFrzS6mDeKz{P_Rjnt>g^5S zxYISZ3^HPfvP?HSWiOOaM1|sBCT2z>yOQNvLrezc+9Pe23fUsEG=?nK{tb;K6y3tu zB^l}ajJmh)zwmv%?!4yp`R#l@&pGEg=RD7OJ}mt$4nj<&9)gtt=190(7S* z&2ZuUeUs~zpJeUAco0xj+I5JKpOZrl@r_5vEDgun&1skO-45BzxPWobo;0ixFre=zv`O@eupl>nHE8+z6bRibG8flb1_IJe z!w7YC^>>*|UgG;&%q5PnWd}#|aW%&>5Bu@@m!UC$p%z))QD}n6hI&P9azU#@KPK0G?-?ZmwWH8U)*T& z87%RM9L$3(ejbg}j?=cF79QgVAxYlKc89QV8w8jQMi1HcNg%qiw57e53}VUT8`$gs znnZuuc39xba%_vFrmqUX47@B^oCKNlLzJKqZHLH=K2|_Kh0qL`jNahx$OLIPevJoE zB9OrO49#E$E!^o0sEF?`tKvbFtknCwG%lCXTL=7G$0t1?3`ku`MN*aM^tOk!_v{aC zd^pl@sN8&Rclekwlc~;7ELCgl(}uM*LY2S~qFcj@>;e?-MpgFT4%-Y=AGqg} zP%L3ao!7UXrknjf)(dO`zsATANU< zQViSFxD8^V2Z==}oC;;mKE3#Ml8cLh-U~+8r$?cF)+7{Fv;c34EL{e)^`BKf6FSpN zwH7?kiC@_!7*oKNoEq;=AF2;r&dd5mPZ}xP0~;Wz03V9wa{Q&id%*y?tE1@~fu;a~ zx}*fKnvw%Df_OH(oVO{jf3|n>6tJqLZ-=fw%+d)tE@IepRn{!E6)67_MG>Ee5)DK? z5|=%62j(x?G+Hr$;Xd$X?sd)64oFQ6YO9=h$`*79MPHI!NNTsQj2||6(@R0_MBa{!8nlKw^7Ye7RQLQnJHlq! zRwMZjsT{m1#U@$xmHo?(!R={FQ~RE;9p-v2UHK)2fX7dA>aSe^M}fIt$S)h#Y#oAS zuLI&nq$%&nG$7wbfL{Gax`A?kUXE&7)c86a|9_d#AM-Iks=*3{^lGu|fC#Z6RVMG%>vKTna117YCp+TCImmCPX7h2$$@yEB1 z1jwCkiSR)64w^jCv)%m(fxGgh*@+Vs7Fhae=u9y-JS1FQP0e#~ETji=^M3DeQT(}x zo851K{D>{HzN}2G97A@}C3T4_E#5RLybOC1xDgZ0e1TxJU*47!m8P z6Zg~r21D+6b7YkB4o1J(T}CybIW;l7SV#*Hp{vNsr<{l+V1J2<>YwJ+GG0@b&jCQ< z44K>Hx`gnCrzqFGc{nY4nNl!*ueCbQQUHDm^ojP4j$|leKK0t52QeZ(SG#N>s^M#k zt!PzOqF~#>Ix^jS82x3xfW;f#vCjzz1R}s#dXSqnQ!ZE87;Iod?;Ec4P?vO!XTZ^k zin?|sL~4$io!sN;Zuz^~SK@uULr(-O6vlhVMM$hq73&uwVeDeDZ87)1P7bW@X2h7ME|K9$%8Rf0AGskZounXt{} z+ws|`mg|_}uce=9qg2#$fYD%H2dMC@FEsM=a1hlYKJZ3(KbI7~0gPWnSD3NXWL^ku z)#uCls*u(xfaRFX^S*m*SIMG~&fr@FquQSZOyMcB)cO@v5cv@buzyc7g6Fq^w&)R2 zKI+tL>T2bl0PZZ6(1B%@n(V%2Fc3K0Zl6*zQFxAcC&m;-B(bt96K ze>$X%kCU3Zeogqs4czXCS9ivxrh4mQe0dmoX*ehBXs(+`qSw((RR8M-NkcOg7mNP7 zX~3{KYtx?cr~G<@Sow`2xJ1zfA1Y-20`1o z6$<^+eTM!hrZWX_gsR05hxLj2p zC2=7&(HKm@tq;-sl8^oGy3Ws(g?6(5eA?PvDQtRfl-O8=#v5-cRZ#J8 zn}G~Xu6uO3vLYI&cherqjWpVdpc;dxw2(I@2~MesGs(9&Efwshf7gG}pwo(fQv>YIVz*;{7!PNw< zn%RBcoh@fb04*Cb&Z&-7Et2flKW;f|vKpU^J2>1zK(nXw6_@h75LsdD8hW+gDU}~b zu*x$RAX{x|%$8l!#WG|98Nge_aS<`g8ri&a+603U#aD?N1PNGlW9>1C#W~G$toO>FF8~u;$WrLFe418Go z+S(d=c@c2WETSGOVMP``R$4ne>xw#G!eg|yLqVX?wrXn0yT-xZCN7s0klg#vnQIn!##LlSK5_nTXB$N@Ek^QyfZS_9x8sDjVBu;@9Asm5hdcAdp+-Wke z`ji&~B*AZA^kZF<%#jbyaG z%J`HHC)2lop&4A!JtMTF0wRkwyR60i65)>Cv=AYDd;RyPJYaIm-##eq{5CzT=lC8_ zDUpWKoqxB4E3I$1`*;uVX}%8#y9jv0)yRYw|9sPw8VifRP|MX_{YT3x7S8*qw}SEa z`G*gjkpWWoi&KP5Q2OtUsYl@qR*E2~{_}RAHG&lK31Y+MKUyk*u$KhHDXhKfd#8j! z3Ok=Z`uQI%y;;Ii#%<&NemlUqzCp#CHU4KfC?Kpoq6Pn>yf3IcZUpYAyR?&(QAr4yYK7g`~Q1ga_R2#oR~9n&&)k@2z;U}i+_#k8X6iJzPy}_DjFI_Klq!7a}|8@ zvQ@SY4GrDHLR$KXytFjriG!`Fg_Q{!+TF01Mn?DK+3vPIe{N*d*3HUt%>kww5D=_t zRNvIl-OvR2Zqy7({G_Khdy{Ck$%n+Fx}^apm2=InOJV24buhEkBD-75l;R&h<33$fKXk)_dSPz*b_pU>9%WKlnzKf$92a%%F;26)Ke;vHN zP6%-({?z>)LxY5P;kvOR+8BmBQQ6f1IKgvWpV4;8Ko-d|>^6=7>IR>|riYD_4XPty zKGpJ9D}^$+L?lz1BjPBit~+28b#l3IRdC(q`owdSON$%9Ef}4y5$QuJyiDScNLt@ZLX;vnLm1ZF2K4+0DmOsz1V?+D)KDx5w( zKQE@ba^;xu>f3sf^Yims#QAxBkni4=`E^MGG)9=kY_KIUAvg!P#WO9ameOP4m$ud% z&y8(gm~gmQ+ktaJLlboo2LH4+fj)=0SXtZvfqD|{#-oplK#hH8XC|JSNe1i6CEObmFNi^h-Z{!Hdy|7yW zXc#yUALNf{G$~#~%)cI@Cxrxr5Cu2g`1ds;NWhW^>Ur>{KM05M*JBI(3CrQ)|`)^h>UmPr)R0hm>yT7)GlX^(-uK;i$L@8)e z(AP${LjGEw6z|Mmvts`L2u05Kzgd!xm2v_<0L;uaR#BOF$TScE)}N)p7}HQaU) zw`<-0ghxh_Rvyd+Wx%$km2X#tHctQZm zle0dus!G`M`0LBo+}TS17r9dw78VZeOfhoxUZ?5a=V#%uu`E}x2_hT)u{%v-^i8p* zenvLk59I7?^03E{_)1)#KSYGu2ck&w<;ygWy)iRYb#MK^QO@~Gr(&V(k zrnmSJKbzilF2)#gFvx~P=H{|l%2fNSjlq4uQgKIFp5Y=#N@Y%-e?!IQz1uIl-OilU zmX+j1<^0pDenw%i5Bed9qpD3|s*jXFL|=MPn73AXP>fe_Br^2v+pBAif)wp-Z67Bm z^(e*MO8yKywIYXsQ?E`-`pkq>CK*Vw?@kX~#AKq(di&=)I-M0tYj}QT*A;GTxpT#1 z#q;2^@M%u{BZGkvh0a3%#j+l2%j&B`hMs?-_`d1G(v=D5#I1g$l$Dj`spW1^JvM|* zIdR)e3d*af*zRjNI~Rbn-am45LaIYRs?QRy?R1THnbjCQHX*H5sd2YDEw^z%l)``b>|d3-o6pjNv5fH8{qNW z&LPKWQUEn{_oTR^H@9XZO*YivNppJyb7v%rS}rvy=V<%e3Se!mQSm{yFAYUf6N}fy z>v*HwWMjOPBHaoxqdSE zoA1j5s8z-KEQy>ia(aK=4wUX4%ARitrex4u(7RDJMzSVMpT`pv85JczpWQ-kU9}jk zyU-)D6{*NsThi<-{@nn;!(NLXI#Q#m?k4XDpC9&mmt}s&V^>(#soNZ1%caJWE_b)r zw^cZ?JASv_h)t(8h#0e8A1luC*omKsOiiV?kt}&S_e+_G2HTj!7imavlD_OYja;qU zYr}b#PkqD-jXll|(s2@Xc_%&kDzg>KqdJE3bah4LpVCcozihc~`MzeS_ow47fQd9) zg+SEiu+Z^|gMxx|yME_}_SgMxp=L5ORwm7#-K5`b_CKdyriO^OhM%oipN-`_=c%JSY5&1DZoL-o7`;%ph z;DnZi;cU)J!*-Fa8iuH-s2*QUeWxgo{py)0DgUcN6Iv@(U^^rDEdQO*uWxvqT4|r1 z?+cquMe1e+^c;5)S2nz(^SWzWzf>_wN0MlYPon41B}n>m z;0*ov(e8b=?5(4MC=Q~O@T!^g80_AYofcuSn{7bXF89(8qUPDE-8ZN_-J1~6rq6l< zRQBDgLKG_7lZ=*H#+)n()Q63a>#W7O?aU5MWWCR%E0mFyT#ojd?-tNGk&ETGj^lHe zh30>($sm$pww)PtxHQQsCCQ|Qa_EX`ziYBnJ0j*b)g=Bc!NQ5vg+;t`>%p0Y)BPD( zoXbwPK<9qlzSuGkYZxAIP&O@;sKdbW9T-q$^(hOwDGdEQY2QjSx7A(KQYOzf-RT_J z{uZA)A$4bG$D}7gsK-o$o&FYXm)(hGBZGwHev2M@hqO%7E# z?=(E0)045~^;ig#P3X;i%?Vsu9hodW(o!ifd+2<7y1PpqlB=IEW;s7duBy_}sH;;2 z6TO6cPC1RVOPudWz*5r}#52qs1d@i4&QMYmbN!b2u?~QPJ3eLi=S~vgaQ2y_rE;0& zpTmp(gyMy!a)FL~dfq~Q<|DA%_?Mm+@)gT@YkGCkLrFSwHKaocrVoAgQ@t9MuSn%K;bg~K_ERn{rf%19h*N=_q-%_Bw+@;Wo9A0beyg)w-bVD zfSNmOFUW)QrvUnAX#R~H3F!zwpAhl-lkHAr-0~T(>g!a3ZHHY7FRnd~<$k#PR$8Cn zmLP<)?%>4>4PL2N4~gIq8Y#X@!Tj>`=@Gjn+}U*Sd){P7J*}p%B;DMAF1(+Dd9=5| z`sU3WWw?Rc3e#9MtY9ZL$`tI@6mJ-L8bIZaWWIr=PTWh;EHH3b%PV(I;Z@$?K!?A5 zlJvryGbLMSkAM0h2!_a!!H{GO)V1-?*25lQ8x|aV9SCBR_>&%&Ha8DXo`UAH zXKkxLa;ARx2o6O?Wjr|78T6I#x!wKBTlI$ zk1tIY;n(m*Jtv{qw&v#}GdS56PH>%D$@NOBfFH?I)i4p2)Lmsfkr84)L{FtX`6brM zbN&K8t_?&8?C;jst=ik$<=sdMF!a1n|0oZB%4U$1BHHj^(vm_#q8#RMuJ-(`e3d)K zzw>?0%FYn5IU2x#JkMEC-w)IR@2{#=ncML}+xm7wX46J6 zTRu=P^9Hv1@-)MDOpH*`{x^!z!sq^ug<|+|#lB=J*USxe18y+nmaU{sm0OL?wL8C^ zxcexzDQZp-scD6n9UUFUy@_JORXt3(oY4zJH{%6{Ek2(Hz|5q>^8k`+zZ=xMe7Ne~ z6IInY(#8l}n)m5il;R#0*Qt2hUaf(kEu*N2n`L}ThXFTLSM519(gl^=vqSXV9xJns zFXa>y)6cJ&>(B^&nFhmp6iU0nyEz4$aT<=WeghF6b4%E}guOm2-|lz;5i>)$duefx z!Zr?~rk_F|8unvWDV|?mO-*f^S3Erfv;X$ZTog_oW26W~EG{jTd2_jovGf(2sZjF0 ziu}QkpDbNZl4xZ|{eeEf=E~b{6r8Kh4ys5*f#6w-nonRaT#>`V6)h?J(d-x20*CP` zr!@^Tu~-eD64~Vztd|?nCn|JIDaCO}sYYAzRpYE)=LW%Sp-$1uRstcn-Ot!MWZEPk z*VvgezZ6us#%vRz1a!QIp$XD>uquWJS%2D?Uqs8W$-V_iwQL_hsR1& zc+S4{e5uXbk$D|$IX; z0|NtWXs2GlW;n9Bz?#2QTd-ZO8BTJr5TTMlQe0dNchU&gg}7_YRwoxjBV%Hi$*xL4 zRhV;i*MJ0Ff04X|BM-icacQ&B&|AWTqVb} z^1Utnb~Gn-Cc;=pH9Y%k2^lg1qNn)$<%@%I6=+gg~`ooQ|jRrqkxK$y>x%>LSlO2{oCdJvM(&}?R(V)iGlDpUFYgWr<77oKT@ zRM@-^Vm9T;+M1E>5^E)5C;a4(!O})>TUmWAnUw+^rCXC$`^rXLw3FEfY(gq4tbVhe zgd3;%h;{b%LZHafoP&>$iVS(8uWt?av|nxRxx!!dM6VoPb-Xbri8jTm_>8kUp1H=lhy&p3rU?dPS-z0b7$w0O|yIdD_U>5N9_3B$t)x8<|gEYk=3)_$I5s=vnW3rW8jv3h4{$Rp5G0+cp{jfw6s!<|{$_=wVUNdfJyYFz6)?L*5MJ3J3eMhN(09y|hA51e_*S9_w z#2GE_dDQlMzLjbBn7CnYvwm}pWor+MUHVeRe`2o7dRjIQ99Oz}A)Zk>{Xsjd;Qbh9 zf8-;daqvT#(hOt9kCit{nsGUrzWwVkm5HHsT6!Gyb!wGQv&S;<^$9g*&+Z5+KD$?w zJJF)Y%NwTFKBQe0Dp%=FwG3Pxt1B&jyILe1dd3QlPUapQakGp$P2N*F*Zw`Z({uED z!(>i{r*ZE-%^z`gC+t1>sn|O17k=(d+;9;+e{*<7+SmTkjJGGB>~uEs$OeRJek7G# z{YToqpRZjwR%KUDPWu*XxV4QArQ6P9mI}QH{`JKQtK&7gh1i(kwz`*l{GKXNfv_p& zhniQ%Cz$v7E6=M5+k$m@zuQDAt?~KrThNX?jT<7W}7x4oYcXJAtA zW@xSIl|Q%BfLeaJj+1G%yXy3_?Q3nu`D{wKfZOXtqi9l=1Bv&>lTL;a@4N&xX%mH> zs#0xC67zH#I8r&!+EJXvJIlwZS<-w^e%rjcW^B%CErax_I9MUHVz1ySt_8Jj&9hgl zan?vaG+}=Vo5z0iA!2_x_oGr0)_7K0+k}P~XSTP1rVWlUti(0D-_TpmA?~r@h6p=f zQY|yJBEP&+P}q7ywprKwz?C4wBLfcud)lCeql0+agSEA_Udvno32x$zZm$gW%gxI? zoXshyE9zL8ns})KSsBx`IjE9PJ6OVJf0QARH(@+=cl5^e&)(_qW$!=1q~}f%2RrYL z=RWQ^_{7V?MedE{9ZT2K3A4Z8X2a}n@9QkNae45{I2Rm}asn4@IQAi3$=s63%tUWz zIGL~BQlIlKrTA0Y17%;QrsUGo(cgHP^>|_iBm358{-p8LxS{dy$~<=59W%^5Larl5 z42V3vaHo%Ry|W7`Y8$yI$^8(<2$S(kE>MygQ#ny|C1J6;tvPgvC$Fud>v8N??=-9O zVu6I{m7bi1duGYCQBPmG`&o!(S(MitPTyWzfBF#Ci6cT*F&N9SBtw|)tOxCX7#(;< za#RZd!|n}a+#jwGaS49`ZyU1JvJzf+h{Le^S;T2r#My06?W2YgoZ`r|gxuu_u&z!% zJR&p?n@+*LCE)_GoBS&6a4<^Aljdk7RO6jgdq*S(qrVc4l_wH6zq*HEvJ?4z7W3{q zf?EX9%XQ_EkxPacDKpgjrHm(30_B1yj)KHUoC=qm?V=RX<3)eoyY#W|gOUo3;5W=x zy3XGuQK~2PhFphzgN(Lzh8CjElKf{*QJ5{!$895Z{5=-alwq|<3u|>bKVkS6Vlv;bqs&E)P+qL zYPR*4Tv=*l7c$)ou^8rc>Bg>l$-bL8IM{<9wfWiI%u=3cN?WMs5}q37G2%$9ImO&| z3pJ+>X&<*E++9O<@2gOIY!x(b8^|fD4#>8Ztjt*5*L3W8t(#GR`#v--d;Qg8GcY2qh@~eL_E0iXxiljBKGFk2PG{bv_ z-{v}R>n#;|zO*KmaN@ZBfuXyt)GCT!V#nE=l|%R@s8sg3RfMm52dl0@Gc~^emQ3`a zo`-s>BOiR&W-O>P_uHMvpCE+d2S(6q%CoB9wiYpMFx4*whvtjG+g-PieMg^?Hqc+M z>Y-3_#k^KWLC28N$bg2U_E_&DgEjo6L+-Jb>+o%+PrcbGzRqp=Wg60JL&NuNMrn^u zag;Xfq^Cr5TbItApksq$WO5U-PF%q1-aR$Lem}pc7Tn_J5MB>R!Sy``%+J@GgI6V!tS<}p8YQGo@#yLSY)sz zC2{OA(MD{3`fVio7$)1!G<3u8@%Iv1`|GbVRT)BdkDq&+P0oDT(*;aS9ijVWS|j($ z*X#61dt+pb;=z@S^hYVbDR6E{I8Tm6Cnj#@y1N3a?W|RtxIAP;s^s}ux60V@YED6w z7FCwJI%K!{#u5DYEB8`;%zlGS{qeV#p&11hNDtw}yS`LxI3;(Uc=P_6>WhE6QEyP< zoDfrXL&iN@zyj|wB7I7J6U0esb72g!2~HYBIAou5a*D;@dKo_0cejT_#)E1V;*mNQ zE~%jrtu_EyX%M>-6=LnW+iy+^t-VC-H&1W=iaI%VbauwA2&JI$yz8ji0@+P|t4~-d-bE5ltE6{vN{9rZh>tsCXZeEE zqBReQHh%VGSRe%}ZFKPKr}BM6kKb>Kd#+*JBRYcPoaVdezk`ggi9JRd10uBnu;!bQ zPaKn5w9T;4$pr0YeXvpl$nJFB>K`}qGtZeg#75%LIB{EhyS=mL+2L}JFe?sX|7TY$ z54j9tpOMlSMA<1Z77VCt<0hAp4?-~pgm^*XwI9K-qvf_qSL}bNYYMHk7d{|@U$1>k zjm%b4C2ef-)%~vax$kdoys${vB=fC3j;qH@d-P#jq4?V?Y$>SL<6EyUPs;%>m{x2Y zeYDst^q&-hH^H1pq}B}4mqL65?KFM@5{|b9nIA4i0DWrI_36GcZng84!?3ozf$vnc zv&aX+Q!hbku{t2E-vpViC}BbP_|#iMXe`N`b`ejrv!6$Ym{(L)27l34SN^jB5}(_+ z7a3J@ga!TO9{Y(ZHmlYWUWn(MbZMJ09bzgc2;Q!C#yX=<5r7-mHEzELI;eIC{RS<-Z-EJz)03=&Rx;W*RF{E-OXm)f?bpCz-VW)iH#6>}H&&7Rt| zKj^GuV`J@u2Opz}W!@PTzDF`i8+aEyXKE;HIOEF~h1J#7aS7krM3RGp`A|6j6ksNL z+=?!;X5G+110+J+<+2!H%z7Ove$=LI)_jbCkB`6I-|8zqvYXhq<+rapQ}hP2pIz+W z@{G_hXmB1UifV)M(Q_Jg4UIb=K761_XtXK7vErQAnh~A6+{6A$3j_W)*rUUayIp+Dq}1=kf&{=v^+5h_T$gTNpA3 z4H>Y`ph;{(-y{=sT8)g1=HLIlQ9iRd-6E5{gxUYpFa0JmB~~&TFR@ULD7TrqCk*2a z62O}FKKJaG)S;kx)6>zhZ7R)RP9_D#)VhU|boYf_M;CZ*SPsACG3#k_->My{iQwzS zxh3QvQxnyU;@HyJ?5#ES-dB91pxK4kB3N`f_YU9zPhNo z>Fkys>yDI?6Uo53LyM81>gFr5S<&RWy92nN#U{eo?H)YC_rP_wS8vs=@tPd%u7Dy< zLT@{;7x?-N+IZ-t9g@JDOLXJJ3#R0G1RWP~*Dkk# zCs>IB5>~7cYd8C~2<)oqJYp~_v@4E}wb63Y^9LZe#NG2RsaYRptj)S(n~S30kFuj9 zH`kpH0%Bvz1$?hYHuZ~sPkm4>KKn^`CdE;^s|r@j4$36DZomGmM?$#V_pCsHuUz5F zxH=Q~^Ym^M{R<|$gI~|JtDW=IPV`|@O!KXwF!5psQ0v`&bH@rr!f{;N-I_5x`%QP= z&tfuxJ)zUJH@j|>F@Z%zO>Ocg?nmlYmHDq+#|OnFfR_KyjY=j*V8jjlhHrVa3h8Fm z=6wEaCte(W6T|y-%`l^^Z1_tT$n+O|v1#Rk-!r+eY4qia*1;57n-$ zn{&bCMD<^D{+${|0}+S>9yKNgMR>Jq;lKtS@o>CzucCOcxpQ6)E) z)YjCkIsr8ZJq_EpA?~B&P4e7}b#n4d*eCS*b;T@k%jQdX4u>Mq#+S|1qRT_AP?H=K z1`*Wkv$Il(`a4IG2tJnyMbD1fFU{9|i}k^)GOkP33cPmjFQ&5Z-{tSp%UK z;FlPcRui+S`Pp(eO8NdW9zwKF_yb%Fuk|5?K4aWBdteU<-glFlfX4qNB1jd6_sKk+ zuITJM2wI{i>Yd%8oDVFDEJrxBk6U!iXc2(Zc15JTYxCWlgZV?Mf_xHC6(%@^R;^p< z$%=}%!?iZ51g3J&4gQs+{+Y2Bg=H)NHBqn`&k+`*?*TWqRnB)+y1xAkfO@@wRP8t? z1S0*yQ=+~T#Q9Odc{t$GbTd|mdJ`YtA;;axpQ>H}z#0u|I>K2i zS8QD(%bRXQ0p$D4rHX?BX7yPRQfvb{>hRJ`6$I|5^5+rt!gePotc+=*qrD}f=+9}MYE@pz!@<@N z8rJ;DVm^)J? z?uZ}EUSV~ydG*R6&J&byI|S+vl`0RH;^Ef0-!(w#x~wh|r8G~8F9=rS#g3@$m8|Y; zJe}RblqMNjBiWE!lo_l{2KT{2B8q5CE_xXh0F-1?!=_!c$Rzhyf3W_(xA~I_1jiMQ z7F3sqXFxhy<64Z6*4f*b7)f;%rpgKZO6&Q@yuUKDw$KB_jN8AN>4MID1idQ(hAgfb zo>!SyYtLPj480xmQH?!73wJ{VW<1oOAuD|km!o=UqF68LyVt4t*`P73!=VDV${$Uz z-n+Lj+vv}}n#<4|!K_kdLv>5k#V*LxBjBP(!lSklxh5PLZKI233=lW8x6*lgNVUi4 zIRq{&ENBVbCZOTo^*aqL`U%3aR6_aeFJCUI+Imme;al4NW;C9r9aVTPRVG*dw>hHB zCq%9u)b&zVXsIF->vuP&bc^1ZboQl4o3wqr9iEuzb#~kSIG6BI|FaLC$NSnU7DFtj zI=cBa+X|H{neoO0>GG**eQbfCoCFv2lu|;e>@|$#9(ct5w7behRI&C*^mKAOHEqZr z8$a6j3bul&X=aq}WR^)crHSa?%n?$2nr? zT}$>0wlbG+iw9KI{)>}vpydsLB+R*zRk^uc3j5~PuZ2rOD6$OLR!#>pK*L8W3n|sJ zHJmb=dPHgvV{;6Bt=k@Pcl9tt>x!{w(Oh<>+Q8`OU1pVp$hRc;sl^r^e|~5>l$@Cd zW#POv=rkC)VG7SwN$U7q9L%_LGEx>uJ&l_>)u%Xg$p@ihrqU-5&TmXs4p!OQj^yj7 zN;%h1;K%*RlRBN+vC8_GEyX%UXalj3`A1l_!*@Q-(_db zB#vYMlt{o~+JiiGC)^%;oj^9AIBm7kQz*q*!Vo6TD>g@T`?BG@W(CHSPuhy?lmVd$lrioZfr_*BW9o-Q zG-?LdkWFpAG88!QScd*Jg;Qc#01Ea;esQQmssIi`j`y%{3T=1R6W3Z0cC zWmDys5pmDC6P1YI!L`cqNK9(DVZ=C|V#I_26b`3QkQA9P)-WZ9{uyrdi0Jv5=k-C> z5+`*&r3+bEmW15KS{DMt$+d0DBNT4PFhlpB2%$V_hR@2LIfnVT*J$OON%1|g=d z9}KxTxqBBKbGR$FG_7K;8?g+thW8U{)TSbQ z#AJC_ZrNk(kD7qFhEsS^ne{gkBSv9S=8#wVK9DLWiZWzj(5Dq^JG-*@Kn6O%rt7j6FU^-R&xc0l`1CBDFZriF z=q%ZcqccNxdHCRAh0saZ{D=!Pr*Jm#dlm~3mJ;@Rg;K;5%$tEBNu`SQAm&?jNCqvF4)Y=wRpA+z#}TE z3@RhXDFpI6q?eHfctZ6)64ld|e**$<;?s}nBV!6sMr@`yphmGqrwLrLc+;<;RJRS7 zm4aT2`5aD~lq$pdpxv)t;^YUGdx#0)^TgPwXP+fu7G3Ay6 z&|eXg%M-z0!<8eDf;UB1T81zS$JV3wTZl+Op|@C2jRt!3lEUv4h3_@yfH!7oWd5M^ zy$T~3v4Kq4<}|#T`rh0<)Q^OLCEnfHC{yFR@#)YAHqCH6`2^}Fy^8W>d24g z+v@`u(H}8+b`z;)!mP=MeN|k|`es-{y#T7pm|Reu#l0Y~bFT}UAC~k(R6KROjg%w8 z=AxRdt1c%mzq0wo&NBu)_BRZ_=1dkKTOG*JEcm4Yau1yw0ROPwC?VxKs{~F50OnNx zJwL?eljS(%=Y8y8TQIz8W=@bOwcBJoMSUIz%qcIsFpmec%Kp{BW0wIw&tETloy#i) zec+52bxBsD`D)>C5!2xPSYQJOAao4ZxHRUEkzjaclk(Q|e$4*+&oiDNHzOwlmIL&9 zQk&V&9JWVYit4IU2E$C@@+w=X1Fcgh=SM*v1=h{Ly%z<}D)Ar)XocsX4C^1aq{6y> z)rHU?{O|gVWU>>`BxQMd3r}X<8v&qMTNs0l;Ic6Q4IPHZMZB?jrkFkslvUOC2?cUp z0oh=C_ZOAEe8c^_=b)V(*n-2pH?J;e!BJA`KyCUpIRNOC>Q?nAh5%=E*i&uPS-l6t z==aZ0J^^6#1b-Oi4+nuT6E~+T_(LGV`ICAmjH*%tM!h+-KU@l&+P^hYbj@>QnN4WIGr!dB=C*RR`d7I5>n6?yH`q)T(GYo&PNg&>;=Y3OettMl(!q1 zZJ^(90DVz1ia-!L;gOdO2xu6%oM30)U}2W`5e$=dTV9sQO;pVHuiK345am z5-eN{e8S9oVfK?$azr!&uQ*WD-qeMFVEaC>d=;V%cwIUZY8YQ22pY7nB33{Uh1r6r z*6m3SfXWl@T=uIf{~PeO0DWn_DH!;kIY2qJCo}tU9L8!eO#2yrfWa|tYMPn6JaYJt zvlr^+f?fI94YQwPmF)|PW78)=fTmi|J(6$v#*QE35opyfKu7L@@q=W6LG1$Qx|JU^ zprR51D;phCw-Q*kb?nfCm_t_RSEuTzL%py9}o)9%y~Ke1cc!vjz~Ztk|PD5 z6K60_8WQ@M@9*DcN1Z1ZmfSK9piG1TFR=MB;0h9u8dyPy*S&trV5NK08j#_Cw}7Vs zaoK<0JIDa>#B?JsA##p5thPW96XjU~HPqjkw?b;GT^M+rV>K7E;2zjFjOfR|H7H!5 zNn)MGoPnT|sUE$A?w@gd8Hs_`2MUX{C@BTnACg{De;{mvDkN`zSWZ7U)p4|m7+TMZ z!X@O4)P8xQT5Mm*c6REPZOdIf;9$H&xZF_^c2<}#rF_BQlfDJBKZn?(lKt<75dBvf zMPCMLsM2RA2s@x8k^<^%F|CF;SNn7_UV<9uP=xC6O@mVq#IkIDE1vtjhlP_mvHzSC zDRgLrr2j!zl%q-Gz#DEyg~S7;C#RG31%$&boUro+iG{HYJjPzDnQeP%tx{t~VYWL2 z*DjpXbw&adg?4~9q!AqyT=TTZzh7bF?V{=zmRqLnHP#`dA9$sld#z%5#r3IruEH># z%MUq){)syN&&7^8hL5pP?5Z$`-Paaw*Uf#CMm)2QT*^w{KUdaF#)jS6AOCo|7D8dV zi~#L$-$-U#zWquNM8&dmY#%*e*0ptLE`+rzG;38TM^6sxiaLALDyvRsb6Yn}lxDaRwA8`6%>J`#3N9`KZb*vH6HbzD1Q9I@> zn|a{j)lNlhK%}Hxl2d;JSKZYQ@p_fu9tsgsFb?3Ei`n*H;%_QpYp0GP8=c}& zyk<+7J2Jv2@1}#$wl3fik)7aQ4}1(1ysbu%3aMdpY_oLHtcUYE@mCOiYXZ?7C#|$$ z8Pu;Hl$TZZ$X!&dz&)C3TVmS@2D!igjPsx^#3*nQCa2@&c_EGWl6Uz*@ z$g?wh?UTh@v9QqgG#j(q%vChEoF6iRjOGRfHmWap5;J7w?v5%C9@Itga5ZDQuSGQW zc(5`&rvFH&tEayTPnUrLbT3Z^^Ps#r0S11OjU_?y-~f{dgMOF(>p20=q}Tyv+q=8* zS)MVq#oESJzNAaWam=r?v_j1-a@>Mt%9)z>2?eE3S~-=yc`BA!fp;ZP`WrC>&HzjTwNhXTZEv0K_z-BveHm_x=#2^7wQ^7u zg-)bOe6oJd7A*75_xtVVxWG9zf0shxB+R#kDw`lvc)9lzYrS5Led^$Q=HcHCILYZC>LffOhZGGA$SPNsN8QDq=;t0Pe?h{(A9GHhlCWG`0QD!}g((9>v zvoWd1*B*vMwSBqgzK37m~QI55b~wA^9!`(14E zE9`#2Rv4J3S=)o_HX&EIV^A)n2j{?KH5fyCXN|a>C61k^dk{}o_tpOJr}*o|->3pN zfv-e#zgdteztdhOhxzSfid;VI7OI^cM@BYzyNSEvBtKXmeUsJP4n%b|6#W5CQHg56(PaVFEvf~)vdf;o3Nc^ou2GxKWp zlp)K}C$gDUVpqGalEQnkL^R|i*iH~ol0YXyrbm*=)L0(EPWq)a+usJ9S^J;;{*s#w zzca`^A=WjXEyXL;Pa|S4a}1o>#&IDkuOoicU}nR{o7R(kM5{GIDUwT>-tDh>!VJxu zKTM?zRQ^QZ5LL8m7L;Dn&neU-OpsiP z>4(s`HAeasO@RE-;}%?$lB@W31xDx)CYKVM%2dfG>ACcY{;<>4-H|KLWtT!GPGNub zQsaD=YLM8J(iY6$z~kGPrZ;J1_7i|xA6N8=QOVE=IWR&=)^KFlKpgemI004f3Z$Q< z)@Cf;tk5}XYaZ~Hbkq9r;vbi_DhXhez-ROWlCGHIe4?h#QToSJM}((TzgF4aOvTA; znEDlAyS)&Lt?1$B^wR@e7OIR;AuD&#lQHpD?B_*9Y1w$nXn1C-nDwK1=bwUE{IB{=a`arq}Ia4_iS9(-PM=| zZN&|_Fh?0XXeYTftC6mByMU;8Al z2cmOgn7Gnq)(4@ypmZEkBfjpZjhOz=b*Bo+WN_IbRc-k`@rGZI!t6ajAK5KG;X{G@ z2cM8fBr>fn{Mrg2m5xr#gdx0B4nD6fy(hns- zgm1X{3ZtkMJ&`U8 zP#}{l-%U~@#ww9Nwf5G2=C->Gc_R-VXPDWF#Z1rzIahW|Ro=Tx4P$v24(OY#!DQJmF zM3$%@VqwKn2bBm&p}R9y<>wQSU!X86o#n4>AmWG!iie@Wxn~x1JF5%TQ8_k^^QDTbD}&IHc{twEz9b$ zx02NtAh%sef()~2?loM|tQjxCxy&lmG}qdT{ja_& zUjvl60J$?UXP^oojpll?R*Mhh|>O|7Q<@ZZV4^*6OyQlzZ&!ckkUI6#qI`)v?;-?JH{pmV!ee5gdmhG^E8 zb;@Vc7f|-@$s`sSDA+b=pbfkOPuqFh?s7*8%DpFSjj}d!jEmdXphKl>v;`Y;98k0W zHSpbu>A8*iT>y}eq~OH7!QCGDehn&08i08SK~gdfWU^*Ij*3wzaIW9vk_4?qQU<$N+*am*<)~t(EJNi7s<>RzvZ_w7 z7-~hLDU%1+kcfoNiNhuRj>7G}mo-qvBtA`4)Qd^}kD8KH_!H;80$0U{Z$CqE@>Q|` ztw3>6!PC=o|Cvutm4XjsTz%@H32J*97+rlgSha1VmG8@M#hXshLhEn7^Dj#+i@Be-G@YMKkatom;rhwlKUD?QEP4n{lc zY)oUU*I32#+Gn(u8f~#IF^_BSt9Y2I?cqOEeO9j*oyST@`;i?sB_Vr9pV}Wyr{H58 zGhULX#=RLW`XB?2;`|kA4TxjCg!+KeC&TJ`y)vs&NIdJSjCJ@(UX1sU{`U9KtoTzl z@cRiA(M=Hn8TsWMOQCP3S53D-K+2$BxbcWf4}Z0`e5GQU(xGRfR?Nt}Z%1`bKMSs5r`8|L z;c@c5;s34$0*sqi)I>3G7?XW^arHBHRf3FHRu!0ide)Px@JoNFdNwSm#m^VLT~N?? zRLDm1EPA7?IDzjjpKSV~{Mp?IANw*51r6J1?tji*hHDdC3T>ZOI7HvmvT#(ls&1Z9VOSDbZ}b@@M3EI9Jyw-)u{4D#bWiDyElo zl=Q}M7T0e?nAFcqdTipYaJ&B;RPDQAutS-9`(xn+w4W=3oe9{ui&quku?+2EbI|r2RaCPAXOP14J8CLkMrqU7jh0FVgD*VPPP+VDkwKHyxF@dFF6u%Fz z46om?XW`=y=Wh%yQxPa7It)!ZSdeX;72PRwE&5fbmvdDJ))P@#8XDIy!R&ZG@_6jD znn|@%;NVJ1_7G+ty->tfH1!~>$)Q5`N`B1Th(gA!ug99lnN&kJMhXuUxY$bOmxizb zp%=DYWB^q#d^5lBZ6gw7O;)xH!;1XxEttyqn4stDci9Vm(N~ovO~WW z>*o@f;lVVd8E+{btx7&qtma7~p8!@*B|8_nUadu?Xg&+MKi=XBlVe=xec!Fz-;Lji zB4YHPk>YSMi`GbPPlOJ0*3=xB1c#Vo#~itlv?Z~}JZwaB`$m`;UAa%HaF)JOWcPF3 zXw+u>;9#KPIz+74cuA}gZC@)pk`{+_fm%pEGxXXCSN4?YqmB){PsRK;Unpiq>Dv|m zw3lE%_BpkavMW%zM(3qNdu$U%>SdYVMM?KrQ@T0i;||NHsDSj{>(yOm%< zdkwo^ud-Bl`;tv^9Jssl4h4^HI(;+c!H8$5v}k0FCk+VC+iUyMe!2n4D)(A*(01$# z7M{7UVj6-s#B?t1IB59msC11V)9=TSM=V)x&a>KkIev{_3`(Ib`UgM=UTX z*ZuT-nc+A(+$vsmtBbyz!*ix(AX4-GsHz(Ncu9o{dUe}7 zN0T%B9j)8lgE(8PxM6!G6ICxZyGPT(v5jG~cU(_lYyQp8I^Qbc&m{?F2*p$$V)&Lu z-`_rT=RD9eq<(x$ekb03HCl1lX+FW)L}Z=%MsWtBtbx$yNXgQFBQH#yRY}~%PMglk ztX)UVmgj-QkMv-@icjXTK8Fv;%Ul#BWzz_0dFvCjiOgjIGwEzM3d)whNzFfgsr5}B zkuV;0rP%9CmlrDXdUMs|N73To)pkm!>Dzm)8m5(nFrlruE2o3@+X+{DtNJk;gU(u2 zwSS#=hppqiCGUDn+lVH#A0ou_vd>`}!y zc&~NfIhj@dn(1H|=bTX+S19~ac=>crPVs7PdB?}SHy1><3PtZamM9LWY- z!0OeKCB#l#6P3<>eLFXKWVR8p#@~5@mBM3%={)L-SR%{O!G}5}t~~U{5q0eoZVHC> z($d{6J%eWy?ZVyK>+88&Lr$2RTjB7XEmvtSprQ#n z+4v!e%fZ){M6qFjz}(8s$RC&tVB!8*m%W578A2M{?`TG8kvUtu(+dBv*Wp@PA~C+T z_q>K-_62bEz5kDR0UDk(0D7O$7BaRroG?m*QX|!#yyI)0fe%Trf8>{1AYwVJfQ*aH-By9~bt z|7VS~Li%f))T?b0{JvaeQ}A2%cJS1>-ipnqb2WU@lF6Su{g-OfpE_G+Y~c6c*A!fo zF-yz#2<^DYKcWAvx7k5;=5h%-93J+o5L@Z$UY%~QQ?`JNrxQs8-;7vgF$g+m`wxS5M^hIh^1|QPq#+N zX^eZ%$~LnXUqk4e{}i9UgxO!jhypkV*!a*@Q66Wiu1j^2Rc|I|ytsw)Pf}~=FJxY( zpYsfu7DkZUz=_9b>=rAME$W#_|+WZzOnfCCxORv`=W-)szosE}Gvv**fjBwqw!>?wt%e!N=-+kpr z4KB^?E_SOi^7zewm@>fu#;#+ViM%i(>1x%-ac}m;bqvu(wl;)?DA4ZjU z4u2~_2prJGcXOQ&Qmv)>&fp&s@SoM+$#!-#S{S`j98Th`sb~rVhgqCZNsAKEyH}31 zseYkcUIptclvf}gv8byYX zjJD(?lWSsa+rFpuDtznJuItaMVM6K_Qc!#QBlcu# z)h77sQiKw%r$6!){QZS9d#*NS5=!5O+f;W^o$RJaWA-o89W1AJ$~Z^4Yb7+DuCk}R zw8Erp>Lf!*Y~u}&M9-qx)AFwQm7X9w#a zD0hI<)DKTGtY;z<-`o_T1DCZYWYhQ_u*!c4hF4(t$AOyn62g*y6Z#8vaiVgoF5RBj z-PFnXZ8HIMZwTBE0$X^fDa!#Skbh$<;<`*irx^%tmBYtc5hR+?A09Rc75x8h7G z*i!+@=iL+f&a(g2@i}tfele;FxB&Wyr+qL0NQR5=0i@Hu#vOjXLkCv=&7PtIRH}r} z@a-E1ZozXpYU%bPlw8#K!Vj*jMv8f|7M4s`(@ z9Ibrpw)LQ1OIKSW)vAA>atyE-#lZuUF_Rl(~to-d} zo>=(QD{y({R^V}C(M0 zdD7|Z;L;9wYpeSNfv}n9M$q|OkwR9PT{~bn6`);FgZLkHIAhq%ogBTwPO3EW#33zb zGq;Zbt6+U6W7gxE2y4^;N6{MYc`Fxr{Gs$c-1gqf4Gt#yrd zO;}n2u=J0Ok2ePWF!o-tf^<4BNJb!q`eHl*F{acSqc+3f?{f}q1YAA>A&@Iz!Wdwi zs+b$WNWh?BwxETGrzaWC^9-%%xn`gsXKr*km{UGEYCs+%Av)iXVxNuf7$*Nv-`qDJl~9hb?j}&w=ib zf=Ld4;s=7CmxI6Wy^r&12VRZ|k%T|A318ubcZw@`+PoBUM z$lWWp2Q?7$DeR2CCkpY)GFWkt3)peJUXSHjj^{4-eu_b}+bgwtRg=XEe@QR!D)^!~ z;5>7YfEMDNUt>p;DP3qM1VFdiZ{~%3f#kyRx2GGTYX&)ascis@mTr?5D1c7SPgwAJ z#=^vu(EIw>>#{kTd5EN_3JoA!kVG^G+;pYK*%c4-q+Z@W*5y<3!z$~nPEIX{=s=n$}F=RWfkq!05L@VQhhS{EC6Eq4fzSEWbx`;5K26Byg= z{bbR4L^j{CmJ83ReZXeQ1hez=3}<^?iMI^=%wn|hISnp}BWkcmjw^8Iy@Nx*i(S%q zyo0P3X&790MK_pTb>Rcw2Z;VrA*|v-MzvStAMKLesC5*`s#rhcEc6;4U6N>?bOzPk z;}TSk_!+_fGyJE{H@NSQx4w<`?F`ZfuX30REaw5ntCP+IWGb9sxEyiya4Qj4NSFSz zb}8~i0qtbw0NIpIY~(j{@N3Ks4RO#r;mnc>sreBxCXAk}Eph2HKak~%JELpU+i9j0 zGmcF94V>vVvt{TlEi&v?`U#z zZpr?2t@k;LU(jEF;&;FupiiIW_7s+)dd7srx!U)cqf9>3@>(3BZl~&-G6&!J1TzBU zNjH#W)ay76=#-JxYUw;TdCDIl)Z$rWqQp}^$aSy9t8jiY1)cd;bIYGWDR84@E1zk^p~i4%JUJ5q z-U3@c-(QqiDeHB-1aQ&*bjn_6{K6FMEv|FL2OCi&aQ$V z<;TmAOmETcC5h!b9R=sVOT2%3UxQBMWu9>%o9i>bo6QcBmZL};=+c3g+5JYaMlN3l zE{A#GZN{09&dto3zOh@x&A*sv&7AmSg$X$U(G-H;1HFS~kEpLpgd+sk&Y>cGpH-Q@ zw`T6BX}{4sIj1Y1v3R08u z2(jLrY%!ep(=ew1j z>nYS`?2Cxa_@t7z7<_uQpj1z3o{mW1UV4|yWLoCW^)ca zZlV$AYX6F}#7pkMKfD(Xgx`oG7Xdy9;tdp{`A`lAF1Nc4AEWck39_>iC7WojP~9rp zDauw(;rF~Abiq0h(9#_06eHN0ueDWu*`egu-qXRB&kNhT zo5tG~p2TL+b_Zj?A`?fP=wwl``B3@3jwNv%fgwTdMhVhq4Cb>)dmo2Rr@*IMlSt2b zjuRZIK{CldvM!=u`u#oVhAtBVz`N2qMz#al^WL{HDTrG6E9AeZHf>Y3{|jbqRbGj6Kin(* z)}rO-n3=q()mkuk3Y{p9UYX`**Lz!=2!q}r%i&`^^)Q)r>kybde3{HLH!ILfW6yAL zuak3k=~QX77o2!FOJ#5sg#B8}gw~(5u_r6!aW4as2ed~*&K|;%yg%IKxUU8Kct!@> zN9dRAut~Gb>Zx*c8z|C^$auCuSrQHp;}F)4MP3v(1PIFZumQ@K&qzy|5C6sMF|Njs zWc;QB^*`jy3QcVRSVwiGBSlYJzmH|;zv=V=xuUait(*`3H?SpXisMU9O*P=maaMng z1p#?0BiQ5vqif(FGu5=Fmc~DVD*LH51^rdT;4X_&QQ!D>A$3lB=QyJl^Hj89$wXo}I{>Cc(_Rt_q|^*#3>)8^g}pPvhq%RPAC}*tV61tn|X@ z{^y>=y1lBVE}Qs`5UQ#melt^U!IiPT0Pu~5;P@Q-(A5J(tKvnjqMBduiv7ts^geQL z;=BIe1gk1FP3JUEa(;mdYVGqp+HNx=hM}zH@5c~qB!`s8J{AKs zi>*9{V=;%l_7RHoDTW{H!8S2`YG7w1NXd+Z-NOLmLeP^6*skOo(F`G=Ecn=${MSjG zQwxY^w3SvB$g>Z$zso`?(b~66sN64b4NU*j5)lBedp$dZ@;fL_WO|M;cH`=qB{Qfs za@1jeMN@m7%@<~8pF%6=SDeq!1W*J(sILxR@x+HJ38mr4?8x)IaN>*B;S`1Pq#hv9 zTlZ?RB6iSSEa{sW&`yzj2!6U8feff#m$G1G*i2EmcblL*ub5N5Bo?c^uZcQ4is`Rn=R-G|vUYe|+dgE~)eM7irfeyzZ9$xosTAwWm^-42dI}(Qc zs$Qx$7;)gYSu}FAD)6BtIx3gr$_Ag2;^BId@G}K-nC05CKK>$=Y*YycZstbv3*{QGZC8TEdXtahqe+XlYqHnUlhd#9KOExwJp6O6=Do!E1!BrYuAC z6~Z1`;;usVYr4Z-XM03iK6mml9UgQ9S%cw-p;s+xQr7@Rv9EHzV; z)^fhAT-#pT{XW1V?EPyen$DC~qkA}7VN7w*nmnsX$@XSv@zeftO^0rsDQn9xb(4M3 zex8WcJm{FZbTf#3Io2V|TjCbsoupBT^%$MU!2W?3!$mEx_A%J$ctlxk4c=SL8qsfR zv&h|0t2n4PE*d3mo_(LE!cT_EskBx$?Wu5BHQQWx(`H_W@2D-!Z{^CadryJuox;W7 zkFH}gvui7qCBG&xvd+VN_MiQUbR#?UWmEqeV}Q@Cw_Qw|z_y8dzj@bVvte8Q`n(7` z9We2f45JqUP8Z2dTXroYh}RPL!mOdCwzie>US2KyceO7gfdG72?}Dsp?m&P|<>d~K zH3a&-%ZQ?r^MdjETB&J2y3`N!bjP5`*ALl|SZBgG&tiZE88S^!`VVH#{1U9zBzW=E zqbl!&a7tvhO(J^SG_upZfoApbnM|U3YL`wvs299{6-|iK>~8$T(tDHf$)JPb-pEQ` zGSDJM)ohiHeu1{NE%Y}u#6kyzKv%|>io;p6lPCOzie!Ul}0!PDu)dwv|KrrI#WwE z2T$sZLBW*NhTki1v{r4yj#{#&qR7C0kDkO&FPYnlEyPlF_6gjiUS=JtVB0b)KEhnF z@u&JvPVUuHbPH2TwAP-Ybia+;1OqyzU~}~^8bhUjLqQco=^QJx3qv-l^9zAm+W4+1 zv1DT;NlvN9yjF+b1#A#Gxg0 ztUk20+`6FT1wztnUKiU9qi76^K13>6qVv3?{)(1B1Wyzxi7Cy8d>kBnH`OE5zD_4N zK;E<*H9&kW^!PMACe+(>>r%7tG4T}%%xe=T`f|T3{u*ghm+F3xsXypstK>OoqUA4* zg%n!X`?7lG^m4tR;ib&kH3hg!Qz@UeQ9>^T^pRUx@RGiXO0V>dS={`3O6T%U!wkF= zSEIs!)Fr}~Sj731M_LHqu`@QY5Mi(GPeoN9vlX`v@bgqmq3e62?_;PLWHR2Ft$gH3 zFSCEXXj%S&+9ebe(V+Zn{Zw&C7^0L=yvd!^56YfSP?hsVNlWZUf7ImV-gvC^Q1nPv zgD{q#;FOe$oXJ`Q%v=#Ez03uGy}DWBc}u2S_|NH~f0<}6>|b#-Vqc3g{gbv3!5>&m zc`n{prIj&N#O{^vVv|(d%r(5#xH*thCUBR{G0C!PCOjf-8G{_)yEP9m98PcO+o1Tmh5wv6J9~fbLq%f(YLVMzzdh6sn)RR5=H72V2*8U`~i~l5}Ox(_q)~v5fwfW$7m2uUaPRXg~ccZ#UiGlC49ay`J z|Bn6-t^V(fFcXeO5$99Q?uG?zlj<3Pdo6Pv?%4-s$U}3$q~xCn7-I$iJC8S+1*V{4LO8l6t}SHI zYg03$9WJT`V)<;V-2#Kb57&ZqfNdkZ#XcX~`L{!*Jb~Y3QL_ywFFp{&vS{OWv%>-wGj67O4iRYovvmg zWn@S_B)|Qn`&?a#{^@-gJaS&3sEX6L=vpmRZ6G^i{Ol<%DJR=T*}ic*sDxR6 zuqAAWv8ro!nw}-*s|aVaa)jWf(rU`EPkou-8qA2_Npy-L2FsQEZ@xDE={;vuREOhZ z;hDyS%qz4xpiX7QO~q#vdY)#3;pS9&y%qe4i8Y081nTEIu+hX^AgWQ}W{wpEFghmk zUalEakN4ojW=8%3pj@`zr+v8*^^crspQ{81(Q|ne> zpYTk0yG|hlp&?X~a*|zB8sf0xr&Ak=WPP(hUBYia{-J&P!;t>*uKy6WY=t8@fLggX zlGl@BpR%8zw}m%94%u%SC6S13J(4XrPc~vM*jOMS@Z;E`)MjHG6NycRag48E z^z^6R{%%3ukFNFAXCI@5&vIq&l1#16D)Q;{WEYc>32iDSNnd01MD&t$g*$8rnZA30 zN4j;aZe5imjTH=^Uv(gF_Y*k<>6h+%G3d0B$Qg1EVc(M)^iz_re*I^$q_>~;0aQuq z-}pVlBF*J@rhD-E`vavHp%KG{cdi|y`z7bXdXZ@@ou8iBlyFb>(-+)zH(Gc=$Z^ ztVWdZVY4#W$21i07}Xa|r+Qy*>aL)9uf_!IhIEg2=XD>q4S!?@7q`3%^z`a45|HpL zHo3{niBI)d#N_O3k?V6oon?=m#=BD0!!jL3WP9FYl$iU{&JzE1i$r&gyb7LTR=*f; z#Q~z2IR5^3annjCGf5$vbN;ovvd|}MRE`Dv;Yq;zZR#w*>yTPbYgbC+pEVJ7qce>m z$I$)wIc9H^yXBoF#T?EV5eWR=4Mifs$Gvo%pXGb@gtFBDNxDMI?q2la*gJvX6o#fG zCi_CIJu#R&m`&Cye^iT@iRygL{=?DeN_F#$hYJPM9n*Y;T1jRMx+44(aWke}ydn%2~c3p8HhxPsfFaO9D6!ul(QI8e;n` za7TMe$+k7WX=abw#!3Djukp5_A`-%NAW%=9_rCW01Frx;4UYHX|C$kM3;8Waah}7l zbmKirjsiJNSD*dk9e)`yU7h_WG;dyydA45Y2n{fJ8WeF?1@WRx_oze$_eL91(057% zXt?)&w2RO31~ie5AFcHM^5X>TSkVtJpOyapE!-I*X5G(m0%_f!3-DOgUk(T)4&D!B z-X}u-mck?w5Tmj_cdiB>pgrXs(p3h6{Dt41ay+KFB)~7_2HJJfJCC~VdpXk)pTFo5 zl(A-3UQuiX-+d4SPi>^IJgQf8XaAy|m=_q&6x>Y8QJCN-gsth15A)_&d1lczIzq)) zS_8`X&gV=FvsamCB-&Le57C#BXIWbl=`bm%M&==-|9Nd5iCjtOAHUolC^gutT%B%> zVJVCJT_Q1~*$RBfJ!L*+^svG5X`6Cfs|5?+whr*@Lnu|!ORX!23020!ea21slclUlJG46+le^23KlF#IGqG`7~+0#j%<8kf#OuX46+v+rtcEzhp$r4_h ziV_S8Z)REZQXL89=}>6ivpg6-K9>buW-3-qKU+dJ?k1XhYHQFvc=tbf5-O2o(;=i_ zns4`sC*iEg;|bF%PTr}K(z*ovjv!P{=P+r$Mi=9!91GwI21soW=P$tMW{&wblkfL= zzhOC5AIjSqGc9sqtg_*Jsqp5af%bKEK{mvmviYX_T+D|5{Ifli&aFj$qNB4dJn)xsZZ&1@!+%RBW0ncJN&6I>+ zC(6luGU3%6U1UA3X0f}U(<{bK{4I)9g^XK|Lnw!q@_d{(##8+`E6Cf$z^fPD!1oa$ zX)ZWT)W40DRpE9%;_3)**NgoXTo}S*hR-_}>V9}dFITc>)PS+&+&*-d$8fh9s3ynR z%#zutX&1ulSt8p~b5%Oix$CZ^JMrx?J{kN@LAm$uw_oS}3KY-!xr4)dD%N_Tg`<@} z&Gq*KU*ZjT>T52&J*u@<8u;V{EIm${bZBBXQ$u zIWyn(VLa-o2tE`Q0pY9a6?g|om3+RmOX?-5ao@OR+?+mEOq~By3CW@LPHO`HjQ7)5 z+@438YO_q=EZO<~x4*&?2ruD#p82f1MfIo`%t^0(`%Lv&$&*X7ZNxDFbv6gE*zUnb z6KRD<*apGPpZ2o9>r5CIv8;Ayl{UJjRD8}FnfOt4$dY6%|3Vd@(#mw~svT{AS{U%n zyvm%QK`WsKk(w?>v%{eKpekStw&P%D-zuk3B`%Hk5SMIf)$nadL5KT2eNMjT2R-9_ zzTR<<$x?Z|9pN~du*+|#9ReQDb)J2&%F(Y0hMXLx=~v#h-s zXZb(aFEW|nPx*mq?hDtnn@|_w;CQaK!SXU{df2E0^Bm~Co7qc4SUtseC0y){R*(I?CS93_?h~+fD_B{YKy~{oC-1Wm=L+XJk)<|?M&6(0&#!> z8kb0=iHqH`r1AI;bpE*c!>tI|sl4IUCA^AJH?9GWkv>uKk+QXkg)I&`nmr8 z4GMmET&_*pJ(A-3!cllro5FKigIe6s^!eN*IVw8bkou4A*&(u+=huMX%3pv!hOD4i zM!fN$qfit> zsXuw+wO&%@NTznntUiq zrdG(@3`JTe_Yh$xYONTc(yM$Eevo?kT0Waiw1S=nX#`{MsG%s+Q6Iv9%HN$gxGuR< z1H*<>DSVEq+3-N5?UTXE;YFCovLx}%p`K;%We?NX$~7d%-9}O~bu~@Ew9e#4fRlR< z+AfOiSIuf@v#M^oGbQI#C3+DR5ivT<5um&r7Ezlcbl_K@;^oy&aLiX2kvX-yX7RS& zk`hsxWlClhCW`#B7xTnvb5zv0XUpIj4b};4#Sl!y#&Zx zp&MXXD6_Lgx>8PUAe{qZayb0(yy;i}L^n?7E< zQn8WTlMI{fO@@;<2)zvi52&+e*suYSoV>iceM?0#U-=b>PtP|+Z>Aq^P9m<0a=kyE z%FUNJ5FW2rG~GV<*J4M2YC!;&buS=ySGUM1Z1uRIo^tKRX9&1Q5^QI=z+2Iwu3uwF zFKa`)kiln{q{-E9ctq>^bniwkhD|K+h3IGKCXV}Lvw=cNci9~I_-9o5<8og#UH)2o za*EwQvDf;TzCXLZR~cD1rzQmP)?*TffNf4ODh`dY3Tho4oAas}i$YqqX+j1urAIw5 z+TmpVp6)O8%n#fQ?n(QY8-by!keBj)zb6r8vY7vMit|S2*xkFH^H1*{lCR_3u}?QT z_0{u)ObYq~sNzWl)%EB0&LM^yIQ4WL&iAk+Qo%G1v)Sjn1pcwVaElvCC#`%w=iXyJ}$<8J~ zzAwo(ui&bOn$xc1}ATqBZ4oxfgX!&yTbr(?Hy8MfOfV$pW`;^`BSh5q(mB^y9_RgT#A z9u@>%`dci_rqd5QzH2IyR!GK@~Z?Z4mXsdyj%LBrwX1aJEpQV}R_x)~1JzGtrrz z^xGv}3s4##;%<0aMy`f;kk)Tcm~HpNpDHI}D0;b$goTnPKhYdMd^?SKx}6!;Irkpx z!%y4jj@eYgt1x-OqdeMU$pzom>*@fbKjRn%y36H<+$)?(VF3O1xGSN{^5+JX&NkkW zqD{xmES7Zczp%h_qBM0l&|`L7jru#zp?-BHoJ4vugmM0Qo~zVog$NJ5Rw!3t*m`wx z?F~`5&HxdVEX3GjYqs-{EPI}8Hso5eS_~!2aK6nSDOCV^Jo;k)F zM)`>_w2j0|!jaDFAh%lQ-86Q?4uphB@1D5tZxge!c9RJ4;D2I7L>XH}MxFrN>g3{L zIuogi#xrRU3_2rGr|rK1bdJH++Bd4>4rA`Li$r?wSTRN^t)?Q_CmOoI#i z-^oZDRcDj=V?sDlT{Wm%c4Q`%YQgeVPMBx}cZ8O!Lfej#(l#|T@K0EE22Jwf4@b1@ zZrcWm%i`U6znvAYHUTjO{SM`BaCRbUm1?3fMrGP=9=2aONipndI+-n`eW#gW<$X#H zEcO$w)F(fIgNhe@`S@2}vm@E#RyEtIPNH27bk#d-#7x4W#3F^}7m|49FKby+@M@-` zt`&x|^yR(JXD$z(2h%xKv}4^lMuBX*t39tTkE^V3p(^U?kdNb7C9HL=L^}-%kLPsb zs=HG^zh0HbYzSSr*_3d#$gRKBGN4K#9{+Yta*%2Lu8MfZ>5jwLa1ivJZ^l8*)x%k3 zTf`E*0ws72K=^y&Eu3&ykGSS6lZT|g*K>IFO4?N`&W zUv9H}!l331!zFOAw!?l$iShYq>-Bl-rq(0k6jxQcSeV2x%zhUB>T`=8E*#I;w#mUJ z&m9q^vtq)mns7AFLPC<}!&(a#_Jg%_s%moUU!Vcab1T=FbCdBXuLc(PCcylxqXoZfexXz?8L%1+-{tTn zu#>)zjueG@so{X<95YW;U6s#GyIYn8vMEy%h9|34&!jFe2m3q2Pzd6snTQO}Q78j6 z{XdAULCs!o%!F;{J=*J_+ZONAoMj>UBm zmQZ|k-5q#?qY;Nn8?A&=KkTuecnedfy4pDhF=Uj=7dRoWH9`)GiqVjaTUHj_o0k{`I ztQ3Ch`gkEcDKYF;^m6bW{_3vK077P0Y7Y^doUu)Ct1p_w#&(Bb2n1DU1wOVR?79+7>bJ0GdY6Xh#~g55b> zHac|6i1x(b-LS4fVUN5sqPq}@UAlZOz4*Uk_I^rdx4Kk)-91+WsU3~XS-&@W0mVW_ ze6#YKc4eC`u1X;qnUn*Dfrnw9cwn=nxF(oCd*tmcA4STUNUKBGz9WVf%9S+o72FXf zgJ-slN1dX8sfQmEUy7mxPkL-da`7_Era0324$SsBnDmmYy?1|+ zl+d{|U>PbaqALZJw{p6#$!YS%kuWyM+o(;3QZM1yfAykEw4WyCQS13n^x}iq(G~V! z=a*G14Y8XPqs(ZiKeo1eGy#=zK73Fx&2>K$+7F;zgz%weHbV! z-(-(ATAF$9ON1!EJ{6@I1_y^3NE(IKv+42^jH|`1?oCyP$Ekbs=3yJZfj~!Nf}_$76^V)f;zfcE=RI z455_pmd&ANfw_|Y_W;?xx&Z+RI%58Z{UroW)PK;e?osd_@8u;V^_{d zyskg*lq`yJn(=*ApN~yjF~q@TTA{|C3^qjX;-e0F3IOd52U$6Mqu&; z)a`!0y3N=>p$pTgHW2RzN|3c!Q@CiaAzID2xy~qpl>5pwe>R1 z*)2WF&qWueJK&91YE3%qVYyyZh=0Y)=IPNeW2eBpjFxx=83IW5WZZO_;~sTA5B5mz z0$g_aaa3`1VY*v_$RC&YA_gtYF*n$l52ze(oB$&Dme+Eg4cC9VBhRcFPo>BzI5(MnpWtj( zG*k55)Pv%2u~%qoiu_H|BjF}%v}tR_nZc>=Z*h>Hb|NrjH$n`DidaS^*vV8M{}-+U zaORB?D71*^$S|Y|bR$)Fx#i7=p_$N%s>I%hy5Q$3e?i;4L|Q`(QTSpsKu>!e(+Dc` ze?-_y+NR*9QM?Aatl5C_YG=C3U*9SCg=u)5e`qx3GTwKX0nM)w@d>2PKcw`bRdq5j zJk_JKPrgK|rZ!iiyk9&>q^EJ&sY64$-V>2(K!J2trCI)a6v^8E*fDWFbj$qhytAWU z#)&59H>2nr^34}pGiBr40;ZaOtLO-CPT8k@_u&kF^Z>hljnM(e+(UTE1#2n{R6`3? zrWwRhuMc96u5bTInh!!zv~Lboo&0By5{>FVhpM2L)UrOxAULu6qG~$O0!w`CRGvid zra#4GkBry)2OZJ_mCo7!5=oP4T`Zm-0RwNyR2TVU!%+UvFiMWZrk8NYzu1JK zP~Z8E$p}Faiz%^rzYy2q8A`3jh#~|EQj{ouo3ESj!n$^e!MLJ3S4@1*`jhls7FD## zyKyB~0}Evgs1@{qpC)~aZp;nTXg;Pq#=?}Aao^)Ug*~DaAg0WH)#N>znaZ5Be))z&zCw-hqJNx^!OC$fklyy&%HTpP^Sys{ETw17 zdhm!&^vx${8yOThvbPDi$ltOuY|y*)2OAUc5spzPcjO;%D=Bw;zXt=&(?>oSxprE+ zxHHni9e>>KJvL{=?y?_Hi|Z7cA(`(>A<_t;XuFJx$89AY<~&HA@`L+Z{P*{LPZ4o_gG6!%scpXksTc|^4`?+;i|-pFkgE23Pzb7SWygv|f*E;} z&FMXk7aH?blg~thUNR%DW~(ZzbnWU7#RBRubv6&f&SLgMZS*?52?W(C{I3~QiYM~1 z+Hfz~J|wHMA-$z9#zVZCYf{Xd(BxC(CeB1vFQOtaC;(zWI(FK50ar@o<)g@5QLfit z8WBq7-$h^Gt8ALmM7<@Sw;5Ad;0(HqL6xvrKIVU@X>=9V7R`JGzSrw&!%aRn*HtAT zk1Vn!EM+m(1cA5kzV|T$!nDZPw&>vcSiEC?Zg_F(jL3xLy|&ynJm2t-LKBv!7wZfO zP(-8x41Zu3<_W>knoGnc&_LrMT15pRYdG}FQfUxuKG!>nd}N7EKAb4J+bTuJ!b)Gr zt4><~X+E~zrG>s0p9XjN(Ov2^BI(htHl_T4fOYKCyZx5H?OcN-SHH4T@nY0_a6mMv zxLgMj!LvzEwPA;vzkgOUC0lZmsOZSw1&-95iFBo)tbU#dCqSPl8o^3CKU<@EL|0nU zWjRxrO4<_hO*a-SZm%}#HfoVl0vp~Y?)|N?{m%BXkw*_DO$?F z7vxMu{~)zz{P`xdR4vQC?B{m>pUkT9YDDV7ba`Q^x(%Ql7m*mz{lm_eqZ%xv0q7)e zV&iW`p38D=gLFrV-0mF3H-e78^0TI3T8kwh-bY{>c5<{5sZK6pheAl)Tz)myDU8GH zA-0Bl1glT-NFdDM{A96LK$a>xKDp{9Ml1RE=%lXGG_8VA!W~f*RaQRvbIzBC9-@l4 zqmC+<-Tjae!P(&Ay}J^TROnBPFA(DM8Gs+}Wpt6U%iTs`h-%pbYqZ?n`L2=%Th8k1 z^=&_1zEgR5QlQtaQF(kc2R{soD)kf=kr}re>an)Vs=j9JHHZu!CoBDcF(_>`YvL}L zk9~BHal7;)l#%7vNXX5x*bgU@v(I9j=x(Fy6kT^pPCfK(aFz+`sUkJvSDuNX8(+M{ zkM?t9ll}4OTa$OyEmlf-;Biu!5jm$)AWPET;MhC*Af?jV5F5u;8XoV;&G{F?O;n;| z6dttPCDA-z^=?3`GgQHJ+Xs#LCok6Z3pB>fc19N?bYIiPixw^vf4q;m{fO|d68f*T zdgccu67xhp!8g?6q0L_%uI0x`SO1HApOXE%VEgon(epoRFY@;oDH8r*YJ^F|#*$lFq9H~{GC_I5nv2{R#3)Tle6y01 zlA6;;C=YG#49rkxV|EHt8UmBYt%y!v;N=t*hExs|z73{T7Sgk%Ll@ufhkYwYw=B9A zJ-Y_6dkO{27@eBtLkL-G0vc#KB3C;sa9N)qbCHddg!}uQr8~lYY6WHbm4WqHPuY!5 zYN6>zjX7LehwCazBJ+z1VPr+9vEwX1-5qel-%!})&S3f$CI(z!7BTwF70PueB-$_{ zG!XamjlR|e-LP&fC?)b$F5(3oP5zK;Wfxt7NRz^mxhOg?7KtO8bRanKc^|CbZBQa3HTvgW`7;vt zwmB5t^S~5JzHBydiVz#1ZWCKlO_8lG0U<5hpEBs(3~HIS?IQ`)GJMBxJ@F;~z~P8k zpk5Eva1c};{j=I6DKI#okf?L)d{LFJxe&(KG9;a+%RI&Tr-VPMjw&f$d}-OHmJ}wX za33Y%DklHuDxEg@Q~1Zen?~ zny&0`yJBl`=WW&p?_wi8^EitUEEG8Sw~Xr9YFe_Yy`yJlGnNSNK?PFT=Ypycj5E0?!Yb zAylvA%YWPb5NAcb&LJi)il&OiTixP2Wo%axs>7_UFm52KAb#Ld%0nG;dQwFnC= zYY+}>C&W=jen6J=p^bKkk;NE$?}J!>N$==5x=oGM$j~YDs!5h?h_k`s;e6QgLhxY`@jrNHLIs7M0Fr&t>;vi_H~n(R0^R3P2U@x|+3x)h7G zn8b*}w|2BAX)X)QyF9#cV;9j3^>vC`xld7hm23QN0EHE@At&k+oWau0l4?BGYcz4q zZME3q8r7BEp?#7V!+FZ~4mrhU^y~S8`fXS5M^`PvzZ*;<@q$SLmu`4owK{S8XNil; z1mh=JSE>Fm!}fE-5)m(|CYJ@C6|h;M)-=A-dV<@0NxPv@2KB3-Q~0LY*xbTAXz+7; zu^lV%Y>8}|leb+FwoP^NT^c*kqhiJwHS^=TXjJUhQ6=44DcN%Uehx42bJ3}gAx)G| zZBn3_KvLapT@q5Alb%9+x>XdK#Iwu@>quHwV*EcPGAT> zjUi6m#&430xu?EAB+DKc@SJ|xbVL72Ts4t=dTx!QiR3j5wnl!bUz+X&KnfMB)Bj&}8=O{s% zBgba><9=4IS%LEBhGHN+P^~#4nCrAd!MMS#ePya?dU8bMHm0Ina_<>7Y2?~_9i!L( zz0&+Ql(SUvfP23}FZU`guxy+^P(yM2iUxc?;9&V#e;40}yqn6QrMOmy`8;E-;(%Yj^1loVd5v zrfMRPh4FjFA8eP4Aux1MRWjrov8Ey{2S)~EVKXVVGmhPMKpP`$>*frlipS!W^D(hO zo@)snVF;jE?J?F*iHEu^TeTQe@bpNHexS;mDzZBT<+7l9S{8djvxK{|zo%)qKcb^WYoUU#fED)TjPQ?Ppf z9hacHIFpGN4EGzsAWU04Z^8-H{)+6~{Jhcm`pZ`#n=ez6@V#Hd9Z zZZ8kt=#m?6dYLb>n!XqGJ}68|rvY9QmL3KN`ju2ziBrIzo5`^E!)>VO*ojTQ6d-n*{&O&2x4boC{NvzHpQladm#^ zuYq3GPocQ_-$sL7cpYI!x+Q}{fkN-kUwk$=3|mxZ1(;DueQ)`Z_2!WSvEuxEhnH0% z;5bS->8F<5VCshF$~&T))i#%=+ocunjxln>e?Q?*RN%*T5j|)a*t@fhv-eUvYf$N& z>sH3<-&7)|U3kn!uwFSQP6aICH?fMRP^ICf=2bEH$@2NR`7BW?wu3~2#jYr=FW-Bd zt}Y$sA9^Z>+_=1u%4ykRR9-<;%355!;xzdxLDmtudkDHy5J`uF^!gwN4PU%o(u zh$Y{(Uwf@@vj_1vC>GuY?Nd!Z7Jl9E2Z$Zom#Uf1UuGPF zbEq?BMyRS#g7`uE%{V#mQ^j1~*OngJ($p5-xT{D+nESb}7jXgHDRX|{ExP%?>kUvP zNL|rL{q{K)bPL%W)?6k9mXH3-$TGDh!lM}yM}~5XKoGx3$hK3uCw^&WJYy5UqKXu= zY^-*#g7@xEW^3bppdmA!@5K#T@A8(_*U!0Sj~C6TZYO!YAB1G1{h*%Yzlxq8`ekwH z-$}sdamvrMw&sOWi$o72NGcppXVoI3e#*nf4auIZtg&8ii}fMS&Vv9RW2s&ffd9A4 z2mY|4@Jf5q)LN9Sw;VRfWZ%1@)e?WVL2_n;B5Y-$TARXxpcZ&vrMd;Rvh+b@@D@n4 zktOVn2P2;bjC#cS9zN4LJPn~!xIsY|1viQpH9A?Zl2NJg6iLbYVOVN^M-l=gID$!| zOp^Ty=(3++0#qoRnY_T7)K)jT5FUf2=I$N3a6B~jH^ZjKJZZa9F=M>`CZ~P_$tb#= zBlGFI6Lyw0TBjfQTAUa?6f~Mqo?vY*d)YYO^xUu;AaJD#w^vZ?nF~BM0lPMSAW4oG z?{WX-xi?cvd?NYcHQn`WT@L`X3yx&tm0+@6{|>MN426iuEI}ETx{b;M@KlXB`0(+` zAxBo1{@p+&hb?vecTU>%YhAzBO&lsXmO3NC@lxfgpc#i4UN{UNndSC_Pjv|VlW{V- zEac zAj7wjInB!9scAGg1SGV+$_Yj6tRV^-b~-AKd$F_KQIF7 zKDsMKWiRD^`2h3g5p@duUNB`!@M21=8tAOH6xvT~B(|HjUdTNTfV@Ya0HT4@7DQN@ z?rmvrU@)5Or}jx35R-pMXrCI(tV=`|jm<7<`D(tb?k4=*aE#*{6Pvz+qVq}Coy44$ zgaqlp&=4N~HVbk(r^&J%z+K78@`9G9EZ5l zx!_{~UljvuCk;bnHGzO2`_Rg8R7@Q{DP9<<#^-sWnx4dSjFru-a7S)PI_E<7;pe3$ zoS=5H?@(7SuPqgtss?-LLX$RnA$`-XRxOA_Echb-1j#EgUdv@F$z+ ztzc(^Oj3T|RLT(!`y(T8{m@EAcbXS1@MC!F?WP#N7rp=u=6qSYq6iZvQBg^7|H@yJ zBepmu8X-UZMXGN+N3l;N&;s)(sc#v0)MW&i=;agI&2tMH z9NRX#W!K;2ujF+mNWp_Zl$^EHfewOLZT7nf{JJ# z5YRSq&K*au6N!H7iXr>++xp<(;1m_9=)*h#C(KUFo6lQF zjJJYacEX=p*O;5E&H_i@!Gj2NfRs+TH0J!}wM}tOriyY-36n0sbwGxTjKqzbkOXag zs7`ADX?d2k7a1S`d=rjEQMTV-8_w}u@`?)VLCb3i3o=|Rk3O`MTF1Lz;he=PWCr#9 z$%!-EZo367(#{UY${#0=x zarJ#{csK#@D>=xUX@8fvWmdA4%r>$@Xd15+!OoPa#LlnY2^!!0*mXX}9GJ5hZ&aid zZ^q^@;2Ws;XWZKaL>~pK_XL)N%AE`{2(9GFRqW2^VVDPD$2-QEJ7;);G2H#fxR{$8G z3+IT(Pe2bG(WcEgW&i ztVBt-2SOP`^LeXU8tmr6nI+~)Y;d5_HuZ%|hMs1<;=Um9LX%BH^^s7%bE?;y`R(j8 z=fF3NWaWC{nG|r}NAlFkrdXP=-D@ zcWSC-BU;FgmJo7${-~v1V>Eg`1M%ibj5mM{+B1pPJ?@tyJ2~$R5P1N>h!n`*a0il_ z1U7|5LY1YrzWA#BekN7%`5p+Z9$e?JAy^eaMX6?oLPXrA(^XtlKsPHLACsCcWL`R5 zg}`$PI5QDnNlyB5mu5QW3tN0VgGK$FR;w@?``P&LX=fif_QAd6tjI9a=E zMqek_v-vfXs%$$cc*ke?RFpe$bx_8fTNDn1v&YTcTk0Y1WnsnM=+vK9f4y-0&kOV2 zy;F|L;arLP6m0>>C;kQ>sRs+McOhqeep&S1QP%LZdl z_JFhuT>4X3fA0|%OR7aWx0{ZB#)b;mzs4V4&Y;zT3m-aR3zxn(-)EG(o(r<&d3qMJ z!dCC5UsUb%h}qrSLlj@`{rTs`DO73I6CD%t*Fu)e4--JNA)IP-9XhRYX(ye08T~T~ z_b6+IbobhL4yO%pIsW;q+D=L~l&v0lh)yA)Mmp-dul2uHBq)R8djmFVW9i<*l0`1| zEX7*8JWVlSOo}B1#ByC9W4j=EkVkvjLsmt!pwjz-a>+&ex1(uA5s?%aA-gwAB3C9g zb$tbdxwnq50?H8fF@Xb&AQO#PZuts#>E`f0b^sf$6_vGtwaLPBGDkpL(N-xx>Kv$B z!#8=V6$^dfQ8-znl@1gtC$Acw>i?~8{#PhxUb)gE)wUqfneHoFby?~5%C2K+z9Nke zyzw>8S8lhfG`@S2yQ5%ntMaaK5LJKo+o>T;NiheO<$Xuio1S7uzKj z{0SMx=em~-IOW3JTs$u)vlB#ld?@{VQW}YbR38IcBtI9Go%Ap!m{<9X+)MWrVP{tu zkbo(f7!yPvv1CAcZ^O(-J!T&3FJ^k0Hmvfl`suPId{85)^uOM@wVKvn(s>uLiH3Yd zH#7{XrR@qJm_u}yAf$j6YPMZs^#ZhQV_V)Nm_)G>aZgkK9SF7XuCPV zu4PB~+3I|byjBftqilRlHhyY3&pPEvX(7jo3Akg81=Cg?@1>3J5-)p?Y{Y-9CFVS@ zjOOcU(N@`X@Azjh_SvUVh>pP9Jc4mSC+AuR{J!PUwi@I%sg+=X4RKkB(J*<2SZ=c3 zsD4V*)32Q6o1|M@VjwZ}t0Kc_vfc^UY$6(Dr=ZoYI7qNlz40mb>~VE+a;6<60Wdw==6z|BM^|@TV)Z& zXxwWTV;J?v38w2-Q)f@LLs~#EgE>5qRi^o1GrhU9R>sF<@rnlL>5O5ZLD=*PPNY!8= zPiy1Dh97Wcn|?*Y2AcL_-7UpVDvL566{^j;lT#v44GBrvHV7ekX#Z#?8KZ@o_o+Ab zQ{dB&Yn8M(hHGc4x&XgIF=0Aj_lf{_%)f1bh6kIK&ZFH7Pd2~j^=NRGV3E6Av45_` zb2KF>NvG9cWR+Ny47P5xURQa~z5(h`t2J^>W%&wwhpXHN+f*G;xj+0&?NVl$#^1|{swJv5 z6a{zoAMtriOE+PGjv4HW)X0TnQ z(KcqZrX2sEO$G7h^Am)bc*+O(oW0*AaS09p*>}7C%rZ}$_wDhLca84;YR~^JAe^8p z^n0MkSwqU?l4So-F#oOi8b6OU!s^|cl<}6@yHdvNA~zRmd0Og%7!z6?W1j0a^5&5V z-yBA|OIG&X(0Eyjyuw|&^~{A`AjK?F=~?srjnOjE zuM#E}N$y^urr$n_<`U9LOB6b&C-=DTnH1HrGg45-6cU}-u4{8AMfo={nZ^F9ew%+2 zaAm%G7j~6{Q1v-ygP`Y!_v5t{4^X`dk}sj1vxZe=^D)|NNGI5;tf?7|FW)AyU=CEU z^Z17}eJu=MptY!0lDYyixNp8|#oK1jMVOwFma?xONR(G%sly%_OE{>f`Ymo~|A(Rk zp24HhnA4Zw&oPQsSy|w-kuP~vTcuSAW;cvXA9)MiWchc7gHu6Wqs(na%V<`*j;qBT_REr|S$se4ZY|60u-4psG~R6^@>FPMG!Ff+l$4J< z;A&1yO@-J;vBBl}7;(#gV;U@bWigmIHXvi7ADv-vSaK;}<rR0E{NJ@81j9WS&0&XOOaL>>Jg|}di#2K)OfIV9BE(mu z@|%+PPM5PP!oBQLrh@orpeiv*zrCxdN-Ft!QjY}RLU02VmBNv@Bru{oYCsa-H6%1b z)OhNbqrSv-E+Yw6-knLi%3&E5aq|u8A=n$cS>Rny0RJ?AL;mhhD4D#rdZ>Bg)0jGd z%bt=_wIVySkI(8Oqv(Ou%qUA2x9P@Ib<{7NyzLZenk77jg^#nrtDg&>&L)Qlnq(6l zjh-_B%q=bfTJf++oB$ON)s<}27ltt1IpE6xHV#2TF?P584~mO}vKoyJbq%Y&T#oPE#@AOV-fT&?fdyhq9D8I z+pnKhy>C%-S01t-t=+U`X+NY3i6keo7{I}Ew|rZ1eFQIv`)$a`sqOgsf!XHL5Lnr- zl4BxIiFsYWjpYdF024={VTXVxowkbs!s39}7iU1Y+>{U0x@?1SS*}bdp$IMk+-K=ZmKy4I=PP#+)1oft zpPdpU3nt;zlTkza4p3>Z{YLd_+U;awEk>7{4o9ztK0b#}wR#03XR_*nU9~?nq?TjU zRLpV9Ff}D7agV&W)g!ZUA&@P+2T7met`onses+2X#e!Y6isYM>2SfHI!#Y|fTtd+f zM(3ej949!7f|`18P+(>Jf8b;X};VUFj5h&W^X+HOt`1;Te#|4 z`5K0V`IfQx37KlA%S|$kGq(<6u;V8-D>w0grK@#Df2s#QmEAHTnRherYaFr|rUM|2 zAXc7YR{x|XA&;67tF>QEe?0eyQ`fbhd#LYLr7)&IkL`O%r7f5$L7 zdisfOs=crBkxn`0-L)LnT534t;Al)LbFt{?`e7c@5WX;?Dy!T7nTc+hA}VLK;_J6R zMU@dUZ1^r${|wy$#YDi7!`tEdR6Ln=E@mkg-CY1T}UO|jnxOE5aYD%3c#^u+Ij z!5DhY=xEx_>}8t0Z!rv3wBE0kQFMSfiutKcAft(DVUp^hmQ6>f)l0>Fpt9`8b~`Q! zs2jOWM&m8FSi$XPORw8>>}sa1{c$+M-vJzvp6TN(JuwuK0GN6o@Q09EgELE=r&V7`H^p_NGyZecDoMe5+e?0>Zv;%yr%WJr1 zaP@7W#ps{ub?sw6Et8v^>saBUILThOI>2SMRR)e z$yE9l8oO5WJ+oRfYdzKFkz_sZpE1R7y6=tgd7O)S+#T+0{+;6VFvRd_ z*Ui7hqWKl_2O6dsbm*qVvG<%?Wn5iw6YMJaNA*vFEv?qt5xpW8NYuM(wctl>UKw8J zW4P|*epz4;*gW?RWbwRYwY`dLCmoeyGjv=}Kji(MiYYJw<@;G26m^a(2tUcS>0wL@ ziDxJOdDbx#U8Wk=Sl>aMZ_MaFG9%=>)A@a8B#4QLSt#8I6e3ru_2Vu7T05K|R@i9Q z)77>Nu@kL(0&*CMM@$6_!yd;5fVVv2_iU08=IHF^L1!a4-F=29ef#ONlk=;#R=Mkx z#T*$%xMH%ASB2@UQL6rk+gv$jO}Kuz?08-IA#S{-j4wH~G5i6RlzMiAp2h#31cVF? zd)K)kEOFwdGqDy4COEK7a!dd-K4kua)GQm(Z8d6el5Qu|7K7*GQda2!Se-jHOUTdi z$_5M*WS>Mjt6rB<7J^ZHBM#aAEz{{=2y*+l5Z8fGbH)=;ir(BWosoSf|0TQRKlv~D z6e+rJy~Xj0gZj8Npj?qbW7gsU3UJEIurcGFcuR{|p~O2T(}`BVm}Dkcql+4wxz+l} zsY`ycY~hfd(|dqTxD9xG)dEQ*BbnXpzIONW;s%;8%D+1ude7u+qZt01BYnq&_9l#( zUa=e}-$a4#oEpG9>9pvyBa|8aBT|qZK%<;YyC;h9Zew{yYL}4HeLtSMi^FDQfXYy5 zJcr-+o9%DurOfTGjp{1asbzYf(>>QjCEWjMdAaBE{BLP<|?ckjE$KHdH zrRIO7UeF=BBNZ-usf_Ed-1M~qLG=4aLXY0lS{W(j3ByBl6pHs2sZ`(ch&@QM^s?!c z%ce&nugczNIsZAjooVk}_4(;uW-T#r;Ojf+b1C93%cJ8e1MxQR16QhPrBQ;}{hT=> z7xX&RvvgN3h-4{m)MSU(19VH)ok~HEyKF;lmBN3{I)%nGEJcxG$35(?{lH zZvVu2v=Zmv%XxWuLIf}mA=|i!d(pkm>uU(|0sT!GyTyUV5 z?Nsn9Bs0-Xjt~o_5y?v|S*MRoF8=96!f{QVRc4!YC%@ma+C24ds`-TpCLERIz0@{S z`#$-dlS1yToZ_y&88rbn%!E&$*W?#odjAVC4Q?xq_ z*pt2g@7z-0I{^h;eZZ@K8^8iA1~Lr2&*8ktgnH^!4cd01&DUcYVBSGPx_)@#6JoR~Pe7TWc8u$3gt67c{vdo(IQ3~o2-kTm8Z}h*9ie|PI_u(+vd&Rz0ejQ zzb<4zXxd_<3D13o3h|q*RjRf4@05gzf&wjP@(>vSX4JpHaV;DEIvJdHhg5SzLe?U# z7#J8k00*?a7H797yN-E%*)WxZAC{&)?B~G1^6`g1b@(nN9c-h=i{yrAG!M=E4&Sc2 zS&mMCkN@7yLy{o#@eG9JS->4ft<7b-ib{a!G7cEWADui@sVbhxnL*n6fH{K|21*bk zV1o>hw|(r5A%x5@u-F-646Ag%O@a60YS3y7XNp;Bv5pHi>n{AN`=Yr@@4vY{J07E6PXSxVv%&7_-~|EJ9E^h=9GwfZt-7z+v6_$J-BH1z61gg_M25vc-331^ zk{27drW;7Hlg1NbP&kh5>)*gU<%;Ja2Ql^hnTZK)Ggw7B{_?MHtcAZPf^MJ8c8+@F zC7obbS$PI%W$ zgqpSkH!8kr_ZpgPc?Ys7Srr(PuKq^^Xv^V$tyT5IYlG_7AW_`1Vu-jiV2 z83jvkYGGk>LKiS6fr=d~yu`V{1Oq6)ozCL(60K%S+8}X6d?ksuiLJRtfX2nRxwNq| zQ7zMIGBw!8!oByuTZ9MZdYMOHB#b#VDQTaLq6F4C`qXo^)ycj-nynXiTTlK6QX*t2 zb%VPlo!KaO)v*s{LR`PJu%Mt3;se-tsQhuOyOp4Jm%nbW(!*PVKd-acDwq<{xVgX{ zk-ERd{XELHIf{i51C4hDa8yXo#k=X4K(je_Z_|14vJ6W2SAp5eHo%vS7(NdQC#XSs zbAX*~a@6(C>@P?t{EZ^8;X9fswG~yaVC=^T);G?P0H|Wc#CA1-vC_mN8H_YLGh=c0H%;mu(|OF<$rsT<*9SwhIhxijL0F8M`*p?M@~Y$Fva%&*PD0I|sI0syCC*Z(Ipyfsx`A8vP@e3*9vYyznS14HtC(BD)Fk$ejQl zk#JsZ?bOMqJ>oLiSd{{=rSL0#Hk!o3RWuv6f!UB-f_l3dUS;P_R;FgZRzmiFm&3RB z&_h9FgXVInP3e0}7SY74Lm(Eiq7XLIbPMG?6bw7#G+!Q0=wN_2SKTD$(jS3-322ND zM3srNu3F|D(e7O*OYINP?nJox>i@_Tl!bkfpaV=zN?FK~r7K!5!J8nHPqH;4Yg5#;!53x~t^lkEl-Gbw`)bo3>H^5KR#hXk#B_GIy{^ z!`QX|Cn>U_eSZ8aV)j{cFB){XWm!QBgDkzj*5JB+H^1E4HDP%OFuI-qAtk1khO|sJ zfoZ@8CD?ri&Veqpi9HPr6y}}KFJdMDy){M521IZhunITODimziIfmEiRp+HeU~D(w z+5$8Is)@FV8p(|E)Mf4pEtb>dnx*D(f3N2`7-*?lY99BA%)<`Yp>zn`P8zBk#FzV> zG`j8(+k}oI*#u0Hhv^0$zo#QZeAPW>PNpZR(969^wzR3n03-fXfbh?lV_h-wT8}gR zHNx@X{F9ooVQfa~GYLeu5g#OLTb7H%wIjHi01;DcX`}O!m$q{nPD= zgPSNI51{poja+f`22)(T!RLwOf{6)FvzP~xK(Et0adP6!znvr#0xmgJ(|?E|0O6xa zoVC+`u!)lsFcV_KZ#DsDS1-)dI@6(wP;XILUP!Ar9|t5IfQ*dnI_pia zPAgg&gPXZ2DaCoEKw(D0WZJkeQRlK;DVk$E4!r-|B+&I5=NDg*qka&6GEXOq{3DM) z$wG3)?UTkO)<#xhQbqqgQ=dzam6mQOxB*~;q+MKm%KA#SIL0>a&1N^Bj{xUktoHTm z*XuMdn}n$E-Oo=uL&_T7I;SmCt?T~O)2r{r@Cb>HfdCj#)@-`i=?PI9+x8f?Z4!g+cO$Nj($R+a}M}>zAy{-B5ZMD za5uz2(vpf3gv`Gcb4fK)kk910IV+Q$b zE=2*J;6taYThoS%=kpAjk%6}SowM1O%w2_ElneLnZ(~l#KR8XOr#VN@selRT92ZQy zzNjzC?;B2c#=eSU_Upmx#Wa1wfg&H52^U2^NOn)lY&V5)4a+1J#HIhvAl1Q&l_Z(z zLh$FHT#Nmh>*`!tjl%Hl$x74g_|v?*6*tBCL1`{x=p6G_e3lz&??WdrAwRg~T;nXd z|3RlW+nMwmx!$e=2^!OIM3);9ioA2f&QtwBc=?l*VqJ=BG&*&q(!=|VbQ<`uR$IxY z*KEGLvgqCI*Cz724TUu#18mfMCo!-;;enxlYA0AO5Luhv!dmiujSX;3K%coL6GHm3 z<^{fNIL7Iv~iFiyTc(SQeyB=*AmA`s1BMb+p30SyC1rY{_IA%nW) z&bN6GY#C@NJV1iJKonxT)S36vb{~)V&HzwllFdm7_Abh8Cy>h`n6B`6Qb>G2?L7l6 zD8QWR>i=qlI4qVnm7{NZPnrDwu&?hi!KRmLwo11i-D5^gMztQQW)%kI6T37o^Rbf* zpJ4Pp?JtzOtI_kA{IY**QDNWxVBX>z0@c7_GY{W|mdWV=zStgkx|(v|fR+ff`5FQz znl7AvCRG=hU>WFhHO_TIvIo=#c7aAktDgjO;M#<}B29o4^SK|?f-iISd(^O5R_)f# z@YRKhYEP^vEY#m-Ty!w^)t4^Fth&MW6X7RaD90Ns&JC6AH?|rZE5dlR6fMaNcM@Ng z<~2gT2V;^@M+=lrUx|JZSa3q=L^n1NTJ*KcU1My8$Ai2pO1W$5K~@AU@kGn#3hHs% zEf?jYx;{}{>@VgUyJpPIH^rJjLbAdS6h)^9ZpFZCExqX>O)9t)M zTO5g~acO&qp+?{w9Um0-Yuq@ca7N$6?=l_6@K9fPWA<;zcjV98Hd**&a=JK2}tnm9<)KLa@Axpak z4d>Dx-1(R)jf%{0LK z?K-svO2fY`qn4Id`-1g$Nerv+qGQdRu{Hij8wi2|wez+m*Dz4- z5}m!SIP`Rl%@QfeXlK4&1-!)Nwe(%x10;!IfF5HCSex*Ek5Q?e zr@(KuF^Mz%*Z9$jLbc)itQ$I1)naxs>$%aLID0+nd8t@NY}&v0$YRgRLnPp?2A97y zQk@eK_VQ=OOm^+Zm%kpWXU|g(ZsR(5ju`SDg1izkfhLe{KI&|BvD&5~F~f(iyS#mG zEba4xmf_s7N?5g3GM*MY0OaiskD zUk<7;3I$ez?cab3_$cpfH+zTsf%Q$@jgOwFvBQ4;REyCL!W~X>6eP$^jj70@>rGN_B+A?0MFXnH1 zoGVkU;HL{#pn;n_2h?te=v~PBTi?nh4?O3UdlxVcY4aQDLIQ>NGUG;q<1Q(iy?Fc> z7#LEvW@&rs50xXWh$$}YFp4*7>sZ}PYrH{iKkM&bd`#VpmZZ5_#3)Syrdf_9jf)^@k@eKHxti?l(^!DHQ?+4pv>WKT=uLN~zP+#dpi@fUj zyA4zN(@d7?#Pcv<_YXqY*_2}2B;$x5+eUy=^22~*#9y~*4jGT|yNr8k89vcivY+uL zC1s`u>{;adc?lO@@6zM&SVZ_#7jx9&77RSP8m)hgJh<=?k;6Oc{T1T)#aLl1Ez*3$ zJix(HY=lmBrT!~}&-r;2QQ3EBh{F4uZ$cpch-!-e{t!I>u3+pugyfH~O(p3Z06on+ z$3-!9YhqdxKQ>4E-mf#EPhalUfz#O4&0}R$i4S1hWa?OtEL_YCU6+=|KZ28l_(|#B z(}wA_YT{q$7JKE}ddC;tB z-($9HNjuZ=DL01{xgb}T)05m^Wa(QpC|*j;hDam-0ywGHX?U6V3lmdcmI0Tfe1I_@ z76+;X063IjVVCMpl+&J=58I2~ejTw}VsFIn;+oDS0*@@Ciyntq4y`@u;JJSdBl~=F3nLoSrXd|s8&0hLR zUkb4X4I!(JPn6enKd%Si+F_mKz(js;zd5>o6KA3iWY#T@DsK#yRsiMqy0V!78sR8;|z0=Kew7~(J8RWlNZ@3UG<%#!Ni8;mmyu}#UE1$swa$x-nh#gfrNjLq+ zvP??Uaks1389K&|ri4`0p_P}|Wls6}xNj5{E>f1WvJw-m(gQcHjVFIZ7b%URp=P*@ zRXiDqxgCWa={FnO0lPUH%5YMAs4lCKyT zPPdGnOyj#Vpn1rdP}$Ty~rV^#rDq#vV3F&vi*L!B;qK%z&*1Jd3)bM-VI@6NUVFGN;CJJhqy=VWNoz7O_&pVa`mHxO)T+c7) z_zUxOQM6P@oc3s6h&9zR-^-jUPrG+s*7A4hHWW@YpuK!jtsOFhoM-J)N6Q#KUjDcx0^g+) z)jQA8d+ns`e2uAfF^YmZ*!O&4MWY#NKrJJDtFT{IQGrf+KgKpW=f?W#MBI?czOc|Q?_>e>7y0~e|5o$xxgWBCZO8d+&Ro+59WuWIc^ zz5te3AR!?GpQ0!Q>;rqBhV@uc5@=_yEuuV0=#W+nO{ig%q;x|Z;$Dykr27%*#5l^R z?A8Hq*0rEY-_E!7c0s-0i3j0eC(K5BA9_;V!oPx?{?L1EuCJIGW~$a3Su}qbUH>>b zgJ^c&4-I~P6un$XvI84v7PxEEuY9gaJnqFi_;rQxkvr(NM$-UzF@c^8y7pqdc(K4Y@vra_l41Q1aQbI-J>k=0(wVU zSk4R->BIpxB2>P0YY;KDVpgN>4=Ainma{p}vVEsY0C_i}+CqKaelKdX-4oT7U zN`pmQC14y_YDazrSCnh%a2c;{g9l$EaN(nsUbBOtSCcHcCm&B0LnvJl`^MBUN>?k< zNp2^Tf61$)8y+?4>w|tevvK3~!yWNbtwzE;TQQj1s!>;E zzH~w4{b>C-7k&`OHSv_1nbh}fsuTk$(HxrS2;eDh)g0YgISbeWk6As|@`!YT9 z`*(387y2)UuA3D8hn>X7HwcqbwJ zcv)HAv?3qgt6_$qBZcoZ&MuXPg$IADV9r{8GIQr5H_pYrpft+EAzn^E9i&*1HMw3% zYT%Wm-cj!+saOeei>s>DYU^G8g$ep<+qSin?7osvJ$6Hp>|N;*Z5r5jfIO`BM6}r1 zdwMJfPbSQ)2PA{~>yxEN#m3HGHtE>6i9?*s1(i4f3AR?fY2>69udf~Mq-9PAz=Aew z5;|CEsrdsChJNWnBalFc|22S7rBX~7VU1SnQ0)j(gr91N>Kp4kdq8(4GZjVoB@!m) z0!_+XBXDP(6Be9j4qq<3FuSTVm>L-M8SeNu zM~29>qRf7WN3w3@J5--jPD6@P`9^K6F{PrLNEVK0Xr$4ur=9}2AI-6G zT)FSC{mm^zV4=(371&V?yeXqld6y%N_3;RxvrK6Z!{Li%R7y1scPeZ334|cftxV8U zTR!LboF@G3x-4#cSwv#Dh7+lDs3V}uat4PiK3D4IkJdHVc>7G3Y5a0gnv{nhUJJ#F ze3xHQ-7W)35xW!9;NxW)3B$OtVrWGB{-)M@+}fkizWsnIn`8zMkfDv;eSd>$Nko`q zHAK6EJ?>0Xv>QAndWs*9aiRQu6zPnY#Ho{g$jhoeD8748`<`6Mms!#$ndgneok8V` zA(!AP?L>pzlfh3M746E~S(hw89V$ueY1OdKzHNXiD-%9wX09kz8J_$NG5r0Eu2B_O zKS?LZl;|_UW#>tuZDHDby<|AQ2El~_@}YpA+my7@;l64wEjrx}eAvjaH15H3(LvYQ z`>`ucHl;p;_FXsg@rd)mVweUh2Ji6u5{D&4sSVV64W1Ii8_p=k03@>fFID(96o+cCtLeZp&W~XPJewZY@ zB3gfjQ*GDb`g!vxtt4rQ5BQSOVP1dvPnK^&cO*9F>7wc|`?_R_a??-O{R65pb%(Of zB?`z$XO?ZUS6_0b$xj1faC4j>m5hBA!w6`EZam&&9&6}N9kGCeFY(AW18lo!TW)O#Z4F?g$%-8IFXxx6%*0} zFifuIX!mTqNa-b%KqmQpWPj|gPFidC#HnXFXe&}K%06|ysEy=YmpsA<^(j;rbSW|u zDy%;=S6s$=Gg6lP+|P(g3Zsh|QCr3uiu)tg)gAV1nn)TAS>KA-m&E@*KNd7-LL@UF z5TTF}MB~A}K9QR9kzcsK{Xzv3fkevBnC3S&r=79@5+Os_p% zSI}{Vypzwky#q!^riHb5Sn(J|7$5stFETU(&Ej=Ot3#t4mHi5*B0%<^6hHatLfTK= zP#AyQgp9@<5^~}4m03~16o|n_Efz@qP!VQK!5ob&j#rPsA|D)b;&f|?pq$vtc1&ptHg+M?=btUu zD4PLCTf{m!&q(XT`vKSebn9~Z7Ttp458p5>Db{a`<^?qVycHYd>Tpj|$&fZz=^v52 z6pK(gA1b{eDux$)xT93ds$}FbVJYwvd3hI<-WK#-#jY~D^n$5Z@vV%+*XJepJvus$ zHIng|G1pojU_K^hQ(S!6}()!4x-i z=V$g)G{e>}`@HTODfbAJ2MBBT;%8XC9L+yM*IDYR*r^?&A>9 zlfYTB&6T2*EcGp&^L;qc<%r_@QJWfH*`v$$bU5_qL)lkGrm&i%4k4e0&fDNq#VXPo zw_BNcYKOURR~^NWwpWT6xW?EFDu;1DGX#maMRRUm3z+>&f@U$(nfn1Qz`(rg8f+B; zO$ZZ=U?~*<0H~Q^3lY4fQGxI9U{TTJesn>t`Q*y~xY)}0+RT=yygB}}>+KyDmg}eI z!#D%%vokMhO2u2#ITpQUXhlJl`IpPoo@fpHA4Q4*kF%YnKO?y@&t?`#&Ol?bWpVf6 zi=AFBoeF>>3tADqN!vN>6w-W)hrbT9#HVY#7`}-nlMk-XvG4q9&o`xUUn>ui$lfSZ zX~r2oeLuriruw5pcKKeyG7J6;Z$Pft<99#{^_ems*#p>JWCpIL)_hGp8kvH0o&WhQXq^QxJ5{#>;l zC3I>Z1?v>;je#}zhi8XWZW05M)I!4vSimB|Jyn-cyt%X=Q+wgHLtGn`w|CusS?K7q zmYV@(;S|mgPwd6(dzOb~n;HaI_<>f1f*=kUlnC~|0;l#h+gua3iK9bLx^TFVzamFX zrFdkhuAc=?B-No7DrkW`s!Z2q&8Uhz#M-McXsC==_8Ei6M5Te@$AsBFyyIPCj9(4W zi0@0h--wJ-IAvXu6JSe-GG#u#&n1&c`SZI>^|zr`$}rh^#Z--4>at~L5RAcx8RXyU z%cfYGqJ{~=Eo-e0q%%=bS8bhpnbF?abbbIv{Y;okTAaAx(IwR+a{-rc+Ru3hz2)mPAX)`v#Dtu_e(!anZYQPTpk z1%Ler_t6-#PjoI4oc=kwj`aa7_b+-Zq$qM8D*Tx$2L-r5$k+uWa9jdU_~-4(So^&iwz1t3|1&7ZQjlX+gwRL@^ArtO$P zmh~rJ^pG4w_~+n4_c^N;kS%fv#h+rQ574}E1y+LwLaZ?1ZM*N^V+C6cn%H|RA|0Y+ z|22!cxl(;Kv%}px7dE($yE`Lt6H!4su%(DDwt(Gq6V1u%vy3=p`26RO_uXs%c}_x%cB z@v`qn#P*OVJ#jiHOes1tu3MvxuSJOefGW21LP4Vo8vlGq3M@0u) z7Bqkzg_9PA9qami4dfyiN~8{ z{th@p`EBLmjQJ0t3+AWt1PHG9P~IMgeh&7)2JGoi=Jc_G|6ZVp#97tf)%EhE!LN<_AY|pR4EW0*cz--kR$%kcSW^wQmbFW?l5$d%qsD- zgUa;vTuMhYhP^$UiB<%T@H-4hUArgxRn?Ux5Xn$rY#U^SRFErA&w7=n(g2GAwnX5H zkAYv}-RNhO&>M0#ZQJ>-J9nk+tcGVc^Og^VM63Xb$C{)xth=!~Y`U8O z;*W0C5qEJPyG9EO>mds#O9OKe{wj=aH5tHu$(^55TaQ2HSu&2Gd{rv4ogM{)2$Ji% zp4Z*Arv!;*DUAry!reg^I=N3->`Z&R9^1M|JkqYN-!QrF4`FbVdbHe_E~NXeeBNn| zL`)IB+r(6$H+6XHN!d?=$n{{!`wh;TKF`^zxqFiWpO9ZyZa4Y-k1o;PC#n9OU#qO? z?LXW<%UFl3At$Ok<%@6Q>n*bD>*&}}6Q?bXGa7{FsiHgV6JeDC6!9tog_H*QI;t4P zF#}N%)f$OzSj6kXk!Rn5FU%leayAwQ~CUnFo!&6=y8{0s@pdCm0nUPJ@PARaI37I-i3b#4uBbw6<8R zo+7*Pf*?$mN|el!TyJs34r>R=an0Oo-eCM3ksIZhNVJGM6N}!A6LXL;8>x8{aqi5n z7#egHize95WrTh-_eDWAB1@;zbAU@=5=-53ITgM1>|agy+3lJ~ zrmVApkkfCOHXuwz&$bA12)4Zk;nQ2JOsR04G^5}5!?wZ@imnG!VgW9`E6n!CM(6j- z-&_1)Ex9ursiEY%fLb#4x$qgC-FbApDK#oDbMWE-`_p+ad=YGfKIl~os8O3@ool;S zI6_3|Q=ru=iNzzScXvT6h;PsY{ThIT%J`NNkFsVJQ`qx4Qq$GFdmOeev|)R()kOhr zFA+v!8sJHz(qP+u?d|O7aYGMUiZqX&Zf3$om}y~?EaP)^4?TLHH&rvzQ!GI2`15=o zYl(g34K*kR<@2y@)b_wderE*4TEL2`@bt{^>e1zQvSte=-A*w!daz*48NCwW}5pyUMhjApX_Ac@yUt>zTI zN}Ejm5tEq`X$rP>H3|3u+ioE9BZLGtWcv$~Cx&hQZ+E*{oFF7>drpfnHWO&1Z8W=M zSyW`{osVpj=>R-ug_0+lDH7OnESOTP5&bTeDyh;+3X$}sQF?-_3ftb^{@nw5);tKw z`D0pPXXhF+ukBGYfDgR!y`)8#;P*qkz6gFIy2m zBl>Y;Idbk+(Z8@_*@+DR7FdnK+rVh7X^}wW6`&HkwRd#A70dHRVUa3=fpdMdB2350 z$$gyQ0IpR^H)B>=8N(wx^FTB4hA}EP<|6r24=hI$rRLEWR;+0(kbCzp0=Rx|0|bh8 zsJQC{a|OF#GWFx$_u{U^U7GFMwWbO*BeFdLH)#?) zi{Msj4-O(KP=xKXS(Ig)N5SPvjdAc2;lwyVete?&K~M_e7I4U87Yz0VYn$}^cm?9}kq-b{&75PxRAwaW?$WeZ7kSb&8B4im2Zv3?I^6 zyUca8A)}DgLG$OJna-^-ei=; zkcRheTY*ZQVr?$B`i%e{t5!|71kL2n;-%Tr<)*G@`<{TOVPcksPgQ3aF^(-Ukfg7m3B0Vn|Z&TJ+5=|sxXs8Pm5qy zFLMmDM!hGvR5)91nrvVjWZzPsH`8BPdE&4JH4^C+Ttt$Z@?+#~cRFo1!LgqZEO=t| zS^hED5to1W%rJ4Pb*@_nN8CljA9Hbj?&O<~HR=GdEB^3(8$4kJH|^b?7VIh2jerre z85mm9I`x|Jddd1qo&C77H&OCZ+Q>$J7r&I()U@z-0$J6kENGqUX$Sn*hxH=;al)sl zW3c@?7_oM5GZXi0=Vg)qi=a|eQypEap`(Muq+r5YXD`iyJNZ= zyc2B5Bpx5r0&z2tkOi|T;f>tnq6NY3QDAD`P@&DbZ|L?8-wRUCBB(Xz=8~TJ4BbcB zlFeMxl~Xz;%Eh422g5T3*L=%aNA)bZSni5{BXD74<}><)%v?^0^IG!Fya7A_*+Rjo zPNd9Rd2`TJ)pp94rp`55!0+OpB0HOF7+$cKBP9A-;xT6|+D~auremg@9JCJSz;^y- z3o%#<4Ad5py&p(CaPf1hZ&0<3J_+D~+s2nr7{Gxx%Wm+a`z{j4t=(s0vI>%mTx^7l zy&*Dnn*7S6BRPiwM?7uH7<*zFtvx{jyLgG^w$|oRZOs)%Zg8<119`d{T(PVh zwmWEO{@_F@3l$(b)JfeTR4Iy(7X};g! zEW!=1)hSqR6?@toFhj~PY^Z4BVdSUaVd1wQ)FN1bVTz-228}98^^D`e=cz}`x~~Dn ztoI1|Qr*$aoJh(y?wnRUX~JQ7>=+{}F_28h`s$cU#bNeHu`O2yvF=0YFc;jkc#QH* zs9QYeX#?fo?uwjMT?hq;w|bL2+_Emn3hQ#hrtX%GgcAc_VDEoz8a8A0 zDdgu8=2a9z6}ctej4mQzc^&QVvvpb{V+tq)l002~{%9i1T{`yztz0y3F)Y3x1|m2Q zV;h%=dEwpo#srFL&p&VtcXcsfDqt)CxyvqL!D`po-+m<)BZ3cML6oRH?|2qi^UV8p z-Zn>TbfzuS5f)x}RNb}a$`_UyM-VM)o5gu1b+5ichk_%g@%Ehw5&%rbxWFJTt(psNmO z0Tx-8vFc(3A9LZ(KbS~~G#_HV_0(^!ruJW$M+r#fMEeaU)d%;>!9>2lP3z?}1s z+G`=l^KH>`HT}2f_si8{>C)Hx)qjRqGbfOo;3>+xV}te)Kx5OfYy`QSKf=SyfLL9U zS1ttn%NQEIn1v#4!Ra)6h<|3c;xF7bd4}MS4t%fC_U~bm@SQ5iRuXOR7-YT%mvFMw@$&NGGLM{1B*56gHs|)7 z2Cl<_)R*>MiwYh1{|3|E)g7g?RNvUE0 z^#jucri;r9K=98)Zi8&+E2T6v%w#o&I4T2BmkpyuyywIz<8)gvQxkf$r)m+Ro&|%b z)G#O*L%3QA!)qEH$tL_T^%4;#-~+cGZsd7L92E|xk4b0qI2M=b$XJiD&$jqV(+>nO z`N>Ta`+{fc>Lpt-{|;@iS0)#0k>8J?Uc}T|6^-{^!)FSM3T#)>z|qv$c1I3|UnuY| z3uGz0b8>!1v2xGHEfLI4X<~%tAlJ-B-NMHqxR|8JEe_Ke{t;f|Eix0>Y`K+u9#c!i zPVU39cHk=aV5u}r)|K#mqA}u zVbes#d5l6^D>+6oZeV`~JTJ%uA8x(bt9+oh|K!c}KG--@kaRSbW3}{A7R_P^y`jAO zBiuZjXO{7EusXa~CPXqK6hVga8JP_y({C^OqmnAz`OocSCWMbJZrgzBUHx-7=gFH_($El>4I?X)if z{I`9IQa9@)RW(skTOn=8x9w*8z? zAS-AT2zFDP^8(ydzNX0)xP8wF-L%+7wB89KE}XOhW6V~_A+Ryx(~Q&z*8o?VBO(`` zm@6nR9Rp|b`BtsDS!4ds31q;3jVid-$YKw7T#dKb0#05;ODD?%ZC|f+wOI-7U`qQy z(%^%R8spDkM)RW=Ff5UuZhjLXFVi<(x;m44A?;V!sWBo_A{<^>i71KKgVwBoFRZRl zgKHIf7c@On?2s4ddFLA^zn3jZk$3pWds$iJiQ4eJ#HF~?VLP$>r)(X0TZ;H-DDff| zSUKQ{=&PZ88Fskys(eCr^r0OL_LxoG^@r~QkH_^1+1ZW=AMGgYy!^k@JGLrcT58Jt zeo~@Oh!{urkh=nttb2*Hc zuLCuNlX`q;$F34&MQIhvDKPm@hNS^AjIVx@HIC&MGG!ksB9~lP$m%K|WtDZ2A|7AhLT&)&t;9zY9XVNX?G!9tuRd0&SJ2d)N={ z)KnvOAjF5cUhMB|c-OV=)~DqOhe07FdrnOae@mG%e#*eLD(vM-+}ryzVITNasp{PP z9g2Ub5S(pK&{e6Ru9ZWr&oF=Js@G5VBf`!`-5heR{<6{}hD!Hdmwb^$97J_n_9ezM z%6yfO%f?OvMgGWCx4^pEjfsC5z?B)}e1(iWm{up}M&}7h`NJ{~RDTSlcU3|3QubN& zN#n`l+|wkXx_pI)opp;_R7=HvmbyXA_MpEOc1 ze2RDif0vHUNPXe9t!|Q+jQhcj*sWa>{#R1ttRLY!KMkx^&fDB{KVmtDIZ45rn$71p zDFr7EF#sX}CkLD*k-CD1lEDHCHvJm_-c)}5tE@=wB2j!#X32hCVTkUqknV%W%g;%I6k(`-?K7_7i7`*>|t1f@lcrxSm z#Zm!tkfug_((xl)tJM;yR5yWjbIn5gaYx_1+G6Eu3=w}SI03WzmJAl9)ZFyB(LI9v zFbMq>B{4w9KHo<3V7_%Q3V3;lWraA9LzR@;y1U;a z?0g2tL*{KtM)wS*`daT@3iLIEr%J~1>Kv6-CKD%Sm6v(MMD|9@m~9KNdS4n{thJhE z7A3s1?BX}Zj|$G6>anL@?tj*ua!!bgjXxjJ`kr~u2Gkx!5R_`1@sYEstxg2~ExcQo zfEN`w7L)0VeY1(qPcRz`HGDfr%XW=iaIf!mn8`-&yo)W;+7}0Knp>1qRed*n+9H%v z09cL%=42v5UBOs0`)N4htzJy|@XU3Wn`8{B0&4ClQ244_`Gr~Cg3TRK>*oi=gTt@X zb>iPTpw0GET5q`q82P`5_*}^qJRnv#2rk*o)VI7zz5kf1jt1*fq4d~`vaFk>%NWa9H_Cn?Je-gD)m@= zxZ0EdJ!4{hVoPxy{C5w*Y;Ek_fmkC7vl5e@A5qHBK~z2}AA;shW|>_>l2=$F7!~HeL_V4TpCoI}*EP5vp1b8D@JX z0P<~{|5V@2-}3S+o`CyD`z}&SPDWPweEFnz}<6vmEUHeXu?6QthHWny-{NuTq>^exwjyoBwiG?pFf!fhsn} z0lDBJRS&KtNKu1jHmdNhAUjI>vP#vhN+W7I)1i!>ZxbsR${zXo;Ww^e0UUJrE)NB$_Z>;g;oSRGtx5DjXIDBcBOebkr+{8$PXwBFql6#^fwST z2HP0z!~6Ga(}!Q85+r?oI~et+D(4r++V^E`)J@LIjFweOv3dM-E01fDP^Z}Xtu!1e z5{vJdNY7NZ?2@7(cRIlw`)q2I(m+}^HT3lhG%L`*S%qP@RO<_sUDMf`!Cfk@VA#eL z9Z-iIg6EY!<{YAKCE3qhx2mhES`D%FBF5f?s1;m+FsZxhy`!7#~_^|@y&zzmCPN0fpcoKgr4+9EW+=s430o=f~@5W|)!(9+)f8VMclrl7`r zab$-aidsKNU)vl7OP_8fR+3uiqL?g=eV!d0gUfb3ZQ6992_4tO-TNIYV;XHZU58Ka zGn*Nc{lo*gAC^~ulDhhazE!SpA_svucz;YA7+-Atk`gfGqgkd^07*K45o7|W)4O1C zWN_imB?$3LQ)gk{=ugs!+1pdp_~lS?V2IiaE(|N>5~6B5+m!J<#6=GG9+)t}K>~%6 zQ|a=bS}(G0rK&%^6h}oCxCBN@n-+sds&iUO8MvOK80Qe!{SFoeqP7XM@9O{R zBG0qG*}JpdB6$_K=I!cG=N2S8dT-ur2%<3f;Ijp}Nevnsfr!PdWf@$^-S<*k^N%nj_ ziml2&!McTg?C>*X4sTH^j%HMn3tmRIM1{fBk#3A<5Yha+S?I7Cq!L@+($e4b>A(*xjag5! z=xETrtSa5k{|nCWhADq2u({}mo4HHA75gJa_e4Tg+D!4m;9KEY%6#gl@K~V(6)6Ju zB0V$-G4X_s8jViAV#D;T#VQP(Or$8$O3UrzY8g?5G-l{s0H#2oNgpnUnG`+V{j0OESM3pc4j7qaWb`8b1t51~Z^M*V7GN%TR_Q zci#l!=@O}6xdrDm_IY$(k@YxDOA?|OeAZRP5H)6o-#c=O?l5LO#xv$E0KzqRS=>t1 zxXx&7J`<^^qQHI>z3?+DlG{`=eNHuE$2W#8ZSA4gz3mOl3DUAh&(g+s#=I_Z(^tQ^ zub7(s;3RPSP-{LOuIaMjy7~s^%x%brvGDp&YyWK?1KoX!tZH_gYPM-kvEEgmW=t^+>s1 zxqIW6BRRu3d0A&~e%+9e`4oI15k8G9>>d4`#c_h&O2Cn*!5?loh#P)Y1aG*=0}IKY zsNI{)reDh(%E*gh>T1{dQ>C6>DZxvIDf@`~AW|dD5Y?$d$8&`EdP2#Ov#$$tVj9Gds>FH@U3iFhz&VBFKwT^62 zko>ebC}aUTP-_uytX_l+lJ7G{mEk9Ed>M%#~=Z1?yL1@N<^;jRXv z`Z0&I;3s(FX`YLw-!XApbuVFGiAz7;$lO9qP)ab{Ec~Km4a2{N+tFnjdfbF|4cVxf zUw+k;J;BhwKuSB!On>K8tUtl}`*z_FUPg;A+csf0Q>oKdz|8*H@Q2xSv5l15fXRYO z+_X?mSC=qLIuat{AD*uJnER8had}ECw)-C!@k4K;SN?obgrj9kLwKJ@pZKmx67GZC z29O1lvjUV@w-CYizb-fYl|RR=6LT+97_PYj*63phVi%a-o9P}&)cnx2+W^uPHFFGZ z&_IxKTbU*v1*^7jy~DBB`f`NJQTp3t=HXh1{;=HS2rQLSxk~K`$JHn=V!Bgpbt}^;a4cr)q034duoT9O%L5hYm$$X{k6b)jmb3>e7hiX z(@;~?`Sq9zE8^WA2f=~?5)krmy-`nMc76MdGmTEj{SfU4nqOgt_ixd*5Pz}V?)r`C zW%fH;6I|S-2#S{#4|P0n`y^C7ci5?MBouQ_L7sGDSt(1SY10WPVBhr53$`+Om47HS z%Bxh8I!{v=GHA*TH63BCu)1e0Ej@f9{~at&4Z>6|LM!dyd42Zt7#NSMT`chZ z@bGX1nl1PU;0Jjc3;F48qSjSr5F};%PCm6j zJhtRJ&oU8N5%0WkL+^m$G*mz!RaqTLgfq zMU=oE@xgP(Ylzl~0S=34-wZAYih2YjFaO~3(IV7Fclr8Nu>mSo0VLSeeYWFfI#K>8 zQxC;efalPM@{8A7`}&0@{pvTw3vyh?Zt3@l@I3aZyEV76IwUzucW- z{S(u~`u%pvCbtpHA9AQypsaOnI z&)^Cb6a1 zX{_)6H8m3hG}W5PKA(c1mH|^o+YTgdOMRkJi$SIx7@GCj3MuLZ<0CT^P}Ju^1ueGO zwVD+6d(L_eay5fWbv`fohK-PqnG6O^qbM!O6+qTbVIa)M0l(0CT&;UEi5;ms% zp9a7Si)@8#jeOV-+mf*Qrur5;b8KXU!&HIZFH7hfpFOpMRU#LL&zrS(TF&UT=+n`Y zsZuZIOz=@}Cs2<0OfiN}KYUGaa^eIAl$MPJm?_TF=V*YAh9e^vzjad=*eb@wgdZ^!9D$cSzt_#%I@s9wit+DGsg6bfL^hG^MIC(8@TP- z7AF<}15L*ckVH_mTYQUUE$K?~qIV0^#Vw>v%YchX@?yso&>zs?d20Q*6uY|KQjFVV zExf(ijcUvoUHRTUj@`avzrI5^LhdxJULnrTL>DAeR0OJwDLa zXY8$8pkrcr7!xRS9lL!fcM+8V1d0id;J8K9v1{D9>J5zxd31z-+`D!&CHjQ>5!|2X2)5#H?wGk#-iW!3_>V^E^Wl87bWW z$?FyAllW|h6i4XQA9p%aeSXC84}}DdQW2mb{0X=t;LNE986kU#_PQ;+pXZxItW-D+ zj_Rh)Wr`sr-8Q(c(LE|esSILhbHpQO^dM`PaTT2lZ(n!4#=DOrec zsm2+1-G9B;^sj?>UWC+4ny(XdaUp;U$rTwryzuoWi}lmqJL2d!zJTaVY2}xv;~71& z+w70Gd(J*8^dX|?sN2s{SNlkR>)Pn|c2uIkd0fs~c=x#eFe+{+kMYZXHh5&}NN{F) zhV}$Wy5QSskdF3kX*ch2TzSHO!zy|lO1?k`7-Yh5v)>YgeIQ@PZwHSIK26f) zxH;@6@1_P!BtsQ{um%K3skd`{Evig}^4JyVRgx}w!IpM9=G%34=~N_9tN^B!0AVx* zJUt*MfP;kz7Rt-Qj_J>Wb6ol%MH~#Wcpwk}6sIpxBVcGsME< z*xozu)k*v0u*}tmahZ!r9onD%26QcpgBRpW4f7}WH?qHcU9ognn0kfMm7GH$ca^Te4ze2QP6QYM(^XDd_lGN{XTeR>Pmc5I z>lr>H$y~#-u4dJdDk74Ig(JxM%SoNLZVJl=@_xc+PbSYWJypGbADL&|WR7_9C3e>=2>Cjkr&p*UDO5M= zacBIL*!a^1s}@Ho8uFhFB?P?8>vpr`E-*cYzlVQ0C$~$+a9iE&$>}$wB{}O-KlQUp z(H3@E;L}dZkHI@zW_ke-ZUUN#XSF8d&_6%up43X=+B+P||F9U+L{-g%2b!DDJh`@E zPf+1cb@Dci29NH^oiBM?uas}SSQWd5mJLM5|4_Wd{3jmFU|>6)MgtOu#3=)#>6`@K z>uOXcRr0sdCVZx5e5ueu?J!0;bxeNv=po;iMfG)GP}wp5!CsLe2OAS(cpiznB;ZEI zIM59%yx<}`Jvx+XVgyC#N8CwVW%5rcktn8C|NIO&5lkUNM+DcGEx!9Qi{r&7b$f)= zZM&Ms1I3vtK8~ga4enQSSznL)o!>Yj;-Yx|A`DfUXrbwJHfPnUOMVu=t{_1WH>{oc z-{L-vB(ThC?`>a-bS{5F3NUZtSfOAi84x!QFSbpL1#9}|asA_cTE?4V;?6#wM> z5F4=e(?hlL7 zo4?GEqlqm=mvD&nrQzrhg>e8)XjEe2W#GI8~Mb-194N#!!S9VRq@iE~rV@me{^wZW+ zmcXX!H1s9|_xffvCx&)4O0QPN#sOT|$BAv?djPpK2S?iSLRn6vGh}bqjpTL5^02>i z&;88Ni`;SxweyG^BwTL12kR~TC;8bvBCAL!$6Sr&(+2*f$-#JXClmYGp;kMVIWo@5 z_)^C3Zk~GP(szs_7h}Hysp%8B>Tj@9R&o+Ul61rRdvfwg@ZYGvC-J{u=lBkNZo_5H z$emqGLd?sH3UnX4y|ugCf7%63MJHo@`T4=1^eHJu3aPM#hG12&PP(;$9MC|+7$tekFCAKRVSsEpsS@z244*Ie$; z3w4DzpKxw)s=*T;3yj9>ezgCvB?TH0W!IuiRdP%eGvvT?c18)obM*kZw0{5@meS6n0=&+nygX-yX-8N?| z0MjHd3)J0b)m><)vbxsVJ*_iJ+2L!sMAw9PxEJCnYsS??u4FlK%WK(_i<4!xI&~%) z?8R=idfEMQ;D|Pp{}rTn;AO1R z>$6<&s?^o6-{3Jp{ef?eL0|gg%=-L?6x2gd{AEyoqEXW+=%`cG6vV5i@JSZfQfmQyqTSYkbmw&#IXrZIV&t#`(I?k35N9A!jEG6L%x+(a28a9$KmGFQkF z6-aGwcC8Ayov)pR9``Ea-A2h^BP4r$nns|-QfSt3ZqHIab6LjuJ5?Cu*2!(QnsQB} z{wF{3S6v4s9QGQSXG&EM>SiF%i$Cdwa4bt-_8Mi%r6LrETl8%#Xl}}5kSjgOtm548 zaCN1mrx$!oxiPxNI_L3Mv&iE|v|0Fgn?|TtW6yS7QmUc!g;ysnL`xRFHeddbtQPaH zH#2iwnDdJMQ<#V)0EH=7=^bV$0!Ku^q~$Mti5p*!en-1F=X0k;1@9|Vlj$#y7y~Rv zJ&(SokyZecN&wM)Qd^CZ#?k<|wGhi2(N~I}Bm#eOtfh9&(Pq-qC7A0v7WrTw;;?is zk~qXaDpOppml?+Ik&Q1J4ms7Q7dY_)F{e*(t!Qot9`wI->9MH zwO3htP{Z|9ZYm!!uA&=;+j27@Tah9oy8|{awDW4IACU}IGxl+vlS-%4$XUi{y$ds> z08b{Hiq2x;aRwpT2}*bF;{CTN-~f8A0^bh-0-PaAxU4jM9I*AW=y(^Yt1hk5Um59a z+)Ji%ur{5Z*e7plg12wt^5JXW+i{W8{W_k4U|*0NlQGYrIqK55X-FK_1+&^gFycH0 zGidJB@Clc&t)DM-v|E&Zorst3tRXepAih00IIpMY>AjjPkN)`Qv#g;1+VBl{Q}lpe zK;uVSZ(rh03w9z~5yg6Py#7+#)KluV0nlI0*;`L8Xs$EWaF z%kCyu-{rQfa^_o(_No4C&Wmeth{0M}ONr@+Ym=)_)c?sA>hmg(2D2I13oT&Ua)iUQ zZW!Fx%>MewN9U#TmBCVZmi&_3fUylR@7?t(O}3^nctXR#CgFj88d)jorcu))dp9YI zn^w^#8<>5qK}s?!eH5;_#i)b<^sC~=UBJf{N& zZ8zOqR~t)4jD5Rb`ct_hQ1$H_*TvW9$P>b_g2O07h@@t81<`?he6peYp%4zf=GSl= z8{Zp+TiKgvpvbj*(b%(D?Vkr}>OC4==c3+kCo?(L58hi|=^bhfIcgfdWp-m=fqB&1 z#-Xfzfl{{lcnGt*f3bcYp_OA~elN2sF<*hUluC7!yOoC_fpGpF*(JA z2i<9sCVBqfNl#2QVMMCM+WZ190vA&L^eJ~Ns@NqYbwSEJ+28adtciFQmn)}j<27O1RPBM9|8SB+CkEm0DBX@<*@*eWrn>b5)!U?n9Plfjw|JhI? z47fR9rJee|R=}w0N0Erdux2qk858g5#Tw00ou0lkDdoquN%2|WmOJG*fw71f*UxPI zZ9e`_UIie$dl&Et{XfXPKmgWJqw!DHTsL!aIhnffrG~W^GsFFY^<X2j)75`hZny&4D1Z#I{D%@-iWxOagWPXZ!`y?zGZgUe<$o7vI$;0h8I$Yyqr>#L;XY`u{9n;0^zK$>RTBvgeGJ`Mw-zpnkauDJfWAj9Zm)c=Q#{Xcv92S)s#8~r~c`aehD|IE?w)T!G9tQZxN-9ZIi5Ug`U(Y_VjQ{`u literal 0 HcmV?d00001 diff --git a/docs/networking/time.yml b/docs/networking/time.yml new file mode 100644 index 0000000000..6efaa5f38c --- /dev/null +++ b/docs/networking/time.yml @@ -0,0 +1,37 @@ +finite_state_machine_id: 8 +name: time_fsm +states: +- id: 1 + label: Present + x: 256 + y: 123 +- id: 2 + label: Start + x: 245 + y: -161 +- id: 3 + label: Past + x: -115 + y: 129 +transitions: +- from_state: Past + label: onRedo + to_state: Present +- from_state: Past + label: onMouseWheel + to_state: Present +- from_state: Past + label: onKeyDown + to_state: Present +- from_state: Start + label: start + to_state: Present +- from_state: Present + label: onUndo + to_state: Past +- from_state: Present + label: onMouseWheel + to_state: Past +- from_state: Present + label: onKeyDown + to_state: Past diff --git a/docs/networking/toolbox.png b/docs/networking/toolbox.png new file mode 100644 index 0000000000000000000000000000000000000000..14f676bddcbc4371d4a644934f9cebe03dc34102 GIT binary patch literal 59368 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 literal 0 HcmV?d00001 diff --git a/docs/networking/toolbox.yml b/docs/networking/toolbox.yml new file mode 100644 index 0000000000..99fd550b54 --- /dev/null +++ b/docs/networking/toolbox.yml @@ -0,0 +1,98 @@ +finite_state_machine_id: 14 +name: toolbox_fsm +states: +- id: 9 + label: Disabled + x: 885 + y: 141 +- id: 7 + label: OffScreen + x: 1140 + y: 217 +- id: 1 + label: Selected + x: 1180 + y: 959 +- id: 2 + label: Move + x: 1409 + y: 741 +- id: 3 + label: Ready + x: 892 + y: 429 +- id: 4 + label: Scrolling + x: 567 + y: 431 +- id: 5 + label: Selecting + x: 888 + y: 710 +- id: 6 + label: Dropping + x: 1358 + y: 431 +- id: 8 + label: Start + x: 672 + y: 196 +- id: 10 + label: OffScreen2 + x: 1115 + y: -12 +transitions: +- from_state: Ready + label: onDisable + to_state: Disabled +- from_state: OffScreen2 + label: onToggleToolbox + to_state: Disabled +- from_state: OffScreen2 + label: onEnable + to_state: OffScreen +- from_state: Ready + label: onToggleToolbox + to_state: OffScreen +- from_state: Selecting + label: onMouseDown + to_state: Selected +- from_state: Selected + label: onMouseMove + to_state: Move +- from_state: Selecting + label: onMouseDown + to_state: Ready +- from_state: Selected + label: onMouseUp + to_state: Ready +- from_state: Dropping + label: start + to_state: Ready +- from_state: Start + label: start + to_state: Ready +- from_state: Scrolling + label: onMouseWheel + to_state: Ready +- from_state: OffScreen + label: onToggleToolbox + to_state: Ready +- from_state: Disabled + label: onEnable + to_state: Ready +- from_state: Ready + label: onMouseWheel + to_state: Scrolling +- from_state: Ready + label: onMouseDown + to_state: Selecting +- from_state: Move + label: onMouseUp + to_state: Dropping +- from_state: OffScreen + label: onDisable + to_state: OffScreen2 +- from_state: Disabled + label: onToggleToolbox + to_state: OffScreen2 diff --git a/docs/networking/view.png b/docs/networking/view.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d7f261b2ec69a44711a3e2a862bae3a20d134b GIT binary patch literal 70936 zcmZU*1zc5G_dg65Bm^mulukuLkZzFfl#r5=?v_%zk!}!nORtyA|Nov8XFoulzGJ1ZDeF<*!`X9!EHx3)sT<~Rl}CAt>0U}Qnnj* zP-Z4Se?CWqGxyc+?yH8*Rb7O zgBp{Ou9}{XVX}%a&ewBGf==WZlTx>)@dHA;5TZ#qJ|Y>K$t+^|Ak`)|r7K?Y_jZ`( zUA!es6FG!&m<&!eN=OElk*?oZ4{_)Nk!sX#mJrfbzrn8(ZBwnPqp^MsGAMPtAKCat z-gU&K5|Us#qT=+ixwF->F|s9d5V2{oud(wae$b5f!{=KiR3|XaHIHZHfr)czwDAhp zWLMFCmX>PcVAWQVYU^(Of$#wf-yA1L7}a0F9G^cLr_Z3f)v(}OYwOe-9J$oa)-rEw zq&Mvk0+M?LP*8lVP=ah$Z{vmI&10Egc8Y}5BzFl>TE>mZewlh#OE$Z{pTBe;CJX_R%*&WUvah)q}EbUrWChxG^ONZdBVa* zEp(fbl2X9Y#Eeh%spQ|`;7O4BrL(g=A1kYyn;VN82aBDfIqPFyUS3u~z zJr+11EA)o-F$){(|IH1C3P7LoDOUr04sEWe;o);tv{avatYlQVEsSR3*BCk3EDtF5J8Z6Dyr^|xY>mM zTw}EE<|Xak3Y8 z5G8ZNXhmTE`svD=MZ}~0*Uv5j%=uEVwOc**KP#=B9`OHXAdFnZFRBHVDCObXf3Jf; z=Iev{pY60Fq5ar9L7-8S_|H1oivv6V8HnzV0JcqTXi_Ns?+t?8oRR%|gOrHalp1WKd^0VHg*l#e-VNb zk%Ah&ZmppI`MD?ly?gT$pRE{KSR`d+(0hA(m9@1)fB&`|8Xgw??e5|6&TVIAZ7>fT z27}?;zaQaqbx!r@5hfA}reAb4w&&4rmkTWIH-BlmDGz9u;a|ZENV0Jk2le{bN4f5v zp0+xtRr~obx32063k$n?@7(9X`T6rF5uY<-5H>~B*qGMo`T23J9@jsNsG<2GcjaHy zqDQCC0PWn3L0Mlvibg7G?sRXF$6+CW{HL9qIdKQ}?b~98hLo2Vr(XvKu!Wk1JU_G$ zdd{0&DGZH_yw9yi;kKO;kuVL+BxYo09_D}*PsabesjcN{@9Hw1s&zPj=LbG}u*dO- zjDCWEpEalw)uJ$3LmAjYc3xiIVyizA&#Tj2W`Wi&#V=W*Vb>AYLeb`G>)IafLY|q&9yB``M9^UJ-gH_}0sk(GTwuH}& zn9$C&-Tu1`pvii>!7W=xFV#ZHSs9?IvpEb$3{gtL_sC{05UzZV@M zo;?mC`o`Jh>@^DURuVPuL7sf(Ytc6-y!leATuf_qaR8aq{5GpEjwkIlrX3{Wv^mVF9zXs$ zA?u>z*@IB!G^*G{l{{5%KQAV*+lr)T9XJd(DpMI?0=F8rBN{|*`ZA|g#ml2o-(ixV z;9%qaX(vkF9l_5b2dvm)swRV_d6}MJ7Q%u@eIiZJ;OskK_mh5K)BrhF7$~*9Xf^Wi zdpd;ewgpa4Pm>MoQRNsdgj+86<)-`cRdRMWD+k`&%{F6gCw!0l!%QuIur`xcC_500 zDoQXxwe57_KxB)tz;^vc3Bjn~mAIlJme9o|2e3WspCku_ZyHSplRfL13xpxT8jAzt zy?&YKf(>9X>|S5&7+)N3)n^r`RCZ9|gN5LiHgUW<-69_ZGW$gGpLKA216Ay$$0a)pPJnwq+7iC+!}2Pd2RiPzTW zQDt4-jasa6cDP|F0H&Gyvj^jS^cX*!)89phJIG%(_0z>!0cMgm2nb5*XAp;`rY&ehs)W3UxEyCwj%2sxR>9l`=PNAM!oV>h6}F zs@x@?Rx&fA7rNXP4?gL^g8Qj&QefDd`>BpKRN zf@f0y7OMhr1Nr1TZ7yIA0f`L}foL%s(uPUHG9~9W$1AmJY%?@<*-tUapNtLS@TeQ0 zpT+7$r8&9247N^(>oY#!O4=7p{MSG-c@f7`p2>bIBQo2y!0J@&!sFvyEFrGvOws1` zKCjBqG2?)r%ZwhJ>)XgXYuY(~WFUQE9OnHmzpJUK3C8$2sgrZt-hW|i>|*YFb1|DS z_`r+mkL|X90MwqPW1t@>LF(O@M?#X#*A|1hR8O8%otctPrnFY^=dBaawcH$UO>Rx` zrNJ#e8z~rNR*AMug@{eR3k=}<`g)Rz^`Z8qP;&V%yoGYz149LBF6LjF3k!RB$tnpU z13`(H1cftro15|ARGj{ejRKn!pXIVv==F-%dA6N;Ki~%h1Z02ugo(E^AO@GtW=J|q zZ3>9dwZd9gvwTaI$$(>hAvN}5fcc|eU|_}*hqsCP*7jHDE*Mr~KjCJ8k6NUwg@&(D z5{NfGjD4cTZdPde*?xdY-eHeoDjIN*Iled7`9(!vt8FGzS$0Fng{DhE{6vNloH}Cj+{{%goW&HO&ZtFo;=1kyW058j^ zfdFV$k(kmWa#Pvw&WRR>@?1Y!p`@%{n{MFl?dx+`>c(|KlXw81ep3wWNsp}p7mw1v z1P=sFAOMeTjtuu~kR2#L3dW`=;RlkT)o+~vu_7HhL1k#AFjz?eii*zl98WIJ&)?R_$I7*K)lHXkL+3~6&lC&?@g>;;hkdI^9MuDnsTe~`=XX=2NFG>6jq*Lt zbUim#OPX6Z&+Pog>(DJ67d+7m!+(;6$jBt(V_`F(U|H(w$s!X@fPz&e7(-lb=@3it zm-jN@cy!+--$A@LG1q!9k2_DV_5nfLyf9w=u{0S4lCXjB)98%QJc`W1{)G31KL=R5 z65VFIxSwr!GPX2LtiL0&nX(Nye_m@ZI?!LG5}5aQV#ri?U`jg3w349_H5?sdZ`iT8 z*lLPS`A`xzV2zP09(MgePsYLhg#FIa#P$+FyLYvexkt()WeclLOCvq_D=J5<$1dA` z7-<$B$xdGj_5?LD!c!9 z#$zlu-RHDx@!Esq@r{>Ud^om=X%PcjDO)>3JJpb`?E|UxvI1$9@W4003@;ouT;&%I zt(J#;gS<=h8qCrc_~G6+5P093vOPvVbgfvw2uiuV{e3aR?C<_u3FOy5wf2{swBqpw zDK(iZhP$sxNjZi?npCO?OMkuPO5Y26+RMYK8-s(9BKckT?MLyTLYCF_lQN@F%Mp?7 z$hNCWH8z)dM}c~GSmTdEvK)`NM$}ukEMzd#Ql35vgL}3g3c#~9phwjJ5q6Fiu_$0x z$bFD;Et&8Nhu;)kQ?MGp94|IUc6j`WHLo$o_wo#j>lW+ff8Bv8X$J=sRNr~YyeVJbeqnZ7&UZ`F-ClIP0 z?bVETmud6&9z20lQ36ViLf$dbqt{~i_x8#b%kIoHDHN#cpR+7|9{V_oUH;YD3itBT z`WT51Y1W?jr;<#vXbB0ekYb!*L~HUN5ExExY{b}D_iH4E9}ZUUemHF> zDmEV~W4y|AqrMy5GUb;+XuCGB+~sPt6S_EdeDeL_M$1E&voY_v+Wu)Zrk_Fy9^H1; z=vo`D)yq|cvLDji&V7R-G1GYU5M1Gzx#2U|%0-{mv!gL05UGu$4GoVkOq)8Cu7|bQ zAPe(+B1+S2nUHT3q5ZD?IzDJK#|PQDFn+!^h zS8pHP>TpOPJUktjW8|y7t9B(+kOw*s*J*Pm#0rGk{`2em-AkA=DE&HzRb%=RTQ=v z>n=+fyk!^+kEvYg48PR5HvI1D1a?qfvQ>*sM|U#*-mD3=YB~z+Gbb16*?G0+7b>bh zVu~n-m%V5wQ>pS2uZ#teCeqwR2HTkqy`WiQQQ5nD}9CDm3+62^85BnHCVD zC&&AhlUgs|(bZ9k3zI3wNBk4umY2)^Fl3f>eRYwhDR~=Vu+c$qJIlIDFR;u zEus``+f6DeD!B=9ui@d5GspsO`YT^3fCKU4hD1e; zFyF%`7%PzAGPZ*S*#uz{zu3tgCx;7EI25zty1UB3GUW}&%8Ycbn4Xomv!keO_#E|W zFlQMtsbR>?!+jW^gkO|z9$Psc5I#Zl2%^WZnVFfGX)jiTI_rVl@x%$h+TNPr~Z zt)V3pfK88KUBX!qJJi08IAMxL$)WynP!UWNk_VqLUd_Jb)s_yU9Y{e2tBb z%Fmxq@k~q|X}SjsqTT0rmFJF2O|^_(8AE|fK@X4u?i5i*EOd7g|JGKKZ^@6vi@Z;} zjg-`8pPZ8my5i&gra7+NC~1(VA-n|-9Lp%cDUOYM5}{Mb7#?qqpZ9}|got;kVI1FX zgWg{p6?>h(g8<0Lv~?il;Xn+MoC27}p&MKg>lcg@5*GHQ%3`ppnmC1+{QYN-Apy@r z#;~yP9}RAH-!k||1zxM*!IiQBG7Hbrts{U^HXyGIWR=<_sN4+ZDY?}U&x}gIDvU93}AR;2_S^NJ5G(JIbv&JI{UYXYK=$Ir=(A;LdYepP>&waQ@M+AOv&9+dC zmKsP&OQUVqt&$(0Ny@-QeNz#Pn~tpz1ytO|JMDLCMgp-s07Iz+1)Cba?=~yq;q-Tk zkG!Rmn_FBQ?BUmrgmWqka7y>5WqbvdZB>1@MbvOKT#JG3L(H@4f*Z*aY1;B)rTR`W}d zWCW4X4$IhIokxcdhj%KBcFWBV z#jY!H93L)U2X=|nPP!hH+H3!fuDLp+hp_@x5>C}Px2|oD@zTqqU&u=6N z_gM@Fa~1MSN;+ex#CIHh}6ZRt^XcJ_D&^l<#Gi?^9`MY5vb1scKVvW{;fT6412&U=G4||6#=sqhQ7#9`^GY3M3i6;K+AVQVtAfOCzmLq|JUu% z{zwN7aOH`+{2!=(*5`s50>tQvJeVPOiO&73UD z2EWef=;>boJQocc+fB~90510;13>O~c*U!MvKmsu-u_&%^SvN-baMKv4m@^pIy%pK9|_J=V3 zint&`d?5b`swR`E>K)Uboq zfv#^U9Haw#|J0!PjL{lHW4A^kvIZeUd$K4!{A84)cG8kP(Md{S=QFWbwGE zUeYx&Hy2-uAHSW`E#3?8KTs&ZT*RM+>zs6UA25S%#Kd-xYvn`i@ybd}%oX>Om6IbK zj&)9c{PhK!NjDCeB25;V_HUJFIYQQXv6)){FTetI8Jo=#@LxWfpNH#%(_9=XITe3v z9`?6^{ZMTCrU;nuO;$wmX4wVQZaO6@gFYD<8F@Y~3m7}xnWcF5?j0H?+7lMH^`_ih zl!{pqfXmFhluLi*flyFXWMwUtewCa|M9aWHLq~V(moUa3dkF9fsB+;IYr+p$kw{OO z;)q2d6tUP9E$)hxsGN3wey;1he{>`<+w6@?M1*!DbiKAUO4=;cyt{ua#(J~^jBkE% zk%Ws2r|ncNqD+gnzGl>Z*uL(>+q>T9hiz*=b8smrq5}euF^Sl+zk3SE{4r}HFhPLa zw&Wpu0KnW&7mxALqoKWHyw^^k_(tZ&s_5+&9Y3F<#x8cv1qkgwRC;);&^iwSz&)Z7S<`gL@`&IeABd3`7Q8q8TkM;Y!x7yN&0`Txwhv~Y5A;wEdb8Suv}`XS#3 z5pYXRcLii5Y9I??BQGNXX=!T?(Daj+2WlQ5VCctTWMy3q@iiAI$AX{Z-xxnJntl5$ zre-!KSRefSnIOMsggqC%hv($l}F_?#m+qMj}6 zHe_5}VG(~FEJQaEb6~%BFzH4>qz0-=pxlvPTDrQRAAN;Q4oo&t=W?n)d22Wv${ zHmRBg6;gfuOP6(o#(!eYP~_93Z=q%5_W&P`(Ab)1J-Kq);d{1$vP3RIc-9E8M&TAT z_={~Q+-lHz>j`$j6M#T?%!kZG3I{yxA)JxLub%~I?I&?Rn(LABdvx$uYstV~N1g3H z1S~gyVuiwkGn0fUAbS(D8-Us^RJQ90_#%uNh5%P+9>131oi37zUSf zdtLC#E<6N+W(88fE5Y&ep+CUr6r^@16%3yNVahnb+^#6SD-i$uke@>UUEhyNV?ZA! z9K-zRd!L>X!z~P-IAp3AA!hn@;eq{irDldfCe?2e7{eV``j2AZ_{X%Zxc}6jz+(w5 z5RZhzac=On0`#HL(VU73JP@1aCdU1(UjCO&(g0^??U%%HHS?~aWL74J2?l)G6-clJsWeD|Swbx?qS=81jj`%INKz>gLE}Qw9}Z1W zwc_5mw(kDLoalhY)qEId1do0kP!@|~dJIR^<{p9BG9*&3z(KX3Sfno{0tR=1&Y*q6 z7uDW96v%xgI$P&#<(ASG#O2m$XbSFdRSK|V11@0lv~mR%d-s;`_)>tG46~`Q$j|uZyPq>MXZb|L- zRLjSoO1{`$I3pU$^DY3zn1P^YaEWZNv1f_jXjU&vo1~(UN!3i4uUFVzl86ORX?yc&ju%cmx@;W!XA6>)c{G)^0Zb(#&CHMYYGv0 z^?4)Cz6uCa=^={4vG3#L742$Kc=R_CF;sy~wf6lfAp_B%v7hz5#>ru&XdQp=IKns2murWZ=fE4!@gshO+$dv?*7190uviiJW3ok(`@==@d$J!)IHbOR;mNZfQp zvhg(V{8NQzn40f=(Xdt)O*G7g5cG36&1=I~w!$-vXyM$FEP%8@Ld4@#qwvKq*~=4* zg8Uxs=vChXl7};=_w=(c@V8%~+mUNv!)RF6|doPD>b zZHu~ZAX^8Sy9G#Iru^^$l$K2cqZ>P~p{W-uozOW{^|(a3Kq(~h?OCOW2uL|G8b|ZE z|FXfpy~Tz_;7=e1bpQbsv*4BmM(f7|7*(>sW1@?S+=XSrq;=ez+(}vWe5DH`A=QC_ z0i{6X;T%xfZUR|geS*|K=3W$pD}P~ZAT~oo0fr;{7P~4~jZ51}&yFHJU!_&vZFz>_ zJ^X713q$IPzx``Cd?*1maIzT0;qQTgbk6Xz6M5j;eD1Vqy*hHlk5O)^>CXQwZQ=6b zXk*ltO#hGj00d)#?Ds7u6-gj8u{!#H3j}^AmrB_I1 z4O~)^gMg$ACcl0STnNu=5rt4zvzrZHV~OYAOEH;#9P!gy|B>Ept)xD*u4waI2Xw6{i&SAh%lh56J|(_ z)QJsEsnc9>%POmBzv)fY36L5x>`D|i{JxuIeAX_yHZ17{v;gV05*&lmdJE1bml_FW zh0cj03jLmqyJ?YRnzMW71LbxLw=d^Y#%qS+aBlz-Jmr|l@Q>VDLL&41MSKzv89C&N zlO44qZ(M$d+b?p&oHZ$Y4Jb~}ky~rnF|34`+rw4~sM>3`;xpM74D|J9J_>2W%QzsH zg78V=VzCr3?WUyRw#O+h8<>WuSdpRMOFdA(C!k@E^SsaJ_e{> z2XbxeXa|bn30!7ep>T_ZXdDeJ&wqCgX7In53VP#Yt)Kvjh71Uqr;)oR;WNrZp<%%{ zQ6umOC`xl}R(0F-Z@uFoh#U0iI?uuMu+=p+C*=#=5_(#6IcljJHG)I*TCFm9f zy^uEgi!%S1HYoAdEXQ94Sdcxje@F-#K0bc!Ctue;X8>~?Lr^I>{x|^6{B-Z`f`aYK zmyDpOyE5Xb$_t<51oCW6TXI`~JeyDba;{xrJ!Wvq{U^@H#p$~FJzS)Tmr?-?3fNGL z2nMiix^r`=&_plO0FfqFzEr|@J_7OMAK{?IAt&#@)y%RqPy)B9o&YlT9U!iN^ctHI zP~-nvYq=S~xbJu5kD%RvT%yIBr{ZsrGYTaTe8*6cvsDZ#9?flSMQ)6i>PlON!9iXT z*uM$pEDa7rwlFUNKW7}|4jqwH1i<3~WaY?pmEn*%!2UsG>SERiBEeR1U^7mXzpB|l zqwMd(glylV4*#)^!FK@#Kt+N1yr8r+xDoV#I_xb-EG;inadQ)b4m*L%%S)Z1w7tm?F(8FBrqQHIqvGJ zTOYqOXz>Binqei!Usw`E|2T%46i#FQs$dtjF(Lq-*#ofPm##c>V=8@peWf`?`D*)k!1D^#sLA&*uU#mE_;LyjabnzfeQGg+$GVlMf3=l;U!S_b@&3C||=;(C{ z-ih;L(lpw9{Uid@1K2@Jc&iq`ELxq=r(8z$payMrc6O4Rv96Ae&%Af%>f-Z3?@Sid zCajn#5UQ4^NX5^u%YR-oC~Jn?)Yy2q^9FQRflgV@8C4Y(M2{YMkqzpNHxOd{Ge^pJ zbF4fj7?}Xi9^YU8cc1o7e&;v~q*}G4h-V3BgIU+={4l_f{pCI?Y-9*mAEBqDIZsJR z;c;GzA)mG@j@%sl36v%vFo=ql7TNpaSORLbN4h-9Zz@&ln{Hlq3;c%GyW#_|Cq^bF z@%1BBG6MDI>JeY80-4RQBd^=fS{9@DDXoG*yL{x}D+a^{$b1n-SVM?Z92%s}E!Rh- zpLgO;fMeaEX{gZl_F7Y}k?gDT^M%I?X@Xqr#x zT`J!>8fxmpT%$KAcYZ|$GCclRLOXXGwtrA(K0v-XS%baWpJ7EM=I+k(9#x9QQW>8b zRg%x-P4oK&YU#C@Z^+#f2xl2T8)@n3#n{6YG6m|+EXd+;=<9B%IELb?(D2_f58j%_yCSg^m#;+&^wPe45d7FtUnHvF8&fq(wxfHpk0khvE%xFRHXb_E5 zbXD@LT2MMGbKhH-+G+mQ^+j!-GT2|-)z^@r}wg{G64aAFQKt*1y~>y{v9(6d2221vFc2@DmR zyd5Cfm3Yzx{u-TsM8yR|Jv z;XRZbrbh5vQ8pz2)IJ^!EQ79rv8-kTI~@96&~v-khDs6eNyU%~WWGQWL$FFg?Bq`gD@wf19rh@n4D>-1WFU`A%bN&$Zszw>(^kll?*X?X_5}gBa!i*~MHsobh-W!0S@SV!POk%9-D2tk~gNE@3YH9xEXjmlOAcm)~+-g&+z~^WfCbVNo@h4 zy!bwspgj^EfC}~-Jnm;HvrS%Zi!(?SsD7L5EE_Repqm+3=i<;^aM{LB3MVFc)@5Ua z^Y=q-L3+jXeC|~X0gE=I6vWe&R&K@z4}SjYv(3g9*hLuh*ZGl%UuT6(E3YJZ{;_+z zhQs{Z5dVT}@x6_aM0pA5ss$5eN*KBKp$ZwyL@$!Djs}AHkTt3SQf#OLdrNci6*;S{k*aTglMDl^7I$*UcY=cPNDJSDJqxp%U88 zx02%Pk$lbXy8Xk#|Km*9J*LBntf=jI&H*U&t=v?wkpV@4S9=|V6YGR%Z#_9&JPZ4` z=S!5s!otcfHl|~O$sSZlpUnqTNSc^@xc+t{-TE7uHuENud*+9c?fbqDp>g)B8E!gy z#pFE(dU}zp)!zP_o7U4&pH8&voh_m#mcDO8vE;?r+f-n<4#eNC^3QU(bS)HMj)vkxWNG0X9OZtf?7vb2WQolirl(baFqa`U!{L#P*Yn zb2UYodZo|{b1J}%ODxDAaQcJp=lSBA8Otd@tKOvto{LO1n)P>~W#X3~ zSzMHR@uq0qE1s+^mlfA_$9sEif*3oZ^70X;OKyqTpu59!zVwiRg9@s_SJs;e(?b^s zJ_Nnf0**zHfwDpGdR@u)^=1GQ&Gg&Fnau|0)~E+4-V^c4G_7-Cv}LshaHPU;BXG2Ssy^x0g4M zprXqD!R0SG)_G8J#K|-8DXBC2Xk65@)6y*J&B<|AmxP+v-rgQ%x1do29mqyOt^@Md z(ux}(gwREbS-+v1)n14t7Mi#~=|)m+iSg;~?+FUHmJXtkF?|U6RYNmaY11)@$*9g* z$*=XD#*g5xh;x{l_8My`us>p|M3wI3fEAhMPL}d8TLqDHAMI`?3CA(kdUM>yrlx6~ z)on&Xa`GsDBoxcMma;NsbA*&97I@k&sPsDyw-Lvat!9OE!*tMhLZe(u@f)Lp7=?F) zW-O~&cVLQHLXl8K9`t-gZ6|J}MIfxYDwyGfi33c>8@9f_9;tYfT>Dsy%~$k<^uUkb zlz8F0l%xa_TVc&mo z!ptiM3=dYmpVRmF98T}})L7jqt3E)(&ytcup8h1#6KVe~a|WkU&mB4K*TMV_ z_K>>fdwg;GS;3xNm&^dyje^3$+byRZtoBF0HQJ*>I^Ii!Z;n(maH|(I6H>f7_19lx z!d6ofJc?&n+-01ud)t@uO~F*A^$M+u(6XU|hm(DOfrQe8J@XY?osCgp#omfU^JhH_St4SzQjsLfGr~K9@0aztNf&Qzf4m2k z!gh&ZMbAn{v>!sq1&C$DXSD03L{u;77$(zap)kx7(eOm4qE$~{CHeD9zE_pUMzb@R zn-`Y~)^$_WQ)dRnUCrqSd>E<{Q{Jc1z7FpOS*e2cWf%BH5Lq95iyx@_Wi)) zLt(l;5~fT{-*3k*f4QKnD-nLhidFDvB#GT=MV|A`yIeLc01A!$FpA`N_dT5tj#0SQ zn#uwh!%59!PFlnox-#P~tXEe@qdLOcyMzP;E7kRUa^>K*6?f;&^)jpP&kIVPBD>Y& z#qfOGo`=Yt_Q_Ud*G*(LACHWJGv^K3A+0(=DzRDCT);Z>Ni;I46wBp-iiO-y z?)y|e3V6Z!Rk@M{Jvz~39hIa~zm{JqB;GD~z+o9eQ~c$1D&akYEbleieYuA?I70ZL z=;Ag;oJkyIX68}78Ca*-DS>BqEz6FJ74l>5qP4x>VG*nK@yi$KWUwkbQWk=tbiH_~ zo-QkD)xJ17*G*_uuBLZzC-TdZc6f{c^#_j~GIH*us--Wun^Sf7EVL1mxrw!IEP{}E zs07x1E>8m`WT~AJfW0Q_=;&CRtYKBn%S)Bx;f|9eKNdc}km~O4{$V7HcQCNfj4c1y zcD~si+%Ssq%R%cNs)}E0`yO%GA2esk~Ud)n)@$ApvB{c3u z;8pQIqA(*$I=mRcc5CIfc&r*cRsW=Vv4N3^h9<6UWRvvux5?d*{)+6RY>M4u|5T{R&%bTi%Mv zqK{lJcr;eAP?WnprSdBMwaQ(%z5U8tk72rpAO5!KmXo3F(Ajb zT=KL@^4;!k*J)3mh0XMGh12Rl*64=(Q$PNl#yBrIo-sU-jabU>J$QWcXdAkq<29Ky z5ewQd#_C;|Xgjjv8@B8Fm|dJdtFpg&8=w54BPIp|y@~A^Z}h0<6*9%nt<1{-;W_M^ z0ol_+l`YPeGsGDI$|&y;M{64_pW|ozIM-JiT1R@5=YeE9C3f~D5;UANbNsMA5`tsR z`&w@>jRRVE?ob#YZ0ibP(}?NvUUMrgbQpY+kWqeky^)Z$Fiz+bXdqZPQOX-VEbN88 z+9Vdk$E!jU7eo3$g}9lI`=5Uc0KGCce{nyudWd@Z(Q>m?Van@n#&14i3;Rk7A-``f zS4PX0#n3db^gGrD^!&4gI3#Lf_kNC`j3eQU>(J}vMBZG!DF5A&C5Fm*M!z7efA&~Y zs6zI!Zf?d+>er1lXWK%?GkUwCgOmn~&?or&~IK$Kxo zszxo{qqW4xo^&-&yo_ivZwgY2y9-+@S+8!Zy?&^!rC43B{5oj}GcfbJyC70CwLyxG z$@7mh8lhq3x7NmBDb}kKH^TQ-_J7P1{=y&OFcDgbetCoS?0GDzCpvE|HPx#}{Zw0~XOufu_;M)!dWD_X^^v^LB zHj`W#f;|pRE+3akHg6>FFWSpT**SK5FXnRd?3^*G=d9ejvv<0xAxW-_E+QSX)lK9y zig?yRXNfO(zpvR?cTIPHnBIg5)obwCJ!#GIQ`J;*eCrc)inrIpu!)$?#HG7RvZ^|vZAqR{0PQ$8`>=W|8_?Q|Qpj@Tu=(#ET@Xp#{) z!PSzcRBN08=Fk5=>{k<>gEZBHO4BeboR<-jtjJxo%{R zKjvOJ>4+ld697X*ka1{^PFI-96*b^1(CGL_fVTtoxgu_x_|= zZ0(VsL~ALX=(egp&3)!Jcak+mkeZ|QaraVIFeIXuqE9kk<{5QsDXjF$_0E2Rlfh5Qr`FH$Rg<_MH8z22yGlx^&*37)oX6*6 z9+Nig;Z&rwGDLKya>v4N0{NICEgs~o&CMiMGta!Qb}c^5=)OL?sVWpD)b+md{f>Lt zBJ6{{A*>xqaVanOG#~t5X!+bZrNY%5V9h%1?Z|01X)@o%8Sp^6?vEz#qv2VO8FZ(U51+{`yX#5mzj=W6bgBQoM z1f$pXF&j^VKQn6fgcSChzq3?+6ee^bIFf}cb@R}bqR}sW_S4q}ey8EpSJu;Is6LD4 zlw8)7VlC4_S67;IH>tUu7a9-0C0%dL#?s?HE>vIkesjm7j);@yUgfHy*zmgit4ebQ zuFX?CQm!qO1%gH4Yn#yiM5HW#>HF$|_wR+No!Z>#EY%9UkQ{pNI)*85Hg*+q2gOn2 zbV8w2?A7tl*}ZZ9aOt`m3jwbh&p9-rFy2M+Sf~9bvW3HHqC5W5-gDZQhCg4XQQo=1 z&S0Z(iz1B>YHCt(DApRj6{MIN+?C}+uJ%4H`gnA1jWCO<#uhVin)`l|Q#h`m19|3E zn|KIU8_pa+cw#@ADVcom11RXOt<`-uXS*hKOu{#{ z_Z3pM06umYB4TUH;&W7Fu+G(dF}?F*&d*=aGMd{hx;3#_x5|kNwCxItp%uR&V!j$WtfNXCZGpN=*ZK44(fa zvRy2y7)Q>hYy2HyGu2XpNB41fceswLL$pNHZ-Q3NZksO6N2UoSxc0p~XNlRJ)P|qU zJ_=cuLzrUk3h&~4;6lZ)>N|kW zULEDH^m+;BgG$1~?*u{5_!hIrcIorFU&AX}EgZ;0?-w{NM`y_@t`VyvBy?Gbib(hd z{fsxd-+YM2rIE}AcPXQ`B8uI@T0`b(=5}^U%@;8*oPSGdvI|0;c{5M(V~_>X{2rYa z6&1Z{PYyS3UC@D5bf174!&rkGE4llezaoYc$T%C%R?;3~C^c7`>vH#ke|{{Ddw#j& zaY6N|^F{hI9y7s9@o)RjQi49j>fEav8rSn}n6_B8i?>!$eIb=>LH_<=zrjku;X0a3 zIQQ$`+c=xF`MRQ#%nyVhSgPQ1p+m&LlU7n1Rpmf#z26zvG0g9_6~MEOm&Lk%WBbg+ zb&l2dEPTejIHw-;Ym87)fPnRPe(HnLp05_5)fik`IBdAzCpW_0E%|bvjfQox_Q*?j zG{VcXq>T0dF?G(teRW?Kk8LN7(b!32J89h5ZPeJdtp-hF+qP}nwyodIGxN^8o&43# zB;TIgd(J*m4$`d;g!PcBh7i;H{vi1+@pomQl_+_ zjles$$EJ71-TJK3xh|daou8lo^5$lS5mqDi*U%p0g5g&Y-G1AXhx+m%uAz>pbrGSH zVCWtDn`cr9pnnst25g{O9*HOAG}Y@OVT-8-{N>fcD{pwX_2ezlr5#)T8Y`)#PKi++ zb9CaaQh-DLs+TRzXQQq#zkYz-K5ffu4 zBY3#BtA-+9T{|3(;=HWAgwtS5(Hc!Jlak_w1)W_72pAl3mt_i|L?D$fxm)yVfrX2(1NL8OTX2` zEd~HjzV`IRmv0uGz)n)vv%KAH$se-g2BatYH8ITwR_gJe_M_S&MrC5ziQ~LiT|&Rx z;p5>IV^h~VEfQ?ya==*)fZGlmk?ZM3(ewlB`Vbd~c6Q6|Ey+~PB&Du;6AZ~^^=3ny zVRXY$CF{Ni82z8)+Du%Rj~T5uxJyT<1T#Cv?uv>_mF8NvurY!Ra&}_l(4zIx85ya^ zmclKM08xud?6jM*($r92%WU~>70~6Ld1BHmR~pC5BWsxdANFF?3BoY`9zYs2FIQ&u zSgEBgq)7^7xMq1LzEeg)^ z{YE4FHC36a@LJ@DVPRMovbc?cNH}}mApf6mnhqw>AgII3>`vne?z4uX!8wc~_Zx-1 zFF})du<~}gurM%!3i>F+!y^uOdO&31*F|AE9?)()_W6$-A?OT)qdZcMb5_yTh;3b_ zoB~H(pyeesuR2d^VG1G5NDEC&ht`8eNo-{J!+Ey(jgKR|(s_izP-j%+wtri z7D$ekV@4J(&R^_gW)Cu{AQJaP{9--C8M^K5!76{>X&R~(2i7UTTg*Lm#Uf^hPNSf5639X3@lbdf2W`Kpb# zzMScW9VklH|M6v4w?0sO?fG(>Ga$<#YxyckBL#qCag~$RpJIT?g`wGpF?>cFIwN5u zODp5Y127h~EHk>4uWnf%$cn4+_l)*nTF5YG2IM<)157b=M*G(%=C>mwldF;Az@;-a zO>x2nHENbuT7E-JgYwXm{fdT0ka;_fZVhVz*v$)3$K$QQPJ zpk&_Ok{$QhAKc%i@Azm{Qn1w$u!!vkUKSk{S6zs5$s@%)vNc;72xtogxY;qQNX&Kx zx{=*C)a1Mef0Vjy{pNk^Mh{VA1=%xkGH{pUGGjC*mEjx6rPB?YU6Q#nHN((%FBe+x za=42@@uiSC&>!EXjKMqd=WesLvkbP(X8CeKU9iDug`~B_%|CZ;81W?KoeX6tW=u!X(w)0f6pEzkS03 zIx2G{pw&%Dr_FRnWJV5Z!aUr{*i6~LrmGd0IWceMzy)?yQbW}q3Xdt=uil{t6!Qp2OJ*;isGF9Od|g&> zJf0~}P+DF1w+A--Dg3`^(-FZ!({wgFwp1FffB&xTB9LuFC|{SXkD5&m^=r+ESav2ph z%3m*tRjqG1D4G21;ZRm)m&RPz4BD#RrEe(Q#CQf}qDf1B_K;#UgX(!ehdjK}n7MsA zM06@b8}f%|c(VXb?AZLwyyH(YUJ(}e zHfX4&Q?er+jZ%4FgTJ$sL>DfChl?^#BkW?3peDJ@nx`Zu({d~^qW~2{Ua_5^){(UvSQx@ z%4ma%dbjw%lp2$Xc}A%$J1;uHmz%-nc+_Y z7?uLTVI3|Lob@X#{>wCQ6BDO=lVZ;Q%Tst9FSA5BE4d!qwpe>k(AqGDMTnBd0?phX z@;1g>sv_ronKn{+Tt!)VJS1k8iZm#cwo_`bpbfb z-MkM*zV*a-d;_-?~$BzB0ER(laTkQT+)%*f*U z^COvad{KOLizql)ZQL3)U)h09x#_C-bSdItF8!B^*|*Mbw3$kv`)VZLz7k%=nhi`& z#TLim-SHcRPmN6|JNIFb(U-u5{Pl53AwqlTTktyQAfYpgTWNj|PrvkZVbAy`;hZ8tkX+ia5pkcY%|=+31C ztA(U&Ro}h*e?IlmtypMkFvHD)t(fj?trZeqAPOAsxgN|K^ayWE!b&16mHfPhuRI!S zm>tz21(U9cbVRQbND$nHZLn`@eY%$A)m3PBJTexdSvZeYC^itq@E;4N zS)l&n6Vga9l2rETB=+~IYKoQ-23d~b@VZj|t5Au1QHX*+RBOAdglUXq{_L-%UAX2& zPTvfDWv1HNWvwtJ=o>Qa{g;f%WXZ$OYRxC<8wT0rifWD=SYQR_VQ_#TCgrLkEItn= zGf#~QcZao2BORoogi2V)pp{Etx4c9ry55+!_U*w>zfuU(%^3@J%IeIn+F{h}aJ=0b zur}Nck-BGHypON*s64F+s#U1;RQq*Rx{#&diTc0SU3MI&zVcIb265<#*@U=VG8T!f zjv755u&Rw2XOW{@x4OY~wT&oh;zrC~$g0sht$t;~M4q4?fL@6rWAsE{mr?U4aoe9j zjz#V2FD8qf&FZU+UHtw{h0S;$f0_D=GY+5uB8iLyeL9?FI$^*McNPj-WEbz+i^I4f zC>gp&Z>$T|-Pzq5w)jL#e;mC=pgsq!gFP3=KC8aF|NfmYj1*tN5@I3f>x_{2wqqx% zBikcmp0HCcJ?FTxDwyB~qR{hUSDc0xRP-k&wuKUTBlk==U>K6HgQ`5Od7Ov-8=5=r zw;G9wo?ztQR`t~+AI=!1)FYtNt!Tih%x!#(o}8`ZSd3d&A7)*vnN%Z9U(7hSB@9Y4 zJ7$yp8Z`FfH~-vE-MTK-qU8rN!Lqxn-!S=6$yZdWV+!D4G<-H54Mf7TeIQ#L47ZkE$53Nhs0MbLw{e`k6DTiY^*z^Z4mx?O`AyN1H6T+K|ZJ( zXDcmbu8%9Lr#>U%+LqJXMux8nl_+eq2q||9k!jfmL=q^?pk387vDI zlCIGRHl9m$GhGDeJ<2>LCW#dKoPYYkeG)KWe;{>~S~I7k3BG>HLM3d1lyq&`!8X=>&a#NTs48HDWPd|-R7}!bF8YZjZ)o3C| zcWh>PHCoWV>eoX#eRV|{>}X%77EQ*tfEs={ zV|m&8O<4D=;Tk$=+}A_M-hK+THV$NPn*@Z@1viL@M%$&z$i7twGe_=Ucb!>SKWo%K zo6ILGF?gS$&PG?|ICCBjgiJ`$gnu_>NgLBVw!=@_334|om=7g6asJy^ZGkjt`4~DE zp1Z|oE{P1v@DID%M*J)fxnwPpc_$u^iUd1f(o&0w5X@w%tIStW>%JWR0075=dv}cG zV#DAgbEJHC5_K$Ud`zvuh|qIEg2j&kqNM8E{wWEEuu^Wh%w8T2H8tftY~mn@DNS-{ z_ZAdj;`um32NOT7z}V;NizIuD_Y)JR*P=j(HmoE<7JxS>RW6em0i>Q&zW_dy<&^iz zZ@H`{4UN+-D(oO_=GrWG^-sw3Pt2MMtUD-Z4*NwF!izn|Qel<6<>iqLC$Hm1*kd7K z`U`U$ujHGy114p9ydSZxW}13)MT*h5?G5i?iJlN$2fg~|Qy zcd!DvG~q(H-{GchA$cjnIPecfy?Vxvoc~s3W8@mg2KRq0j>76x!$YGr-c^Kw^Zc3t zsz~xlTrly+!7=WYL2r|DW(k5i8^&5{5lQo8gaW+`HQSalZ_t-PKf#@RV>xfjD(NqO zm7@wJi7aGBBu=~C{~hDr>D*ccnBjc0kHtBi?}8xzHS>A%K)~lzxM;H(AaMQ!Ca;~> zxzea>;8>l)PMtxy=Jde)_@R0TfN{@GD)|>HZ(!KoBW-n)PJuiq5bOG?|DAeamaW|B z>=BBi!_8^~>y-T-RUu1!MB+G;RM}LAmKD7i=NoIF0!H^aL@#qvqlZ*Yv+Iqq#d0m| zHcWnQt{@?pcqHCj9)BLa?K>ccEKMaxc_&?R)!DiB$nEyat;wCGrY(K)D_h8n{DD>A zqu9K#Udf;T`}6#_yK0Ak-6^?f$sMSpkm6r{@BEo-XXhX-l{(9#DubHw)gJEo|E%hO zub{_;n2~{@AK-6R0wx@SVpf1q^hp_DX0B3(3knSUJ)n1g1^{-BXZcEZJ;3r1se}98 z>E=Iu=Fi`9p~wLpC4$<>3e}p`a4O$yfKEr?=YLKUQ_^d9A^~#QMZE;d*qxqJ)y(ex_0gJ9o-} zHH;!5CIL|@*Xua{i?mp*1A}uP>tfk&x&m2L^aXW5-oldZusyA^Uqnz<6>B_=Cm3J= zVP9V|xn3K#F$fx?pMYPo;57v=u}4qnMl1_*!{ z<;X*kS5Ix+!Zs{^DwmlXKmy_vY_<*6cXe^@h1d^VK|p}ygu&Tz9n-x}OdS{+O)up3 zKI=OG(X3NI+n5M;zga5aL9+#~I9J%=13ows1@ORyi%jZB^kjim+sgWofTZ7v#r#O` z?_N(gWnhKczJ-Q@>Q={KSn^XJmFTP-x!md*qr|5;{*3=Q!+)^`B0o0dphK4=u3LRV zF-$(7sNncZ1S{uXea#ojk9d#?c|!oM??Fu8zis^Cma90~4E0iZLGj%^A#E(%FC@Ba zbJE>``SKZ27Xa1P<*aTYCN>su{JneMw?mg7nZ13aHIEw6hka*p#$=Hc1D9o)?8HEq zVQbQ@nx0PCRvA9T>&LE%qhc9ifj=w2T+5D7`p%T)hfyxftu#pd1<&yBnCYI**09$B z*Dogho~+lU{+m|aUl|imsle9_zlHm_efq(h2jiO0c%5u}g(pg;D+z#LG|PatDItgg zp?xPn;u45K)rUA05-d~Q8H^=+yy}fEeOv)or<{Gd8X($JzG!>Uon%04f}A`7Rwr(2 zFPO-y^Ju1EkJM-To8<3Y?5b}XJYV6NWCq9t?M0>Ap6)(1exj6-QQ!N6KZTK{uh_aS zB>SVu{(z7_=&S3z=BRW<`9Op0tgr$9w~jw5OUr(ld3|6S z(Ha(Hf~7LDm{0i!0Rq&!@tH%{j)0$ugS)_drNKIBsKr8H>Nz2tp&4eEfdvTHh>!B$ zBA`8|&V0ZzzL)MS;O1?pdeIWegx}u`O|Rm59+)hP`(S#nEM8)Y`=B0*Lh$nO#ipca zS=%1>mS5(S5i7j44WUUg@XDr_0|<7DuQvm%+d%qMbMZ<+9pt)5_+K0U20qrMG7eV#%& zfJW+pwx_3WO8}vM<($^E8_NX75C}reUfZUvqC!#QUOhKdm7l~?9`{#=ox8+27i6ZS ztU7GEd($EuTq_((YtK-Y(m6CA&UMD#uEExU1o*z@0+B|yhf^#*4;H_Gj8zX{CL?cp z9||me=xdvBg4eS)fk5)MS3k#!O(CFtKSOYW=9mcch6W#x{sn;p`hrTl;idE%9Y$}W z`g%4{)@B|@k>+F|sZIaxSNd>k2Sw4^cCe)RQX4LKuC=efk?%OD5{AY05aweIaGCOx z%F<`7d|YEc9G@1PHcaZ687$BtObI1{EXdN=Gh4DKe~_C;o^fp(c^pvjOjs~unVHxe ziPLJ9Z%t|PE3lw7@SU4o{#-aUX-rPL#?<9K|0c72oP5yG&@{_hp&=j!zjn~P3~SD4 z&-=nC5PS8Z{z-{;)#bjtdM$JU9?cIYsJ|&D5IDZ}5}V8B6=lv_kK!+5O&q<>_0(Yd zO~lxUiS=TgF2f-HeKLAE&}|51;dz!Ta_qmyuzE(~sg)}YhvMwR2AY7z*q(BTZHTFd z1~;-VBeEwok=*Lr=*EWY&7`ipv0BExaN>#)@N>Qi`PBb&K3(*!=y=ByfnqH)N%3(b zj6~VshSdJ^w`p-Ar^%_QuKEXf+SF<-TDup2rPy;3{h;^}qqiUln0a6px-V;Y*7@)D z2LBE7I!TS>h`4{x;~eKc!jKy&!YEGeIIY~wjG6$?CYKh287djvm|jjkL!y4$u$i5xk>03zUZaxF2L(HN?jcPl;@SJULbc}f_~MyQ&%(n*!{ zY2v*|2N+Mi5FX(J(eAdIc&}q&c}7vnGLfMaTlE$#=s8(Mb#}GKp$z8iXB!fh_3!@1 z532E}aS7h@r5cv^tZzT*^Oh%dqF^$ z@>=q*ZO7fb)>%uaITrz##g%h2^$rp%m<}@NvfVpi_uj>F=qv;|(w)3hIDAD-(E%xM z+Q%C((KtS!UUSc#84cAoOpqY%UYXu1z|-K7jn75X*gBs19+0O~fh_|Sh*rFg^^jgw zD79asnWNArn|=6Ou_7Ps=|#riaE?fqkj4c;LFD(hhP2|Kd~I4Ra_K_8x2*aZ$AOP@ z9EkY-EQQCn0!v2U55D&6t{@sj7CSfv`qhAV&sG#(Y6SlCS!{3?RZ3z#I<&#GAu>)h z-DNhzutnLtZWw>X$isu5nXBm$xoMiQGJ=s$(Gr^ro{)n>CE|;IuvBZcigYXy3a&xK zPRWays6SdTI%TA$mo=$|769O=5a@1#N`k_JTEiV#Bi|Pol=Ip~N}4@@%D+AT!V}x-4=SxF;C)MkQ$bLVmooE{EUm>buEB6nNpef zR=ibtv;EE#?bevh3VGlHX=BdP- zqA-Jk4r-@SLv2O>m3p@k*zq0qo{91WLzVCE48|f;&fe_sguSiV-z-4>Aew$i!4fMf zsk_8Q2iutTi2t!m`s67f<2D24;U*;Gt#usra9Yf6vy3;U;B zAmQZLiT;F|7U;8dKdCWsaKHivy!!gsU_e0i&z&jlKbF*L2Rbl4kE_5@ewon1Wv#0U zjIh(V1ZpbZN^NL&A=>!G-A1uRbp^zoDvw-Py@yvjg4zsVw8cP3CNn>G1yzhlbA}_5QMpps6{6rJGBu4DO1&2>!v07LJWW}vH-Lu1gX!}s`o%Xw zRH>tQJx+*)hk%g$R1{Kv(axJfyfj}2sn6nqit@x1~?=nV+Cy- zU~koNv2!(z@S!CU6cCS3OQ;SqW5ZL!`_~h_S9|5ax4#=URc?nRR^LmHaV30}XjXJ! zSSl4IlxyQ1dPzRhn%P!6DHF*)C-}h>aL;_l>26ZIr5`mL&ZA(7a! zqH_HKd;TUXU(EL<#Pu&Zs;0h)iqlk+55C|S(smWJh6cz-msaEO_kOrrMC63 zlkrfWpRqDnVn0LIbgzRe(qV6R=WKbB^B;b8S07!IWt~hf{1rX&3*^71+g(X2FTtb% z7@lS|smC-ZbfxdPhbAV;{5;D_$}#m(PIA2oaS`9C#9`_)xyw4KB))3<_Oer3z3u%h zFxcIPxBT#$Q&H&;qDFenn$f%A!NV+siPO+ZW3>BRRhywBdY1 z(J65B)ESH&Aad}C3Y|9kP7qTvaK^-d8K`|K|tDXEG9MYCPa z9}94h-ua=4lj`Zd9tNNbFcjEtlw_ifYdCcAF3k%z>Wvd2&T}9|TniirJj=T>)))+; z_aPU#Hbqn?BZ2PUPd&&_C%Xzdz{J7p%6n&_vr%sp=zbE04xopIN@7%83gsL1E~RIp zw_lLMZDHM*Qxu(LZ^+VJrTg!u2UMPqj3_(>cB!#hFQ)jjq=`3P<7}=6W3d_yQV&YZ zdyxtE{4-f*pN(!HiRzmD`SOm4j2sO7Sv8`eqB7kGcD$Ou>3jWyBJzjp_fPEL-n`(6 zunrk{M&u||8+wTTC<5B1?vLjaMhq;>=J`#02R2Ii^j@zqucemC%1WmTqq?-P(jT#b zt{u8kR@t2?+%#sPaA8?nFlf^JX}JO!0Y9>KgGT>OZmZw!ky0&Wi|1tOgxjSACqa`i za}j3se1~({J7?>)oSwZ?t1VUP$*BlF+B-*bcBUwfp3}It{6@n7qn1^>mhbldlX?5x zz)VleKoewk!wf2a{Z6WpFES?$qRb{m+IO4>Ep)_KCMolkLn+GLJncE34w?Ks z5%SE1*Ynw7i5v1hDJiL8=TEcMOgx8gTs|s$1@1+8xcCAN7wx;wuXNY=2%iz+Pu*m& zDuzW+YnJXTyfeMceHYxr!($Q^BmJJXI$KK^zD^!hh2)jt5BrJ&#a{fFbJK%*jO*ISWltY*kp-Z=Ls<}w$r^6Vkz9c)5BD_H6 zME8w}AKl~z9Bkk@3AcU?O78!994Lq0LfrO-N~4o2%GRB$-vqHaoX*MoPL*-)QPd+aUNq3n=eg(Q<=>* zh8I&$Odea!mm98WhAT!zy{zE5Pn*sJNqO75GY+p-2om8vdS}UP*Ta6K8Hc+WoWn-!{70%u%?&D?$*6gaU;%zltN7jT zr^D~>elFtw93Z7>AJBwqNlhD6CnKyl&Ww+oO^8JxU`?#i9k-M86<0NfzUE?aL96oN zOVfjS_MkZaQ8*XdwCZNXwPVggFBPKR9cHy66#jmKpsd4O&tz@8$OEzE2-h+6pcl9~ zMVQaM%}5W7oRHn2rXKV?k0*SxHX4KsHt;s%+mU}mJab_JQacKrzt4^|clVV#*<`ec zGqHac3yi(}_$G$?CsZml&zyMj&6Y&xS0S%7a6PrbgaELRf?gfBJ4U>g@nOCt5o3z| zS0jua#I&?BES?G(Av-DKzkk;XnP>OeX+&9S5eL0 z9ASllyY8aS8vJU>Lx-_M^=d!6NA*Mn?rPNC{^pLzfPWaxUx(NF-cOiyi)XuOO3#ZS zK=P|c9RDSQbHn@l;|Vev?^DvR&c5I)sZg|=9P2-c7jXmtL&@Fooa1BW=fnc#=KfK5 zRn|MFVl8|y5|+V213PE?3%du^Baa9czwZ$E5SSNi^INUNZ55P}JAW5tpx?FKA_eD_ z2)&s%Kjd5t2vsp$F_g(L;`<^+abTU{gMg}PcCKHHFB4YX0-H~;-T9U z>2oNK_QHryDR#Pu!I_GJyEetDHwLGoM&ORX7n`9uewfC)Z=&PL(L)?arFxerhlE~P zII|^Cd4U<5JrT%PL+yAFqv`>9%0X-?f6Sj-gt&NN7Mk?PE2y7u{a5z*8*TM_;I>4c zKvRF{KzDoz+O|$g6>5-CIn-VG;x7SZa4YLCy66K2USU`4NS!!6Wfc(YC#kg2F)=+?j4su{Q*EW1W#i^c-{UNW}=Wo~;LK4&y5H$8s zz6Q%CzHW^35=xxB1g`{EyWJN4KzMz7s{(Qe3dZs^3j6cveC~c;!ax*}K05UH2B_A> zf!MatDBZqVG+~)MuJXNY@#euquWz$w-Ipiwg;Q@4tk`;Cx`qiH@1SBIiZH0F51cs?O9o^ft+LW3_3G*ezZV2& zbNb*R&x@g+gx6NshL7zcH_NeKliHrUopS|vkB7%_4r!zns9WV{{R1o7grm*cB&YXO zof9&%`R+9RVTe@)eKmWSz*Z;`6unJ*j{UuqG60h3oGWn~jg471t|9uXtPxJWMn zg=z$0^|nd7-U9cF+&HR)Nn0eQw@Dh;IR{xMjAUO17Kn70JO9oF>oR?jf|`FbfYRo7 z;RZ}1wLNk1@epOI=GYj(D8QsJHhQ>0VHsTAH8Bu#;OdD-4_Dr}4cD@~BOL)katZi? zHvy1FoIXm=f}?-=FMZ9>PVa|Twe$5U%ZCkb0ZI`I3a@g7G)!-`zhZ>oWRqg3nWDPU z{Lh6Bpv1H{vzJ9W7@_rF?m3pNKNEIKn;ReR&VOBBkO3_1?(VMJLWaF3sRr(mSh5eb z`eS*^Irl>TV>?4gXTEHTp{gDpH{>(&7c@21NgZrrA8_WP z2E$)H$wePnGY0gG%{Au@u?pQbatsv}6;k-$ggthIFt5PCJix;jOOH~}pm9|2IdM-4 zNT*zJ5k2ih3y^d0oPmjoi>J#gdst(O>I2rrOc3w5(b4Ze!qia`YsdiarFO-wJTL?? z;k`y=znUG`!KOE5;27F6e^rCl>e9dkJ7Xf&mNQNT!nA z4T(`)+^dK4DbH5+~@8(k(G_*xPK(Hvn)B6QsK`IE}|LH)^&IW)EkIyF+w#mmVxDr2a@)_I=fB}+s zSKPU`A3H=JhuM;sm18iKO??p%9jssxK0RcDqlAV>1w>5OigocT)*WpmOB3Z2&c5-^Sp2F3Ffh#OF? zO%ysg$$ew<%L~l|^#w2i5HI%^e;^yyoW9V$Ax=6Rk<~vs9~~WZv4V~JcLG#$BQT=t ztGWaWu3&m#6rTb&e`m_6^=LPbv3Yw|-@c}D+1bzWesRLX-xAbk9VkIVCK zi2y@joL}qWIUKt60Z#JVfLvxH9`Fh}Ac1v!yz{)@WPA)7vep=n#qnO5oWNeappQ|) zHpM@_JY}3EGB~GcJ2`pOf)7L!CAHt@-p+In5t3{i17znS!zJa63u!}#=kcao)4#ALnB+t`#`Lsqvs_rV+~K))(lL zNM?AgNu~W5lsY0}$#qRs({hR3OUH1{eyklhmQP$dspueNH6E>;BTr%lI|u}Ue9?8? zO91c*vA6B`nIPdGd-CG`7%#?Yw!aT_ocY0`Lp zMB{UTJ$RGHZLrM2O3jSs6G6+VW^NpW)x3xXH^Pk0iA)j(M2L|jluT3%F zQ}m{=7{Mv;r6X4|crj|d>Uj9MxNmqZN}~WQ0wD39IFvNF z7mQ^{7a{0QD#(^`(ec4=+9fZ{qsm^zQe$7Q0`2vDM2SoOecl_*&KedDp_<1kv+Q|l>I|Qb z3np8LU26|uxNFF}FJpN=0SE9Uz&r58@%sn&q&?H!Nrz!CG%TR;OX{gW)Zp_&k;SB? zMIsBlqSV#ZT`Q{Td9qU>4UWt_y?-D9t)^|pu#-p}HmvH)mlsxFA@>iWCh3>b$Clym z*PJ+@loCGG{c#yIMhCNNmxKun zki%?mmFL%9&RZ%()Hy>oxcT_n_(2q!K|;!|g%-FwKDeeHJpmmD9{}yYZ}`Qf#Hv)b z1!#u=68Lcnhsi-6LR^a8%{s%T2Ai}M0-zs3-kaYqwA{tC z{BUt`306o}LJ%`B$a2`h<+LIG1fhhlYXHY-7H|s6UzdFz3^5K7^X7KcWRvwU0KH0b zFJpC+YAZowb!}~nYf{+@S|RZ$D!$_qo2H|z7hp#TdAu5*@ZW9yGcWG~q;6p9Gu@vv zN5hUsjq*T1RxD@F;KdW~EJ9fXGQ6*-WO*Gm0T@2OhsHOg=74`1dq%qBGsn`he;fB$ zIJui13-WjxOMT8I4+so2*d2_rH^{~b14fx-xj&qv$-5_fh2#CYm+|^BbdiDMqmJVn zCF+A!ddo8K@i#*@x#LA=23gHrJF?yLrT(cqe*32>v|rreC)?)ne9`$X!{8WPJgMwadQHrHxsWgC$bOjuqGT{Q2X@3~$H9*FM zXMGl4w2`f?t&>GZwa;NLFePXJa0*nJs@4X9lAI~NA?_&x2^6N%V<>x3UA5(C4ZgeT8a6fL8TXdbg4T_6E`cOx+;v%P6%n)OZ1cWYlXO zUXNSQM__^oBF4qMz{*U81{wnaG-833aTMuU``m4q_9?HKK}4Tt#}WDl$q{{%+mYAr z-QiTc6Ds!?8;oTifcG{LUiiAr*Z_hX9Mmo?Cy-syZ+iP5l@RrZm=-+s^Ddr*&?Uw% zK}Ia`Og>coz7D>ry?R`rt%M3zoAw6G{QU`}<&+dH9jg50fQdoiVYI~NsmXqE7^8=W zhvKj6xxK*Pn2&_l8w>;s>RzbnkALsPNyN)_#O8YLgFyMAsG~!#0|F$Gj(?#ZYUJui zrwmv zI2uUP4hXS|_PYGN(m_F@1_HND)+}_rP(XmH^e9zjTKs)-9O9`H9h!@pxcn4Ka9DtT zTJnjK$OKqGR#X?j=bX#Fj>Ut8i*|S;eNoFQnBo518mG8vu>qRK#o*<%AmWoY(7c{{ z6N2lD1W0@PgKY=EIG^Kq;=0vU?cZYgM26RGmNXa`D`+_ipO-7f=P62aqN*vBw?jbu zP_8J|x>?9Om_!z!ryG1o@(6&mg?{J#wQQNG18Fa4HN&4gF|;j<<(d5jpT#hw2=PYj zu2TQu|5yOnUImND%ZgcjcxPaf45;vAlNvBFjvaZWjkizvf&B1fn}?qg;~{JB+b@Vz zd|!YQ2Mi2H42h1uK+%+vX&mJKnAfu20;clZVI(Ckj1AP{m`Yu7EaW)D>E>Zi9mk^3 zLSHS;)P$t@kNI-YnAQF3o^Vk zpX;3r8J&QxA-z`p;+>>jGM8Ed?)5`~n)31YY-oxH0=pfOzKm_y(H6v8x@E}BPJTOB z(jXKk=t_PSjK)_WaVx#|5s0lDBh*oQ)71^*fC08UAfKV5>)?d3{hA6CK$su*qDd>p z5Y0?rKrjZB4KMQc}z?E`U47;qY*%TR!o99**{~Osv%Y z7`;QF>0P0l@%p6z5(H8Ks1?paL8#wrStVK==z!{ltTU{mW`g)31J#BJ0+=^W@4rgu zx*Lmhf(wcKCt-u7&(oZ6T*$RdsKovRm`XB?19Sbtq4hyc=m+ekj1Z;|F=cOOfeZ~R zQ!p@Luvni0fCdCGV1A94%diD83Cx9>(-9cWbasz05)_IF>_Sh-K;=*Aj57*am*{xP zbzT~veSaHy!X~j9Mq3{9@hkrJ5Ev~l0gT7Fk=6!wIaDzG#T9WuJ60rLpe!Z$iPwl8 zA^1X^xl)HpyIwBZTP+&kR!=mC4_VRQ+v}sv47hYwOqgUf^BzI%o-%j2dec1lg?SXG;u z_$`?NXt`L{%|BtGIZ_z4MFP2Lro4>j(~AqnB|oc{v-%qXKf^h$3-Lx@IU%2FY(|Z0 z(fuF9!}34%4S)(04YdBhOphbly{&V5tl`j<^viDUr#y+XDJB@1TXrbD>VCwuUaI_t)Vr?w4e;(cD<1Mt&{{Y1(U58L>L2NS~>NoVPdW`}2H*yKslXko* zfQ@QbK|dILdujU+yurjM`nA93fXI01N>~?;2B#M#GlKbr*HjeuA$N@UVQrvz^QVj^-E{G$RK~ zsZS}5_kQu$=b*+x($Q&ll+XEM-QC@lW^*(O`#>46X#q27MalY{ib&CZ{vGDlCV$*c z4=AZR8q#7=^yDcI#@c!;sUW=LiqrUZIDlEUT_z$t5En)9% zd|Vu9!Uwf-KBv?~Qwl*SNE25&mwgcZI{_@WK*<`azFLM;@L!@%m|6j_1R_eZEl>K@ zmv9IjN*YLIc|wGBRP_f#43D)4s-=|h??tyI%r8UgE_Ihf1w`E0o#Y&Ufty)__rc00 zEF>f((7+kg(D7K_R9p=RCl-tlkmtREXn#JY{rzTLVxW>}9`j=R?OKL*rC7@9fEQ&_ zB_S*Khku+M{jG`S0*c0q)Z$!$W23F*fVdC+dla9*Dvx+45(G_b-2g#JoC&;^H#-9J z7t}{XHs0Q}=Ui+HO53#8u?6JnqL8t5WWW@V+I}`VIkopm9#wHmhFO)19vc+o5B7R5J*Du=#lk$Z@ARLKh zw|pjY=SkA-8|JcdqKGQ7sVbc@bO}AYC82TzI^>5vevE9$qaQlyq~F>EtVE9vP=Jtv zv1=a2rwbo(kka$R5{CcU(ku8Vehy*N$Nhh{3M;`;zAYFyq@GsXx7cSTr;?PN zFXQOG%V@oP#;VT=HV?PH7feKj7~;DfX+E_|{yNByfXqDbqr-`$FCi5cI4XH&ENp>i z+Z?9xsryYG3{BNoi0=$m)J+rO9M6HOq;+;#XKZ+|jgpNGV|@2Q8{j?xSmvTi_Ae;d zOkib#o4~ZqeoQrOl0UPG3N6vm(QD4;4Hf~av*|RZ_HXn@w(jmicPcd3@`0ow966`` zHy*T!mQv_d-=zaaL&Zfi)pB&?#&~Qcm*Rp4vkXi;Bf9iK20+p}l4jQS`%q^XO{y`6YJ&lOW|CnjWLE?;S607I^rqcYV;Iux% z&@!sgJoe_G5Ajv@A4K&#B_*XlR>HmXo+7~}?o!A%wRQJa5WKDDo~@mG(xq>A2ySE^ z#;|0P=y@5M4;i?yb63(M*3v1+6nz;rZYu%7jGWY;waRqpj+HYegu|elgnwspR(C>y zoEdG^RTTurJWR%$K~~7S8pQ0MY?Nw7q^1+}m=Y9Mw-*{kdMq&UMl!R16;E;{7lv#2 zd~R$2bh1>}c~%(ESje^GOT&TEjus*W_ZUD1#%rWgk1!hf@TU~I(QJKLO_ilNxhvaN zMO70?v8m&Z$N$#tOeMBaSxXBhP8Q)Ef|-Ol)tU_DDW4(U0-{l6k3!+4#3T&619VLlDL3ptJZDh`?DoDI z;Y=(BNW(V?BE2o^RP~DsYeXjLadzNJH=4>dvuqShPs_lNh^1k&oMofV)bwi%g}8VE zD2g@#y=@=}@ykCQYt79)N~{I;$hUr(b@}djI6==BUgi1I^z%X&6UhAmYf&TGC8K}* z(GyzYowSqK%VV~ZUaatVUHds@R6_4jY~+2T62n#mvt7PUXWf;39Y(27#rC8&U3$W) zH7Bd);+=h{g+-mv7Y8}+MBpq9;dD80M7`u=;#W7nVYCSmxAP`33x4&{E_GK|^v(vO zqwZUFD0cy!ny^oK7RX{c(GYuoyg7eDUtr+iws&RZ*zL!cG@kXyAZ?zm_Zm0-;4P4Z zZ@g%t+@t&&rhQsDloecrsor+7Q3Pgc(G}|HVTV@4GTTg0_GRINq8pYO_Gb3MpM=9< zOxH2mUxkOPzgh`NVr=9jX5ow-&tkUTPRkt56d+S&zyh~3n$O!A4eQX34KuKNwNgQL z4R!zAKxc|KA+~b-nk!r7j4}^$M zAB_Kxs<-f};{D=<<STE4Yr=)ox$@2Ej{?q zBGIrV!dD#be+&MVTzD(S$PDvxs2{zPJtHxTz!h{7PbIb}g+W*?D%fbf;lCBlx&1xJ zBtduBhosjkgFqwUBQWLf{(X}P%;bb%9{u!IP(h<@80j@SWRZekjkk3(jvJy6fyvC? z)C=@&C+0M|AUnY>6GU;RSm1A>!0@fY=~%S9#=Ohf)^J$eeCHjl=P+axVH6ucfb1aU1l6N!vHuqA@>Q zbd%Ar`8;_OemT#|bRaqUyVkY1*WFk_4kPKwF8@S`R1RZ>M!`pHYEKkhDW12wzZ=*+ zuc|U@{#wXuQmfwcOHvZeuOQGlT2x~g8o_--hru{#$xXR5j5~TXPTN-nIBIj zcFWU>SU0{0Azjdg3oEGP^jE&`=_N@>$1+Y6jM`lE(F+gAfiM-LVmaXWDn*DHI)5u4 z52y+@J5l}+@O)G4AI_P{@GRR%4Qa{QiK-qnb+UsCab2!2ha3ftrwCn<&0PGK zo>_qa?N%QCs}7;tV5fp@A-T#p~ocuuv+{nQghs&JwZJIZr5a(PJ_epoF?h0s{nGOt=a$X@rU6NA$L|)zI7idL%R@%tj z@j(4^o}UW+!YiM`xS-2uKx}a}-5N5Xbd)&vO(<#p(@<#ujz=auW!pjW%WIVUw=dI8 z8qVdO=Zmf}zhZ_AVH+*vze^I8MT-vk5O#ymok;3VWexVVtA@B)JsgDP zX*wosZ0?%>7DNd=-K=B&JQqU!=QDfzLdU<|it#>(LLw!o`L;ilO+H3@Ff5F#LQ^B> zmvt7zkH5Uj7165SfD&P}eAAo54?FeU|H%{Z*(K?Btrmiuza>l_g0Z?6*kosG3b@8P z#Bn)r00J(`!^2_yve+@dG~;s)jg(XC8Fw2h2ySJeGB26vvcGv+);fr^~p&$rb0Al@5l^3{J)??fk(W~K{JKj z>j;ShT0W;b+^EkBP@%hnr|r6|el2CSxDwmju`F&e!@TehJ&FTb8pwmvYm%hpBGy|5 zXU7cSI^WaV<#v4Tha2uY+10F ziwo~mxgm$=lP&wJg@^okr9K1kNlTd6nv0|f(Ow1GS3|OPDMly6{GG`sL%HM>b=ZO5 zQPhtVdsR=X=uwWJ_+#m|?mqWsu|0j3x>?kCIS?A36Z=;~kdU_GRfVbXQKLoPWg}hT zD+}*a7Z=J z<2n#ylN#Z?cOp_nzQ#-WL+m3?O(~h*^C7yRX~|5Us!b|3Xi1ij5v#gDfInMXGFHzw z;(fNRfLlyO7-rfV4zL0dJYH}d%_N2SLk}I^CZg%OW$h(P2+FNKQH0l|3r8LD$2fCx z%K62~b^l2ui#gG2*6lDSHIDR65J0u?aL49>Mg34;=3aX8Q$+>m%^knY?_UU=nTOm+ zSiRf2_N{d81&n2VMJ~<+MD;us7Jl@ctSI|{*q#tf;^~>PhHnUmS-XIPh3c1y|3=X1 zi(yG%+`?l>^B6Ofrem;OBSbN@{W(75lq)lGHnGP8-^ z&IZzn>eK+km*(SdNb1|A(t`$MgG(R8&VHg<6wxvjkj6-iIen1fS4BaiCS*fX8PYWF zo|BRqMG0-{5#=@L5)=nlw`Bpeqqu{^kYccHf#^ zZxdvI1oeBb+jaP-6VoBGsMKjSoJSDgGT(rwzjBI$RcFJW+G8VSW)xF_g|fSgvAlwG z%RTjxf}bmE1uXLA^4V9Go~p&0@J+>K?&)ldSHw(ME`dONb;y+3|2 zhMtMew|<1Xh>madu&+{xysO5Dpp)-Y~6^6DK_?2x#l}YP&YL_K*2?!CsxSM`u@Wxza7Sb zM0RkAxlny8G)>}&ni+TPYb_%Qoe9-sBIsr5TyFm3dD#H-jFJNrLFmrUfHcDhpFcRW z?tIPXUua7F=WyuV$rrC;$6^#h{Et(^@4y!09YL_JU1R=c6oy}lo)6%2aRHN7#A7D+ zc&G(Y;JjV4;>V}Rgs6rmChIyrE|h$cgFp{Bbld;(y6PtEOUc-+#X1TFI;`h#s2!T@ z95vkE879Qn{2`*x&zgdD8j?m=!aSEK&OslUn-74eXvBvQJka&jhV|qb!+oQG2`)qP zq|WB>c1K404xOneu?z1Q95e8Lbk*?!I>3*OdqUE{Vvv!I4c;+Nt&0m^rUMV__voms zl@)Wu-g!}RS(UR~s=R-V!}01Xcfmj&jpE5@LL}qE@_Dk!sFIysdou~^4AkB~sA}di z)WhYwK|}v~qB+_kUYzm)f27olSL-tzDH+U@tLp{P+EXJtq)S$d_*;q1EX$%VmK$a( z8Y9kyf7N$9+B*B?xGeuCB861eN~cNBC=9Z4{Lnd86lurAJd!)oG>K=kEI8=H5cwV-msP^Tb19F)B9L3w}&Hr zd3p_Nsq2)J$1(O+Wq&hx6xNDOfDpn|O<6NY)>PCh7uA&~w5XFXwO0#kMO zuT0mX5tXbC1F9g6Y%bOp^xEXFiHJZyTFEE95KsguznXOEAC?)bp* z?rTvRzJVD>Es*acZ>?ni%24St!vw5;`au>L$D#>)!wN+3*lE}bFmY1vfS(9yb?^d< zOg1`J!)h4G#t7-{y+5!<)zno-kL_p*A1EEAQ2Cty9E1O^^2bFnG^GKC>pFx+?$g0{ zfjm)FLvA+g68@=BRJ=zB^vyWMrDLma5LT;4-J-mxct6-|-%$MS4IhbWk5=Nu9PPxy zLL-MSpr>@SWAs6cu&G%?WOCFYWUP7LZcd{b4d2|a^8*mRK!#gCFri$#AhH#`Kj85> z|06(&A_dOlBost@RXi+U{~Zm4MJ&P4S|y|McxufNwmVZAx0^*OEqvqZDfGZr5C6Yq z1^yY^<5X$g3_L3QqvO~GV+poPQ6=inuAwi{c;n}lpUhCsh7Q-S&9$=FZ~XsChYd&h z6J%JB{|u%y`jvqZ3``JwFWDGj1!k9} z3$+muSUPeVQ}xT}o43@B8KJ>tJecYCBS-*s%8bQ6fcA@S7FW<063-uMz4KI zWr)DBIjF+k9suu#B_E>Xv_zupe$-0={a@Fw2j_L)-OjH@d#w|#f_!>XyRtEzMvzzH z2^qMne7N|l@1dSe3jr#hqRvWt(X~6z-PSZQp9v<`eEJ0vzCtwu} zgp&Z72=>-D#`i?ShQlRFL=YR1vQh?^d+A*Ab>uwq(8%k< zb;%TLZQ1y3W&(gZC=ddlsm~}9=%>z3j(YhE`%{@g(4J0Z1WFw?u4OR{ zV3m*(1_HXnT88=e&MLDZhr0_)kZh-YSell_fnsH`I$yz}dFF>*ry23XpnjBx2osJ* zdHjm^hXzZ9o%okYw>&Y)aD77A1Q5vO%^{+(Q5l*EJZ zpVBNg{vHA%7)0q6Fzck1d4tWh>TT9*N0aLBMUZW(lSlPWc=7*t`H{<>#NliBFO}-e zTOzX)@eBQu3jx>)oj_=xTI?d?*U#dgqy2ft8ahH>3ScOGQuvw&QnJlx@UrAMQtoiG z-;WAn75#kp$Mo{%e4zL{lS(y!O@4g9dc)Jx^>dmt6`HEH8PKEbJaJq1SsjF1u~KSKh>Eo+o!wCYbBh&{+g)lh=@jgf0(B6Adx_CVGw?!= zCg4aNd?s^W7%xeB`fD!9u%>?&RlGv}nxs+yAtF=4xVBFD&UC7;L$(P6r9O)+^dt*+ z!u9bvR-%%?E@d1ul+Q;ADT}c>GwYS+1(x|@EaO~q%piT+lg+996~}LQ_7DJ$u=8dJ z;r2^*SQJW4UHKHkYA}xEy>BtQT$mU%PSCD?UDW#_De(c&>5ay7rP-#OQ=>Q5cs~8c zfUJ7t6yHbbuvkER#epA;N!yKTsR7z#8Jj8L&4Yg z4DY`NkV={+12W&-Gr7L6OUaG<CH#uXp|9Y%9~Dg5|N9qIL2=lwOpZyQ-ftYvNdqc2OL&duM2+54yzHiM)^hK1H{~6Z-5pA#r9F z3@I{U7V<#H2nwi-s}alur%iV6^pbRy>@jc9E(42x!u^MmwE2!xPaP`d70O+ZIcBvNA1*H22BqP6kaxSbnjg5J!`wDx75g657V>Y`ew06nD z_}-~dBL+e5#8*o$=EIq%ov`6FOFx31{ORL_@5kdRY-9iHP-FMEXT>{$y^L&+iYb11 zAGBKpY^B0hO6*BTtXehFn1u7itJLq{@%9>!q&T8wO;oAZC?Z12zryTt2VE6#w&v)Z&@R=u7PXBhf$SKe|a9}*Tew>Vps*^30tke7r{$!Q+b zhHME1Iw+|=xXSs8D6+zaL}bEVM#uq4Xl1j0*>9xRs1U&O#06_L?~I+Hk6FS&d6n#3 z5*rZKj=34>KWvYD#H_IjfsP)8U15?~$h>uay4HU+2GPQ!dp_h~noO68U%XGC(?39I zYK~l#NFZc;yNi7D4>$KmXiPtts8XKc$iw*n@LwN>lEwuol@>d(NQ*3W&w~C4VTLw& z;N>zzturSu`>AmA^8L%Mm=LZ)P^pecta@{}RB76O0tj#GBZ9|0z*a+d9yYv0iCrN- zKOZ25GR1f1V;TyZZBDw_6Dh0d1Um-q?Sewy`^qw+b0{3cx<;AzEgG2XgE0ZY)D>PRCrNZ1`DJcn@J-Vyj!xD1sZJI`ItVkZ|5~$myK($?Uc}V&dn_j~G%Kn|LU%iPHvm8| zo3bm0bv~DEnK6qwQ(sjZ=s$-)q3j?4JwQxlH1Q_jzkU0S!n9U8SHU%Afw z8^~9Y_VV$Bkc(v8RDEp$X|s^C(AH<|g_FJlMwTExjN`k$Z#w}H9Lx8~u@_+^b`|B@ z=4gei9O61D+T_R-Pp_bVZElz`iGJ*$qLTNN$xkV;C_^wk{x#muPjgP2zNBp zh_aOc=O=+x>FOUvMSuz>*XU!`R=BW8@qbKMLcNu~@ck6hPn#fA;Xkd-%+f-GH785^ z6s;Q~UFj&NO%zTSy=A&Ph->jdxo_R!l+8qUX3d_2q;2g1iL*HVe3U9aI|-_ch!SdW zLFG~tU-7_@?cj{P#%xaGST>L;YpzV=aAo#<as4`7uX3Cuk|Nx{;?iXkkkCBvU$WMCD3J zNl+*6Csn$(W28(>PVj!nid2cHOrTnffzx?tc0aZg*>sob$bTvD`9G5o|K=e5vq*Tf zgciAUC21apBjY@Baaf`Y8ANqYwf>^+wqcS$*iC3%j+nV$%&E#PIRau^@L&31F@*wv z1-+}q_58juuy*MO`r3{31Ft?Jwy81>oSD+#(O-H!rSD~b#~7`qmGrd8rmhQ?A~Z*b zPPS_zx6ji+*AxXA0h4iB(r9xqC2{W@Yy~<2JK-zL3ZDpZ=W#(^$&|vF&t5hTO1Xre z!Nt$v=}m;25f2F0dT6U|T71Y&Y=nF|!%QV{k9HnGmax`MOe4+vvH!v{Cs2E{^f>X9 zFHZyH55kf+Emx@Y*llAIS)WR71Rj)5608BCyxsZ_L;TNREXui&2CcIk+?IzsVkmuG zJJzhOdivc74w7yQg731D%-{#~s$$$AHNmB#&^w?ONJu^PJm1Rkf5w-~7qob9dzzk` zOTDqRHMoeHKOv%BXjjEVRtn}%D)r4UH_r`{wb_*Sm{PR3x{>Zh+&sWDC}48$ zXeGsgd_^3$QL)$Gi;!0^WJpPz%H=y%MC%X*ojtqs=SFNUCTpXaZ$U95fD&MetS|MK)tZ1H|K0LwdhF<}W)9DMT@db+pa)^U0SmVIIM=XI;+A%KD zgxJ9(E%uW^T|AFp$f*MBY@Dw|!>qX>WkDTa&A@0G$kDVz{r*4RUjr4%Ct0+5sJ#kN zGb##mY}iO~kwKWg1b<>6OVEXqn^fV)cbddebi{{<;MA#-t zDzk7IcWR-VsGS^rC*6-UmuYg-F7K&2@M%=Z;?fssNrqmPr!U|XU`5}0P_NA!F*-Tq_KAB<{s zh%7N^KVBi;Kqo8SJ(!QcF2y8O@`(Gg;!m0+d^tP*1cjyC5m%_La3tZhmtPz3PYs-+=J_EZiES24YFNkAyNa{aKdr97w z%#Ph4qg1k#d5U5pVc5HfuM&mb5PhUgi_RideX#w#_2;WG?)(VQ{8XuQ-$o5gESDRK zz6WW)I@__eUCo->mu%8n-{XT&U|a0b0^^$zsXyOD#Po*-xxyl~>sKTcqA|{Vi&`M2 z`|Mo@gi5o_6u@=_F_2u?E8yml!fS`>%jWaeSNl2ByUMy*!ZkTluumS2v2n@Nk! zSq}SOTrk^{B6QVROQK^?wksv9A3$6qP?&^E?xPGFM`z@Tvk+=~DwO;il{X2IA@2W~ zcU=(T%up+m-nLT+c_=eK66iSuN*9lKrvsm_|NbK`?hGr$do43+pS&#A?oYF(SwQWq z@HP>CacPb$NW`a%>NVMyte3KXfs1O}*}HqsM<;7sB>b_hr>dODy|-T|&>8QV2mNTM>Fw zO-ymP5dQI7XJw-P>k|;r&cbb!=;BTh!ejW!csbDbW+xg)+53K_Xo-Ga#cfbV&uKpM z#L@Z#yeyH8nwlDy`;i`S2&_;@}pimy6BAz_RgMdNA&hPi1mNpj*}!QYZ}l*@y{UGab6$Qao(uzN&q$m zl@JCMjxoZoCjzS7>ct0~xaeZbdd)mNZnMV;z4h%6X6wkj+gN2Ml@`bBzaX;b7S~EZ zfiHiga>%>gcq#>d9xgGp?9lKe+E+Z98xiTn+=Tv&y|VM7Qd4mychlj5sI-~8JCQjj z4HdIuW}U!hBnHG|cbO$JeWajEbJGA7?6`<$blr|95LG&{kitD@$6rhQvYDJA?x35& zFspOst8Wj2llx5!QKVXURQBT)W0aF<7cfj?G+eZg>pD=W`Lm$?q7rBUNvY{T?<5mj zv_5O6*SkAX^Rbi^2mT!FS}zsv?8W--{_F_5z15gvmQ&$Cfbr-fPe@WOzL80j zv$Kiu>{y?BU;pFbFZCWSqz~R`1&{osX$19Xa4Wii9&=R7~ z5RfgK&zN6jZXvdqwnt(N8eX#y|$!jP;TS{$u&sG+sE&8rTw*T&o|+ zNp#`c1}nAy5k!-VHVpE~6ZC z6ahVSK%Z)!i#hX{!+p}pMj9|5YnpR}uJ@w}1y;C~ zQ<+Vh+Gb;!8iC@Y;b;a=!QtK109z}d0XCcsvXruaDneDQJpae{`dm@Oui2Q}=STeq zceIOUykIZV-tSf1xHh68etE$wDzX%QlPlwdK3WH8p0Q zi0kKc-?;nps~Fd-I_ZPK*2J*EA&GuY}38 zM+kG8++LUM8i%a<%NQRDWKdjC7uH|JU=z$%Tzr)UdqB}Zd=+5Xz(sH>NVCHIvOnZj z8-TUMgcCxzl$kr9hdT7z5(*l#j`-*YbThcVrmRb_E+>lYOjD=cZ}&nY9`gYf8PzXg zqTtviI)BAJY6$WHGHS|cy0iWI)WtLGO9wbK3U;3;C6hl2hz|QbtCvwN_t~#vT7Tku z-e2YHBq=#;HB|;p_Fv@bo%^yuj#_cZhxxWpp6P|cK1g(QcT2JlzPn}6!kb?flp&qF zg?@mSq5L<0cTFI5SA3D;_!>&ekhr67Mx2nx^7wD)xXyI^xfIXwBSp@Rlzj-w_0Yc? zL^$DHhTpTdR2YwZuf&&J*DJY|fCY(^r{^08>>KB7Jiv7wfEsC1fNa+zKN3m@`5$!z zB4wJrY)A=9)JbMAjfI6+Pbb7uvFF(R2%p-q!*98Wc0?0Lz7C1xEF`Y^PwTs{0>PiH zK_{{T@TCha_oJewp#f-`;_zn^0V$5c=i%Iw@3Ft^ZsihYG8EvW`~b^x!=?~EETcG$ z(o9yQYI!uk$%oKK&(!uGr?!X*+*=r!sw*6QfTD}WfVl7Zoj~kPoPT3vR>L2slxKfj z?DzMg=0B4@m=P22CDVjDFvWaW3ebp_%$ZFNc)-aeY4wG^VZcq9=TZKAsy|s1Tq8MO zN>VJV=(y@UEf(-xt#3;1iMt&oW=oW4vW7*#L!q#i^reZ7z)Zt6+1=F^;ZJP9)u9U# zz75k8k15nyaD3$#ryvu4Ti0g6FCUQGBd4H{B-&ri0@ZYr%Ypmda^7Wbm2r@*0 zxeBX6%gkpU@+1>1CuZ1R`C;^~#K}*duJY}5+QC){N!>&}4ic=N!xTAv1@@~d9XWix zS)fn`pQm07*$@F`+b4;7Zpy)g6TsZoZtRkd2{@mJYz4LLzRKbVcA= zf3{M-b!ECbxRc_%@ClM!CJ)YOh8)2@tZIG_Yx$nXb!PQ0AbfAax8b!56Fnwx<1cNu zRS-m-WFyeCNfqpeKZMhNR9&hCU)3kGs#;V&0!>1MBd(w0)&Vv3{ zDq&4R*r^ULZMtx$aYsik;K#F% zwZ8g+6Ji?)_MWzp^!1zSNb1%oIZdX>TT0}VKKjuPgMzs3N*A3OSxPHC90khf-CU$| zh9Tb$br?l0dn%(u9roc2cgvR^oz{M|_w0Erd#t0sE0;}m)JjnN${*+xe%x|CLNCcp zX?^MR$)5omk~zM90uwkLhgrG3k8@L=(T`xOs^QGmJ+m2M+EZ+m()VG_9%f(^aPE4NH*H|s{WyvFV2zcSlJta zxD|K*)RbzEawK*Q@3e_8>|i|Bc0uTprpbC9G9#2r$a<%F!nijeF-T%(!=aA$s%xVUmAsb`>>==RY8P(n6>GG`J#S!ltK&)DQvBZ!U)p=mczgt(w^&XK) zFEINBwd9IcCL=|r@a3vV zI>3}hN(}D#G0Pc#Lp-EbY2lg^;rKC}5?ZjHZX$Lh$Rziy{^_K4Cwn?wPHGv4`ik8K zwUM8mo_-BnMXi2hIVXtQ2_UmJWFRAQ{|dF>gP?@48U&{sk!cPq1^fgcT7Gkdl~Pm< z5N*R6Mt1gVB>SyZ`fX>ri?J(!C8+u*quwnabuE*suI(^`kx91Qs9_GXm<&Kme6e*ijdk>Z90Z=C}WvPdtn`N5P zI^5`NobMsv_I+e;QK$j+i93{AKJrdH{GkQ=t0*oXA7A3(=fPwq5X@%nN0Y$K1PTJYqkJu!1Ko%OEx)i$ZYg_ zCA+k8&d7aA^Pzjj4%2k0JPjq%b zDn8|Kwd$u*ct7e`#PvN$S6m$mh&GGZIlXeBY6K ztMH|T3AG4WoYC8xh8PR4)J3h!-v>@YO6b+gEagHDc?Q~7@nJpy9oz3nm3G!RlnOQNMCGNTx+b)LdQ`P3g*=!`|7(8YB$i=;+R>85l4DkBzlwsdUY($$4GSOzxYdFn!m@c_60cgpdV``S=K>au~(|z?KXk1{`C5 zBS=@tt6kfb)pG_h90^onOMQXhJYyQ-0WN==BVu3cIW=^09M%H zmg~~_vydk$6Sot;<;p@M zyo;t`ep3hrSyPT{{nGD+Ml!O3KVs%7 z>0_`kovo|x_oRvC-g(y%S*sc5v91rjU#4&7+K z{dMEC!t9D(uQm6FT1=PeM+0wH2|u`sF9Cwk?A7BB)dQ6?(v-Hx*sR)>i00U8%FXv! z^Jzg>cwHLt;8&mLJeWdRaHhb?ZO$o)Y-`<6}E8fJeCdzZ(-5L!G_dbjmiyPxXi9 zg#K4U_VnKu>-o1^LptdslhEVQ3}vI=0t3VMKr3gu(h+Evw6eL-=$tNKPyPvB`3X;J z;)9gQ-!kn_9s<1`_mZpI8hWTp3^3Rg+<`#b#CR>@{d8Y{_S&Y&jM7OB?JWjcV8J*2 zu>~KL_~op?(q$RyPwE!H5*^^RXpZ4$r_cf_aCsCEp@n@B=YfP7X)&IQerv?{wXe^Q z+eM&H|6yA9^jC8pH>ZD_`rek4e(KdJ)2@!){)Er=u_1PxLx-8JFzx#V^$ZBPo#7BXGu3N{_J2E&I!i1HL0dyU#`3T$>IVDcl_du zNjcWp_W{_V!!L=aR~KKZ${ zMLd>=thANm22JHntlWW@1H$JRtLR_OOPy!#hP_2(#a~W`uA#~i<5}tH5ifQ5=62No zh=0D_sX}Q@Ln-s^BzuwNy?dwoiIlB^kfLb=T(^NIBH3}n?Wb>X5_(RIVU1O-Mg;Jh z)G*hyl;++qg2$BEYXDul?FN@(hb^~H6NX&cW~_Iu|AIFjqrRY_vJ%EGk7*H%%y94W zUuuPv-E0SS*4s%3gthA`G9Wr*25wAhf9J+JZpXH2z8sVKkr~m^QlCcoc~e3P!w->a z(s!)h`qf&HjHyI;SDr>hbbL>3J44Q-u!R147Z?Mg)e1g{W8cl_`aJkRs~PnaY<|-1 zdwt^X`ftg0H}5djCQ4x(%^x-Vwz-&nIpGyDu2{DF@zNI_(e}qf`%~}*z*p|ZW4F}w ze^h>-h5NJ1Ymbtng|DO-S>eB?Sf&GFoO=UaBO|LF3y>?F1hS50J*-n0+)sj+zTB@tCM<55J$lvD50KFBL6 z;Dga{Iu{ZeS`Sb*=NLLwYEis@SnLt(^T9Xa)fyErf8SaBtet3j;XP#+%{BU`FQ~Da zt8Bk@)UWKOc_vv+zZrS#q@DzirHLAbEgd$u9V2|hdRUFQuJ>R$Ho#pXvFSiZiaYJ` z{@V0>>(A9~$2QdD52p9kK_{S&z;fMv`Y6FlANd0_N(1*c(Cw&$~*0g&2&#kM@FVZ&r;wP!d~h7E_`Nw z^Cbw$J=}pj`RJdD&bPI=w8QXa zy?uQ}>5EPua)4yH*NXHuEA%({TO10J{m7VPTGv-2JPxEHKCN_H?e$pWg4DdhhEbyQ zZ6eiU=g?jh6L^q6I|TjWkh+G#hzgG>eDP)Tf<-R}jrI0VbVP&cfr9XJSvRMb3*lRS z*1L@IYbKT_r>DqJxP{nP=>G0sRpJ;p0>ipFYnSPVm$3u23S_7+5|9b!@=NRBNy9^r z54t+EK5v(19Vgvf0--oDNMQKw-hU|q3B;88;TXy@Xkp7xg8+fgmp zn7vH9441@L`Zkda8}XR_dpz3J$5}GC63yS)Tc=i2z?&gF0U zsZ9G%+kdNf#9@4F579=z(|HCE;h#>zMGKKI%U;{*Z?kBXt(5yevNX9JqMR<*)|YsD zqjV|Vchq|!sH$F0av1+?J?4d?{#<;+Yil1~A`78iae@Q+?VRQ#q%WE+w?o{IwgJd0 z`!$lSs}b)e{a*b$j&NL;HB}DpTJ({64n-x%l?=z&nXN9E43qSg%YF}Mag&BjLDGyH zIIko3rrh)1+cM1NFt^@Y>AO(mi-l7Xy`qG&@)rg4YD%;1k<4Pv1E3He2(sI5P0a}U z!D@J;G0kom+qs5`PosL@g@%MCz8nY(oluRQOKQU7Xop76Ix{Tepwrl!KS+Vg#emze z83P6nN)Mn^hLx93(zxdvU;5V>uOu1I%{?zBCXmK%W^ss1m0St0>_&^5_EXt7Ao(~o z2Q2-1+AYY+)hO27nXf-y#JGurTL=KhLLglVqw^GQ$#a{mW-=G%W!HL|NwTn>D;@pP z00;rfft%b^;!+DIYJ$#F`rVXG2=3Km8PSt-nwM$c4=lmEPk7*1x>HtoU0Q*No_B}j zAH9VvwO^a!3sFp9oFI77Cc+4& z>IJ3oyCs%!uaMr0ymce*!toiWp+s%2Gc)R9xuSclQs86(g;x8!n6@_INS0_kP{yOa zpivcV-%qZFC3*^+D<(16)^kk~eiA)3nkJ6-FJm~AtR=#s%&a&KI4WCWJ0QW#jsPkL z;(=47V}#VUYn5>~!ogzmv|XMdFS4%_GrbTm(MzAu_VHvyyB(V1Uz*&$pDqpW%~|H|eM&PrSSYak zTu2~#r|w^WZHNuZuT11CyeN3XWyw9^YC#Nnl#fkJQ*qh^ecgv(X7I_aAV|U%k&f z$3U1w#I9rQd^i--eRDD-0ijhcLsl==R1`MsSvGulD%ENkUtf(REFyW#(D~g^Buh6` zP^u;S-owd=T!7*iI3DP7yu6@I6M&HWPee7NN3Bx@CSq#?F+v=!cklLwo)@l67z=dT5_**GX4Ha@W$rWszj%mm_^g3 zBO_}sPdD3;{K56XXEk9Pg;KBsETdjLHqI7T0it62@)Qqm^R6%`9S&x=O7t4Bfuo>{ znEjS(LTc*I#6uw<5!+d4toDuiWJR*z{-D7*GC{W<93`PZ5IiLM+V7zuAK||$cdE~irbO6fODYP&+uT@< zP9g>mKW=E+^-k;mxu_B$VrR#o`v@I>y&$vWwiq*~>bBI!bVN_vZ#U22 zV0j|R@h|`Tr=D^m&;4+Bvk@1YR8SO1fn9NvlsT6hCK@Kpk0IG?IXgGW!-~rFHPxal zR-uBA4>w1ebTg4G_Ts_?*7YYNHs{N?HD>ia6Fwbfj$p5a-^I8rTTza$RVy&{U_R~u zq2$r_4uS~I{_VK7pYTL$Xo?o!OE3KLAf2y<=`V7VIsTx}`AOT{qxM=awp+`3)bviadb~Y@qiBQG(W|50)0|hN znY@|#LPqJCIkZTt(nQ+ldeI%wpFMJ1J%Zu%62L|3IZhQ;OClXCG~!^lodegMM#(## z;-nWdl|8KTkl7)_DpfW0ny7{8ymkYOXUl5n6qT6SI@$HXdsYv9)X`s!$NfHJad5v5 zHsdQ!akz|=QzyCIP20d2Lm3D`iWpJ?gp^Yt565+w-ceZV+6fPUuzkao>iyhN?eNu* zY5(vr76|Jk+(LEaIx>-PZ?ZxK(Z85d1qLB~6LiNIO5+G896B@GERx2U8lb8HRN37n zsvj~YN4d7wJq083ER;>s-B`ol1T}chOe=P?ukr`LODP7SqoiEr=)DmIE}y`xp%}Zy zMMC3`>o|us!`6d28MbQeb_V`0)_p5tldG~mQntL~kz$b@sP;o5!sA0Gr@+C43*-%n z(NAj$c)1QS2zGfpFqyMhZfgxxC1a6ayEvY&#{^>L*825%;P~Y&vi4tGbN@-WIm(o8 z1)=2jhvDY`j`4zKl`eY{ak(Bmzg!y>w!9Pz+(56(J!3G)-pnsQg?+5GS_SC@97Rny z$G4_?&N&{p>kciAbPwcj+8*gCd*a4=nzGu9w_O_%UvVT`gBOuw`mdn~V*BSm#I6?B z2Q!uIs4uHbW^9!}nRfo7hI~{g6Y4!f*Xdew@p;32%^?(`w-}XX&LQ=U{B#W?^2cAx zlXDI~x?!_U6-V&BE5Nq+`xE^ol{SbzLNH&am%)R2>3LDg#pb_#&#m3yG1<>6r3|&^ z*<_(CPi|r+cxf~pc>ZZ$xtaCwA%J_W4ZMn}|NIvUwEp-aA@Y7F zttYS}^`5WDs!hK*9HIGXH&VoUUx#OH^{x{L!Da4$q86coIqi>MU0HfOu@Jzh&N@=_ z$ZC;%Ib3eo7L!GIwDrHiKf}Vs4cVJKJSnZLKAzC}+vRDe8};T{8zY{*d#qGtoWW;( zZ7rFR2o)8%0W}as)UsC}q1*9`m`*Tu$#!^@GIphmW+Vr;8M)L(?^%^rXvObnqc~G@yDiK`4ALpuj$s({_$m{g-M@ z&S6cUzM@3W;HGvP7kB^Rc)_%K3(qf$G>U;8_R5F;Sdwkt$r&zWLd5`)CmEaqAvL6k z^4$~`ElyyQptg~wQTQW{AkQ%OdZyLZKo}QCu-AC?+7jvh@;?2oHg>X^(WIzB-@~yj zi%zbV=4YK+Q*UsDuo=?>r0xcWhkF1MUc~{P!9Y6^+rHZ~SP)(htcjGV3mj3TF}ryc-*Iw>}MEZVp$kXZ@M zKs=ob+C78>^MBPw2=3FW#96G|XGc@& z$4|z$BpgPQmip)v7ym|t(GLZXp1kS|*m@YEbnb*)cCcQEwwDK!-fJw#4Q~TYo(}YV zJ^p3E1#q-KV1to?v2ol6#sgq8sxr?PyMIx~Md4SEM>@OgX|R%5Wj)VR=7Kq?I1v=; zW{++$zr|ep3(JI_zMpNzTe?=tAJ4)h5(T-euT#aq+j;t%CcaQk6cLl$#bz7+YAbS_ zqctoN^Qr}>>NTv6Rj8ri)~Li_{@QtpLvOoSL(9$@T*KP&8jjp$G(9EMA(dUfNb{i> zke^-5q8EjMBzp-UQJW<5z;+r^tNVC`%PN>~pDF-(8%o*Wju`@17h3+U0LSoIv~R7p zG0A!DO4jd$S`%@&;{dVeF&rd3gZNLS6;RNX5lvD6{m%nZX^GhDPP3T zVN$_z;4o|}!8eGC%HHy^N(KRnbR7=g<2DDDSf#`!N)=u=0)Vi4GUkcL ze1tLY`NfaTSjK-N?feTCFG7yaP=&_)#Fgz{bj@F;2*C=C$D|s*rP@a*Hz(YamOHhO z6@(tqn#$J>2#A-Zh#&Fqbn}^MmC2EW9!|PJbfkob$7{dSXhTB&PwgGb@1HWSIMjG< z1j_o>^0|KReQfb4yCE2j;M0qL)$Iw44OL79k$OkkktZ47O zgoA@a4x5kewbbZU>9}WygSu!ly2^Bu(N&gVU-nd6p)wWDuWeO_%a6p^H4E@eA34mi z?M!c0C_U5*^FpW3S!xWIz*(n+F^5_#G;mFo>7(kTTP`-$R`+b|3efPuTTb)3Q*ap8 zSj-c3hu6x4?k`y^wtV>Rbt|e~s4N~zb2wd2!K_vG?U`RRDL?^x1%!uuAW3153$S6J zcUTosi>swCB40Ni@Z(7s*gL4zrOKsz0(^`7r?=J z8)DK$%NC|24>7qq?5YEyHzJi7tCe|tS^o4p69cCyAcqo|;;8C$nX+qFygH-F24AZW zYLs?=v4@}S;U-+B{S*`B_xNn7yAb9WGz~AyHB0~VMc0moWBm}o@`Rs7S)}Cz0X<+) zPn_RMAfg_#%Jl8|_URx?c_G`0-C|RU$4Z-c&d%Zv+>(zejL;mWmygT)4;oY45AQs*Jj=B?VDZ5Tr}GL8ZGJ=}u8XX{1Y9Iu4!E-5t_h zf;1@I-Ff#B@B5ASU--s#oZ;{npR>=i_g*pQTx(gVyW>ZTmDZAFX~bV1FY@Z2 z;qqXk@VTNyb%zOy4^G|}aiTD%o502v%9ctOHZOfvGXZh-3lf2LP&pY$J0t%aDC4K< z?F*zSx9Yzfl0g%C=m1n`mObw?$kl{ekuCEhnubY#p{AONbE5VSNhAMfMAqgn*i0U! z@PASVek@TR9+?7wl>}xRJ-ofS{3lH?M<`?UK{6qSkDg=vfcMeqBs@apnV?dV+i=HA z4zt{qu+U#&w-KHiAc0R5O|c=A`wjY2wt+Iyn9;@3baUhHQnIq0U|y#Sv_I4a7%qw& zXH0t2WMyo>6&Nb(wGnd+B8C14tlq@}hW7)>PY418zVCmK*`eKRPzPp0qLm9CjC4_U zy$Ao5oR+rFE6AxWr@pA6fhvLLPdrA*CGkLKQWed?VbyR`fL{w-j<1e}H%P~&W#D40 z;9)d4A}yyucYe+c?woT{mJHB7IuO9Qu>%a74v1q)%i*Io@C)&;@qX%EqQ855O-}BB zt((G0Y#r~Y+!BenD7Sp@OV~1*59bN=>UoW!f0(Ui041hb+U~}ui!7a324P_%^+8>c zvUS=-ReCVUk1G{YG>ML_(PqxIzZZlVz5m*z?hlr;J%GSL1Lrk0rCz92Gi0Le3Odjn zf&dH$TI(kyEG*4_6upbW0q4P}?^V~Zsf(vh_J*-%HBg6wRv^Wrin>*Mk-2N<*KR<+~f)Vp6=NOfS`00BBM zQ{n;VIw6nP&g*Bs#lX3B%C`XIjPm7X!+vu?XEvHrgsar}$k#r#65Y;kcx(rty4wiM z%%vPBc@}I68Xz54U)T|L54D~=-YeENNm36{Thz_2ySXsBnw#GEuny5ej?uS=I%6wp zFZw~6d(h=H(fKS4kFCqg&AjX)KR;-;T)ZUvPG0_ha_2|+2DWkBMwv9=#9MN^Wt8KJ0f#3-yI@M9> zb;Dm7NS50(s*PHH1|QP+yPD)O7q>i6#>3zxelx+O0|%nF5JvoZBWKEFGB_eBuC<87=*s{0j9wZ(J>s)u9-#Jdmp78MaB z4Z#}cI`>;H5c*SoBjzW9dyGn7Rxe3q7#^Jfv)=`CP(bLJXS?_hj+Z`IiZ^aN>x2J7 z)t$ckj_uEIMI+@}KOzX*)-c1F2g<3*82OI|&X3vn+rOM{O_0_ZtX%3;nW^*)eWlRk zaokIC4hahrS5m^HQ1B6XU+^wx!=iN!GusaH(Z*JL3v54zdX-sB&`^{Xi^d=^Xo*Fu zI?y0_y5O>(w?Z2_|8j_7nq{Ajjg1AUeNM^OKocA~jhv+#CVvOps>q!vHr)gy4+ch$ z6_16E8wTX9Pu*o~9FwV@+8md$P|vI#>`8wGdU*eVwA^HU43a)Tm~VxAu&y$jP~y_5 zvq8|+)ir}?q-|``AR{BcbvdJ_sOj6Ct(iCvYV2cctR-6?GZu&V!WYW5F3W3{)klt(xMGCE zYagKx>8!vopy_d507j`uRSooIov1GT?ongbJ2cdV!qn*LPvnf$?ia(;CfBu4QkF9c z&oh_jSN(m(>t)p~I;%ds=Dpq1S+u?GQyk$DjcdX1*wx@v-f=9I!YAI&05%I3Kt_^? z8y@DnehN;s7yvV+M#k>)K`Z|JAVfznv$CV3<2R_52xoh5xYIAy%+s>kkW;IaC51f) z8g9`Vxmw#hrNR%q3ZMMkW?@O43eOIE^qZj&=+ohrChtA&&prC?-!&5yYOeGzg9xY_ z^eCQy2<4p=#`%7In}{#JcV;O*2qw2J1kB-N>YR!6xTTw|@;eg#O_E{MD1UEn2b-oi ziCta;;D^fucdP~X{HTenB#U9J8N|g|mO~B{`qVHl@BEgsnO%^cn_R4aVrqcJyfvMz zwya~)T52l9kxk~pwmD0a8TI37il}zvixpX2SsBFB>-m`w#?NZQcl-=F{1I|377R;v zC$AFf^;YvqD1jb`PA5JU7EVF3DnE?EV(zE)^MbUsy@`=k zaaB?_(4D-?Rx;S%|1q>e)_S??$Lwsw_d!A>eo9INfPB)f3N8pKODqHdB^+|1jS^Q`6UI*ydHTPZIZ5<iOucG@mSac*O!ILj0{*1(bC>WM zMZI2HW-7SB+!P;c!dC<)kK4ZX3s~66Av^3_6R=W%T|{;q0`B|bk3TWNkl@WJm2ATI zqmHScb)fa4!6L`mMmraUJ{maVX9|Xd?MMNCn9vp@p9kiTG`qPpg03^T>>m^)6JqoB z0EZg_tjhB3`Jy;5-~Hn+@T0&L2P~w#Kr=DuNR1%@D{d-cV-AAzC+*tW0rJ!F@7YtA z)`K~~JVU>h)3xu~=Ek3&dU&mDZX$C=p91$LNCq=eq&3Ox0IHY2Qp7lcnN$;UJ_g=2 z0Z<#)#|gu!a1EqtG?3IW5l=eQ)Y?EC2!pyq{N~y_0?CCMGMMhv(6S}!yeJ*`6@lH( z;1{IngT>l)uo`X<|1AL1sXVdO_=w^7Qo{f=L?K)6Q8i8GvL~osVqoq3TwY5i<{=@0 z)D=ctazb6)OupX&q%XAj9Uu-Mm}a;JI4L+wrBMPvU7_;41_1>p*O!<(aau=K+nwTT z?n*FCZqIp|$V)>~7&USbjZVBD5WLcDcE!VVXC);SEYhZ;r2KPpd7QPU7NRi!A)QyfLV+<>rpv)wcryc zuD{X~aRJ7y6$`yZ!(;7850cQnqe}S#okQBmdw9F*BJ2JI@E&xSK=%<;q534*WMT;n zgMRCJ!E9(`6p@sq{ej7_LmHNzrIgM^RaMb}R(bqp&ja?;e6#OPp@s3nz*pS1`gF$q z<>loG{wAYoLMS#hQ*Bw`dO-P7*qV};w`m01V_FmJeu-2lr5nBm17oBB|xC7%{zBI$Nsw=r0EC1nTuXV z_q~8YfxgRdPkSVZF)`#yW|uPye>%!VBae%A;_>sug*sQNTQFv01j^592LVG2NnpH= z#ul~*h8}p8$DihEBYsM#9wu^_`(?`}2&syhjps(#4&!|`RwF%>Ap_&lisk}s`qA6< zzHN+*DA-G=wVY43++l-hM3emalaGmA(KE$D`fLYt(aMF%6kVX*+3YsQI;qQ|{~}uy z3b3$C+e|P-P@yMgWqFC2jLg#&3rLE(QpP1Hc$#kRDinpqfgUtNEF~O!P{|Vp0*$=N zWoLfFotjS*nv}7uwAD?F+~);1k0M6QyO%?S-QzYyRpqYE_tC6VBZS*PfppLeLwI`w zFtEyeu?(7(BL(e zU;>BP`VA0FGT4zyG;dQ-`_)eNp&@Xhk)-pVrs1B21#n2Va)PXg+rJ>aDad>Pj7mB# zjP4I|Zm8Z>#?cavDHB$|im~f?(ihP#PO^VW*41MCBZgWC(6Q}c@Xv{sUyGQChyc+^ zVz2`>;pf{)YR>0=i_(Q^pxfL*tji-x%>?obX=n>?~7y)M?4O?I1@6#9N5 z?R2!X$w}{`l9-^u8;TAvR_{=>%c&>9{uM%|by%j`Ff2nh`N0Xn~o)3)#-28P^#j!H2XglV!{k-h-zEI!NMvld~?{H$t#^6itKdnX9BjC z@$ESt9-ex=9nI^{3rU%7-Kc~UKQsah_Jc&-J0w=V+O7}NoOY_c%iuTxmUiS1Gz>5k z6>nxwazktz@lwQvp5|yc*zzq#M*eam?HT>2<<3mO{7Zav>y1Y*tmhAlwu5Kv%`#)< z8OC!p;3f)G#8vk5bC*;^i{_+PIIWK{98K+8WA1;~ZyO_=+5tccZBW%mq|6LA?B(8coh?;H zX67+_gmFw{YIcMk`Dy|=%N?Ok$Hj}u^a@E+73Oa@8atpm7mZWd;>0j6v%B|hfC^lM z8_rT`I1SJ&-x#^WUoq$c>1=ae+!AGG!V5(u^5EkU9Fb?{Grk`^7sqsYU|V zNGr={FWIRpxU8+M&9d~WZ~>(Jh$l9n8-oFed(a^iIGEI-kDlExA;o{W2Q75Fz;MG7c61|; zgR~#vwKuC}J#en=UC;U+>$yL=1ssMN+U8h!xP;Ks0y*M+IH|DI?6X;D@&IV}xK=P( z?(yLzAjLdNj^-q4<>x(gal*YD;0p-#P|(r^CXD|`%^93h3j<+7Cjf#-`C-*1(x}1Z z4$d%IYVrW}`BP0UHDe?c6!31>Cmf3S<^gh&l0UBYCxvyhdPwKS2<#%YVL%fYRqn@b zS0`m%zjF~DT*W&i39&DnJc$Vj`B#u*?fC|KwNJcq7$5X=%BibehCg8wN_144O-PwA z#m-xUpBw;wvUhIW7g^M5DC=lOCqIrpNpz@aT!s-IZfgFkSkXUar4MlCh=;b7HS~JT z+LXvq!i|`(LjEGd60L5?g}O~IL1xg8mnPD+Ioc#XWa{WtVY*L*{al!ATry&Q2hiAc z{ReMR0TugaUm(t8SL)jgDCEvFCLNoEYbP!>UYjou{fV*Li6Di_CFul~5o|vRQuul< zI#Las)GtfO64@d+4`&Q$-so;Hp9>3#NJp%wgQ+i~^IZ1xwyiU{6!)#wma|q%E65yT zoVoNcECklChAh)#+Py{(YDT_;S$x;D8UX~1&Q={hTY@H}r}Q~}pc4vp0 z&QBt-08aiD_}}0gLf4zaZrXQgxopnk8%ap;IEusNW+E0rJ=VbqJWl0OR*_m7oa2;Y zEyh*^+zu9dTCVa?KSA;PJv_n;mVP#Z|K`AF%vfX!DTciR)~xQ^6Pw>}UcD0Ayau&bf@4Y25f=l{ zQ#e5j+Q8jTaNH3;oDvsIXn~;3(oxOFpNQwr>d=UYi1s+uxy4U!5eFI00tOj=;ni8~ zDEmy%i+~`wH;Od93rZ|hhWkf`k!kD-A;WNy!a5vu|5&W@jtqWp>Z8%mBCR(v@vNOyhys>`Qs7I3 ztz!!-a&xJHzA##s*9B^*DYreVY?0;~ov{8aek};MjO-9W-47aF%vH=x;&Oe6Kz_T! z^IfiB0cncbGa1z?ZhPa7>YECB|D?`S@gR}P#MhRZceFR!a!BgjqRII3-Xa9y>PJc| zE4(3h<+2WdZDu)XPB+tnj)v&Vi4yaejw|Q*dyA6=K4p4<0g~)jDDkC0No{m-@Mo)- z;O-XWR!{PJ`}gF)q9xgGKngGXRGHJ2-I$rJo6GR!7Lc5l<4fz=yf*~er_ha9H)sq8 z%tE+l?fC{BNJKpNXArBa(qexp=Ghng;(YAdd7)RT?`0pI33T27l)>F<@P?W#tUxg` zREY~YoNn~+c`RO*Nx*ysf~d0FZb0|pc+w012BvE0a1#=9_qu3SUF;1V9p(rtiRr2b z=x`7&DiM1m>WP!{NBm!xi533A^2$3@WPjB0i;jnJS7@4>f(y2?$ahAq>TiotT}y9NUOw}HI|Q>! z2uDY&Wh%;NhL?C??__Uno6&)&e~(9SC;q|Hp~Kes7)m6$`|Z%${R`vC!(9o+cx%6V{?!`~_onpBXXq+)m#M_ z;Df_`S#iKuwlDS`*MsShF(Oi~*Q+l$f;r(&!arkH#Qk#=SMaQCY-OOMPu7QFBHjtu z(9Sjuj*P)$Ng&0vBe41ma+rJw>MnP#-Wcjeq)xQA=K+LR z?}+3YOg@$#g-d+6Rk)G@8D+g}v)$ePf-FyAnJ#^e{k1c0n9Cr7BhPSz+ZO{{!R#8C zODalg>Yl*JsVZK0@0sZ}fg_sIf7cQs09trbyi9p6Io@eV0FzvkB+uh<*J$lc(~Ax~ za&mx1d6(43(F}wYeWu_#cxZVuNGJR+H@ZYq{vQ;$2DwKo}?a$Md~8t2W_%@NY&S zh6*cuEp70e*eGrtf;rcT!z($|sRM%UdrNl*1L%s_oy0>46#bofd3Z+hRp_WB4QePU zM`&ALzW~#4=sv#t1?9J4Fqu6)aTED(Poj=E*4yG?T2atR(|IW~OP(WkQa=hu(aZR= z|Gk&n>bK`sok?(Pu6jgV^4GeK`JmaD=&xVlft;qeV|V&*iY(Du2M^v*bSNPgmm0bY zQ=|Yufydq4_<{-t5*dX(aq^+Qsf0?*r=7q!<@a!t zCE=ly`8cPv>TFCZKQAswDoqvf!c&ld?5Dx`lot1SJCPv>IQ&|$);0k*Cm|=N%KPT@ z^A!)p&EFST8ln}~-u`u(YHxi+>s$H8(oFZ-%7x|+DU z0u@|7xwv33;W$s166e#Y*+eo)`iahaQc}JO zy>(+#+z;207aq_)WghqLDjcPcv5%y(zA0;r1db^MSR{CG0X~X_R zl^t9Oicw?eRxoTu?Cq>2Azw+PWn`3}Y;W}Ab`6h(;FgqN5 z>2Vive*Zpx;y8cP|CN1H-&|M(;f8JIa7;uwP4M4iK*Hn&{0S87*)j}XtKKkoGCzpY zfKPbyxmZN{sNa0DI6ez;l?z0s9*@1o8zR1y)_k5#TJP*$yon4MNG#7!4VlMxST2s& zLvs_pQJ`r_y`Mk{;I_fF?q0Uubk^NvdxvGMulGFTj8MQZ-;En#?A4wK0cKQD%%kt0 z2xsR43ef&}sXF)*eUycT1-oMwN7}sz#yvhv4EdlHYC~&F0Dn3j+-#Qp0m}(_f=&Cj z_I%7v4H;%|+n@&LBlYnBD!qDK0BL3$y7DlJyIWVDibvDrMY436p(EK=)rZh;QHKfS z=;&{6Xr7v@Uyd#5bE9|SdtjlP1&fD<@dD&FV8hceAXWs4%wP5bYpGv3<+tpAGHWTLiN+(V7 zZ676!67$$R=?cRQlr-`gFPa)u>}f8l8vW+3{H!f59gcZtG$MS#aG7yqaNkLG%$?eT zR~KtJo7Z15DugVIaDD9}b!6y{FU~Bj|wx}BKX0zK- zf3(fm62YC~hKCKK>fLqNG!<@B!u;XHXM!M7($*@!AGyk-`17-G1REVa9h4qP1cj~sEsd1v|=D_4u5Ter`J2XmO>oQrl+?U>DY$z zU`$M2k(e%@2%yA*r~6(c z+c9mpfAeQfL|*c&GJWm}EBF*~t-brEls|Pu9e7?rVQ?8Kf*NtN@%3Sj% zFE~s{nOrWD(em>66!3Vd#H`z+%Kz6qKN1uCDPClz#zzB_#co_w`(UxcQZ zBHjJV;d^H;#<{TsGkyvpTpT3h1zxn@tIKMwXubq2eoco52Lds)Dp2WqPtxeYo2R@8 ze|an-SU|0rt*fN;JOB3O!7e=bQ4v_J@EVcs4mC&A7L$dcc)A)IN3i@D!&0>TO&_haW_#N(#?snOmx4+ZqXN zF}a||4C&X+8GyBXD+XdqQ>JT39O?^yFWTzfJ7>S2Gd0}ySTorC^~DbVoFJQr7t>5f z*xc^iUx*RoN{hieTyNL;z)>UX^TJH0=wAj}Bp?7Q<+&n`3ZuKe@E2gHbSm zmO1&{qZjMd+^XqK7nFmO=H>{AbkyIDZz$Lsdw1-~-P>7d*(arfpAmQ?SBLE%aviz% zL@K1M8wEmkx${XR3uXf7RnGlpmUqvRn|Ww|;56NT!a6NBlg-VnNSGU>vbl{OasNtx z(ZUo#-NH(*a8e}YHbkF>sRBhsyllitI5{;!>g0C0Vu>5XDQ9aI7%aQBm~`#UXB>Xm zGWcTSA;<@(K)#bLn~-lRC*fSm&Al(uJGmTP@4tHauH75yd7ZwN0-P>gf@cR=?2<-r z#YT|We37O+-l?MW3mAiC^cf|W(gnHhP@OUGHVdcKb;&ugj$;;=`i7{drlM#@ z3*byr0;%ZZ;rSx1*KoBN{wgD8bXPNJwPb+KYX zpnXiEN>E<3({1K=7t|#AVa$|XsERpFDbg^rREzCu`{U88)6d1l8h`G2Xt(IzT!rD# zeGnytp20L%j1eEh@xnh`{zesSRjVb1GTLvkeVOY@iBjYIYcjb{g(~u=! z7nfA>z*@@lYIjZ8(*Xgd(3w9T5N=&+5HL;(kn&D_{4l9n*zKhO8UxeMpr)DAA^bP7S^~ZOb1Ucfg z?UiLn;UNqk!3}ZqgM($;;-$##6|c^)oxN7@9oub4M%gz5 zy4W;r%<$R+XKP>4I4#W!a~j9j=k^n}x)o8oVcw3zM`@jI%r?XFdrtj$Jd3qZ6dE<< z)x5YOrf`{hVrz)ZEOcF#mU)EGIJ1@*M&ocYc#pQgqk3VRql0n^R^z)F4D%Cu$2kY( z=Z)n4Wq&*tR|CDNiYDYA@7~*SFXG^xyo?-LJbsrZfbm{ufS12+eURRby2cK89!RKG zdXbqK--!~SubvdO5;CqP=4WL7PVTJlEU*k@Eg|k&H_t$?aYRt}u@8?VO zgUn9k4gR+b#o61rY6YqcS%vQEz4q)(dI^0u8)%GfLx92x{i{NK=1CySQzRZ5Yexxt|mTk1l0(~K)3Y{kiZ&5szVx^m#oAaHV z>!qw!VvDDK1;USwS^nt(JeDA_2bInHNvQJ%I{7*GWoM$KjID+~-av21Hx$)GS2HS= zOzOjP$5Ac|Pl^W%RR5k;ws$S=ZWBgZtIChh2lfagf7|3Ue5&Nr3*AX{dw_4J#7P_} zh_6Csy9Ww2>(;5x8{@OkR%4;q@R&7SWlU;nYCYAx*G55~uj<;fpGX_=WT%&`vKUWZ z{k8To0;-{>6Gmb|WoN`fdzX2dRryshSq{Dc`_!XO7@H3t3BRJX>GO>qvu{(Cqp~9H z#ED1;ZQA7^qA>rGNvvGmruzE4$J*45ulD04NtWvwJ_HWDh;oeQ#4?GJZEvx-d15hN zsKI7`J9lY|u&^$AjGV;pp-b}z%&}``9sip|^s&J3wzgH7S8u#t#r4hdci+vobO-7@ z`XZ_rx&-iJGA^UHJ^@xkxnbq|ic*y+`8pUEw!r5vi6cVT@dT!f4opihCF*^1pp^X#kIiM z7{vCRx6oj)M_j-$Ot!%yxH@+KHNWOpPdR?6 zU@2Fw8q(ZmMo^mTERxos2~0-H$Dvx5k1eGv;ErnTjjFy3Z#Ra4ff;(<+5GTgc1TDB zaXNODw2hdwOGULSGVuyqo zBiXX}l)l_6h^>siNN&0(PPC#gEDSNA7{ptQ@_g+E2TN?WyPiMfa?u^dyQEeZJq9X8 zK$Li<3~FZnQbImR8^J7*O=^yGEE4RU&xHk8=W}vqZRkOZh2prGMoW9>vv`C7rlf+2 zO31fjEVe2#z~z=vK`95l+*z)b&ezkZRWJ??yKz4W!} zIoCAc>Uy|5EixAdjOTkM7v#|b85V7U?190j5Tg0}s`2@bH5H~I^HPw_IoQ@na*GqLz+;ZjsY zR4fln=9NUlgS4RXKWtN+bQ%;fNiQ`DoAG9eE5OJi4&luwk!X|F)6;_mEG+xYvp5lc ze*O(cewVX7DAu=tr}=w1)MVCgO9riQ94l0zbL%R>lY8kR+)OxPyEbWJ;%% zfL+f#w$-xz26$jq{J?0=e3UDmxu$-{Vkm`x%yt204`hG@o)+N@5K=NDt!|>awN@}( zKf!pgjgP2KtUTm%xRve}CE)ETgj~IB!l52MA16){xU7*B4n=2^$zz*X* zi;JW$3nI@XlO1Wh6}bcS)f8@-;xiegV5r9T7O@xOZ+gf{N+O8Yu}j6k@T|dMnyfM= zDn0S_KvRUv#B>m?O1=Qju=-pW*r2GjN&un(jkyxA4`Lo3j|bWT6It-o=cs)vkSfJW zAZ*9`uXS#vhXsODTuz(sm@T%Gawg#kS<+77E}XlpU}jOVAMM<#_>16c=t`H8_<%-j zW1*>GOn?5$9{mk=>=?#Wvk|wUk!3s)(Di-<+M_f(!ZPd!FwO)C^j2l=HnE)zo2Ac$ zaMO|nhnL$Dp`$;#I%`;dI@=}EzPM7C%>FS_ik{N?cCt{%2iV)V!iep_#TpOdvz;;% zk=XV|FpaPH4U(D6-#eo~;sunV5;_o>9nglIB!g9t2i@%vX4|`)1mDu%39ycbpy%Q2XpqXo{nQTeBw*hiN%v9WCL@E^T-a2Rs;e#r z$YMynK@Lh!<*h6%S1ufYx>US$Bd&1lkQcgNU~D4!I63k9TXf`VQprZ7U@M>DzsY^s z+w&iAdmf7Q0AzDqLnE3`rqn~TCQZFR^UaeF7tqJgipG)*yX>r=h%NZvL zBjODLHN3Uwp2<91my8opnY~LI?S9Xa=_BT~yga*8D-97ubA3WQzjvWNdE#AL_B}gW zi0G4zY-D`tqjJi;bzUIsLx+x=2l_SWyQZcBLhzW0kC&m*VD?XQyyBb`-{qF;oa*f5 z+g>L&{Z>yXzyt22UkgIcAZVWlfOJ97HcClJiDa{a&PiDaU{mzDMJ%*gdXn9Mw)Y3q z%;~(A{Cj%fx=M4NL%*XG69)N>)9sZ#wyzl3ZI5GiQjAeqSZmpGcPZ$2ucGnP-5oO1 zJ*Z=*GGbVLx5DUSU1L252oz{@mok7)b5sI*V+4c@W>xL;U7F)xLc0mO!9}-0<>5ab zVA}vMO;PsCKQ}jsOs`o<-92x5aobM17(p+J6&?;6D3mD0)Z1_Sfj&Ra=RF1n@GLmoEmS@^N7)JM^I*p3OY)(3$Dhl1JLz^JWwc2kS+iNADyT78XBPj z(LWk&s_PN*+1`8sA~|XDEMm2Nm=~wP^YSrhq3DDyIE>H|K`IFYh$@t5-eR*8Zvm*Y zL2o1_r@z}Ofk5hlTeL@J@Y8e=vy`_^u>5xynOy56&nUHX0K3}w_@AGpNW+Tb_uvC< zKPSYkbvDP#qTGW^9FYR#TZrxw#5^wVDu5h7=$L8Rt>x~@9F%aONtrV-F`aBpqZ|>J zH9VIztOlH72T_QMaK1)GcF^!57>etNN7fChJ)w-bK5X5*d%fc}D)RAwAvwvYi^EQ( zs6d7b+CA-ui-zBep0&;NI`zt;?C7VU4Um7aPD6%0-Q?-#M(bt7GqJrvjj9N15G|nb zUr!G!e|dK~@V62eJlYon%8r*B_%O6c>n934#bfS5r)ySmN= zwjgC2TxG#K6I0Uy#S4xTVLunXn6A$j; z+pE$~^ITv3{prep47QE$jfKM9(obo|G$_|U=R;|nW6TevT0nDk_a+>a&9`Z9HM_79 z1YR%lR+Br(Qz_(Y!+h7|0w>>Vn_LW_kpRL9olncrKa z0Ao2*smT3^At8|%dA%=&E_@`ZJCvxs?s(m%=lc5E=-~H{aQepo-E0L_*cVK?9iR{m z5&0WwmPW;Uq+R@coUXo^8kO08ygJL-YC&%!31rYb2UPI?fHzf5W{x_^`12X_i)kSC zWIWI=<=$TmCpVaD_m|v(4(gfl9&GHEGvlQn1lF07x;MZ64B*F3wS6T>+(1DS6@%-@ zl~foOQu1K8*06dAQMD=~1Tc8(&zbdD9-EVp%GT^pq4IM41jBieB<(<*_a%tShUGg# z`1+;%lhz>2rZ!(*J4V3$h`ee9lBnEuE%_qvXb$DIs&D4uDzPZ*Oakt9NwQptQFhW0 z27WGzd;ws;6ly-{08sQ{8zow4pz|UQ2j47`y#9s5Pz?NUcn`njY`y!yJqTV5&r{}W z?KUF~sWd_;u}Xm9fL5xH954k0ff0A9-JtE@>N}=Sf(Y2D34GxP@sSjT`Hm_vq_sAy zeYq=&$k?>1k17d5kn8MN|`5mb5gwSSeB^FBCb zbBtP0XVYr6?l`q}?Bl{7Tok~9ZGy)*+^PPJF()B=&5hE?2 zDi1P#MY#a$xPN|1N?=Y2D(Ok8y=O($_>Or_+zUgpXsSwMqbjHEY-nhBR6C4H@D?4| zEtSO<)zKJd^;BRqw}&w}DE?YOs3C;)0v#Bj?OPB!(IF~LJ$RL#u`smMet7(_plb>m zXCeuPv$3KaQ+e&HUIt1$2z`B7xPT-$MNFWZ`~%4feGE4&$jI7&)L_D?cFqTs-o8v= z{|MX5LEhWi!O=4+>Oc}XOj)quxa3~2PBOCE!^s&aFxM61M@oqK(`8ZfH}>=Fh6!OpMC$46m_smap79ok-& zJN}JUpl{;^CCU0+M~$xQ4kggWF1{_0h-5+TrinNJR?Aeqnb0)E|5h^!yg&0Hl&S|j z4eA(ip}BPupW90yXZ0d_E<9(q;_6wr1k?i!3L$i=g}L5_5=35H_FK8ab&>MoZQ9bI z_|r7N;Fw|o({ItX^Ppw?3#p-p6T(_=Mtt}JGO0bjrI!>KJ`^vBP>|zf!D6S`1TG&o z{@>sFkGz5qd(x2p^9vsS5e3b>oM5R_{)VG}aS#wReS=CJkouw@{049xe?PCl8a^Mv zEAae1%zuq0c>B15;Oj7=X?*_c2{Hg$SX@C)`|@Ak#|#ypkW;@y{I4g($$%&HVhJ4n z_ky7~pqN7T)Bk=#Ja|GBmFLnwbMmm_RwQsIL(*jM{`(26;0dqfA9X&Yj!-lYd^|D0 z`N0>AXMG3`|NY%+44xpH@+jy(hZJlV2@E+@KjO^_EwT82AJ1O!ghU>g=MOl<|GpkE zXzB!S()ag2{%6tu_k#az(f_^Rhl9cVe<%3i&i#LGBJPN{t?!GCUi!y90)He$ Date: Mon, 2 Apr 2018 11:41:30 -0400 Subject: [PATCH 041/379] more gracefully account for undefined stdout * It's possible to have an exception raised in BaseTask.run() before the stdout handler gets defined. This is problematic when the exception handler tries to access that undefined var .. causing another exception. Note that the second exception is caught also but it's not desirable to lose the first exception. * This fix checks to see if the stdout handler var is defined before calling it's methods. Thus, we retain the original error message. --- awx/main/tasks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 3aeaadd786..0de7094100 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -993,9 +993,10 @@ class BaseTask(LogErrorsTask): logger.exception('%s Exception occurred while running task', instance.log_format) finally: try: - stdout_handle.flush() - stdout_handle.close() - event_ct = getattr(stdout_handle, '_event_ct', 0) + if stdout_handle: + stdout_handle.flush() + stdout_handle.close() + event_ct = getattr(stdout_handle, '_event_ct', 0) logger.info('%s finished running, producing %s events.', instance.log_format, event_ct) except Exception: From d8f37e799ba18eae7b44af0552d74298f6ede265 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 2 Apr 2018 11:58:02 -0400 Subject: [PATCH 042/379] hide launch button for invalid JTs --- awx/main/access.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/access.py b/awx/main/access.py index bbbcd0652f..6f621cab46 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -344,7 +344,7 @@ class BaseAccess(object): if 'write' not in getattr(self.user, 'oauth_scopes', ['write']): user_capabilities[display_method] = False # Read tokens cannot take any actions continue - elif display_method == 'copy' and isinstance(obj, JobTemplate): + elif display_method in ['copy', 'start', 'schedule'] and isinstance(obj, JobTemplate): if obj.validation_errors: user_capabilities[display_method] = False continue From 557637afcb1f32607a653d07e5d517abb9b6b380 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Mon, 2 Apr 2018 12:05:16 -0400 Subject: [PATCH 043/379] better unicode handling --- awx/main/exceptions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/awx/main/exceptions.py b/awx/main/exceptions.py index 9b3ee247e1..5514934847 100644 --- a/awx/main/exceptions.py +++ b/awx/main/exceptions.py @@ -1,6 +1,9 @@ # Copyright (c) 2018 Ansible by Red Hat # All Rights Reserved. +import six + + # Celery does not respect exception type when using a serializer different than pickle; # and awx uses the json serializer # https://github.com/celery/celery/issues/3586 @@ -9,7 +12,7 @@ class _AwxTaskError(): def build_exception(self, task, message=None): if message is None: - message = "Execution error running {}".format(task.log_format) + message = six.text_type("Execution error running {}").format(task.log_format) e = Exception(message) e.task = task e.is_awx_task_error = True @@ -17,7 +20,7 @@ class _AwxTaskError(): def TaskCancel(self, task, rc): """Canceled flag caused run_pexpect to kill the job run""" - message="{} was canceled (rc={})".format(task.log_format, rc) + message=six.text_type("{} was canceled (rc={})").format(task.log_format, rc) e = self.build_exception(task, message) e.rc = rc e.awx_task_error_type = "TaskCancel" @@ -25,7 +28,7 @@ class _AwxTaskError(): def TaskError(self, task, rc): """Userspace error (non-zero exit code) in run_pexpect subprocess""" - message = "{} encountered an error (rc={}), please see task stdout for details.".format(task.log_format, rc) + message = six.text_type("{} encountered an error (rc={}), please see task stdout for details.").format(task.log_format, rc) e = self.build_exception(task, message) e.rc = rc e.awx_task_error_type = "TaskError" From dd8acb70be21453fdec1765a7410bc0af0f7fe9d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 29 Mar 2018 12:25:39 -0400 Subject: [PATCH 044/379] add deletion protection to hosts --- awx/api/views.py | 2 +- awx/main/models/inventory.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/awx/api/views.py b/awx/api/views.py index d3378c127d..a1ec23416c 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -2169,7 +2169,7 @@ class HostList(HostRelatedSearchMixin, ListCreateAPIView): return Response(dict(error=_(six.text_type(e))), status=status.HTTP_400_BAD_REQUEST) -class HostDetail(ControlledByScmMixin, RetrieveUpdateDestroyAPIView): +class HostDetail(RelatedJobsPreventDeleteMixin, ControlledByScmMixin, RetrieveUpdateDestroyAPIView): always_allow_superuser = False model = Host diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 83f5d65fd7..c748f841f3 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -517,7 +517,7 @@ class SmartInventoryMembership(BaseModel): host = models.ForeignKey('Host', related_name='+', on_delete=models.CASCADE) -class Host(CommonModelNameNotUnique): +class Host(CommonModelNameNotUnique, RelatedJobsMixin): ''' A managed node ''' @@ -703,6 +703,12 @@ class Host(CommonModelNameNotUnique): self._update_host_smart_inventory_memeberships() super(Host, self).delete(*args, **kwargs) + ''' + RelatedJobsMixin + ''' + def _get_related_jobs(self): + return self.inventory._get_related_jobs() + class Group(CommonModelNameNotUnique, RelatedJobsMixin): ''' From 6bd6cc7fbc55e1e302a70875db8c9ae255fe0f3a Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 2 Apr 2018 12:10:48 -0400 Subject: [PATCH 045/379] Added running indicator to smart status. Added sockets back to templates list --- .../templates/list-templates.controller.js | 17 +++++++++-- .../src/smart-status/smart-status.block.less | 29 ++++++++++++++++--- .../smart-status/smart-status.controller.js | 26 ++++++----------- .../smart-status/smart-status.partial.html | 14 ++++++--- 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/list-templates.controller.js index 321c991011..b2d02ad2b0 100644 --- a/awx/ui/client/features/templates/list-templates.controller.js +++ b/awx/ui/client/features/templates/list-templates.controller.js @@ -20,7 +20,9 @@ function ListTemplatesController( Prompt, resolvedModels, strings, - Wait + Wait, + qs, + GetBasePath ) { const vm = this || {}; const [jobTemplate, workflowTemplate] = resolvedModels; @@ -59,6 +61,15 @@ function ListTemplatesController( $scope[name] = dataset.results; }); + $scope.$on(`ws-jobs`, () => { + let path = GetBasePath('unified_job_templates'); + qs.search(path, $state.params.template_search) + .then(function(searchResponse) { + $scope.template_dataset = searchResponse.data; + $scope.templates = $scope.template_dataset.results; + }); + }); + vm.isInvalid = (template) => { if(isJobTemplate(template)) { return template.project === null || (template.inventory === null && template.ask_inventory_on_launch === false); @@ -319,7 +330,9 @@ ListTemplatesController.$inject = [ 'Prompt', 'resolvedModels', 'TemplatesStrings', - 'Wait' + 'Wait', + 'QuerySet', + 'GetBasePath' ]; export default ListTemplatesController; diff --git a/awx/ui/client/src/smart-status/smart-status.block.less b/awx/ui/client/src/smart-status/smart-status.block.less index 01f9cfee65..8b0b49c0f0 100644 --- a/awx/ui/client/src/smart-status/smart-status.block.less +++ b/awx/ui/client/src/smart-status/smart-status.block.less @@ -13,13 +13,12 @@ .SmartStatus-icon { width: 14px; height: 14px; - } .SmartStatus-iconDirectionPlaceholder { width: 14px; height: 7px; - border: 1px solid #d7d7d7; + border: 1px solid @d7grey; background: #f2f2f2; } @@ -47,11 +46,10 @@ .SmartStatus-iconPlaceholder { height: 14px; width: 14px; - border: 1px solid #d7d7d7; + border: 1px solid @d7grey; background: #f2f2f2; } - .SmartStatus-tooltip--successful, .SmartStatus-tooltip--success{ color: @default-succ; @@ -84,3 +82,26 @@ 1px 1px 0 @default-bg; .pulsate(); } + +.SmartStatus-waiting { + width: 14px; + height: 14px; + border: 1px solid @d7grey; +} + +@keyframes pulse_animation { + 0% { transform: scale(1); } + 50% { transform: scale(0); } + 100% { transform: scale(1); } +} + +.SmartStatus-running { + height: 14px; + width: 14px; + background-color: @default-succ; + animation-name: pulse_animation; + animation-duration: 5000ms; + transform-origin:70% 70%; + animation-iteration-count: infinite; + animation-timing-function: linear; +} diff --git a/awx/ui/client/src/smart-status/smart-status.controller.js b/awx/ui/client/src/smart-status/smart-status.controller.js index 2debca7d7c..8026a4d10c 100644 --- a/awx/ui/client/src/smart-status/smart-status.controller.js +++ b/awx/ui/client/src/smart-status/smart-status.controller.js @@ -33,24 +33,16 @@ export default ['$scope', '$filter', var sparkData = _.sortBy(recentJobs.map(function(job) { - var data = {}; + const finished = $filter('longDate')(job.finished) || job.status+""; - if (job.status === 'successful') { - data.value = 1; - data.smartStatus = " " + job.status.toUpperCase(); - } else if (isFailureState(job.status)) { - data.value = -1; - data.smartStatus = " " + job.status.toUpperCase(); - } else { - data.value = 0; - data.smartStatus = " " + job.status.toUpperCase(); - } - - data.jobId = job.id; - data.sortDate = job.finished || "running" + data.jobId; - data.finished = $filter('longDate')(job.finished) || job.status+""; - data.status_tip = "JOB ID: " + data.jobId + "
STATUS: " + data.smartStatus + "
FINISHED: " + data.finished; - data.detailsUrl = detailsBaseUrl + data.jobId; + const data = { + status: job.status, + jobId: job.id, + sortDate: job.finished || "running" + job.id, + finished: finished, + status_tip: "JOB ID: " + job.id + "
STATUS: " + job.status.toUpperCase() + "
FINISHED: " + finished, + detailsUrl: detailsBaseUrl + job.id + }; // If we've already determined that there are both failed and successful jobs OR if the current job in the loop is // pending/waiting/running then we don't worry about checking for a single job status diff --git a/awx/ui/client/src/smart-status/smart-status.partial.html b/awx/ui/client/src/smart-status/smart-status.partial.html index 1718632bb2..ee3a157eaa 100644 --- a/awx/ui/client/src/smart-status/smart-status.partial.html +++ b/awx/ui/client/src/smart-status/smart-status.partial.html @@ -10,14 +10,20 @@ tooltipInnerClass="SmartStatus-tooltip" title="">

From 1cf8e3cc20f71f2826346db3964015d7c6d2d27d Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 29 Mar 2018 15:15:47 -0400 Subject: [PATCH 046/379] Fixed linting errors --- .../relaunchButton/relaunchButton.component.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js index 8afa1e2057..1d88abdb8b 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js @@ -101,8 +101,7 @@ function atRelaunchCtrl ( }; }); } else { - - let launchParams = { + const launchParams = { id: vm.job.id, }; @@ -113,11 +112,11 @@ function atRelaunchCtrl ( } jobObj.postRelaunch(launchParams) - .then((launchRes) => { - if (!$state.includes('jobs')) { - $state.go('jobResult', { id: launchRes.data.id }, { reload: true }); - } - }); + .then((launchRes) => { + if (!$state.includes('jobs')) { + $state.go('jobResult', { id: launchRes.data.id }, { reload: true }); + } + }); } }); }; From d68679c5599755b6c5a078131d266cd70bf47d01 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 2 Apr 2018 13:57:28 -0400 Subject: [PATCH 047/379] Fixed regular inventory activity stream link --- .../src/activity-stream/factories/build-anchor.factory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js b/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js index 8fe870ce81..2b01398280 100644 --- a/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js +++ b/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js @@ -33,7 +33,7 @@ export default function BuildAnchor($log, $filter) { url += 'jobs/' + obj.id; break; case 'inventory': - url += obj.kind && obj.kind === "smart" ? 'inventories/smart/' + obj.id + '/' : 'inventories/inventory' + obj.id + '/'; + url += obj.kind && obj.kind === "smart" ? 'inventories/smart/' + obj.id + '/' : 'inventories/inventory/' + obj.id + '/'; break; case 'schedule': // schedule urls depend on the resource they're associated with From ea7a0b2f587de39fafdd60f9aa896ba2505dd822 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 2 Apr 2018 14:10:14 -0400 Subject: [PATCH 048/379] Fixes RBAC issue, ensures can admin of sub_obj when needed --- awx/main/access.py | 4 ++++ awx/main/tests/functional/test_rbac_role.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/awx/main/access.py b/awx/main/access.py index 6f621cab46..1af54ff1d6 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -2523,6 +2523,10 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj_resource.__class__, 'read', sub_obj_resource): return False + if isinstance(obj.content_object, Organization) and obj.role_field == 'member_role': + if not UserAccess(self.user).can_admin(sub_obj, data): + return False + if isinstance(obj.content_object, ResourceMixin) and \ self.user in obj.content_object.admin_role: return True diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 96484f9fee..6a94511f4c 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -50,3 +50,14 @@ def test_visible_roles(admin_user, system_auditor, rando, organization, project) assert rando not in project.admin_role assert access.can_read(project.admin_role) assert project.admin_role in Role.visible_roles(rando) + + +@pytest.mark.django_db +def test_org_user_role_attach(user, organization): + admin = user('admin') + nonmember = user('nonmember') + + organization.admin_role.members.add(admin) + + access = RoleAccess(admin) + assert not access.can_attach(organization.member_role, nonmember, 'members', None) From 139cfbfc5538ae78af931955c0a60a22a63a8dea Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 27 Mar 2018 12:17:47 -0400 Subject: [PATCH 049/379] add workflow job row item to jobs list --- awx/ui/client/features/jobs/jobs.strings.js | 1 + awx/ui/client/features/jobs/jobsList.view.html | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/awx/ui/client/features/jobs/jobs.strings.js b/awx/ui/client/features/jobs/jobs.strings.js index a21c2b62e9..703b771219 100644 --- a/awx/ui/client/features/jobs/jobs.strings.js +++ b/awx/ui/client/features/jobs/jobs.strings.js @@ -7,6 +7,7 @@ function JobsStrings (BaseString) { ns.list = { ROW_ITEM_LABEL_STARTED: t.s('Started'), ROW_ITEM_LABEL_FINISHED: t.s('Finished'), + ROW_ITEM_LABEL_WORKFLOW_JOB: t.s('Workflow Job'), ROW_ITEM_LABEL_LAUNCHED_BY: t.s('Launched By'), ROW_ITEM_LABEL_JOB_TEMPLATE: t.s('Job Template'), ROW_ITEM_LABEL_INVENTORY: t.s('Inventory'), diff --git a/awx/ui/client/features/jobs/jobsList.view.html b/awx/ui/client/features/jobs/jobsList.view.html index 6031a956ab..c5d18cecbe 100644 --- a/awx/ui/client/features/jobs/jobsList.view.html +++ b/awx/ui/client/features/jobs/jobsList.view.html @@ -34,6 +34,11 @@ value="{{ job.finished | longDate }}" inline="true"> + + Date: Mon, 2 Apr 2018 15:34:39 -0400 Subject: [PATCH 050/379] Removes --fake-initial from awx-manage migrate. The --fake-initial option is no longer needed and can cause application with an initial migration to fail as was seen in the network_ui application. --- Makefile | 2 +- installer/roles/image_build/files/launch_awx_task.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 41310bf678..e3dd2ecad8 100644 --- a/Makefile +++ b/Makefile @@ -234,7 +234,7 @@ migrate: if [ "$(VENV_BASE)" ]; then \ . $(VENV_BASE)/awx/bin/activate; \ fi; \ - $(MANAGEMENT_COMMAND) migrate --noinput --fake-initial + $(MANAGEMENT_COMMAND) migrate --noinput # Run after making changes to the models to create a new migration. dbchange: diff --git a/installer/roles/image_build/files/launch_awx_task.sh b/installer/roles/image_build/files/launch_awx_task.sh index d3d7c9cc9c..7d5583c988 100755 --- a/installer/roles/image_build/files/launch_awx_task.sh +++ b/installer/roles/image_build/files/launch_awx_task.sh @@ -10,7 +10,7 @@ ANSIBLE_REMOTE_TEMP=/tmp ANSIBLE_LOCAL_TEMP=/tmp ansible -i "127.0.0.1," -c loca ANSIBLE_REMOTE_TEMP=/tmp ANSIBLE_LOCAL_TEMP=/tmp ansible -i "127.0.0.1," -c local -v -m wait_for -a "host=$RABBITMQ_HOST port=5672" all ANSIBLE_REMOTE_TEMP=/tmp ANSIBLE_LOCAL_TEMP=/tmp ansible -i "127.0.0.1," -c local -v -m postgresql_db --become-user $DATABASE_USER -a "name=$DATABASE_NAME owner=$DATABASE_USER login_user=$DATABASE_USER login_host=$DATABASE_HOST login_password=$DATABASE_PASSWORD port=$DATABASE_PORT" all -awx-manage migrate --noinput --fake-initial +awx-manage migrate --noinput if [ ! -z "$AWX_ADMIN_USER" ]&&[ ! -z "$AWX_ADMIN_PASSWORD" ]; then echo "from django.contrib.auth.models import User; User.objects.create_superuser('$AWX_ADMIN_USER', 'root@localhost', '$AWX_ADMIN_PASSWORD')" | awx-manage shell awx-manage create_preload_data From 067ead35aca0d42f0da127710782365cfe8e5022 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 2 Apr 2018 15:37:47 -0400 Subject: [PATCH 051/379] Extend test and fix to include the admin_role --- awx/main/access.py | 8 ++++++-- awx/main/tests/functional/test_rbac_role.py | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 1af54ff1d6..bf74f2a491 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -2523,8 +2523,12 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj_resource.__class__, 'read', sub_obj_resource): return False - if isinstance(obj.content_object, Organization) and obj.role_field == 'member_role': - if not UserAccess(self.user).can_admin(sub_obj, data): + # Being a user in the member_role or admin_role of an organization grants + # administrators of that Organization the ability to edit that user. To prevent + # unwanted escalations lets ensure that the Organization administartor has the abilty + # to admin the user being added to the role. + if isinstance(obj.content_object, Organization) and obj.role_field in ['member_role', 'admin_role']: + if not UserAccess(self.user).can_admin(sub_obj, None): return False if isinstance(obj.content_object, ResourceMixin) and \ diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 6a94511f4c..438a72182b 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -61,3 +61,4 @@ def test_org_user_role_attach(user, organization): access = RoleAccess(admin) assert not access.can_attach(organization.member_role, nonmember, 'members', None) + assert not access.can_attach(organization.admin_role, nonmember, 'members', None) From c4635fa683abe881a4ebbf259259463e6b81d2c9 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 2 Apr 2018 16:03:00 -0400 Subject: [PATCH 052/379] Merge pull request #1199 from wwitzel3/fix-1189 Fixes RBAC issue, ensures can admin of sub_obj when needed --- awx/main/access.py | 8 ++++++++ awx/main/tests/functional/test_rbac_role.py | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/awx/main/access.py b/awx/main/access.py index 1de1e8fb3f..ddecce39e2 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -2396,6 +2396,14 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj_resource.__class__, 'read', sub_obj_resource): return False + # Being a user in the member_role or admin_role of an organization grants + # administrators of that Organization the ability to edit that user. To prevent + # unwanted escalations lets ensure that the Organization administartor has the abilty + # to admin the user being added to the role. + if isinstance(obj.content_object, Organization) and obj.role_field in ['member_role', 'admin_role']: + if not UserAccess(self.user).can_admin(sub_obj, None): + return False + if isinstance(obj.content_object, ResourceMixin) and \ self.user in obj.content_object.admin_role: return True diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 16ad46f8db..62b8469abf 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -32,3 +32,15 @@ def test_role_access_attach(rando, inventory): inventory.read_role.members.add(rando) access = RoleAccess(rando) assert not access.can_attach(inventory.admin_role, rando, 'members', None) + + +@pytest.mark.django_db +def test_org_user_role_attach(user, organization): + admin = user('admin') + nonmember = user('nonmember') + + organization.admin_role.members.add(admin) + + access = RoleAccess(admin) + assert not access.can_attach(organization.member_role, nonmember, 'members', None) + assert not access.can_attach(organization.admin_role, nonmember, 'members', None) From e5dcfda1fee2d849559139d7d0368eb0ad834048 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Mon, 2 Apr 2018 15:22:18 -0400 Subject: [PATCH 053/379] append registered hostname to policy list * The Instance Group list of instances was getting over-written with every call to the register_instance management command. * This changeset appends --hostnames to the Instance Group policy list. --- .../management/commands/register_queue.py | 138 ++++++++++++------ 1 file changed, 95 insertions(+), 43 deletions(-) diff --git a/awx/main/management/commands/register_queue.py b/awx/main/management/commands/register_queue.py index 1e7912836d..85c7842381 100644 --- a/awx/main/management/commands/register_queue.py +++ b/awx/main/management/commands/register_queue.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ansible Tower by Red Hat # All Rights Reserved. import sys +import six from awx.main.utils.pglock import advisory_lock from awx.main.models import Instance, InstanceGroup @@ -8,6 +9,13 @@ from awx.main.models import Instance, InstanceGroup from django.core.management.base import BaseCommand, CommandError +class InstanceNotFound(Exception): + def __init__(self, message, changed, *args, **kwargs): + self.message = message + self.changed = changed + super(InstanceNotFound, self).__init__(*args, **kwargs) + + class Command(BaseCommand): def add_arguments(self, parser): @@ -22,51 +30,95 @@ class Command(BaseCommand): parser.add_argument('--instance_minimum', dest='instance_minimum', type=int, default=0, help='The minimum number of instance that will be retained for this group from available instances') + + def get_create_update_instance_group(self, queuename, instance_percent, instance_min): + ig = InstanceGroup.objects.filter(name=queuename) + created = False + changed = False + + (ig, created) = InstanceGroup.objects.get_or_create(name=queuename) + if ig.policy_instance_percentage != instance_percent: + ig.policy_instance_percentage = instance_percent + changed = True + if ig.policy_instance_minimum != instance_min: + ig.policy_instance_minimum = instance_min + changed = True + + return (ig, created, changed) + + def update_instance_group_controller(self, ig, controller): + changed = False + control_ig = None + + if controller: + control_ig = InstanceGroup.objects.filter(name=controller).first() + + if control_ig and ig.controller_id != control_ig.pk: + ig.controller = control_ig + ig.save() + changed = True + + return (control_ig, changed) + + def add_instances_to_group(self, ig, hostname_list): + changed = False + + instance_list_unique = set([x.strip() for x in hostname_list if x]) + instances = [] + for inst_name in instance_list_unique: + instance = Instance.objects.filter(hostname=inst_name) + if instance.exists(): + instances.append(instance[0]) + else: + raise InstanceNotFound(six.text_type("Instance does not exist: {}").format(inst_name), changed) + + ig.instances = instances + + instance_list_before = set(ig.policy_instance_list) + instance_list_after = set(instance_list_unique) + if len(instance_list_before) != len(instance_list_after) or \ + len(set(instance_list_before) - set(instance_list_after)) != 0: + changed = True + + ig.policy_instance_list = list(instance_list_unique) + ig.save() + return (instances, changed) + def handle(self, **options): + instance_not_found_err = None queuename = options.get('queuename') if not queuename: raise CommandError("Specify `--queuename` to use this command.") - changed = False + ctrl = options.get('controller') + inst_per = options.get('instance_percent') + inst_min = options.get('instance_minimum') + hostname_list = [] + if options.get('hostnames'): + hostname_list = options.get('hostnames').split(",") + with advisory_lock('instance_group_registration_%s' % queuename): - ig = InstanceGroup.objects.filter(name=queuename) - control_ig = None - if options.get('controller'): - control_ig = InstanceGroup.objects.filter(name=options.get('controller')).first() - if ig.exists(): - print("Instance Group already registered {}".format(ig[0].name)) - ig = ig[0] - if control_ig and ig.controller_id != control_ig.pk: - ig.controller = control_ig - ig.save() - print("Set controller group {} on {}.".format(control_ig.name, ig.name)) - changed = True - else: - print("Creating instance group {}".format(queuename)) - ig = InstanceGroup(name=queuename, - policy_instance_percentage=options.get('instance_percent'), - policy_instance_minimum=options.get('instance_minimum')) - if control_ig: - ig.controller = control_ig - ig.save() - changed = True - hostname_list = [] - if options.get('hostnames'): - hostname_list = options.get('hostnames').split(",") - instance_list = [x.strip() for x in hostname_list if x] - for inst_name in instance_list: - instance = Instance.objects.filter(hostname=inst_name) - if instance.exists() and instance[0] not in ig.instances.all(): - ig.instances.add(instance[0]) - print("Added instance {} to {}".format(instance[0].hostname, ig.name)) - changed = True - elif not instance.exists(): - print("Instance does not exist: {}".format(inst_name)) - if changed: - print('(changed: True)') - sys.exit(1) - else: - print("Instance already registered {}".format(instance[0].hostname)) - ig.policy_instance_list = instance_list - ig.save() - if changed: - print('(changed: True)') + (ig, created, changed) = self.get_create_update_instance_group(queuename, inst_per, inst_min) + if created: + print(six.text_type("Creating instance group {}".format(ig.name))) + elif not created: + print(six.text_type("Instance Group already registered {}").format(ig.name)) + + if ctrl: + (ig_ctrl, changed) = self.update_instance_group_controller(ig, ctrl) + if changed: + print(six.text_type("Set controller group {} on {}.").format(ctrl, queuename)) + + try: + (instances, changed) = self.add_instances_to_group(ig, hostname_list) + for i in instances: + print(six.text_type("Added instance {} to {}").format(i.hostname, ig.name)) + except InstanceNotFound as e: + instance_not_found_err = e + + if changed: + print('(changed: True)') + + if instance_not_found_err: + print(instance_not_found_err.message) + sys.exit(1) + From 278a2091c2f621ea8cff2478084886479f5576fe Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 2 Apr 2018 16:47:49 -0400 Subject: [PATCH 054/379] Scrub passwords from extra vars preview on launch --- .../preview/prompt-preview.controller.js | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js index eb88095284..8ba42991f5 100644 --- a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js @@ -37,25 +37,27 @@ export default scope.promptData.extraVars = ToJSON(scope.parseType, scope.promptData.prompts.variables.value, false); - if(scope.promptData.launchConf.ask_tags_on_launch) { + const surveyPasswords = {}; + + if (scope.promptData.launchConf.ask_tags_on_launch) { scope.promptData.prompts.tags.value = consolidateTags(scope.promptData.prompts.tags.value, "#job_launch_job_tags"); } - if(scope.promptData.launchConf.ask_skip_tags_on_launch) { + if (scope.promptData.launchConf.ask_skip_tags_on_launch) { scope.promptData.prompts.skipTags.value = consolidateTags(scope.promptData.prompts.skipTags.value, "#job_launch_skip_tags"); } - if(scope.promptData.launchConf.survey_enabled){ + if (scope.promptData.launchConf.survey_enabled){ scope.promptData.surveyQuestions.forEach(surveyQuestion => { // grab all survey questions that have answers - if(surveyQuestion.required || (surveyQuestion.required === false && surveyQuestion.model.toString()!=="")) { - if(!scope.promptData.extraVars) { + if (surveyQuestion.required || (surveyQuestion.required === false && surveyQuestion.model.toString()!=="")) { + if (!scope.promptData.extraVars) { scope.promptData.extraVars = {}; } scope.promptData.extraVars[surveyQuestion.variable] = surveyQuestion.model; } - if(surveyQuestion.required === false && _.isEmpty(surveyQuestion.model)) { + if (surveyQuestion.required === false && _.isEmpty(surveyQuestion.model)) { switch (surveyQuestion.type) { // for optional text and text-areas, submit a blank string if min length is 0 // -- this is confusing, for an explanation see: @@ -69,10 +71,20 @@ export default break; } } + + if (surveyQuestion.type === 'password' && surveyQuestion.model.toString()!=="") { + surveyPasswords[surveyQuestion.variable] = '$encrypted$'; + } }); } - scope.promptExtraVars = $.isEmptyObject(scope.promptData.extraVars) ? '---' : '---\n' + jsyaml.safeDump(scope.promptData.extraVars); + // We don't want to modify the extra vars when we merge them with the survey + // password $encrypted$ strings so we clone it + const extraVarsClone = _.cloneDeep(scope.promptData.extraVars); + // Replace the survey passwords with $encrypted$ to display to the user + const cleansedExtraVars = Object.assign(extraVarsClone, surveyPasswords); + + scope.promptExtraVars = $.isEmptyObject(scope.promptData.extraVars) ? '---' : '---\n' + jsyaml.safeDump(cleansedExtraVars); ParseTypeChange({ scope: scope, From d2ec880cad64e06a7b3ca061fc144bc9bcfb3f53 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 2 Apr 2018 15:49:10 -0400 Subject: [PATCH 055/379] allow org members to see teams in org --- .../0028_v330_members_can_see_teams.py | 31 +++++++++++++++++++ awx/main/models/organization.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 awx/main/migrations/0028_v330_members_can_see_teams.py diff --git a/awx/main/migrations/0028_v330_members_can_see_teams.py b/awx/main/migrations/0028_v330_members_can_see_teams.py new file mode 100644 index 0000000000..7d461c40dc --- /dev/null +++ b/awx/main/migrations/0028_v330_members_can_see_teams.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.11 on 2018-04-02 19:18 +from __future__ import unicode_literals + +from django.db import migrations +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + +import awx.main.fields + +from awx.main.migrations import ActivityStreamDisabledMigration +from awx.main.migrations import _rbac as rbac +from awx.main.migrations import _migration_utils as migration_utils + + +class Migration(ActivityStreamDisabledMigration): + + dependencies = [ + ('main', '0027_v330_add_tower_verify'), + ] + + operations = [ + migrations.AlterField( + model_name='team', + name='read_role', + field=awx.main.fields.ImplicitRoleField(null=b'True', on_delete=django.db.models.deletion.CASCADE, parent_role=[b'organization.auditor_role', b'organization.member_role', b'member_role'], related_name='+', to='main.Role'), + ), + migrations.RunPython(migration_utils.set_current_apps_for_migrations), + migrations.RunPython(rbac.rebuild_role_hierarchy), + ] diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index db406fd2ed..8bf0701821 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -112,7 +112,7 @@ class Team(CommonModelNameNotUnique, ResourceMixin): parent_role='admin_role', ) read_role = ImplicitRoleField( - parent_role=['organization.auditor_role', 'member_role'], + parent_role=['organization.auditor_role', 'organization.member_role', 'member_role'], ) def get_absolute_url(self, request=None): From fe04f69e891078dd3e6eabd2171796c20ff08114 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 07:50:49 -0400 Subject: [PATCH 056/379] update tests for org members seeing teams --- awx/main/tests/functional/api/test_organization_counts.py | 4 ++-- awx/main/tests/functional/test_projects.py | 6 +++--- awx/main/tests/functional/test_rbac_api.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/awx/main/tests/functional/api/test_organization_counts.py b/awx/main/tests/functional/api/test_organization_counts.py index 9c4f536b09..67bbb81858 100644 --- a/awx/main/tests/functional/api/test_organization_counts.py +++ b/awx/main/tests/functional/api/test_organization_counts.py @@ -92,7 +92,7 @@ def test_org_counts_detail_member(resourced_organization, user, get): 'job_templates': 0, 'projects': 0, 'inventories': 0, - 'teams': 0 + 'teams': 5 } @@ -123,7 +123,7 @@ def test_org_counts_list_member(resourced_organization, user, get): 'job_templates': 0, 'projects': 0, 'inventories': 0, - 'teams': 0 + 'teams': 5 } diff --git a/awx/main/tests/functional/test_projects.py b/awx/main/tests/functional/test_projects.py index 55cc484006..dab22a4d45 100644 --- a/awx/main/tests/functional/test_projects.py +++ b/awx/main/tests/functional/test_projects.py @@ -176,9 +176,9 @@ def test_team_project_list(get, team_project_list): @pytest.mark.django_db -def test_team_project_list_fail1(get, team_project_list): - objects = team_project_list - res = get(reverse('api:team_projects_list', kwargs={'pk':objects.teams.team2.pk,}), objects.users.alice) +def test_team_project_list_fail1(get, team, rando): + # user not in organization not allowed to see team-based views + res = get(reverse('api:team_projects_list', kwargs={'pk':team.pk,}), rando) assert res.status_code == 403 diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/test_rbac_api.py index a390b4c54f..a67474a9bb 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/test_rbac_api.py @@ -58,9 +58,9 @@ def test_get_roles_list_user(organization, inventory, team, get, user): assert organization.member_role.id in role_hash assert this_user.admin_role.id in role_hash assert custom_role.id in role_hash + assert team.member_role.id in role_hash assert inventory.admin_role.id not in role_hash - assert team.member_role.id not in role_hash @pytest.mark.django_db @@ -151,7 +151,7 @@ def test_user_view_other_user_roles(organization, inventory, team, get, alice, b assert custom_role.id not in role_hash # doesn't show up in the user roles list, not an explicit grant assert Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id not in role_hash assert inventory.admin_role.id not in role_hash - assert team.member_role.id not in role_hash # alice can't see this + assert team.member_role.id in role_hash # alice can see team in her org # again but this time alice is part of the team, and should be able to see the team role team.member_role.members.add(alice) From f8c40cc26f353fda76577afdeee64cdb99b21f96 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 2 Apr 2018 16:07:17 -0400 Subject: [PATCH 057/379] properly support `v2_playbook_on_notify` in ansible 2.5+ see: https://github.com/ansible/awx/issues/1705 see: https://github.com/ansible/tower/issues/1205 related: https://github.com/ansible/ansible/pull/32633 --- awx/lib/awx_display_callback/module.py | 11 +++++------ awx/lib/tests/test_display_callback.py | 27 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/awx/lib/awx_display_callback/module.py b/awx/lib/awx_display_callback/module.py index 368063d0d1..8b4c59c8b1 100644 --- a/awx/lib/awx_display_callback/module.py +++ b/awx/lib/awx_display_callback/module.py @@ -274,15 +274,14 @@ class BaseCallbackModule(CallbackBase): 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. + def v2_playbook_on_notify(self, handler, host): + # NOTE: Not used by Ansible < 2.5. event_data = dict( - host=result._host.get_name(), - task=result._task, - handler=handler, + host=host.get_name(), + handler=handler.get_name(), ) with self.capture_event_data('playbook_on_notify', **event_data): - super(BaseCallbackModule, self).v2_playbook_on_notify(result, handler) + super(BaseCallbackModule, self).v2_playbook_on_notify(handler, host) ''' ansible_stats is, retoractively, added in 2.2 diff --git a/awx/lib/tests/test_display_callback.py b/awx/lib/tests/test_display_callback.py index d8c7923108..e3287b2055 100644 --- a/awx/lib/tests/test_display_callback.py +++ b/awx/lib/tests/test_display_callback.py @@ -28,6 +28,7 @@ CALLBACK = os.path.splitext(os.path.basename(__file__))[0] PLUGINS = os.path.dirname(__file__) with mock.patch.dict(os.environ, {'ANSIBLE_STDOUT_CALLBACK': CALLBACK, 'ANSIBLE_CALLBACK_PLUGINS': PLUGINS}): + from ansible import __version__ as ANSIBLE_VERSION from ansible.cli.playbook import PlaybookCLI from ansible.executor.playbook_executor import PlaybookExecutor from ansible.inventory.manager import InventoryManager @@ -284,3 +285,29 @@ def test_callback_plugin_saves_custom_stats(executor, cache, playbook): assert json.load(f) == {'foo': 'bar'} finally: shutil.rmtree(os.path.join(private_data_dir)) + + +@pytest.mark.parametrize('playbook', [ +{'handle_playbook_on_notify.yml': ''' +- name: handle playbook_on_notify events properly + connection: local + hosts: all + handlers: + - name: my_handler + debug: msg="My Handler" + tasks: + - debug: msg="My Task" + changed_when: true + notify: + - my_handler +'''}, # noqa +]) +@pytest.mark.skipif(ANSIBLE_VERSION < '2.5', reason="v2_playbook_on_notify doesn't work before ansible 2.5") +def test_callback_plugin_records_notify_events(executor, cache, playbook): + executor.run() + assert len(cache) + notify_events = [x[1] for x in cache.items() if x[1]['event'] == 'playbook_on_notify'] + assert len(notify_events) == 1 + assert notify_events[0]['event_data']['handler'] == 'my_handler' + assert notify_events[0]['event_data']['host'] == 'localhost' + assert notify_events[0]['event_data']['task'] == 'debug' From a7625b874775df49f47cc4d79dbf046f98440177 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Fri, 16 Mar 2018 16:26:51 -0400 Subject: [PATCH 058/379] add organization to app model --- awx/api/serializers.py | 13 ++++++----- awx/main/access.py | 5 +--- .../0027_v330_modify_application.py | 23 +++++++++++++++++++ awx/main/models/oauth.py | 8 ++++++- 4 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 awx/main/migrations/0027_v330_modify_application.py diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 027192125b..4cde14dba1 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -942,7 +942,6 @@ class UserSerializer(BaseSerializer): roles = self.reverse('api:user_roles_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:user_activity_stream_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:user_access_list', kwargs={'pk': obj.pk}), - applications = self.reverse('api:o_auth2_application_list', kwargs={'pk': obj.pk}), tokens = self.reverse('api:o_auth2_token_list', kwargs={'pk': obj.pk}), authorized_tokens = self.reverse('api:user_authorized_token_list', kwargs={'pk': obj.pk}), personal_tokens = self.reverse('api:o_auth2_personal_token_list', kwargs={'pk': obj.pk}), @@ -990,8 +989,8 @@ class UserAuthorizedTokenSerializer(BaseSerializer): class Meta: model = OAuth2AccessToken fields = ( - '*', '-name', 'description', 'user', 'token', 'refresh_token', - 'expires', 'scope', 'application', + '*', '-name', 'description', '-user', 'token', 'refresh_token', + 'expires', 'scope', 'application' ) read_only_fields = ('user', 'token', 'expires') @@ -1041,12 +1040,13 @@ class OAuth2ApplicationSerializer(BaseSerializer): model = OAuth2Application fields = ( '*', 'description', 'user', 'client_id', 'client_secret', 'client_type', - 'redirect_uris', 'authorization_grant_type', 'skip_authorization', + 'redirect_uris', 'authorization_grant_type', 'skip_authorization', 'organization' ) read_only_fields = ('client_id', 'client_secret') read_only_on_update_fields = ('user', 'authorization_grant_type') extra_kwargs = { - 'user': {'allow_null': False, 'required': True}, + 'user': {'allow_null': True, 'required': False}, + 'organization': {'allow_null': False}, 'authorization_grant_type': {'allow_null': False} } @@ -1195,7 +1195,7 @@ class OAuth2AuthorizedTokenSerializer(BaseSerializer): class Meta: model = OAuth2AccessToken fields = ( - '*', '-name', 'description', 'user', 'token', 'refresh_token', + '*', '-name', 'description', '-user', 'token', 'refresh_token', 'expires', 'scope', 'application', ) read_only_fields = ('user', 'token', 'expires') @@ -1312,6 +1312,7 @@ class OrganizationSerializer(BaseSerializer): admins = self.reverse('api:organization_admins_list', kwargs={'pk': obj.pk}), teams = self.reverse('api:organization_teams_list', kwargs={'pk': obj.pk}), credentials = self.reverse('api:organization_credential_list', kwargs={'pk': obj.pk}), + applications = self.reverse('api:o_auth2_application_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:organization_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates = self.reverse('api:organization_notification_templates_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:organization_notification_templates_any_list', kwargs={'pk': obj.pk}), diff --git a/awx/main/access.py b/awx/main/access.py index bf74f2a491..4b99717ddc 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -593,10 +593,7 @@ class OAuth2ApplicationAccess(BaseAccess): select_related = ('user',) def filtered_queryset(self): - accessible_users = User.objects.filter( - pk__in=self.user.admin_of_organizations.values('member_role__members') - ) | User.objects.filter(pk=self.user.pk) - return self.model.objects.filter(user__in=accessible_users) + return self.model.objects.filter(organization__in=self.user.organizations) def can_change(self, obj, data): return self.can_read(obj) diff --git a/awx/main/migrations/0027_v330_modify_application.py b/awx/main/migrations/0027_v330_modify_application.py new file mode 100644 index 0000000000..48e7b5e84e --- /dev/null +++ b/awx/main/migrations/0027_v330_modify_application.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.11 on 2018-03-16 20:25 +from __future__ import unicode_literals + +import awx.main.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0026_v330_emitted_events'), + ] + + operations = [ + migrations.AddField( + model_name='oauth2application', + name='organization', + field=models.ForeignKey(help_text='Organization containing this application.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='main.Organization'), + ), + ] diff --git a/awx/main/models/oauth.py b/awx/main/models/oauth.py index a1c13a23cd..a15b1cc2c1 100644 --- a/awx/main/models/oauth.py +++ b/awx/main/models/oauth.py @@ -31,7 +31,13 @@ class OAuth2Application(AbstractApplication): editable=False, validators=[RegexValidator(DATA_URI_RE)], ) - + organization = models.ForeignKey( + 'Organization', + related_name='applications', + help_text=_('Organization containing this application.'), + on_delete=models.CASCADE, + null=True, + ) class OAuth2AccessToken(AbstractAccessToken): From e9a128138a8e652437788596725949f45fba3583 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Mon, 19 Mar 2018 11:30:37 -0400 Subject: [PATCH 059/379] add org-app endpoint & permissions --- awx/api/serializers.py | 32 ++- awx/api/urls/organization.py | 2 + awx/api/views.py | 12 ++ awx/main/access.py | 85 +++++--- ...ion.py => 0028_v330_modify_application.py} | 2 +- awx/main/models/oauth.py | 1 + awx/main/signals.py | 9 - awx/main/tests/functional/api/test_oauth.py | 18 +- .../functional/api/test_organizations.py | 2 +- awx/main/tests/functional/test_rbac_oauth.py | 196 +++++++++++------- 10 files changed, 226 insertions(+), 133 deletions(-) rename awx/main/migrations/{0027_v330_modify_application.py => 0028_v330_modify_application.py} (93%) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 4cde14dba1..bbcedf0b37 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -989,7 +989,7 @@ class UserAuthorizedTokenSerializer(BaseSerializer): class Meta: model = OAuth2AccessToken fields = ( - '*', '-name', 'description', '-user', 'token', 'refresh_token', + '*', '-name', 'description', 'user', 'token', 'refresh_token', 'expires', 'scope', 'application' ) read_only_fields = ('user', 'token', 'expires') @@ -1015,7 +1015,8 @@ class UserAuthorizedTokenSerializer(BaseSerializer): return '' def create(self, validated_data): - validated_data['user'] = self.context['request'].user + current_user = self.context['request'].user + validated_data['user'] = current_user validated_data['token'] = generate_token() validated_data['expires'] = now() + timedelta( seconds=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS @@ -1024,7 +1025,7 @@ class UserAuthorizedTokenSerializer(BaseSerializer): obj.save() if obj.application is not None: RefreshToken.objects.create( - user=self.context['request'].user, + user=current_user, token=generate_token(), application=obj.application, access_token=obj @@ -1039,7 +1040,7 @@ class OAuth2ApplicationSerializer(BaseSerializer): class Meta: model = OAuth2Application fields = ( - '*', 'description', 'user', 'client_id', 'client_secret', 'client_type', + '*', 'description', '-user', 'client_id', 'client_secret', 'client_type', 'redirect_uris', 'authorization_grant_type', 'skip_authorization', 'organization' ) read_only_fields = ('client_id', 'client_secret') @@ -1104,6 +1105,10 @@ class OAuth2TokenSerializer(BaseSerializer): 'application', 'expires', 'scope', ) read_only_fields = ('user', 'token', 'expires') + extra_kwargs = { + 'scope': {'allow_null': False, 'required': True}, + 'user': {'allow_null': False, 'required': True} + } def get_modified(self, obj): if obj is None: @@ -1162,7 +1167,8 @@ class OAuth2TokenSerializer(BaseSerializer): return value def create(self, validated_data): - validated_data['user'] = self.context['request'].user + current_user = self.context['request'].user + validated_data['user'] = current_user validated_data['token'] = generate_token() validated_data['expires'] = now() + timedelta( seconds=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS @@ -1173,7 +1179,7 @@ class OAuth2TokenSerializer(BaseSerializer): obj.save() if obj.application is not None: RefreshToken.objects.create( - user=obj.application.user if obj.application.user else None, + user=current_user, token=generate_token(), application=obj.application, access_token=obj @@ -1199,6 +1205,9 @@ class OAuth2AuthorizedTokenSerializer(BaseSerializer): 'expires', 'scope', 'application', ) read_only_fields = ('user', 'token', 'expires') + extra_kwargs = { + 'scope': {'allow_null': False, 'required': True} + } def get_token(self, obj): request = self.context.get('request', None) @@ -1221,7 +1230,8 @@ class OAuth2AuthorizedTokenSerializer(BaseSerializer): return '' def create(self, validated_data): - validated_data['user'] = self.context['request'].user + current_user = self.context['request'].user + validated_data['user'] = current_user validated_data['token'] = generate_token() validated_data['expires'] = now() + timedelta( seconds=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS @@ -1232,7 +1242,7 @@ class OAuth2AuthorizedTokenSerializer(BaseSerializer): obj.save() if obj.application is not None: RefreshToken.objects.create( - user=obj.application.user if obj.application.user else None, + user=current_user, token=generate_token(), application=obj.application, access_token=obj @@ -1252,6 +1262,9 @@ class OAuth2PersonalTokenSerializer(BaseSerializer): 'application', 'expires', 'scope', ) read_only_fields = ('user', 'token', 'expires', 'application') + extra_kwargs = { + 'scope': {'allow_null': False, 'required': True} + } def get_modified(self, obj): if obj is None: @@ -1290,6 +1303,7 @@ class OAuth2PersonalTokenSerializer(BaseSerializer): validated_data['expires'] = now() + timedelta( seconds=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS ) + validated_data['application'] = None obj = super(OAuth2PersonalTokenSerializer, self).create(validated_data) obj.save() return obj @@ -1312,7 +1326,7 @@ class OrganizationSerializer(BaseSerializer): admins = self.reverse('api:organization_admins_list', kwargs={'pk': obj.pk}), teams = self.reverse('api:organization_teams_list', kwargs={'pk': obj.pk}), credentials = self.reverse('api:organization_credential_list', kwargs={'pk': obj.pk}), - applications = self.reverse('api:o_auth2_application_list', kwargs={'pk': obj.pk}), + applications = self.reverse('api:organization_applications_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:organization_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates = self.reverse('api:organization_notification_templates_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:organization_notification_templates_any_list', kwargs={'pk': obj.pk}), diff --git a/awx/api/urls/organization.py b/awx/api/urls/organization.py index b17ffce1fa..911143bb86 100644 --- a/awx/api/urls/organization.py +++ b/awx/api/urls/organization.py @@ -21,6 +21,7 @@ from awx.api.views import ( OrganizationInstanceGroupsList, OrganizationObjectRolesList, OrganizationAccessList, + OrganizationApplicationList, ) @@ -45,6 +46,7 @@ urls = [ url(r'^(?P[0-9]+)/instance_groups/$', OrganizationInstanceGroupsList.as_view(), name='organization_instance_groups_list'), url(r'^(?P[0-9]+)/object_roles/$', OrganizationObjectRolesList.as_view(), name='organization_object_roles_list'), url(r'^(?P[0-9]+)/access_list/$', OrganizationAccessList.as_view(), name='organization_access_list'), + url(r'^(?P[0-9]+)/applications/$', OrganizationApplicationList.as_view(), name='organization_applications_list'), ] __all__ = ['urls'] diff --git a/awx/api/views.py b/awx/api/views.py index d3378c127d..82e017f0ec 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1598,6 +1598,18 @@ class UserAuthorizedTokenList(SubListCreateAPIView): def get_queryset(self): return get_access_token_model().objects.filter(application__isnull=False, user=self.request.user) + + +class OrganizationApplicationList(SubListCreateAPIView): + + view_name = _("Organization OAuth2 Applications") + + model = OAuth2Application + serializer_class = OAuth2ApplicationSerializer + parent_model = Organization + relationship = 'applications' + parent_key = 'organization' + swagger_topic = 'Authentication' class OAuth2PersonalTokenList(SubListCreateAPIView): diff --git a/awx/main/access.py b/awx/main/access.py index 4b99717ddc..075a845ea8 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -580,13 +580,13 @@ class UserAccess(BaseAccess): class OAuth2ApplicationAccess(BaseAccess): ''' - I can read, change or delete OAuth applications when: + I can read, change or delete OAuth 2 applications when: - I am a superuser. - I am the admin of the organization of the user of the application. - - I am the user of the application. - I can create OAuth applications when: + - I am a user in the organization of the application. + I can create OAuth 2 applications when: - I am a superuser. - - I am the admin of the organization of the user of the application. + - I am the admin of the organization of the organization of the application. ''' model = OAuth2Application @@ -596,50 +596,79 @@ class OAuth2ApplicationAccess(BaseAccess): return self.model.objects.filter(organization__in=self.user.organizations) def can_change(self, obj, data): - return self.can_read(obj) - + if obj.organization in self.user.admin_of_organizations or self.user.is_superuser: + if not self.check_related('organization', Organization, data, role_field='admin_role'): + return False + return True + else: + return False + def can_delete(self, obj): - return self.can_read(obj) + if obj.organization in self.user.admin_of_organizations or self.user.is_superuser: + return True + else: + return False def can_add(self, data): if self.user.is_superuser: - return True - user = get_object_from_data('user', User, data) - if not user: - return False - return set(self.user.admin_of_organizations.all()) & set(user.organizations.all()) + return True + if not data: + return Organization.accessible_objects(self.user, 'admin_role').exists() + return self.check_related('organization', Organization, data, role_field='admin_role') class OAuth2TokenAccess(BaseAccess): ''' - I can read, change or delete an OAuth2 token when: + I can read, change or delete an app token when: - I am a superuser. - - I am the admin of the organization of the user of the token. + - I am the admin of the organization of the application of the token. - I am the user of the token. - I can create an OAuth token when: + I can create an OAuth2 app token when: - I have the read permission of the related application. + + I can read, change or delete a personal token when: + - + I can create an OAuth2 Personal Access Token when: + - I am a user. But I can only create a PAT for myself. ''' model = OAuth2AccessToken + select_related = ('user', 'application') - - def filtered_queryset(self): - accessible_users = User.objects.filter( - pk__in=self.user.admin_of_organizations.values('member_role__members') - ) | User.objects.filter(pk=self.user.pk) - return self.model.objects.filter(user__in=accessible_users) - + + def filtered_queryset(self): + org_access_qs = Organization.objects.filter( + Q(admin_role__members=self.user) | Q(auditor_role__members=self.user)) + return self.model.objects.filter(application__organization__in=org_access_qs) | self.model.objects.filter(user__id=self.user.pk) + def can_change(self, obj, data): - return self.can_read(obj) + print 'obj user:', obj.user, '\nself.user:', self.user + if (self.user.is_superuser) | (obj.user == self.user): + return True + elif self.user.is_system_auditor: + return False + elif not obj.application: + return False + return self.user in obj.application.organization.admin_role def can_delete(self, obj): - return self.can_read(obj) + if (self.user.is_superuser) | (obj.user == self.user): + return True + elif self.user.is_system_auditor: + return False + elif not obj.application: + return False + return self.user in obj.application.organization.admin_role def can_add(self, data): - app = get_object_from_data('application', OAuth2Application, data) - if not app: - return True - return OAuth2ApplicationAccess(self.user).can_read(app) + if 'application' in data: + app = get_object_from_data('application', OAuth2Application, data) + if self.user.is_system_auditor: + return False + elif app is None: + return True + return OAuth2ApplicationAccess(self.user).can_read(app) + return True class OrganizationAccess(BaseAccess): diff --git a/awx/main/migrations/0027_v330_modify_application.py b/awx/main/migrations/0028_v330_modify_application.py similarity index 93% rename from awx/main/migrations/0027_v330_modify_application.py rename to awx/main/migrations/0028_v330_modify_application.py index 48e7b5e84e..a54a3a9527 100644 --- a/awx/main/migrations/0027_v330_modify_application.py +++ b/awx/main/migrations/0028_v330_modify_application.py @@ -11,7 +11,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('main', '0026_v330_emitted_events'), + ('main', '0027_v330_add_tower_verify'), ] operations = [ diff --git a/awx/main/models/oauth.py b/awx/main/models/oauth.py index a15b1cc2c1..8626ca51c3 100644 --- a/awx/main/models/oauth.py +++ b/awx/main/models/oauth.py @@ -39,6 +39,7 @@ class OAuth2Application(AbstractApplication): null=True, ) + class OAuth2AccessToken(AbstractAccessToken): class Meta: diff --git a/awx/main/signals.py b/awx/main/signals.py index 5021f53290..aa7222278a 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -626,12 +626,3 @@ def create_access_token_user_if_missing(sender, **kwargs): post_save.connect(create_access_token_user_if_missing, sender=OAuth2AccessToken) -# @receiver(post_save, sender=User) -# def create_default_oauth_app(sender, **kwargs): -# if kwargs.get('created', False): -# user = kwargs['instance'] -# OAuth2Application.objects.create( -# name='Default application for {}'.format(user.username), -# user=user, client_type='confidential', redirect_uris='', -# authorization_grant_type='password' -# ) diff --git a/awx/main/tests/functional/api/test_oauth.py b/awx/main/tests/functional/api/test_oauth.py index 15362e71be..6b462bbd58 100644 --- a/awx/main/tests/functional/api/test_oauth.py +++ b/awx/main/tests/functional/api/test_oauth.py @@ -9,7 +9,7 @@ from oauth2_provider.models import RefreshToken @pytest.mark.django_db -def test_personal_access_token_creation(oauth_application, post, alice): +def test_personal_access_token_creation(oauth_application, post, alice): # TODO: Update this test url = drf_reverse('api:oauth_authorization_root_view') + 'token/' resp = post( url, @@ -19,44 +19,42 @@ def test_personal_access_token_creation(oauth_application, post, alice): oauth_application.client_id, oauth_application.client_secret ])) ) - resp_json = resp._container[0] assert 'access_token' in resp_json assert 'scope' in resp_json assert 'refresh_token' in resp_json - + @pytest.mark.django_db -def test_oauth_application_create(admin, post): +def test_oauth_application_create(admin, organization, post): response = post( reverse('api:o_auth2_application_list'), { 'name': 'test app', - 'user': admin.pk, + 'organization': organization.pk, 'client_type': 'confidential', 'authorization_grant_type': 'password', }, admin, expect=201 ) assert 'modified' in response.data assert 'updated' not in response.data - assert 'user' in response.data['related'] created_app = Application.objects.get(client_id=response.data['client_id']) assert created_app.name == 'test app' - assert created_app.user == admin assert created_app.skip_authorization is False assert created_app.redirect_uris == '' assert created_app.client_type == 'confidential' assert created_app.authorization_grant_type == 'password' + assert created_app.organization == organization @pytest.mark.django_db -def test_oauth_application_update(oauth_application, patch, admin, alice): +def test_oauth_application_update(oauth_application, organization, patch, admin, alice): patch( reverse('api:o_auth2_application_detail', kwargs={'pk': oauth_application.pk}), { 'name': 'Test app with immutable grant type and user', + 'organization': organization.pk, 'redirect_uris': 'http://localhost/api/', 'authorization_grant_type': 'implicit', 'skip_authorization': True, - 'user': alice.pk, }, admin, expect=200 ) updated_app = Application.objects.get(client_id=oauth_application.client_id) @@ -64,7 +62,7 @@ def test_oauth_application_update(oauth_application, patch, admin, alice): assert updated_app.redirect_uris == 'http://localhost/api/' assert updated_app.skip_authorization is True assert updated_app.authorization_grant_type == 'password' - assert updated_app.user == admin + assert updated_app.organization == organization @pytest.mark.django_db diff --git a/awx/main/tests/functional/api/test_organizations.py b/awx/main/tests/functional/api/test_organizations.py index 9ec6787d53..43a9ffb1e5 100644 --- a/awx/main/tests/functional/api/test_organizations.py +++ b/awx/main/tests/functional/api/test_organizations.py @@ -131,7 +131,7 @@ def test_organization_inventory_list(organization, inventory_factory, get, alice assert get(reverse('api:organization_inventories_list', kwargs={'pk': organization.id}), user=alice).data['count'] == 2 assert get(reverse('api:organization_inventories_list', kwargs={'pk': organization.id}), user=bob).data['count'] == 1 get(reverse('api:organization_inventories_list', kwargs={'pk': organization.id}), user=rando, expect=403) - + @pytest.mark.django_db @mock.patch('awx.api.views.feature_enabled', lambda feature: True) diff --git a/awx/main/tests/functional/test_rbac_oauth.py b/awx/main/tests/functional/test_rbac_oauth.py index 4aabd74f1e..ea8710ec7b 100644 --- a/awx/main/tests/functional/test_rbac_oauth.py +++ b/awx/main/tests/functional/test_rbac_oauth.py @@ -12,105 +12,151 @@ from awx.api.versioning import reverse @pytest.mark.django_db -class TestOAuthApplication: +class TestOAuth2Application: + + @pytest.mark.parametrize("user_for_access, can_access_list", [ + (0, [True, True]), + (1, [True, True]), + (2, [True, True]), + (3, [False, False]), + ]) + def test_can_read( + self, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization + ): + user_list = [admin, org_admin, org_member, alice] + access = OAuth2ApplicationAccess(user_list[user_for_access]) + app_creation_user_list = [admin, org_admin] + for user, can_access in zip(app_creation_user_list, can_access_list): + app = Application.objects.create( + name='test app for {}'.format(user.username), user=user, + client_type='confidential', authorization_grant_type='password', organization=organization + ) + assert access.can_read(app) is can_access + + + @pytest.mark.parametrize("user_for_access, can_access_list", [ + (0, [True, True]), + (1, [True, True]), + (2, [False, False]), + (3, [False, False]), + ]) + def test_can_edit_delete( + self, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization + ): + organization.admin_role.members.add(org_admin) + organization.member_role.members.add(org_member) + user_list = [admin, org_admin, org_member, alice] + access = OAuth2ApplicationAccess(user_list[user_for_access]) + app_creation_user_list = [admin, org_admin] + for user, can_access in zip(app_creation_user_list, can_access_list): + app = Application.objects.create( + name='test app for {}'.format(user.username), user=user, + client_type='confidential', authorization_grant_type='password', organization=organization + ) + assert access.can_change(app, {}) is can_access + assert access.can_delete(app) is can_access + + + + - @pytest.mark.parametrize("user_for_access, can_access_list", [ - (0, [True, True, True, True]), - (1, [False, True, True, False]), - (2, [False, False, True, False]), - (3, [False, False, False, True]), - ]) - def test_can_read_change_delete( - self, admin, org_admin, org_member, alice, user_for_access, can_access_list - ): - user_list = [admin, org_admin, org_member, alice] - access = OAuth2ApplicationAccess(user_list[user_for_access]) - for user, can_access in zip(user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), user=user, - client_type='confidential', authorization_grant_type='password' - ) - assert access.can_read(app) is can_access - assert access.can_change(app, {}) is can_access - assert access.can_delete(app) is can_access - - def test_superuser_can_always_create(self, admin, org_admin, org_member, alice): - access = OAuth2ApplicationAccess(admin) - for user in [admin, org_admin, org_member, alice]: - assert access.can_add({ - 'name': 'test app', 'user': user.pk, 'client_type': 'confidential', - 'authorization_grant_type': 'password' - }) - - def test_normal_user_cannot_create(self, admin, org_admin, org_member, alice): - for access_user in [org_member, alice]: - access = OAuth2ApplicationAccess(access_user) + def test_superuser_can_always_create(self, admin, org_admin, org_member, alice): + access = OAuth2ApplicationAccess(admin) for user in [admin, org_admin, org_member, alice]: - assert not access.can_add({ + assert access.can_add({ 'name': 'test app', 'user': user.pk, 'client_type': 'confidential', - 'authorization_grant_type': 'password' + 'authorization_grant_type': 'password', 'organization': 1 }) - - def test_org_admin_can_create_in_org(self, admin, org_admin, org_member, alice): - access = OAuth2ApplicationAccess(org_admin) - for user in [admin, alice]: - assert not access.can_add({ - 'name': 'test app', 'user': user.pk, 'client_type': 'confidential', - 'authorization_grant_type': 'password' - }) - for user in [org_admin, org_member]: - assert access.can_add({ - 'name': 'test app', 'user': user.pk, 'client_type': 'confidential', - 'authorization_grant_type': 'password' - }) + + def test_normal_user_cannot_create(self, admin, org_admin, org_member, alice): + for access_user in [org_member, alice]: + access = OAuth2ApplicationAccess(access_user) + for user in [admin, org_admin, org_member, alice]: + assert not access.can_add({ + 'name': 'test app', 'user': user.pk, 'client_type': 'confidential', + 'authorization_grant_type': 'password', 'organization': 1 + }) -@pytest.mark.skip(reason="Needs Update - CA") @pytest.mark.django_db -class TestOAuthToken: - - @pytest.mark.parametrize("user_for_access, can_access_list", [ - (0, [True, True, True, True]), - (1, [False, True, True, False]), - (2, [False, False, True, False]), - (3, [False, False, False, True]), - ]) - def test_can_read_change_delete( - self, post, admin, org_admin, org_member, alice, user_for_access, can_access_list +class TestOAuth2Token: + + def test_can_read_change_delete_app_token( + self, post, admin, org_admin, org_member, alice, organization ): user_list = [admin, org_admin, org_member, alice] - access = OAuth2TokenAccess(user_list[user_for_access]) + can_access_list = [True, True, False, False] + app = Application.objects.create( + name='test app for {}'.format(admin.username), user=admin, + client_type='confidential', authorization_grant_type='password', + organization=organization + ) + response = post( + reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), + {'scope': 'read'}, admin, expect=201 + ) for user, can_access in zip(user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), user=user, - client_type='confidential', authorization_grant_type='password' - ) - response = post( - reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), - {'scope': 'read'}, admin, expect=201 - ) token = AccessToken.objects.get(token=response.data['token']) - - assert access.can_read(token) is can_access # TODO: fix this test + access = OAuth2TokenAccess(user) + assert access.can_read(token) is can_access assert access.can_change(token, {}) is can_access assert access.can_delete(token) is can_access + + + def test_can_read_change_delete_personal_token_org_member( + self, post, admin, org_admin, org_member, alice + ): + # Tests who can read a token created by an org-member + user_list = [admin, org_admin, org_member, alice] + can_access_list = [True, False, True, False] + response = post( + reverse('api:o_auth2_personal_token_list', kwargs={'pk': org_member.pk}), + {'scope': 'read'}, org_member, expect=201 + ) + token = AccessToken.objects.get(token=response.data['token']) + for user, can_access in zip(user_list, can_access_list): + access = OAuth2TokenAccess(user) + assert access.can_read(token) is can_access + assert access.can_change(token, {}) is can_access + assert access.can_delete(token) is can_access + + + def test_can_read_personal_token_creator( + self, post, admin, org_admin, org_member, alice + ): + # Tests the token's creator can read their tokens + user_list = [admin, org_admin, org_member, alice] + can_access_list = [True, True, True, True] + + for user, can_access in zip(user_list, can_access_list): + response = post( + reverse('api:o_auth2_personal_token_list', kwargs={'pk': user.pk}), + {'scope': 'read', 'organization':None}, user, expect=201 + ) + token = AccessToken.objects.get(token=response.data['token']) + access = OAuth2TokenAccess(user) + assert access.can_read(token) is can_access + assert access.can_change(token, {}) is can_access + assert access.can_delete(token) is can_access + @pytest.mark.parametrize("user_for_access, can_access_list", [ - (0, [True, True, True, True]), - (1, [False, True, True, False]), - (2, [False, False, True, False]), - (3, [False, False, False, True]), + (0, [True, True]), + (1, [True, True]), + (2, [True, True]), + (3, [False, False]), ]) def test_can_create( - self, post, admin, org_admin, org_member, alice, user_for_access, can_access_list + self, post, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization ): user_list = [admin, org_admin, org_member, alice] for user, can_access in zip(user_list, can_access_list): app = Application.objects.create( name='test app for {}'.format(user.username), user=user, - client_type='confidential', authorization_grant_type='password' + client_type='confidential', authorization_grant_type='password', organization=organization ) post( reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, user_list[user_for_access], expect=201 if can_access else 403 ) + From 5fd5c95a1d0e8ed98fa51244234db3ab83bcd185 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 2 Apr 2018 12:16:17 -0400 Subject: [PATCH 060/379] only record task.args in the callback plugin if `DISPLAY_ARGS_TO_STDOUT` see: https://github.com/ansible/awx/issues/1633 --- awx/lib/awx_display_callback/module.py | 14 +++++++++----- awx/lib/tests/test_display_callback.py | 12 ++++++------ awx/main/models/events.py | 6 ------ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/awx/lib/awx_display_callback/module.py b/awx/lib/awx_display_callback/module.py index 368063d0d1..0d99f82680 100644 --- a/awx/lib/awx_display_callback/module.py +++ b/awx/lib/awx_display_callback/module.py @@ -28,6 +28,7 @@ import uuid from copy import copy # Ansible +from ansible import constants as C from ansible.plugins.callback import CallbackBase from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule @@ -126,16 +127,19 @@ class BaseCallbackModule(CallbackBase): task=(task.name or task.action), task_uuid=str(task._uuid), task_action=task.action, + task_args='', ) try: task_ctx['task_path'] = task.get_path() except AttributeError: pass - if task.no_log: - task_ctx['task_args'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" - else: - task_args = ', '.join(('%s=%s' % a for a in task.args.items())) - task_ctx['task_args'] = task_args + + if C.DISPLAY_ARGS_TO_STDOUT: + if task.no_log: + task_ctx['task_args'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" + else: + 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: diff --git a/awx/lib/tests/test_display_callback.py b/awx/lib/tests/test_display_callback.py index d8c7923108..5f7ce96ab8 100644 --- a/awx/lib/tests/test_display_callback.py +++ b/awx/lib/tests/test_display_callback.py @@ -186,14 +186,16 @@ def test_callback_plugin_no_log_filters(executor, cache, playbook): @pytest.mark.parametrize('playbook', [ {'no_log_on_ok.yml': ''' -- name: args should not be logged when task-level no_log is set +- name: args should not be logged when no_log is set at the task or module level connection: local hosts: all gather_facts: no tasks: - - shell: echo "SENSITIVE" + - shell: echo "PUBLIC" - shell: echo "PRIVATE" no_log: true + - uri: uri=https://example.org username="PUBLIC" password="PRIVATE" + - copy: content="PRIVATE" destination="/tmp/tmp_no_log" '''}, # noqa ]) def test_callback_plugin_task_args_leak(executor, cache, playbook): @@ -204,15 +206,13 @@ def test_callback_plugin_task_args_leak(executor, cache, playbook): # task 1 assert events[2]['event'] == 'playbook_on_task_start' - assert 'SENSITIVE' in events[2]['event_data']['task_args'] assert events[3]['event'] == 'runner_on_ok' - assert 'SENSITIVE' in events[3]['event_data']['task_args'] # task 2 no_log=True assert events[4]['event'] == 'playbook_on_task_start' - assert events[4]['event_data']['task_args'] == "the output has been hidden due to the fact that 'no_log: true' was specified for this result" # noqa assert events[5]['event'] == 'runner_on_ok' - assert events[5]['event_data']['task_args'] == "the output has been hidden due to the fact that 'no_log: true' was specified for this result" # noqa + assert 'PUBLIC' in json.dumps(cache.items()) + assert 'PRIVATE' not in json.dumps(cache.items()) @pytest.mark.parametrize('playbook', [ diff --git a/awx/main/models/events.py b/awx/main/models/events.py index 09da2ffb20..6f240cfdf4 100644 --- a/awx/main/models/events.py +++ b/awx/main/models/events.py @@ -235,12 +235,6 @@ class BasePlaybookEvent(CreatedModifiedModel): if res.get('changed', False): self.changed = True 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 self.job_verbosity == 0 and 'module_args' in invocation: - event_data['res']['invocation']['module_args'] = '' - self.event_data = event_data - updated_fields.add('event_data') if self.event == 'playbook_on_stats': try: failures_dict = event_data.get('failures', {}) From 482c159ac602b5583f8375fcd2aaeb262090ef87 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 10:56:35 -0400 Subject: [PATCH 061/379] prohibit config callback with no inventory --- awx/api/serializers.py | 5 +++++ .../tests/functional/api/test_job_template.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 027192125b..b460020e0a 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3063,6 +3063,11 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO inventory = get_field_from_model_or_attrs('inventory') project = get_field_from_model_or_attrs('project') + if get_field_from_model_or_attrs('host_config_key') and not inventory: + raise serializers.ValidationError({'host_config_key': _( + "Cannot enable provisioning callback without an inventory set." + )}) + prompting_error_message = _("Must either set a default value or ask to prompt on launch.") if project is None: raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")}) diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index af16dad584..fe729b8c84 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -13,6 +13,9 @@ from awx.main.migrations import _save_password_keys as save_password_keys from django.conf import settings from django.apps import apps +# DRF +from rest_framework.exceptions import ValidationError + @pytest.mark.django_db @pytest.mark.parametrize( @@ -615,3 +618,16 @@ def test_job_template_unset_custom_virtualenv(get, patch, organization_factory, url = reverse('api:job_template_detail', kwargs={'pk': jt.id}) resp = patch(url, {'custom_virtualenv': value}, user=objs.superusers.admin, expect=200) assert resp.data['custom_virtualenv'] is None + + +@pytest.mark.django_db +def test_callback_disallowed_null_inventory(project): + jt = JobTemplate.objects.create( + name='test-jt', inventory=None, + ask_inventory_on_launch=True, + project=project, playbook='helloworld.yml') + serializer = JobTemplateSerializer(jt) + assert serializer.instance == jt + with pytest.raises(ValidationError) as exc: + serializer.validate({'host_config_key': 'asdfbasecfeee'}) + assert 'Cannot enable provisioning callback without an inventory set' in str(exc) From 9ef1fce5e10e80ab8fcac1b5a3a4c9414e1adab4 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Mon, 2 Apr 2018 16:49:23 -0400 Subject: [PATCH 062/379] add tests & correct auditor permissions --- awx/main/access.py | 30 +++-------- awx/main/tests/functional/test_rbac_oauth.py | 54 ++++++++++++++++++-- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 075a845ea8..29d717791d 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -604,17 +604,14 @@ class OAuth2ApplicationAccess(BaseAccess): return False def can_delete(self, obj): - if obj.organization in self.user.admin_of_organizations or self.user.is_superuser: - return True - else: - return False + return obj.organization in self.user.admin_of_organizations or self.user.is_superuser def can_add(self, data): if self.user.is_superuser: return True if not data: return Organization.accessible_objects(self.user, 'admin_role').exists() - return self.check_related('organization', Organization, data, role_field='admin_role') + return self.check_related('organization', Organization, data, role_field='admin_role', mandatory=True) class OAuth2TokenAccess(BaseAccess): @@ -625,9 +622,9 @@ class OAuth2TokenAccess(BaseAccess): - I am the user of the token. I can create an OAuth2 app token when: - I have the read permission of the related application. - I can read, change or delete a personal token when: - - + - I am the user of the token + - I am the superuser I can create an OAuth2 Personal Access Token when: - I am a user. But I can only create a PAT for myself. ''' @@ -641,31 +638,20 @@ class OAuth2TokenAccess(BaseAccess): Q(admin_role__members=self.user) | Q(auditor_role__members=self.user)) return self.model.objects.filter(application__organization__in=org_access_qs) | self.model.objects.filter(user__id=self.user.pk) - def can_change(self, obj, data): - print 'obj user:', obj.user, '\nself.user:', self.user - if (self.user.is_superuser) | (obj.user == self.user): - return True - elif self.user.is_system_auditor: - return False - elif not obj.application: - return False - return self.user in obj.application.organization.admin_role - def can_delete(self, obj): if (self.user.is_superuser) | (obj.user == self.user): return True - elif self.user.is_system_auditor: - return False elif not obj.application: return False return self.user in obj.application.organization.admin_role + + def can_change(self, obj, data): + return self.can_delete(obj) def can_add(self, data): if 'application' in data: app = get_object_from_data('application', OAuth2Application, data) - if self.user.is_system_auditor: - return False - elif app is None: + if app is None: return True return OAuth2ApplicationAccess(self.user).can_read(app) return True diff --git a/awx/main/tests/functional/test_rbac_oauth.py b/awx/main/tests/functional/test_rbac_oauth.py index ea8710ec7b..6a99a284d0 100644 --- a/awx/main/tests/functional/test_rbac_oauth.py +++ b/awx/main/tests/functional/test_rbac_oauth.py @@ -40,7 +40,7 @@ class TestOAuth2Application: (2, [False, False]), (3, [False, False]), ]) - def test_can_edit_delete( + def test_can_edit_delete_app( self, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization ): organization.admin_role.members.add(org_admin) @@ -101,6 +101,54 @@ class TestOAuth2Token: assert access.can_read(token) is can_access assert access.can_change(token, {}) is can_access assert access.can_delete(token) is can_access + + + def test_auditor_can_read( + self, post, admin, org_admin, org_member, alice, system_auditor, organization + ): + user_list = [admin, org_admin, org_member] + can_access_list = [True, True, True] + cannot_access_list = [False, False, False] + app = Application.objects.create( + name='test app for {}'.format(admin.username), user=admin, + client_type='confidential', authorization_grant_type='password', + organization=organization + ) + for user, can_access, cannot_access in zip(user_list, can_access_list, cannot_access_list): + response = post( + reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), + {'scope': 'read'}, user, expect=201 + ) + token = AccessToken.objects.get(token=response.data['token']) + access = OAuth2TokenAccess(system_auditor) + assert access.can_read(token) is can_access + assert access.can_change(token, {}) is cannot_access + assert access.can_delete(token) is cannot_access + + def test_user_auditor_can_change( + self, post, org_member, org_admin, system_auditor, organization + ): + app = Application.objects.create( + name='test app for {}'.format(org_admin.username), user=org_admin, + client_type='confidential', authorization_grant_type='password', + organization=organization + ) + response = post( + reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), + {'scope': 'read'}, org_member, expect=201 + ) + token = AccessToken.objects.get(token=response.data['token']) + access = OAuth2TokenAccess(system_auditor) + assert access.can_read(token) is True + assert access.can_change(token, {}) is False + assert access.can_delete(token) is False + dual_user = system_auditor + organization.admin_role.members.add(dual_user) + access = OAuth2TokenAccess(dual_user) + assert access.can_read(token) is True + assert access.can_change(token, {}) is True + assert access.can_delete(token) is True + def test_can_read_change_delete_personal_token_org_member( @@ -131,7 +179,7 @@ class TestOAuth2Token: for user, can_access in zip(user_list, can_access_list): response = post( reverse('api:o_auth2_personal_token_list', kwargs={'pk': user.pk}), - {'scope': 'read', 'organization':None}, user, expect=201 + {'scope': 'read', 'application':None}, user, expect=201 ) token = AccessToken.objects.get(token=response.data['token']) access = OAuth2TokenAccess(user) @@ -159,4 +207,4 @@ class TestOAuth2Token: reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, user_list[user_for_access], expect=201 if can_access else 403 ) - + From 9695031b2769f6e2b66d6c94070df444367c3117 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 12:33:56 -0400 Subject: [PATCH 063/379] prevent unicode errors in cred unique_hash --- awx/main/models/credential/__init__.py | 5 +++-- awx/main/tests/unit/models/test_credential.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 awx/main/tests/unit/models/test_credential.py diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 8908fcf8c0..7b141f6e3f 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -9,6 +9,7 @@ import os import re import stat import tempfile +import six # Jinja2 from jinja2 import Template @@ -415,9 +416,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): type_alias = self.credential_type_id if self.kind == 'vault' and self.inputs.get('vault_id', None): if display: - fmt_str = '{} (id={})' + fmt_str = six.text_type('{} (id={})') else: - fmt_str = '{}_{}' + fmt_str = six.text_type('{}_{}') return fmt_str.format(type_alias, self.inputs.get('vault_id')) return str(type_alias) diff --git a/awx/main/tests/unit/models/test_credential.py b/awx/main/tests/unit/models/test_credential.py new file mode 100644 index 0000000000..f71d7fa0ae --- /dev/null +++ b/awx/main/tests/unit/models/test_credential.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from awx.main.models import Credential, CredentialType + + +def test_unique_hash_with_unicode(): + ct = CredentialType(name=u'Väult', kind='vault') + cred = Credential( + id=4, + name=u'Iñtërnâtiônàlizætiøn', + credential_type=ct, + inputs={ + u'vault_id': u'🐉🐉🐉' + }, + credential_type_id=42 + ) + assert cred.unique_hash(display=True) == u'Väult (id=🐉🐉🐉)' From 8a7f00bdf75daa1cf459a8f10d599c4d08f60c0c Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 13:39:16 -0400 Subject: [PATCH 064/379] fix bug where role name was given incorrectly --- awx/main/access.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/access.py b/awx/main/access.py index bf74f2a491..62c52a41e6 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -1861,7 +1861,7 @@ class WorkflowJobTemplateAccess(BaseAccess): if self.user.is_superuser: return True - return (self.check_related('organization', Organization, data, role_field='workflow_admin_field', obj=obj) and + return (self.check_related('organization', Organization, data, role_field='workflow_admin_role', obj=obj) and self.user in obj.admin_role) def can_delete(self, obj): From c3100afd0e49dbc24383f024fd15519a7f4bfc34 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 3 Apr 2018 13:50:57 -0400 Subject: [PATCH 065/379] fixed isolated instance query * Was considering an isolated instance: any instance that has at least 1 group with no controller. This is technically correct since an iso node can not be a part of a non-iso group. * The query is now more robust and considers a node an iso node if ALL groups that a node belong to ALL have a controller. * Also added better debugging for the special tower instance group * Added a check for the existance of the special tower group so that logs are less "messy" during the install process. --- awx/main/managers.py | 2 +- awx/main/models/ha.py | 4 ---- awx/main/tasks.py | 12 ++++++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/awx/main/managers.py b/awx/main/managers.py index ce5a3a1971..1adb75e913 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -114,7 +114,7 @@ class InstanceManager(models.Manager): return "tower" def all_non_isolated(self): - return self.filter(rampart_groups__controller__isnull=True).distinct() + return self.exclude(rampart_groups__controller__isnull=False) class InstanceGroupManager(models.Manager): diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index 145fdbfb51..b7e50ec2b4 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -163,10 +163,6 @@ class InstanceGroup(models.Model, RelatedJobsMixin): def _get_related_jobs(self): return UnifiedJob.objects.filter(instance_group=self) - def add_all_non_iso_instances(self): - self.instances = Instance.objects.all_non_isolated() - self.save() - class Meta: app_label = 'main' diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 0de7094100..21a09c7ff2 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -144,8 +144,16 @@ def apply_cluster_membership_policies(self): Group = namedtuple('Group', ['obj', 'instances']) Node = namedtuple('Instance', ['obj', 'groups']) - # Add every instnace to the special 'tower' group - InstanceGroup.objects.get(name='tower').add_all_non_iso_instances() + # Add every instance to the special 'tower' group + tower_q = InstanceGroup.objects.filter(name='tower') + if tower_q.exists(): + tower_inst = tower_q[0] + tower_inst.instances = Instance.objects.all_non_isolated() + instances_hostnames = [i.hostname for i in tower_inst.instances.all()] + logger.info(six.text_type("Setting 'tower' group instances to {}").format(instances_hostnames)) + tower_inst.save() + else: + logger.warn(six.text_type("Special 'tower' Instance Group not found.")) # Process policy instance list first, these will represent manually managed instances # that will not go through automatic policy determination From 88fbb6706f334e9c5a1f0e237f242428add7ea7d Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 3 Apr 2018 14:31:37 -0400 Subject: [PATCH 066/379] use non-deprecated way of setting many2many --- awx/main/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 21a09c7ff2..ff72647352 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -148,7 +148,7 @@ def apply_cluster_membership_policies(self): tower_q = InstanceGroup.objects.filter(name='tower') if tower_q.exists(): tower_inst = tower_q[0] - tower_inst.instances = Instance.objects.all_non_isolated() + tower_inst.instances.set(Instance.objects.all_non_isolated()) instances_hostnames = [i.hostname for i in tower_inst.instances.all()] logger.info(six.text_type("Setting 'tower' group instances to {}").format(instances_hostnames)) tower_inst.save() From a52b22ffdf3f0369625946654b17414a202e7fef Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 14:31:56 -0400 Subject: [PATCH 067/379] delete user role on deletion of a user --- awx/main/signals.py | 8 +++++++ awx/main/tests/functional/test_rbac_user.py | 24 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/awx/main/signals.py b/awx/main/signals.py index 5021f53290..a6fc73f5d9 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -179,6 +179,13 @@ def create_user_role(instance, **kwargs): role.members.add(instance) +def delete_user_role(instance, **kwargs): + if instance and instance.admin_role: + instance.admin_role.delete() + else: + logger.info(six.text_type("Could not delete the admin role for user {}").format(instance)) + + def org_admin_edit_members(instance, action, model, reverse, pk_set, **kwargs): content_type = ContentType.objects.get_for_model(Organization) @@ -252,6 +259,7 @@ m2m_changed.connect(rbac_activity_stream, Role.members.through) m2m_changed.connect(rbac_activity_stream, Role.parents.through) post_save.connect(sync_superuser_status_to_rbac, sender=User) post_save.connect(create_user_role, sender=User) +pre_delete.connect(delete_user_role, sender=User) pre_delete.connect(cleanup_detached_labels_on_deleted_parent, sender=UnifiedJob) pre_delete.connect(cleanup_detached_labels_on_deleted_parent, sender=UnifiedJobTemplate) diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/test_rbac_user.py index bbfe0267cd..8e4cf9915e 100644 --- a/awx/main/tests/functional/test_rbac_user.py +++ b/awx/main/tests/functional/test_rbac_user.py @@ -1,9 +1,10 @@ import pytest from django.test import TransactionTestCase +from django.contrib.contenttypes.models import ContentType from awx.main.access import UserAccess -from awx.main.models import User, Organization, Inventory +from awx.main.models import User, Organization, Inventory, Role @pytest.mark.django_db @@ -102,6 +103,27 @@ def test_org_user_removed(user, organization): assert admin not in member.admin_role +@pytest.mark.django_db +def test_create_user_role(rando): + assert Role.objects.filter( + role_field='admin_role', + content_type=ContentType.objects.get_for_model(User), + object_id=rando.id + ).count() == 1 + assert rando in rando.admin_role + + +@pytest.mark.django_db +def test_user_role_deleted(rando): + rando_id = rando.id + rando.delete() + assert not Role.objects.filter( + role_field='admin_role', + content_type=ContentType.objects.get_for_model(User), + object_id=rando_id + ) + + @pytest.mark.django_db def test_org_admin_create_sys_auditor(org_admin): access = UserAccess(org_admin) From ee8416140ad2e08efb3e2eef9c55332c4289921f Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 3 Apr 2018 10:16:34 -0400 Subject: [PATCH 068/379] custom message for JSONschema type error --- awx/main/fields.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index d0f6081693..e0af0674ac 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -391,7 +391,15 @@ class JSONSchemaField(JSONBField): error.message = re.sub(r'\bu(\'|")', r'\1', error.message) if error.validator == 'pattern' and 'error' in error.schema: - error.message = error.schema['error'] % error.instance + error.message = error.schema['error'].format(instance=error.instance) + elif error.validator == 'type': + expected_type = error.validator_value + if expected_type == 'object': + expected_type = 'dict' + error.message = _( + '{type} provided in relative path {path}, expected {expected_type}' + ).format(path=list(error.path), type=type(error.instance).__name__, + expected_type=expected_type) errors.append(error) if errors: @@ -523,7 +531,7 @@ class CredentialInputField(JSONSchemaField): format_checker=self.format_checker ).iter_errors(decrypted_values): if error.validator == 'pattern' and 'error' in error.schema: - error.message = error.schema['error'] % error.instance + error.message = error.schema['error'].format(instance=error.instance) if error.validator == 'dependencies': # replace the default error messaging w/ a better i18n string # I wish there was a better way to determine the parameters of @@ -628,7 +636,7 @@ class CredentialTypeInputField(JSONSchemaField): 'id': { 'type': 'string', 'pattern': '^[a-zA-Z_]+[a-zA-Z0-9_]*$', - 'error': '%s is an invalid variable name', + 'error': '{instance} is an invalid variable name', }, 'label': {'type': 'string'}, 'help_text': {'type': 'string'}, From 31a0eab880ddcf4df33d06ab2cc70555fe2ae154 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 3 Apr 2018 15:44:44 -0400 Subject: [PATCH 069/379] add more custom credential type env blacklist items see: https://github.com/ansible/tower/issues/1224 --- awx/main/models/credential/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 7b141f6e3f..c109f28b65 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -446,6 +446,7 @@ class CredentialType(CommonModelNameNotUnique): 'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'MAX_EVENT_RES', 'CALLBACK_QUEUE', 'CALLBACK_CONNECTION', 'CACHE', 'JOB_CALLBACK_DEBUG', 'INVENTORY_HOSTVARS', 'FACT_QUEUE', + 'AWX_HOST', 'PROJECT_REVISION' )) class Meta: From 9f07beed59a143e212ff06429924e880b540fb04 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Tue, 3 Apr 2018 21:27:38 +0000 Subject: [PATCH 070/379] Increases the angular-tz-extension tag in package.json The angular-tz-extensions was changed to accomodate for an issue with UTC dates not getting parsed correctly. --- awx/ui/client/src/scheduler/schedulerAdd.controller.js | 1 + awx/ui/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index f3096a1ce1..ec6cb4862d 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -293,6 +293,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', scheduler.scope.schedulerTimeZone = _.find(data, (zone) => { return zone.name === scheduler.scope.current_timezone.name; }); + $scope.scheduleTimeChange(); }); if($scope.schedulerUTCTime) { // The UTC time is already set diff --git a/awx/ui/package.json b/awx/ui/package.json index 1ed19acee4..2d9f32f488 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -106,7 +106,7 @@ "angular-moment": "^0.10.1", "angular-sanitize": "~1.6.6", "angular-scheduler": "git+https://git@github.com/ansible/angular-scheduler#v0.3.2", - "angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#v0.5.1", + "angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#v0.5.2", "babel-polyfill": "^6.26.0", "bootstrap": "^3.3.7", "bootstrap-datepicker": "^1.7.1", From 4e6ce9af18c17c559f82badf6ccfef9df0d93ffa Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 3 Apr 2018 17:36:46 -0400 Subject: [PATCH 071/379] add license route to settings page --- awx/ui/client/legacy/styles/ansible-ui.less | 4 ++ .../configuration/configuration.controller.js | 39 +++++++++----- .../configuration/configuration.partial.html | 5 +- .../src/configuration/configuration.route.js | 17 +++--- .../client/src/configuration/license.route.js | 52 +++++++++++++++++++ awx/ui/client/src/configuration/main.js | 2 + 6 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 awx/ui/client/src/configuration/license.route.js diff --git a/awx/ui/client/legacy/styles/ansible-ui.less b/awx/ui/client/legacy/styles/ansible-ui.less index 79e554c0f3..a992d805c1 100644 --- a/awx/ui/client/legacy/styles/ansible-ui.less +++ b/awx/ui/client/legacy/styles/ansible-ui.less @@ -2328,6 +2328,10 @@ body { margin-top: 20px; } +.Panel--noBottomPadding { + padding-bottom: 0px; +} + .Panel-hidden { display: none; } diff --git a/awx/ui/client/src/configuration/configuration.controller.js b/awx/ui/client/src/configuration/configuration.controller.js index 90a2b554d8..9752522e9d 100644 --- a/awx/ui/client/src/configuration/configuration.controller.js +++ b/awx/ui/client/src/configuration/configuration.controller.js @@ -3,6 +3,7 @@ * * All Rights Reserved *************************************************/ +import defaultStrings from '~assets/default.strings.json'; export default [ '$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$q', 'Alert', @@ -55,6 +56,8 @@ export default [ ) { var vm = this; + vm.product = defaultStrings.BRAND_NAME; + var formDefs = { 'azure': configurationAzureForm, 'github': configurationGithubForm, @@ -219,7 +222,7 @@ export default [ }; function activeTabCheck(setForm) { - if(!$scope[formTracker.currentFormName()].$dirty) { + if(!$scope[formTracker.currentFormName()] || !$scope[formTracker.currentFormName()].$dirty) { active(setForm); } else { var msg = i18n._('You have unsaved changes. Would you like to proceed without saving?'); @@ -268,18 +271,30 @@ export default [ formTracker.setCurrentSystem(formTracker.currentSystem); } } - else { - formTracker.setCurrent(setForm); - } + vm.activeTab = setForm; - $state.go('configuration', { - currentTab: setForm - }, { - location: true, - inherit: false, - notify: false, - reload: false - }); + + if (setForm !== 'license') { + formTracker.setCurrent(setForm); + + $state.go('configuration', { + currentTab: setForm + }, { + location: true, + inherit: false, + notify: false, + reload: false + }); + } else { + $state.go('configuration.license', { + currentTab: setForm + }, { + location: true, + inherit: false, + notify: false, + reload: false + }); + } } var formCancel = function() { diff --git a/awx/ui/client/src/configuration/configuration.partial.html b/awx/ui/client/src/configuration/configuration.partial.html index 3f61a44603..a567e6bde2 100644 --- a/awx/ui/client/src/configuration/configuration.partial.html +++ b/awx/ui/client/src/configuration/configuration.partial.html @@ -5,7 +5,7 @@

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 D0@a_r literal 0 HcmV?d00001 diff --git a/docs/networking.feature b/docs/networking.feature new file mode 100644 index 0000000000..3fcbfe8ef3 --- /dev/null +++ b/docs/networking.feature @@ -0,0 +1,76 @@ +Feature: Networking Topology Visualization + In order to increase the ease and understanding of network automation + As as network engineer + I want a visualization of the network topology so that I can + easily understand how the network topology is connected + + Scenario: Blank canvas + Given an ansible inventory + When the user clicks on the network topology icon for that inventory + Then populate the toolbox with the data loaded from inventory + + Scenario: Device Organization + Given an ansible inventory loaded into the canvas toolbox + When the user clicks and drags on a device in the inventory toolbox + Then place the device onto the topology canvas at the location of the user's mouse pointer + + Scenario: Link Connection + Given an ansible inventory loaded into the canvas toolbox + When the user clicks and drags on a device in the inventory toolbox + Then automatically draw lines and circles that represent the links + And interfaces of the connected devices. + + Scenario: Customize Layout + Given a canvas populated with a network topology + When the user clicks and drags on a device on the topology canvas + Then move the device to the location of the user's mouse pointer + And update the links and interfaces representations + + Scenario: Export SVG + Given a canvas populated with a network topology + When the user clicks on the Export SVG button + Then capture the current view of the canvas + And download an SVG file of the canvas to the users computer + + Scenario: Export YAML + Given a canvas populated with a network topology + When the user clicks on the Export YAML button + Then capture the state of the current view of the canvas + And download a YAML file that represents the data to the user's computer + + Scenario: Pan + Given a canvas populated with a network topology + When the user clicks and drags on the background + Then move the viewport of the virtual canvas to match the user's mouse movement + + Scenario: Zoom + Given a canvas populated with a network topology + When the user scrolls their mousewheel or clicks and drags on the zoom widget + Then scale the viewport of the virtual canvas to the zoom level reflected on the zoom widget + + Scenario: Hide information when zooming out + Given a canvas populated with a network topology + When the user zooms out via mouse wheel or zoom widget + Then hide low-level information to provide a high-level overview as the zoom level changes + + Scenario: Show more information when zooming in + Given a canvas populated with a network topology + When the user zooms in via mouse wheel or zoom widget + Then show more low-level information to provide more detail for the devices that are in view on the virtual canvas + + Scenario: Device Detail + Given a canvas populated with a network topology + When the user clicks on show details device context menu button + Then show the device details including name, description, and host vars + + Scenario: Remove Device + Given a canvas populated with a network topology + When the user clicks on remove device context menu button + Then remove the device and connected links from the canvas + And return the device to the inventory toolbox + + Scenario: Search by Device Name + Given a canvas populated with a network topology + When the user types the device name or selects it from a device drop down list + Then position the viewport on the virtual canvas over the device with that name + diff --git a/docs/networking.md b/docs/networking.md new file mode 100644 index 0000000000..79accfef9b --- /dev/null +++ b/docs/networking.md @@ -0,0 +1,686 @@ +# Tower Networking Overview +The networking tool is built with the main goal of allowing users to organize hosts and their relationships in a visual tool. Upon organizing hosts, the user can export these relationships as a YAML file and use that as input to a playbook. + +## Usage Manual + +### Inventory Creation +The first step in is to create an inventory to be loaded into the network UI. There are no specific credentials or variables necessary to indicate that an inventory can be used in the network UI. The canvas can be used to represent any kind of inventory. + +### Network Node Creation +Once the user has created an inventory, the next step is to add hosts to the inventory. This can be done manually or via an inventory source. Regardless of the import method, the host should be configured with a certain set of host varialbes that the network UI will reference when creating visual representations of the nodes. When creating a node that will be used in the network UI, the host variables follow this format: +#### YAML: +``` +ansible_topology: + type: host +``` +#### JSON: +``` +{ + "ansible_topology": { + "type": "host", + } +} +``` + +This structure denotes that the `type` that the network UI will use when drawing the visual representation of this node in the UI. The options for `ansible_topology` are as follows: + +| Key Name | Value Type | Description | +|------------------------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `type` | `str` | This will dictate what type of device the network UI will draw. Options are `host`, `switch`, and `router`. If left blank, the UI will denote the node type as `unknown`. | +| `links` | `array` | This array contains objects that will denote what other nodes this particular node is connected to. Each connecting node object requires a `remote_device_name`, a `name`, and a `remote_interface_name`. + +Ex: suppose an inventory had three nodes, titled `host-00`, `switch-00`, and `router-00`. To connect `host-00` to the other two hosts, these would be the host variables saved to the host: +``` +{ + "ansible_topology": { + "type": "host", + "links": [ + { + "remote_device_name": "router-00", + "name": "eth0", + "remote_interface_name": "eth0" + }, + { + "remote_device_name": "switch-00", + "name": "eth1", + "remote_interface_name": "eth1" + } + ] + } +} + +``` +Connecting the other two devices to each other, and you would get the following representation in the network UI: +![Example 1](./img/network-example-1.png?raw=true) + + +### Graphical UI and Restrictions +Once the user has setup their inventory and nodes, they are ready to begin organizing their nodes using the graphical interface. The interface consists of an SVG canvas overlayed with panels that allow the user to drag nodes onto the canvas, as well as the ability to drill-down into the details of the items on the canvas. Below is a breakdown of the panels and their purpose on the interface: +![Example 2](./img/network-example-2.png?raw=true) +1. Toolbox: This panel on the left hand side of the screen will contain all the hosts that are included in the inventory. These are returned from `'/api/v2/inventories/:inventory_id/hosts/'` and therefore are returned in a paginated list. Because of this, the UI will make recursive calls to retrieve the list of hosts. This panel is collapsible using the wrench icon in the toolbar. This panel will scroll vertically with the list of devices. +2. Canvas: The canvas takes up the full screen width and height. The canvas is where the user can drag and drop devices from the toolbox. Once a device is placed on the canvas, it is removed from the toolbox and it can interact with other devices on the canvas. If a device is removed from the canvas, it is not removed from the inventory, it is simply removed from the canvas, and will return to the toolbox +3. Context Menu: When a user clicks on a device for the first time, it selects the device. A second click will activate a context menu with actions for the user. If the user has edit permission on the inventory, they will have the options to view the details of the device, or to remove the device. If the user does not have edit permission, they will only have the option to view the details of the device. +4. Details: The right hand panel displays a read-only form for the device that is currently being inspected by the user. This panel is not shown by default, and is only shown after the user clicks the "Details" button on the context menu for a device. If mutiple devices are displayed on the canvas, the user can select different devices. As the user selects devices, the device detail panel on the right hand side will update with the host data. +5. Search: The search dropdown is a type-ahead dropdown that has a list of the devices currently on the canvas. It is organized by device type. It should always be in sync with the list of devices on the canvas. Devices on the toolbox will not show up in the search dropdown. Selecting a device from this dropdown will focus the canvas on the device. +6. Toolbar actions: These are actions that are specific to the usability of the network UI. Currently they include the toggle for the toolbox, as well as a cheat sheet of hotkeys that can be used for shortcuts on the network UI. +7. Actions dropdown: These are actions for the content of the network UI. These include "Export YAML" and "Export SVG" which is how a user could export the relationships on the canvas to download for their own use. +8. Zoom Widget: The zoom widget ranges from 0-200%, and controls the zoom of the canvas. The user can also control the zoom using the scroll on their mouse or tracking pad. In future versions of this UI, the zoom will control what level detail they are able to see. But currently the only mode available is for devices in an inventory. + + +## Verification + +See [networking.feature](networking.feature) for behavior scenarios. + + +## Implementation Details + +**Introduction** + +The Networking UI component of AWX works differently from the rest of the AWX +web UI to support high-scale interactive graphical design of networking +topologies. + +The Networking UI is a virtual graphical canvas where graphical elements are +drawn upon. This canvas supports panning (scrolling horizontally and +vertically) and scaling (zooming in and out), dynamic changing of modes, and +other features that would be very difficult or impossible to implement with +standard HTML events and rendering. + +This interface is more like computer graphics than it is building a styled text +document with interactive components. A good grasp of Cartesian coordinates, +trigonometry, and analytic geometry are useful when working with this code. + +* See: + +**Design choices** + +Certain design choices were made to make the UI performant and scale to a large +number of nodes in a diagram. These include the use of simple ES5 functions for +better performance over more advanced functions. For instance C-style for-loops +were many times faster than implementations of `forEach` or iterators which make +function calls during each iteration. This basic ES5 style should be followed +throughout the implementation of the Network UI. + +**AngularJS** + +The Networking UI component uses AngularJS 1.6.x for part of the rendering pipeline +but it is not a normal AngularJS web application. AngularJS makes use of +data-binding and watchers which I found do not scale to the number of elements +we are trying to support in the Networking UI. The Networking UI only uses +AngularJS for SVG rendering (using AngularJS templates) which does scale +sufficiently. + + +**AngularJS Controllers** + +Instead of creating many AngularJS controllers and directives the networking UI +uses one big controller to hold the state of the entire UI. Normally this is +an anti-pattern in AngularJS. Here is was necessary to scale to a large number +of on-screen elements. + +**AngularJS Directives** + +* See: + +AngularJS directives are used in the networking UI application using the element +matching style and the `templateUrl` option to include a template. A majority of +the directives are defined in `network.ui.app.js`. + +* See: [network.ui.app.js](../awx/ui/client/src/network-ui/network.ui.app.js) +``` + .directive('awxNetLink', link.link) +``` + +* See: [link.directive.js](../awx/ui/client/src/network-ui/link.directive.js) +``` +const templateUrl = require('~network-ui/link.partial.svg'); + +function link () { + return { restrict: 'A', templateUrl}; +} +exports.link = link; +``` + +**AngularJS Templates** + +* See: + +Normal AngularJS templates are used with the networking UI controller. +The templates can be found in `/widgets`. Child +scopes are created for sub-templates using the `ng-repeat` directive. + +In this example the `awx-net-link` directive expects a Link model to be +passed to it. The Link model is defined in the `models.js` file. + +* See: [link.directive.js](../awx/ui/client/src/network-ui/link.directive.js) +* See: [link.partial.svg](../awx/ui/client/src/network-ui/link.partial.svg) + +* See: [network_ui.partial.svg](../awx/ui/client/src/network-ui/network_ui.partial.svg) +``` + + + +``` + +* See: [models.js](../awx/ui/client/src/network-ui/models.js) +``` +function Link(id, from_device, to_device, from_interface, to_interface) { + this.id = id; + this.from_device = from_device; + this.to_device = to_device; + this.from_interface = from_interface; + this.to_interface = to_interface; + this.selected = false; + this.remote_selected = false; + this.status = null; + this.edit_label = false; + this.name = ""; +} +``` + +The following example sets the toolbox.selected_item value to the variable +item which the directives used in the child scope expect to be set. + +* See: [inventory_toolbox.html](../awx/ui/client/src/network-ui/inventory_toolbox.partial.svg) +``` + +``` + + +**DOM (Document Object Model)** + +No state is stored in or attached to the DOM. All state is stored in +javascript objects attached to the network ui controller. + +Direct DOM manipulation should not be used in the network UI unless absolutely +necessary. JQuery should not be used. The DOM is generated through the use of +AngularJS templates. + +**SVG (Scalable Vector Graphics)** + +* See: + +The network UI is built as one large SVG element (the SVG canvas) with other +graphical elements (lines, circles, rectangles, paths, and text) absolutely +positioned within the outer most SVG element. The browser is not involved with +layout of the elements within the SVG. Each "widget" in the network UI needs +to track or calculate its own position on the SVG canvas. The z-level of the +elements are determined by the draw order on the canvas which is defined +in `network_ui.partial.svg`. Elements drawn first will be hidden behind +elements drawn later. + + + +**Rendering Pipeline** + +Event -> Javscript objects -> AngularJS templates -> SVG + +AngularJS is used to render the SVG inside the SVG canvas using directives +and templates. AngularJS is also used to schedule when the SVG canvas will +be updated. When an input event comes from the user, or an event is received +over the websocket, javascript objects will be updated according the the network +UI code. Then AngularJS will be notified that it needs to update the templates +either automatically for some events or explicitly using `$scope.$apply();` if +not handled automatically by AngularJS. The templates will render to SVG and be +included in the DOM for the rest of the AWX UI. + +Because the networking UI does not use watchers nor data-binding features of +AngularJS events flow in one way from event to javascript to angular to SVG. +Events do not flow backwards through this pipeline. + +Clicking on an SVG element will not send the event to that SVG element directly +from the browser. It must be routed through the network UI code first. + + +**SVG Primer** + +SVG uses tags to define graphical elements just like HTML uses tags to define +text documents. Commonly use tags include g, circle, rect, path, and text. +SVG elements are absolutely positioned within an SVG canvas. The group tag, g, +is similar to the div tag in HTML. Text in SVG must be contained in the text +tag and cannot be outside tags as in HTML. + +* See: + +Each tag that describes a visual element requires X and Y coordinates as input +to position that element. These coordinates are relative to position of the SVG +canvas. The network UI uses the entire page height and width for the SVG canvas +so that the position on the SVG on the canvas is the same as the position on +the page. + + +SVG supports graphical transformations on several tags to allow relative +positioning of sub-elements which makes calculating the X and Y positions +easier. The network UI uses transformations often for this purpose. +Transformations that are often used here are the translate, scale, and rotate +transforms. Translate moves the origin of the coordinate system to a new point +for the sub-elements. Scale multiplies the size of the units in a coordinate +system by some factor. Rotate performs a rotation about the origin by some +number of degrees. These functions are converted to a matrix operation on the +coordinate system which can be efficiently applied. It is often useful to use +the transforms to simplify the calculations of X and Y coordinates instead of +calculating those values in Javascript. Also these transforms make developing +widgets much easier since we only need to keep up with a single point for the +widget and all other points can be relatively positioned from that point. +Hard-coding positions in widget development is the normal case since transforms +can change the size and position of the widget when the widget is applied to +the canvas. Only when necessary should we calculate positions of parts of a +widget in javascript. + +* See: + + +SVG paths are a mini-language for defining graphics operations in one tag. It +is often used to create shapes that are more complex than lines, rectangles, +and circles. It is very useful for defining arcs. + +* See: + +**SVG and CSS** + +CSS and SVG work really nicely together for setting style, colors, and fonts in SVG. +The SVG uses different attributes for setting colors than does HTML elements. +Most SVG elements use `stroke` and `fill` to define the colors and `stroke-width` +to define the width of lines and curves. The attributes `font-family` and `font-size` +are used to set the font for text elements in SVG. The network UI uses the Less +CSS compiler and BEM naming conventions to simplify and organize CSS. + +* See: [style.less](../awx/ui/client/src/network-ui/style.less) +* See: +* See: + +**Events** + +All mouse and keyboard events are captured by the outer most element of the +network UI. Mouse movements, mouse clicks, and key presses are all routed by +the network UI code and not by the browser. This is done to implement +interactions with the virtual graphical canvas that are not supported by the +browser. "Simple" things like buttons and text fields have to be handled by +the network UI code instead of relying on the browser to route the mouse click +to the appropriate object. + + +The following code captures all the mouse movements, mouse clicks, mouse wheel, +and touch events and sends them to the corresponding network UI controller functions. + +* See: [network_ui.partial.svg](../awx/ui/client/src/network-ui/network_ui.partial.svg#L3) + +``` + +``` + + +Key events are captured by the following code: + +* See: [network.ui.controller.js](../awx/ui/client/src/network-ui/network.ui.controller.js) + +``` + $document.bind("keydown", $scope.onKeyDown); +``` + +**Event Processing** + +This code works as an event processing pipeline where the source of the events +may be mouse clicks, keystrokes, or messages from the server over the +websocket. This allows the appropriate processor to handle each event in turn +or delegate the message to another processor. + +The following diagram documents the pipeline processors that handle the events. +Events are injected into to the pipeline at `Start` and travel through the +pipeline along the arrows. Events may be handled at a node in the pipeline, +passed along to the next node, discarded, or transformed into another message +and sent along the pipeline. For instance `hotkeys_fsm` generates new and +different type of events based on key presses that are injected at the +beginning of the pipeline. + +![Event Pipeline](networking/pipeline.png) + + +**Describing Behavior with Finite State Machines** + +To implement complex UI interactions predictably and correctly is a tough +problem. Often the problem is solved by creating a large library of generic +reusable components that are rigorously tested and hardened by a large group of +developers over a period of several years. Eventually the myriad bugs are +hammered out at great expense. Only then can the UI components be reliably +used. This code does not follow that approach. + +The workflows this code supports require very specific UI components that are +not found in generic libraries. The interactions we want to support are not +available in generic libraries. This code develops from scratch only the +components that are necessary to implement the workflows of designing and +operating networks of devices. + +This code defines those elements using finite state machines to process the +events from user input and other software components. Programming with finite +state machines allows us to define formally complex behavior that would +normally be informally defined by branches, functions, object interactions, and +object inheritance. Formal definition eliminates much of the unexpected +behavior that causes defects in the software. + +* See: + +Finite state machines can be represented as a directed graph of labeled nodes and labeled edges +which can be both be represented visually and in machine readable code. + +The network UI uses finite state machines to describe what happens when the software receives +an input. + +**Move FSM** + +![Move FSM](networking/move.png) + +For example the move FSM describes how devices are moved about the virtual +canvas. The FSM diagram above maps out the states and the events that will +select a device to move. FSMs traditionally start in the `Start` state. We +get a free transition to the `Ready` state by handling the special event called +`start` and changing state to `Ready`. When the user presses the mouse button +if the cursor is over a device the FSM changes state to `Selected1`. If the +user then moves the mouse while the button is still pressed the device will be +moved along with the mouse cursor. When the mouse button is lifted the FSM +changes to the `Selected1` and then also to the `Selected2` state. This +implements clicking and dragging devices around the virtual canvas. Clicking on +the canvas background will return the FSM to the `Ready` state. + +* See: [networking/move.yml](networking/move.yml) +* See: [move.fsm.js](../awx/ui/client/src/network-ui/move.fsm.js) + +The move FSM diagram has an equivalent machine readable representation in +`networking/move.yml`. This representation is useful for comparing the current +implementation in `move.fsm.js` to the design to see if they are out-of-sync. +If they are out-of-sync either the design or the implementation can be updated +depending on if you are changing the design or implementation first. This +machine readable representation of the FSM can be used to generate test inputs +to the FSM to achieve high test coverage of all states and transitions in the +FSM. + + +**Finite State Machine Implementation** + +The implementation of a finite state machine in the network UI is split into +two parts: the declaration of the states and the event-handlers which may cause +FSM transitions using `controller.changeState`. + +**FSM States** + +* See: [networking/README.md](networking/README.md) +* See: +* See: + +States are implemented using an object-oriented style in ES5 using the +flyweight and singleton patterns. This means that the state objects store no +information on themselves and that there is only one instance of each state +class. All states should provide a `start` and `end` function which will be +called when a FSM state is entered and exited respectively. Subclassing +[fsm.State](../awx/ui/client/src/network-ui/fsm.js) will provide empty `start` and `end` functions that +can be overridden as necessary. + +* See: [fsm.js](../awx/ui/client/src/network-ui/fsm.js) + +The state variable is stored on another object called an FSMController (which +should not be confused with an AngularJS controller). The FSMController holds +all the state for each FSM instance. If you need more than one copy of an FSM +(for buttons for instance) use more than one instance of FSMController and +pass the same FSM starting state to their constructor e.g. `button.Start`. +Variables other than `state` should not be stored on the FSMController. A +special variable named `scope` is useful for that. The scope can be used +to hold arbitrary data that the FSM code will use in addition to the messages +in the event handlers. In the network UI often the `scope` is a reference +to the network UI AngularJS controller's scope. In the case of a button +the scope is a reference to the `Button` model. + +* See: [models.js](../awx/ui/client/src/network-ui/models.js) + +The following code creates a new instance of `FSMController` using the +`Button` model as the scope and the `button.Start` state as the initial +state. + +``` + this.fsm = new fsm.FSMController(this, button.Start, null); +``` + +* See: [move.fsm.js](../awx/ui/client/src/network-ui/move.fsm.js) + +This code block defines the `_Selected1` class in ES5 style and uses the +`inherits` NPM module to define that the class is a subclass of `_State`. We +also create a single instance (a singleton) of this class named `Selected1`. + +``` + function _Selected1 () { + this.name = 'Selected1'; + } + inherits(_Selected1, _State); + var Selected1 = new _Selected1(); + exports.Selected1 = Selected1; +``` + +**FSM Event Handlers and Transitions** + +After all the states are defined the event handlers for those state classes can be defined. +We do this to prevent forward references in the file. + +* See: [move.fsm.js](../awx/ui/client/src/network-ui/move.fsm.js) + +In this code we define an event handler for the `MouseUp` event on the `Selected1` state. This +code should select a single device if the mouse is over that device. It should store +that device somewhere and change to the `Selected2` state. + +Event handlers must start with the prefix of `on` and a suffix of the name of the messsage +type. The special functions `start` and `end` do not follow this rule nor do +they receive a message. + +The event handler must also define its `transitions` as a list so that `./extract.js` can +find them. + +``` + _Selected1.prototype.onMouseUp = function (controller, msg_type, message) { + + ... + + }; + _Selected1.prototype.onMouseUp.transitions = ['Selected2']; + +``` + +**FSM Designs** + +All the finite state machines for the network UI have been designed using the +[fsm-designer-svg](https://github.com/benthomasson/fsm-designer-svg) tool +and their designs are stored in the `networking` directory. + +* See: [networking/README.md](networking/README.md) + + +**Data Models** + +There are two types of data structures used in the network UI: messages and +models. Models are used for long-lived data that is used to render the UI +whereas messages are used for ephemeral data that is passed from one part of +the system to another. Models may be unpacked from or serialized into messages that +are sent to other FSMControllers in the client or sent over a websocket to the +server. + +* See: [models.js](../awx/ui/client/src/network-ui/models.js) + +The models defined in [models.js](../awx/ui/client/src/network-ui/models.js) are: + +* Device - a networking device i.e. a router, a switch, or a host +* Interface - a networking interface +* Link - a connection between interfaces +* Button - a UI button +* ToggleButton - a togglable UI button +* Task - a playbook task +* ToolBox - a UI element for holding things that can be placed on the virtual canvas + + +**Message Types** + +Message types define the structure of the data that is passed between the server +and the client and between different parts of the client. This provides a known and +well defined data structure that can be counted up on the code. + +* See: [messages.js](../awx/ui/client/src/network-ui/messages.js) + +The messages defined are [messages.js](../awx/ui/client/src/network-ui/messages.js): + +* DeviceMove - Device has changed x,y position +* DeviceCreate - A device was created +* DeviceDestroy - A device was destroyed +* DeviceSelected - A device was selected +* DeviceUnSelected - A device was unselected +* InterfaceCreate - An interface was created +* InterfaceLabelEdit - The label of an interface was changed +* LinkCreate - A link was created +* LinkDestroy - A link was destroyed +* LinkSelected - A link was selected +* LinkUnSelected - A link was unselected +* MultipleMessage - A collection of messages that should be handled together +* Coverage - A coverage report +* MouseEvent - A generic mouse event +* MouseWheelEvent - A mouse wheel event +* KeyEvent - A key press event +* StartRecording - Start recording user interactions +* StopRecording - Stop recording user interactions +* ViewPort - Update the view port onto the virtual canvas +* NewDevice - Request for a new device +* PasteDevice - Paste a device from a toolbox + + +Server-Side Details +----------------------- + +The Network UI is a UX driven feature to provide a graphical user +experience that fits well into the network engineer's normal workflow. Their +normal workflow includes a diagram drawn in a graphical drawing program, a +spreadsheet, and the command line interface of their network gear. Network +architects design the network on the graphical diagram and then hand off the +architecture to network operators who implement the architecture on the network +using spreadsheets to manage their data and manually converting the data into +CLI commands using their networking expertise and expertise with their physical +gear. + +The server-side code supports the persistence needed to provide this graphical +user experience of architecting a network and using that information along with +additional information (stored in vars files) to configure the network devices +using the CLI or NETCONF using Ansible playbooks and roles. + +Network UI Data Schema +---------------------- + +For the 3.3 release the persistence needed includes the position information of +the devices on the virtual canvas and the type of the devices as well as +information about the interfaces on the devices and the links connecting those +interfaces. + +These requirements determine the database schema needed for the network UI which +requires these models: Topology, Device, Interface, Link, Client, and TopologyInventory. + +![Models](networking/models.png) + +This diagram shows the relationships between the models in the Network UI schema. + +The models are: + +* Device - a host, switch, router, or other networking device +* Interface - a connection point on a device for a link +* Link - a physical connection between two devices to their respective interfaces +* Topology - a collection of devices and links +* TopologyInventory - a mapping between topologies and Tower inventories +* Client - a UI client session + + +Network UI Websocket Protocol +----------------------------- + +Persistence for the network UI canvas state is implemented using an +asynchronous websocket protocol to send information from the client to the +server and vice-versa. This two-way communication was chosen to support future +features for streaming data to the canvas, broadcast messaging between clients, +and for interaction performance on the UI. + + +Messages +-------- + +JSON messages are passed over the `/network_ui/topology` websocket between the +test client and the test server. The protocol that is used for all messages is +in ABNF (RFC5234): + + + message_type = 'DeviceMove' / 'DeviceCreate' / 'DeviceDestroy' / 'DeviceLabelEdit' / 'DeviceSelected' / 'DeviceUnSelected' / 'InterfaceCreate' / 'InterfaceLabelEdit' / 'LinkLabelEdit' / 'LinkCreate' / 'LinkDestroy' / 'LinkSelected' / 'LinkUnSelected' / 'MultipleMessage' / 'Snapshot' + message_data = '{' 'msg_type' ': ' message_type ', ' key-value *( ', ' key-value ) '}' + message = '[ id , ' posint ']' / '[ topology_id , ' posint ']' / '[' message_type ', ' message_data ']' + +See https://github.com/AndyA/abnfgen/blob/master/andy/json.abnf for the rest of +the JSON ABNF. + +See [networking/messages.yml](networking/messages.yml) for the allowable keys and +values for each message type. + + +Initially when the websocket is first opened the server will send four messages +to the client. These are: + +* the client id using the `id` message type. +* the topology id using the `topology` message type. +* a Topology record containing data for the canvas itself. +* a Snapshot message containing all the data of the data on the canvas. + +As the user interacts with the canvas messages will be generated by the client +and the `network_ui.consumers.NetworkEvents` class will update the models that +represent the canvas. + + + +Persistence +----------- + +The class `awx.network_uiconsumers.NetworkEvents` provides persistence for the Network UI canvas. +It does so by providing message handlers that handle storage of the canvas change events +into the database. Each event has a message handle with name `onX` where `X` is the name of the message +type. The handlers use the `filter/values_list`, `filter/values`, `filter/update`, and `filter/delete` +patterns to update the data in the database quickly with a constant O(1) number of queries per event +often with only one query needed. With `filter/update` and `filter/delete` all the work is done +in the database and Python never needs to instaniate and garbage collect the model objects. + +Bulk operations (`filter/values`) in `send_snapshot` are used to produce a constant number of +queries produce a snapshot when the canvas is first loaded. This method avoids creating +the model objects since it only produces dicts that are JSON serializable which are bundled +together for the `Snapshot` message type. + +This method of persistence uses Django as a database query-compiler for transforms from +the event types to the database types. Using Django in this way is very performant since +Python does very little work processing the data and when possible the data never leaves +the database. + + +Client Tracking +--------------- + +Each user session to the network UI canvas is tracked with the `Client` model. Multiple +clients can view and interact with the network UI canvas at a time. They will see each other's +edits to the canvas in real time. This works by broadcasting the canvas change events to +all clients viewing the same topology. + +``` + # Send to all clients editing the topology + Group("topology-%s" % message.channel_session['topology_id']).send({"text": message['text']}) +``` + +API +--- + +There is no user accessible API for this feature in the 3.3 release. diff --git a/docs/networking/README.md b/docs/networking/README.md new file mode 100644 index 0000000000..a8e5de591c --- /dev/null +++ b/docs/networking/README.md @@ -0,0 +1,100 @@ + + +Finite State Machine Designs +============================ + +This directory contains the finite state machine designs that were used to +generate the skeleton of the javascript implementations and can be used to +check that the implementations still match the designs. + + +**Machine Readable FSM Schema** + +The machine readable FSM schema contains three top-level elements: `name`, `states`, and `transitions`. +* The `name` element is a string. +* The `states` element contains a list of `state` elements which have attributes `id`, `label`, and `x`, and `y`. +* The `transitions` element contains a list of `transition` elements which have attributes `from_state`, `to_state`, and `label`. + + +**Design Diagrams** + +The diagrams below are visual representations of the finite state machine designs in this directory. +The equivalent machine readable representations are linked as well. + +--- + + +**Button FSM** +* See: button.yml + +The button FSM describes how a button works. The key insight here is that a button is not +clicked if the mouse is not over the button on both the `MouseDown` and `MouseUp` events. Moving +the mouse off the button before `MouseUp` is not a click. + +![Button FSM](button.png) + +--- + +**Buttons FSM** +* See: buttons.yml + +The buttons FSM distributes events to the buttons which each have their own FSM. + +![Buttons FSM](buttons.png) + +--- + +**Hot Keys FSM** +* See: hotkeys.yml + +The hot keys FSM handles key events and generates new events like `NewLink` to implement +hot keys. + +![Hot Keys FSM](hotkeys.png) + +--- + +**Mode FSM** +* See: mode.yml + +The mode FSM controls the overall mode of the network UI application. + +![Mode](mode.png) + +--- + +**Move FSM** +* See: move.yml + +The move FSM controls placement of devices as well as editing the device labels. + +![Move](move.png) + +--- + +**Time FSM** +* See: time.yml + +The time FSM controls undo/redo functionality of the network UI. + +![Time](time.png) + +--- + +**Toolbox FSM** +* See: toolbox.yml + +The toolbox FSM controls the drag-and-drop toolboxes and allow placement of new devices, applications, +racks, and sites onto the canvas. + +![Toolbox](toolbox.png) + +--- + +**View FSM** +* See: view.yml + +The view FSM controls the panning and scaling of the the virtual canvas through clicking-and-dragging +of the background and scrolling the mousewheel. + +![View](view.png) diff --git a/docs/networking/animation.yml b/docs/networking/animation.yml new file mode 100644 index 0000000000..c99b99dd1c --- /dev/null +++ b/docs/networking/animation.yml @@ -0,0 +1,29 @@ +diagram_id: 58 +name: animation_fsm +states: +- id: 4 + label: Cancelled + x: 590 + y: 602 +- id: 3 + label: Completed + x: 225 + y: 604 +- id: 2 + label: Running + x: 418 + y: 362 +- id: 1 + label: Start + x: 454 + y: 158 +transitions: +- from_state: Running + label: onAnimationCancelled + to_state: Cancelled +- from_state: Running + label: onAnimationCompleted + to_state: Completed +- from_state: Start + label: start + to_state: Running diff --git a/docs/networking/button.png b/docs/networking/button.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1bc7aaa1d942e8d1d97c562b6fec795d6d24d3 GIT binary patch literal 56313 zcmeFZ^;?x&7d8q@P(taDkP<{1L^>q}Bn0V}k`_=>5G+~|q#Hz9x+Ikb>23rBX+=W% zj0gAozWY1hKX86HUf1?od#&f0G3JPS++$1w)l}p!;ZWkBp`l$;xGkfOhK6c^|3;_)zxbgp9u+)>(^5 zxN^r2-A&QP>BhNpS1*JmCa5PQ;F>R?jS077$}&mrUZm5nsCkdpB#v$#OoDy|%X|jC zXn=kVpU#yirmG31MNG7C(Od~_45dI&em)=(&s5)cw4EZ5MY8-t8)pDjgYQ72?B~e_ z^^v!})e7gUgfh5ABvYFs;>am4I$t2@H0a-uaRUFOlCP zv-n{3?z$jKnqTv?kVHjFDP!4f`Omyux~lS@+ZuY&-s6#25u{68@Kd%T5qU$2EC z!e);4oTlcE_boWx?H|H9p`nSo3&Tfyi$|t(?)G*LF2e3&3};UW!)N4cE(W@@M;_UV zG3Y3((MdZxThQ@w-sI$F5XYgTqZ4&Dw-i>Fk^AR$_&+g*2ag^-6z1Y`b93W#$GsU{@+XvF8^!`Hpqqi!o|bM&Gp~0;Z;%OTVXY8cMCfm8EbnB2N#$_ zT!4pL^z8Zn>zDt{_^&s0|Mlh#!GFK`uV4OoQj?oW83ajaQnB>>GboWi|m`@cbaeXmKHejWMm2=J(~@0XYY z^#9BPkH(?Bs->dybO3}mbaD3X>Zm;SEd??NzOA(a3BLjEWD-v19v zattXJv(TbSU0k<>TX1*Vkuz{P=PI=xEjjLTYMudU|>yGO}xggoH$- zq)dH%eMH2>SKq#UdxHJ08aZrE(iCo2>&6Iv6xJUVNzyuUPJ=L|Xifzur=oXJQHmZO zRg*Oyzv~<*X=pglUBG+y;spkUsC(hXE5i3)U81_?eYjg&uO`Cpq7UQd3h8 zdIM8Z=#D8v-6%6s>u%8+rM}Y5GFn_&vGM-#awYI%r4hY$fzf*5p2we`-@@O%r6*=q zdH4SP`|6e!Jj6cyqQDS1#Qwa1RZnV4?p)lQs@BG%7NjHRGO}|?prp-vCCGSLBGcv=ToQ`iOl-262eUOST0$tc8NC_{U&CmnS0zm+ z@o?f18*}uVZ+)ZW>RL`M>fY;BQb$8YmHXzmvWiMns)F>y+TO@{KGu(gg=$=*xSS+E zYCG~b^KlmP^4JxWlx%B*d(Q0kzcxL~i86kATxK^@SKFnA&3E(uVAkDmVFt~)xw#Ez zR}+C~W0vTnqbEWxiy`8~uiS3J+MZ$JnlFBhosQR)2WMcwC26{LP=JPpMK{Zl;%Vfz zKF$=+Z{Kef@pC$p#*RwBHDSGFJ?B?1vufhIni|o~N86^f!DJlaV9ey3Vqe~_M=#CpJzQSxKw*vJC1yu`=h_ioVS16t$Mr~ z_eqsWlWXell&#;YPuDV{U7;e|BXlrwojzKHe zA48qGT-f#x9)vZ@<0+}As05F3TvLu?d?RsEx$fB%%0Djv>hrK3mbAy#wvR<;qL0pJ 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%0A^_$*D-$tkRc6otOR`CQ}co#XP$r7{+ zo-p;rn9?2)D=)3Co_I=eK(Z@gY>bX;Np>WcJ<>f@{i2k#lD$maE=((s&pkQ~cG|ii2p_m~q!Ou_#7AJ-6g z>Nzii1xYkx(d zJ2YGu@7M?sI1tz7bM3LnB`)d25t*$!Oq*=bKAfVd{7w9Mx?btISAV{EzFMV%)N^ll z9&kgo^z9LqyJ>G@!KkYF@JOTXiR~fSn{xufLGz^jt1CX7kzX!0S(VC*R0(@dwY&Lc zuLXAUfW6N;a)ewlTdczvb1x4&CMPxSWCwrz=miOA>DC=h;HJQ(pMgu;d%SgoF8%Ql zW6$2+9&5eIUlpg)_QF7d{ZTGPsfEFS%bHregIKY=Ynr@W_UC6B-YgWO&z_Q0sTOh^ z4`{Chu>9-IYM!% zn=qg_AKJF!5n#VQm738ec?N5nko90Db7t0}DK;sc9(nc0?2J~0zO>7aS}IkaPI0)8 z$!OS{AC41_axCc&?In8=LjiTO@2?8a9`r~GmOCK9|8PWp5B^a*jaqL6{n#UI&P{eW zs3^(%7taGvjTwkN4U3MpcV?SQc;mhqO+Nd|#|Wk=03-a^IuZaZ)V3( zNG|w1_Cm(NfIyyEk@An;IA>iYAKc{I_CCvQJnPzCwc3N-T$C z!cW)!*%|})Cqz%SPFWIed4A_nse-dRE&#mjxD*%r@VL5S{sxS?aM8-pf)(k&Egkm7{ap!k938 z-iRSVREic$P?3DpAWW5(zb93-+s*&R!|dl%Z*T8|A)`B7z@k024-E;?b*1Dng|=28 zy@J3@fx(#!dhha}8wLPi6kxIRW*n0$gbQ-T>lK374TFPe=p2X^n{lo?nb7BHJ~SJ_YZoOXfiaE8|MfaIe3-a8wRz0Ey z%@jJ9_%9i6B5%J`o?L3XlaXJ!CK0S&6|Zc(@hFJkS#p2O#>S>yWiFTgb&%p=i3$Uo zkQ*VWS6W0?$yqGcDB-Y~U=Td)#v_c?s}(?Cw=X;=(Y%x~xjc53&L3QjsW%D5F63v7 z@M%`vmm$CFS7XU2>G_P%f`6o~MZl>E3 zql=N1rR6F>D|l=N`yPrVA+(pHRa^)I%R((Kwci*BtS-VYnIsfuEB_^?g#`a96E2!; zncEK0P(dfAKVZFCH(;b(BsA|l$jI4(MlJbkj&_X&h!$&2z^l0G;dL);US<{2ckN%y zWWJ3;!`jJ)4-$T%im2`x4f19p=i^_$ndSexC84N@KK|>Ms%8Pha&qR-w}*S#1WVLQ z`p#@<1H(Z^7vq#9$vDtfYPN2r`OejB{PDv`meuygT$k%_fApx!`E1*j>3CbjENw zUUibFd^B*T+=O9B6n=;&3(0)~`u77dL%#XOQrg7MXqH~Uxu|-vh4q2yn3Dj$N_~QX ziXkSR(^DA8b{LP~0pXy`HxWZ|m3qM&a%ebwoSN&5hNvyk4XAp*-7%S$IP

-
+
CONFIGURE {{BRAND_NAME}}
@@ -16,6 +16,7 @@
Jobs
System
User Interface
+
License
@@ -26,3 +27,5 @@
+ +
diff --git a/awx/ui/client/src/configuration/configuration.route.js b/awx/ui/client/src/configuration/configuration.route.js index c916ecf59d..4b1febbb74 100644 --- a/awx/ui/client/src/configuration/configuration.route.js +++ b/awx/ui/client/src/configuration/configuration.route.js @@ -3,16 +3,15 @@ * * All Rights Reserved *************************************************/ - - import {templateUrl} from '../shared/template-url/template-url.factory'; - import ConfigurationController from './configuration.controller'; - import { N_ } from '../i18n'; +import {templateUrl} from '../shared/template-url/template-url.factory'; +import ConfigurationController from './configuration.controller'; +import { N_ } from '../i18n'; // Import form controllers - import ConfigurationAuthController from './auth-form/configuration-auth.controller'; - import ConfigurationJobsController from './jobs-form/configuration-jobs.controller'; - import ConfigurationSystemController from './system-form/configuration-system.controller'; - import ConfigurationUiController from './ui-form/configuration-ui.controller'; +import ConfigurationAuthController from './auth-form/configuration-auth.controller'; +import ConfigurationJobsController from './jobs-form/configuration-jobs.controller'; +import ConfigurationSystemController from './system-form/configuration-system.controller'; +import ConfigurationUiController from './ui-form/configuration-ui.controller'; export default { name: 'configuration', @@ -58,6 +57,6 @@ templateUrl: templateUrl('configuration/ui-form/configuration-ui'), controller: ConfigurationUiController, controllerAs: 'uiVm' - } + }, }, }; diff --git a/awx/ui/client/src/configuration/license.route.js b/awx/ui/client/src/configuration/license.route.js new file mode 100644 index 0000000000..2efa23bcae --- /dev/null +++ b/awx/ui/client/src/configuration/license.route.js @@ -0,0 +1,52 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../shared/template-url/template-url.factory'; +import { N_ } from '../i18n'; +import _ from 'lodash'; + +export default { + name: 'configuration.license', + route: '/license', + // templateUrl: templateUrl('license/license'), + // controller: 'licenseController', + data: {}, + ncyBreadcrumb: { + label: N_('LICENSE') + }, + onEnter: ['$state', 'ConfigService', (state, configService) => { + return configService.getConfig() + .then(config => { + if (_.get(config, 'license_info.license_type') === 'open') { + return state.go('setup'); + } + }); + }], + views: { + 'license@configuration': { + templateUrl: templateUrl('license/license'), + controller: 'licenseController' + }, + }, + resolve: { + features: ['CheckLicense', '$rootScope', + function(CheckLicense, $rootScope) { + if($rootScope.licenseMissing === undefined){ + return CheckLicense.notify(); + } + + }], + config: ['ConfigService', 'CheckLicense', '$rootScope', + function(ConfigService, CheckLicense, $rootScope) { + ConfigService.delete(); + return ConfigService.getConfig() + .then(function(config){ + $rootScope.licenseMissing = (CheckLicense.valid(config.license_info) === false) ? true : false; + return config; + }); + }] + }, +}; diff --git a/awx/ui/client/src/configuration/main.js b/awx/ui/client/src/configuration/main.js index 3846354546..5e20fa830d 100644 --- a/awx/ui/client/src/configuration/main.js +++ b/awx/ui/client/src/configuration/main.js @@ -7,6 +7,7 @@ import configurationService from './configuration.service'; import ConfigurationUtils from './configurationUtils.service'; import configurationRoute from './configuration.route'; +import licenseRoute from './license.route'; import configurationController from './configuration.controller.js'; // Import forms @@ -66,4 +67,5 @@ angular.module('configuration', []) .service('ConfigurationService', configurationService) .run(['$stateExtender', function($stateExtender) { $stateExtender.addState(configurationRoute); + $stateExtender.addState(licenseRoute); }]); From e8d80b5502ab505e9f6935ff4d399ea881fc216e Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 3 Apr 2018 18:36:34 -0400 Subject: [PATCH 072/379] Fixed event error in firefox when logging in. Removed transition console logs. --- awx/ui/client/src/app.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 9a2e6ccc7d..ab64107955 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -221,7 +221,7 @@ angular if ($rootScope.removeConfigReady) { $rootScope.removeConfigReady(); } - $rootScope.removeConfigReady = $rootScope.$on('ConfigReady', function() { + $rootScope.removeConfigReady = $rootScope.$on('ConfigReady', function(evt) { var list, id; // initially set row edit indicator for crud pages if ($location.$$path && $location.$$path.split("/")[3] && $location.$$path.split("/")[3] === "schedules") { @@ -309,30 +309,14 @@ angular if (trans.to().name && (trans.to().name !== "signIn" && trans.to().name !== "signOut" && trans.to().name !== "license")) { ConfigService.getConfig().then(function() { // if not headed to /login or /logout, then check the license - CheckLicense.test(event); + CheckLicense.test(evt); }); } } activateTab(); }); - $transitions.onCreate({}, function(trans) { - console.log('$onCreate ' +trans.to().name); - }); - - $transitions.onBefore({}, function(trans) { - console.log('$onBefore ' +trans.to().name); - }); - $transitions.onError({}, function(trans) { - - console.log('$onError ' +trans.to().name); - }); - $transitions.onExit({}, function(trans) { - console.log('$onExit ' +trans.to().name); - }); - $transitions.onSuccess({}, function(trans) { - console.log('$onSuccess ' +trans.to().name); if(trans.to() === trans.from()) { // check to see if something other than a search param has changed let toParamsWithoutSearchKeys = {}; From 5f888d8400507827acd4df2c7bffdf0a6ce7c06e Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 28 Mar 2018 14:52:44 -0400 Subject: [PATCH 073/379] Fix issue with adding new RBAC fields --- awx/main/fields.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index d0f6081693..c39cea014c 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -273,35 +273,40 @@ class ImplicitRoleField(models.ForeignKey): Role_ = utils.get_current_apps().get_model('main', 'Role') ContentType_ = utils.get_current_apps().get_model('contenttypes', 'ContentType') ct_id = ContentType_.objects.get_for_model(instance).id + + Model = utils.get_current_apps().get_model('main', instance.__class__.__name__) + latest_instance = Model.objects.get(pk=instance.pk) + with batch_role_ancestor_rebuilding(): # Create any missing role objects missing_roles = [] - for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): - cur_role = getattr(instance, implicit_role_field.name, None) + for implicit_role_field in getattr(latest_instance.__class__, '__implicit_role_fields'): + cur_role = getattr(latest_instance, implicit_role_field.name, None) if cur_role is None: missing_roles.append( Role_( role_field=implicit_role_field.name, content_type_id=ct_id, - object_id=instance.id + object_id=latest_instance.id ) ) + if len(missing_roles) > 0: Role_.objects.bulk_create(missing_roles) updates = {} role_ids = [] - for role in Role_.objects.filter(content_type_id=ct_id, object_id=instance.id): - setattr(instance, role.role_field, role) + for role in Role_.objects.filter(content_type_id=ct_id, object_id=latest_instance.id): + setattr(latest_instance, role.role_field, role) updates[role.role_field] = role.id role_ids.append(role.id) - type(instance).objects.filter(pk=instance.pk).update(**updates) + type(latest_instance).objects.filter(pk=latest_instance.pk).update(**updates) Role.rebuild_role_ancestor_list(role_ids, []) # Update parentage if necessary - for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): - cur_role = getattr(instance, implicit_role_field.name) + for implicit_role_field in getattr(latest_instance.__class__, '__implicit_role_fields'): + cur_role = getattr(latest_instance, implicit_role_field.name) original_parents = set(json.loads(cur_role.implicit_parents)) - new_parents = implicit_role_field._resolve_parent_roles(instance) + new_parents = implicit_role_field._resolve_parent_roles(latest_instance) cur_role.parents.remove(*list(original_parents - new_parents)) cur_role.parents.add(*list(new_parents - original_parents)) new_parents_list = list(new_parents) From bab27453924286411c085077ccc78b9f54772070 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 4 Apr 2018 07:47:13 -0400 Subject: [PATCH 074/379] Refresh the old instance so the returned obj is up-to-date --- awx/main/fields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/fields.py b/awx/main/fields.py index c39cea014c..d53f00a0c3 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -315,6 +315,7 @@ class ImplicitRoleField(models.ForeignKey): if cur_role.implicit_parents != new_parents_json: cur_role.implicit_parents = new_parents_json cur_role.save() + instance.refresh_from_db() def _resolve_parent_roles(self, instance): From 0db24a5c9780751dab0b380f0221ea86085e6968 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 4 Apr 2018 08:28:05 -0400 Subject: [PATCH 075/379] more gracefully account for undefined stdout see: https://github.com/ansible/tower/issues/1215 related: https://github.com/ansible/tower/pull/1192#issuecomment-377982131 --- awx/main/tasks.py | 1 + awx/main/tests/unit/test_tasks.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index ff72647352..e3bb397f33 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -885,6 +885,7 @@ class BaseTask(LogErrorsTask): output_replacements = [] extra_update_fields = {} event_ct = 0 + stdout_handle = None try: kwargs['isolated'] = isolated_host is not None self.pre_run_hook(instance, **kwargs) diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 6def112522..90023d9076 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -13,6 +13,7 @@ import fcntl import mock import pytest import yaml + from django.conf import settings @@ -295,6 +296,15 @@ class TestJobExecution: class TestGenericRun(TestJobExecution): + def test_generic_failure(self): + self.task.build_private_data_files = mock.Mock(side_effect=IOError()) + with pytest.raises(Exception): + self.task.run(self.pk) + update_model_call = self.task.update_model.call_args[1] + assert 'IOError' in update_model_call['result_traceback'] + assert update_model_call['status'] == 'error' + assert update_model_call['emitted_events'] == 0 + def test_cancel_flag(self): self.instance.cancel_flag = True with pytest.raises(Exception): From 4b518298a66744475e0047a773f015afcef4ee81 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 4 Apr 2018 09:14:48 -0400 Subject: [PATCH 076/379] remove old crusty test fixtures --- awx/main/tests/base.py | 687 ------------------ .../functional/api/test_unified_jobs_view.py | 2 +- awx/main/tests/job_base.py | 541 -------------- 3 files changed, 1 insertion(+), 1229 deletions(-) delete mode 100644 awx/main/tests/base.py delete mode 100644 awx/main/tests/job_base.py diff --git a/awx/main/tests/base.py b/awx/main/tests/base.py deleted file mode 100644 index 8535d0c4ea..0000000000 --- a/awx/main/tests/base.py +++ /dev/null @@ -1,687 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import base64 -import contextlib -import json -import os -import random -import shutil -import sys -import tempfile -import time -import urllib -import re -import mock - -# PyYAML -import yaml - -# Django -import django.test -from django.conf import settings, UserSettingsHolder -from django.contrib.auth.models import User -from django.core.cache import cache -from django.test.client import Client -from django.test.utils import override_settings -from django.utils.encoding import force_text - -# AWX -from awx.main.models import * # noqa -from awx.main.utils import get_ansible_version -from awx.sso.backends import LDAPSettings -from awx.main.tests.URI import URI # noqa - -TEST_PLAYBOOK = '''- hosts: mygroup - gather_facts: false - tasks: - - name: woohoo - command: test 1 = 1 -''' - - -class QueueTestMixin(object): - def start_queue(self): - self.start_rabbit() - receiver = CallbackReceiver() - self.queue_process = Process(target=receiver.run_subscriber, - args=(False,)) - self.queue_process.start() - - def terminate_queue(self): - if hasattr(self, 'queue_process'): - self.queue_process.terminate() - self.stop_rabbit() - - def start_rabbit(self): - if not getattr(self, 'redis_process', None): - # Centos 6.5 redis is runnable by non-root user but is not in a normal users path by default - env = dict(os.environ) - env['PATH'] = '%s:/usr/sbin/' % env['PATH'] - env['RABBITMQ_NODENAME'] = 'towerunittest' - env['RABBITMQ_NODE_PORT'] = '55672' - self.redis_process = Popen('rabbitmq-server > /dev/null', - shell=True, executable='/bin/bash', - env=env) - - def stop_rabbit(self): - if getattr(self, 'redis_process', None): - self.redis_process.kill() - self.redis_process = None - - -# The observed effect of not calling terminate_queue() if you call start_queue() are -# an hang on test cleanup database delete. Thus, to ensure terminate_queue() is called -# whenever start_queue() is called just inherit from this class when you want to use the queue. -class QueueStartStopTestMixin(QueueTestMixin): - def setUp(self): - super(QueueStartStopTestMixin, self).setUp() - self.start_queue() - - def tearDown(self): - super(QueueStartStopTestMixin, self).tearDown() - self.terminate_queue() - - -class MockCommonlySlowTestMixin(object): - def __init__(self, *args, **kwargs): - from awx.api import generics - mock.patch.object(generics, 'get_view_description', return_value=None).start() - super(MockCommonlySlowTestMixin, self).__init__(*args, **kwargs) - - -ansible_version = get_ansible_version() - - -class BaseTestMixin(MockCommonlySlowTestMixin): - ''' - Mixin with shared code for use by all test cases. - ''' - - def setUp(self): - super(BaseTestMixin, self).setUp() - global ansible_version - - self.object_ctr = 0 - # Save sys.path before tests. - self._sys_path = [x for x in sys.path] - # Save os.environ before tests. - self._environ = dict(os.environ.items()) - # Capture current directory to change back after each test. - self._cwd = os.getcwd() - # Capture list of temp files/directories created during tests. - self._temp_paths = [] - self._current_auth = None - self._user_passwords = {} - self.ansible_version = ansible_version - self.assertNotEqual(self.ansible_version, 'unknown') - # Wrap settings so we can redefine them within each test. - self._wrapped = settings._wrapped - settings._wrapped = UserSettingsHolder(settings._wrapped) - # Set all AUTH_LDAP_* settings to defaults to avoid using LDAP for - # tests unless expicitly configured. - for name, value in LDAPSettings.defaults.items(): - if name == 'SERVER_URI': - value = '' - setattr(settings, 'AUTH_LDAP_%s' % name, value) - # Pass test database settings in environment for use by any management - # commands that run from tests. - for opt in ('ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): - os.environ['AWX_TEST_DATABASE_%s' % opt] = settings.DATABASES['default'][opt] - # Set flag so that task chain works with unit tests. - settings.CELERY_UNIT_TEST = True - settings.SYSTEM_UUID='00000000-0000-0000-0000-000000000000' - settings.CELERY_BROKER_URL='redis://localhost:55672/' - settings.CALLBACK_QUEUE = 'callback_tasks_unit' - - # Disable socket notifications for unit tests. - settings.SOCKETIO_NOTIFICATION_PORT = None - # Make temp job status directory for unit tests. - job_status_dir = tempfile.mkdtemp() - self._temp_paths.append(job_status_dir) - settings.JOBOUTPUT_ROOT = os.path.abspath(job_status_dir) - settings.CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'unittests' - } - } - cache.clear() - self._start_time = time.time() - - def tearDown(self): - super(BaseTestMixin, self).tearDown() - # Restore sys.path after tests. - sys.path = self._sys_path - # Restore os.environ after tests. - for k,v in self._environ.items(): - if os.environ.get(k, None) != v: - os.environ[k] = v - for k,v in os.environ.items(): - if k not in self._environ.keys(): - del os.environ[k] - # Restore current directory after each test. - os.chdir(self._cwd) - # Cleanup temp files/directories created during tests. - for project_dir in self._temp_paths: - if os.path.exists(project_dir): - if os.path.isdir(project_dir): - shutil.rmtree(project_dir, True) - else: - os.remove(project_dir) - # Restore previous settings after each test. - settings._wrapped = self._wrapped - - def unique_name(self, string): - rnd_str = '____' + str(random.randint(1, 9999999)) - return __name__ + '-generated-' + string + rnd_str - - def assertElapsedLessThan(self, seconds): - elapsed = time.time() - self._start_time - self.assertTrue(elapsed < seconds, 'elapsed time of %0.3fs is greater than %0.3fs' % (elapsed, seconds)) - - @contextlib.contextmanager - def current_user(self, user_or_username, password=None): - try: - if isinstance(user_or_username, User): - username = user_or_username.username - else: - username = user_or_username - password = password or self._user_passwords.get(username) - previous_auth = self._current_auth - if username is None: - self._current_auth = None - else: - self._current_auth = (username, password) - yield - finally: - self._current_auth = previous_auth - - def make_user(self, username, password=None, super_user=False): - user = None - password = password or username - if super_user: - user = User.objects.create_superuser(username, "%s@example.com", password) - else: - user = User.objects.create_user(username, "%s@example.com", password) - self._user_passwords[user.username] = password - return user - - def make_organizations(self, created_by, count=1): - results = [] - for x in range(0, count): - results.append(self.make_organization(created_by=created_by, count=x)) - return results - - def make_organization(self, created_by, count=1): - self.object_ctr = self.object_ctr + 1 - return Organization.objects.create( - name="org%s-%s" % (count, self.object_ctr), description="org%s" % count, created_by=created_by - ) - - def make_project(self, name=None, description='', created_by=None, - playbook_content='', role_playbooks=None, unicode_prefix=True): - if not name: - name = self.unique_name('Project') - - if not os.path.exists(settings.PROJECTS_ROOT): - os.makedirs(settings.PROJECTS_ROOT) - # Create temp project directory. - if unicode_prefix: - tmp_prefix = u'\u2620tmp' - else: - tmp_prefix = 'tmp' - project_dir = tempfile.mkdtemp(prefix=tmp_prefix, dir=settings.PROJECTS_ROOT) - self._temp_paths.append(project_dir) - # Create temp playbook in project (if playbook content is given). - if playbook_content: - handle, playbook_path = tempfile.mkstemp(suffix=u'\u2620.yml', - dir=project_dir) - test_playbook_file = os.fdopen(handle, 'w') - test_playbook_file.write(playbook_content.encode('utf-8')) - test_playbook_file.close() - # Role playbooks are specified as a dict of role name and the - # content of tasks/main.yml playbook. - role_playbooks = role_playbooks or {} - for role_name, role_playbook_content in role_playbooks.items(): - role_tasks_dir = os.path.join(project_dir, 'roles', role_name, 'tasks') - if not os.path.exists(role_tasks_dir): - os.makedirs(role_tasks_dir) - role_tasks_playbook_path = os.path.join(role_tasks_dir, 'main.yml') - with open(role_tasks_playbook_path, 'w') as f: - f.write(role_playbook_content) - return Project.objects.create( - name=name, description=description, - local_path=os.path.basename(project_dir), created_by=created_by, - #scm_type='git', default_playbook='foo.yml', - ) - - def make_projects(self, created_by, count=1, playbook_content='', - role_playbooks=None, unicode_prefix=False): - results = [] - for x in range(0, count): - self.object_ctr = self.object_ctr + 1 - results.append(self.make_project( - name="proj%s-%s" % (x, self.object_ctr), - description=u"proj%s" % x, - created_by=created_by, - playbook_content=playbook_content, - role_playbooks=role_playbooks, - unicode_prefix=unicode_prefix - )) - return results - - def decide_created_by(self, created_by=None): - if created_by: - return created_by - if self.super_django_user: - return self.super_django_user - raise RuntimeError('please call setup_users() or specify a user') - - def make_inventory(self, organization=None, name=None, created_by=None): - created_by = self.decide_created_by(created_by) - if not organization: - organization = self.make_organization(created_by=created_by) - - return Inventory.objects.create(name=name or self.unique_name('Inventory'), organization=organization, created_by=created_by) - - def make_job_template(self, name=None, created_by=None, organization=None, inventory=None, project=None, playbook=None, **kwargs): - created_by = self.decide_created_by(created_by) - if not inventory: - inventory = self.make_inventory(organization=organization, created_by=created_by) - if not organization: - organization = inventory.organization - if not project: - project = self.make_project(self.unique_name('Project'), created_by=created_by, playbook_content=playbook if playbook else TEST_PLAYBOOK) - - if project and project.playbooks and len(project.playbooks) > 0: - playbook = project.playbooks[0] - else: - raise RuntimeError('Expected project to have at least one playbook') - - if project not in organization.projects.all(): - organization.projects.add(project) - - opts = { - 'name' : name or self.unique_name('JobTemplate'), - 'job_type': 'check', - 'inventory': inventory, - 'project': project, - 'host_config_key': settings.SYSTEM_UUID, - 'created_by': created_by, - 'playbook': playbook, - 'ask_credential_on_launch': True, - } - opts.update(kwargs) - return JobTemplate.objects.create(**opts) - - def make_job(self, job_template=None, created_by=None, inital_state='new', **kwargs): - created_by = self.decide_created_by(created_by) - if not job_template: - job_template = self.make_job_template(created_by=created_by) - - opts = { - 'created_by': created_by, - 'status': inital_state, - } - opts.update(kwargs) - return job_template.create_job(**opts) - - def make_credential(self, **kwargs): - opts = { - 'name': self.unique_name('Credential'), - 'kind': 'ssh', - 'user': self.super_django_user, - 'username': '', - 'ssh_key_data': '', - 'ssh_key_unlock': '', - 'password': '', - 'become_method': '', - 'become_username': '', - 'become_password': '', - 'vault_password': '', - } - opts.update(kwargs) - user = opts['user'] - del opts['user'] - cred = Credential.objects.create(**opts) - cred.admin_role.members.add(user) - return cred - - def setup_instances(self): - instance = Instance(uuid=settings.SYSTEM_UUID, hostname='127.0.0.1') - instance.save() - - def setup_users(self, just_super_user=False): - # Create a user. - self.super_username = 'admin' - self.super_password = 'admin' - self.normal_username = 'normal' - self.normal_password = 'normal' - self.other_username = 'other' - self.other_password = 'other' - self.nobody_username = 'nobody' - self.nobody_password = 'nobody' - - self.super_django_user = self.make_user(self.super_username, self.super_password, super_user=True) - - if not just_super_user: - self.normal_django_user = self.make_user(self.normal_username, self.normal_password, super_user=False) - self.other_django_user = self.make_user(self.other_username, self.other_password, super_user=False) - self.nobody_django_user = self.make_user(self.nobody_username, self.nobody_password, super_user=False) - - def get_super_credentials(self): - return (self.super_username, self.super_password) - - def get_normal_credentials(self): - return (self.normal_username, self.normal_password) - - def get_other_credentials(self): - return (self.other_username, self.other_password) - - def get_nobody_credentials(self): - # here is a user without any permissions... - return (self.nobody_username, self.nobody_password) - - def get_invalid_credentials(self): - return ('random', 'combination') - - def _generic_rest(self, url, data=None, expect=204, auth=None, method=None, - data_type=None, accept=None, remote_addr=None, - return_response_object=False, client_kwargs=None): - assert method is not None - method_name = method.lower() - client_kwargs = client_kwargs or {} - if accept: - client_kwargs['HTTP_ACCEPT'] = accept - if remote_addr is not None: - client_kwargs['REMOTE_ADDR'] = remote_addr - auth = auth or self._current_auth - if auth: - # Dict is only used to test case when both Authorization and - # X-Auth-Token headers are passed. - if isinstance(auth, dict): - basic = auth.get('basic', ()) - if basic: - basic_auth = base64.b64encode('%s:%s' % (basic[0], basic[1])) - basic_auth = basic_auth.decode('ascii') - client_kwargs['HTTP_AUTHORIZATION'] = 'Basic %s' % basic_auth - token = auth.get('token', '') - if token and not basic: - client_kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % token - elif token: - client_kwargs['HTTP_X_AUTH_TOKEN'] = 'Token %s' % token - elif isinstance(auth, (list, tuple)): - #client.login(username=auth[0], password=auth[1]) - basic_auth = base64.b64encode('%s:%s' % (auth[0], auth[1])) - basic_auth = basic_auth.decode('ascii') - client_kwargs['HTTP_AUTHORIZATION'] = 'Basic %s' % basic_auth - elif isinstance(auth, basestring): - client_kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % auth - client = Client(**client_kwargs) - method = getattr(client, method_name) - response = None - if method_name not in ('options', 'head', 'get', 'delete'): - data_type = data_type or 'json' - if data_type == 'json': - response = method(url, json.dumps(data), 'application/json') - elif data_type == 'yaml': - response = method(url, yaml.safe_dump(data), 'application/yaml') - elif data_type == 'form': - response = method(url, urllib.urlencode(data), 'application/x-www-form-urlencoded') - else: - self.fail('Unsupported data_type %s' % data_type) - else: - response = method(url) - - self.assertFalse(response.status_code == 500 and expect != 500, - 'Failed (500): %s' % force_text(response.content)) - if expect is not None: - assert response.status_code == expect, u"expected status %s, got %s for url=%s as auth=%s: %s" % ( - expect, response.status_code, url, auth, force_text(response.content) - ) - if method_name == 'head': - self.assertFalse(response.content) - if return_response_object: - return response - if response.status_code not in [204, 405] and method_name != 'head' and response.content: - # no JSON responses in these at least for now, 409 should probably return some (FIXME) - if response['Content-Type'].startswith('application/json'): - obj = json.loads(force_text(response.content)) - elif response['Content-Type'].startswith('application/yaml'): - obj = yaml.safe_load(force_text(response.content)) - elif response['Content-Type'].startswith('text/plain'): - obj = { - 'content': force_text(response.content) - } - elif response['Content-Type'].startswith('text/html'): - obj = { - 'content': force_text(response.content) - } - else: - self.fail('Unsupport response content type %s' % response['Content-Type']) - else: - obj = {} - - # Create a new subclass of object type and attach the response instance - # to it (to allow for checking response headers). - if isinstance(obj, dict): - return type('DICT', (dict,), {'response': response})(obj.items()) - elif isinstance(obj, (tuple, list)): - return type('LIST', (list,), {'response': response})(iter(obj)) - else: - return obj - - def options(self, url, expect=200, auth=None, accept=None, - remote_addr=None): - return self._generic_rest(url, data=None, expect=expect, auth=auth, - method='options', accept=accept, - remote_addr=remote_addr) - - def head(self, url, expect=200, auth=None, accept=None, remote_addr=None): - return self._generic_rest(url, data=None, expect=expect, auth=auth, - method='head', accept=accept, - remote_addr=remote_addr) - - def get(self, url, expect=200, auth=None, accept=None, remote_addr=None, client_kwargs={}): - return self._generic_rest(url, data=None, expect=expect, auth=auth, - method='get', accept=accept, - remote_addr=remote_addr, - client_kwargs=client_kwargs) - - def post(self, url, data, expect=204, auth=None, data_type=None, - accept=None, remote_addr=None, client_kwargs={}): - return self._generic_rest(url, data=data, expect=expect, auth=auth, - method='post', data_type=data_type, - accept=accept, - remote_addr=remote_addr, - client_kwargs=client_kwargs) - - def put(self, url, data, expect=200, auth=None, data_type=None, - accept=None, remote_addr=None): - return self._generic_rest(url, data=data, expect=expect, auth=auth, - method='put', data_type=data_type, - accept=accept, remote_addr=remote_addr) - - def patch(self, url, data, expect=200, auth=None, data_type=None, - accept=None, remote_addr=None): - return self._generic_rest(url, data=data, expect=expect, auth=auth, - method='patch', data_type=data_type, - accept=accept, remote_addr=remote_addr) - - def delete(self, url, expect=201, auth=None, data_type=None, accept=None, - remote_addr=None): - return self._generic_rest(url, data=None, expect=expect, auth=auth, - method='delete', accept=accept, - remote_addr=remote_addr) - - def get_urls(self, collection_url, auth=None): - # TODO: this test helper function doesn't support pagination - data = self.get(collection_url, expect=200, auth=auth) - return [item['url'] for item in data['results']] - - def check_invalid_auth(self, url, data=None, methods=None): - ''' - Check various methods of accessing the given URL with invalid - authentication credentials. - ''' - data = data or {} - methods = methods or ('options', 'head', 'get') - for auth in [(None,), ('invalid', 'password')]: - with self.current_user(*auth): - for method in methods: - f = getattr(self, method) - if method in ('post', 'put', 'patch'): - f(url, data, expect=401) - else: - f(url, expect=401) - - def check_pagination_and_size(self, data, desired_count, previous=False, - next=False): - self.assertTrue('results' in data) - self.assertEqual(data['count'], desired_count) - if previous: - self.assertTrue(data['previous']) - else: - self.assertFalse(data['previous']) - if next: - self.assertTrue(data['next']) - else: - self.assertFalse(data['next']) - - def check_list_ids(self, data, queryset, check_order=False): - data_ids = [x['id'] for x in data['results']] - qs_ids = queryset.values_list('pk', flat=True) - if check_order: - self.assertEqual(tuple(data_ids), tuple(qs_ids)) - else: - self.assertEqual(set(data_ids), set(qs_ids)) - - def check_get_list(self, url, user, qs, fields=None, expect=200, - check_order=False, offset=None, limit=None): - ''' - Check that the given list view URL returns results for the given user - that match the given queryset. - ''' - offset = offset or 0 - with self.current_user(user): - if expect == 400: - self.options(url, expect=200) - else: - self.options(url, expect=expect) - self.head(url, expect=expect) - response = self.get(url, expect=expect) - if expect != 200: - return - total = qs.count() - if limit is not None: - if limit > 0: - qs = qs[offset:offset + limit] - else: - qs = qs.none() - self.check_pagination_and_size(response, total, offset > 0, - limit and ((offset + limit) < total)) - self.check_list_ids(response, qs, check_order) - if fields: - for obj in response['results']: - returned_fields = set(obj.keys()) - expected_fields = set(fields) - msg = '' - not_expected = returned_fields - expected_fields - if not_expected: - msg += 'fields %s not expected ' % ', '.join(not_expected) - not_returned = expected_fields - returned_fields - if not_returned: - msg += 'fields %s not returned ' % ', '.join(not_returned) - self.assertTrue(set(obj.keys()) <= set(fields), msg) - - def check_not_found(self, string, substr, description=None, word_boundary=False): - if word_boundary: - count = len(re.findall(r'\b%s\b' % re.escape(substr), string)) - else: - count = string.find(substr) - if count == -1: - count = 0 - - msg = '' - if description: - msg = 'Test "%s".\n' % description - msg += '"%s" found in: "%s"' % (substr, string) - self.assertEqual(count, 0, msg) - - def check_found(self, string, substr, count=-1, description=None, word_boundary=False): - if word_boundary: - count_actual = len(re.findall(r'\b%s\b' % re.escape(substr), string)) - else: - count_actual = string.count(substr) - - msg = '' - if description: - msg = 'Test "%s".\n' % description - if count == -1: - self.assertTrue(count_actual > 0) - else: - msg += 'Found %d occurances of "%s" instead of %d in: "%s"' % (count_actual, substr, count, string) - self.assertEqual(count_actual, count, msg) - - def check_job_result(self, job, expected='successful', expect_stdout=True, - expect_traceback=False): - msg = u'job status is %s, expected %s' % (job.status, expected) - msg = u'%s\nargs:\n%s' % (msg, job.job_args) - msg = u'%s\nenv:\n%s' % (msg, job.job_env) - if job.result_traceback: - msg = u'%s\ngot traceback:\n%s' % (msg, job.result_traceback) - if job.result_stdout: - msg = u'%s\ngot stdout:\n%s' % (msg, job.result_stdout) - if isinstance(expected, (list, tuple)): - self.assertTrue(job.status in expected) - else: - self.assertEqual(job.status, expected, msg) - if expect_stdout: - self.assertTrue(job.result_stdout) - else: - self.assertTrue(job.result_stdout in ('', 'stdout capture is missing'), - u'expected no stdout, got:\n%s' % - job.result_stdout) - if expect_traceback: - self.assertTrue(job.result_traceback) - else: - self.assertFalse(job.result_traceback, - u'expected no traceback, got:\n%s' % - job.result_traceback) - - -class BaseTest(BaseTestMixin, django.test.TestCase): - ''' - Base class for unit tests. - ''' - - -class BaseTransactionTest(BaseTestMixin, django.test.TransactionTestCase): - ''' - Base class for tests requiring transactions (or where the test database - needs to be accessed by subprocesses). - ''' - - -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local') -class BaseLiveServerTest(BaseTestMixin, django.test.LiveServerTestCase): - ''' - Base class for tests requiring a live test server. - ''' - def setUp(self): - super(BaseLiveServerTest, self).setUp() - settings.INTERNAL_API_URL = self.live_server_url - - -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local', - DEBUG=True) -class BaseJobExecutionTest(BaseLiveServerTest): - ''' - Base class for celery task tests. - ''' diff --git a/awx/main/tests/functional/api/test_unified_jobs_view.py b/awx/main/tests/functional/api/test_unified_jobs_view.py index d4dd4bd53a..391cb57d18 100644 --- a/awx/main/tests/functional/api/test_unified_jobs_view.py +++ b/awx/main/tests/functional/api/test_unified_jobs_view.py @@ -2,7 +2,7 @@ import pytest from awx.api.versioning import reverse from awx.main.models import UnifiedJob, ProjectUpdate, InventoryUpdate -from awx.main.tests.base import URI +from awx.main.tests.URI import URI from awx.main.constants import ACTIVE_STATES diff --git a/awx/main/tests/job_base.py b/awx/main/tests/job_base.py deleted file mode 100644 index 0d36624a79..0000000000 --- a/awx/main/tests/job_base.py +++ /dev/null @@ -1,541 +0,0 @@ -# Python -import uuid - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTestMixin - -TEST_PLAYBOOK = '''- hosts: all - gather_facts: false - tasks: - - name: woohoo - command: test 1 = 1 -''' - - -class BaseJobTestMixin(BaseTestMixin): - - - def _create_inventory(self, name, organization, created_by, - groups_hosts_dict): - '''Helper method for creating inventory with groups and hosts.''' - inventory = organization.inventories.create( - name=name, - created_by=created_by, - ) - for group_name, host_names in groups_hosts_dict.items(): - group = inventory.groups.create( - name=group_name, - created_by=created_by, - ) - for host_name in host_names: - host = inventory.hosts.create( - name=host_name, - created_by=created_by, - ) - group.hosts.add(host) - return inventory - - def populate(self): - # Here's a little story about the AWX Bread Company, or ABC. They - # make machines that make bread - bakers, slicers, and packagers - and - # these machines are each controlled by a Linux boxes, which is in turn - # managed by AWX. - - # Sue is the super user. You don't mess with Sue or you're toast. Ha. - self.user_sue = self.make_user('sue', super_user=True) - - # There are three organizations in ABC using Ansible, since it's the - # best thing for dev ops automation since, well, sliced bread. - - # Engineering - They design and build the machines. - self.org_eng = Organization.objects.create( - name='engineering', - created_by=self.user_sue, - ) - # Support - They fix it when it's not working. - self.org_sup = Organization.objects.create( - name='support', - created_by=self.user_sue, - ) - # Operations - They implement the production lines using the machines. - self.org_ops = Organization.objects.create( - name='operations', - created_by=self.user_sue, - ) - - # Alex is Sue's IT assistant who can also administer all of the - # organizations. - self.user_alex = self.make_user('alex') - self.org_eng.admin_role.members.add(self.user_alex) - self.org_sup.admin_role.members.add(self.user_alex) - self.org_ops.admin_role.members.add(self.user_alex) - - # Bob is the head of engineering. He's an admin for engineering, but - # also a user within the operations organization (so he can see the - # results if things go wrong in production). - self.user_bob = self.make_user('bob') - self.org_eng.admin_role.members.add(self.user_bob) - self.org_ops.member_role.members.add(self.user_bob) - - # Chuck is the lead engineer. He has full reign over engineering, but - # no other organizations. - self.user_chuck = self.make_user('chuck') - self.org_eng.admin_role.members.add(self.user_chuck) - - # Doug is the other engineer working under Chuck. He can write - # playbooks and check them, but Chuck doesn't quite think he's ready to - # run them yet. Poor Doug. - self.user_doug = self.make_user('doug') - self.org_eng.member_role.members.add(self.user_doug) - - # Juan is another engineer working under Chuck. He has a little more freedom - # to run playbooks but can't create job templates - self.user_juan = self.make_user('juan') - self.org_eng.member_role.members.add(self.user_juan) - - # Hannibal is Chuck's right-hand man. Chuck usually has him create the job - # templates that the rest of the team will use - self.user_hannibal = self.make_user('hannibal') - self.org_eng.member_role.members.add(self.user_hannibal) - - # Eve is the head of support. She can also see what goes on in - # operations to help them troubleshoot problems. - self.user_eve = self.make_user('eve') - self.org_sup.admin_role.members.add(self.user_eve) - self.org_ops.member_role.members.add(self.user_eve) - - # Frank is the other support guy. - self.user_frank = self.make_user('frank') - self.org_sup.member_role.members.add(self.user_frank) - - # Greg is the head of operations. - self.user_greg = self.make_user('greg') - self.org_ops.admin_role.members.add(self.user_greg) - - # Holly is an operations engineer. - self.user_holly = self.make_user('holly') - self.org_ops.member_role.members.add(self.user_holly) - - # Iris is another operations engineer. - self.user_iris = self.make_user('iris') - self.org_ops.member_role.members.add(self.user_iris) - - # Randall and Billybob are new ops interns that ops uses to test - # their playbooks and inventory - self.user_randall = self.make_user('randall') - self.org_ops.member_role.members.add(self.user_randall) - - # He works with Randall - self.user_billybob = self.make_user('billybob') - self.org_ops.member_role.members.add(self.user_billybob) - - # Jim is the newest intern. He can login, but can't do anything quite yet - # except make everyone else fresh coffee. - self.user_jim = self.make_user('jim') - - # There are three main projects, one each for the development, test and - # production branches of the playbook repository. All three orgs can - # use the production branch, support can use the production and testing - # branches, and operations can only use the production branch. - self.proj_dev = self.make_project('dev', 'development branch', - self.user_sue, TEST_PLAYBOOK) - self.org_eng.projects.add(self.proj_dev) - self.proj_test = self.make_project('test', 'testing branch', - self.user_sue, TEST_PLAYBOOK) - #self.org_eng.projects.add(self.proj_test) # No more multi org projects - self.org_sup.projects.add(self.proj_test) - self.proj_prod = self.make_project('prod', 'production branch', - self.user_sue, TEST_PLAYBOOK) - #self.org_eng.projects.add(self.proj_prod) # No more multi org projects - #self.org_sup.projects.add(self.proj_prod) # No more multi org projects - self.org_ops.projects.add(self.proj_prod) - - # Operations also has 2 additional projects specific to the east/west - # production environments. - self.proj_prod_east = self.make_project('prod-east', - 'east production branch', - self.user_sue, TEST_PLAYBOOK) - self.org_ops.projects.add(self.proj_prod_east) - self.proj_prod_west = self.make_project('prod-west', - 'west production branch', - self.user_sue, TEST_PLAYBOOK) - self.org_ops.projects.add(self.proj_prod_west) - - # The engineering organization has a set of servers to use for - # development and testing (2 bakers, 1 slicer, 1 packager). - self.inv_eng = self._create_inventory( - name='engineering environment', - organization=self.org_eng, - created_by=self.user_sue, - groups_hosts_dict={ - 'bakers': ['eng-baker1', 'eng-baker2'], - 'slicers': ['eng-slicer1'], - 'packagers': ['eng-packager1'], - }, - ) - - # The support organization has a set of servers to use for - # testing and reproducing problems from operations (1 baker, 1 slicer, - # 1 packager). - self.inv_sup = self._create_inventory( - name='support environment', - organization=self.org_sup, - created_by=self.user_sue, - groups_hosts_dict={ - 'bakers': ['sup-baker1'], - 'slicers': ['sup-slicer1'], - 'packagers': ['sup-packager1'], - }, - ) - - # The operations organization manages multiple sets of servers for the - # east and west production facilities. - self.inv_ops_east = self._create_inventory( - name='east production environment', - organization=self.org_ops, - created_by=self.user_sue, - groups_hosts_dict={ - 'bakers': ['east-baker%d' % n for n in range(1, 4)], - 'slicers': ['east-slicer%d' % n for n in range(1, 3)], - 'packagers': ['east-packager%d' % n for n in range(1, 3)], - }, - ) - self.inv_ops_west = self._create_inventory( - name='west production environment', - organization=self.org_ops, - created_by=self.user_sue, - groups_hosts_dict={ - 'bakers': ['west-baker%d' % n for n in range(1, 6)], - 'slicers': ['west-slicer%d' % n for n in range(1, 4)], - 'packagers': ['west-packager%d' % n for n in range(1, 3)], - }, - ) - - # Operations is divided into teams to work on the east/west servers. - # Greg and Holly work on east, Greg and iris work on west. - self.team_ops_east = self.org_ops.teams.create( - name='easterners', - created_by=self.user_sue) - self.team_ops_east.member_role.children.add(self.proj_prod.admin_role) - self.team_ops_east.member_role.children.add(self.proj_prod_east.admin_role) - self.team_ops_east.member_role.members.add(self.user_greg) - self.team_ops_east.member_role.members.add(self.user_holly) - self.team_ops_west = self.org_ops.teams.create( - name='westerners', - created_by=self.user_sue) - self.team_ops_west.member_role.children.add(self.proj_prod.admin_role) - self.team_ops_west.member_role.children.add(self.proj_prod_west.admin_role) - self.team_ops_west.member_role.members.add(self.user_greg) - self.team_ops_west.member_role.members.add(self.user_iris) - - # The south team is no longer active having been folded into the east team - # FIXME: This code can be removed (probably) - # - this case has been removed as we've gotten rid of the active flag, keeping - # code around in case this has ramifications on some test failures.. if - # you find this message and all tests are passing, then feel free to remove this - # - anoek 2016-03-10 - #self.team_ops_south = self.org_ops.teams.create( - # name='southerners', - # created_by=self.user_sue, - # active=False, - #) - #self.team_ops_south.member_role.children.add(self.proj_prod.admin_role) - #self.team_ops_south.member_role.members.add(self.user_greg) - - # The north team is going to be deleted - self.team_ops_north = self.org_ops.teams.create( - name='northerners', - created_by=self.user_sue, - ) - self.team_ops_north.member_role.children.add(self.proj_prod.admin_role) - self.team_ops_north.member_role.members.add(self.user_greg) - - # The testers team are interns that can only check playbooks but can't - # run them - self.team_ops_testers = self.org_ops.teams.create( - name='testers', - created_by=self.user_sue, - ) - self.team_ops_testers.member_role.children.add(self.proj_prod.admin_role) - self.team_ops_testers.member_role.members.add(self.user_randall) - self.team_ops_testers.member_role.members.add(self.user_billybob) - - # Each user has his/her own set of credentials. - from awx.main.tests.data.ssh import (TEST_SSH_KEY_DATA, - TEST_SSH_KEY_DATA_LOCKED, - TEST_SSH_KEY_DATA_UNLOCK) - self.cred_sue = Credential.objects.create( - username='sue', - password=TEST_SSH_KEY_DATA, - created_by=self.user_sue, - ) - self.cred_sue.admin_role.members.add(self.user_sue) - - self.cred_sue_ask = Credential.objects.create( - username='sue', - password='ASK', - created_by=self.user_sue, - ) - self.cred_sue_ask.admin_role.members.add(self.user_sue) - - self.cred_sue_ask_many = Credential.objects.create( - username='sue', - password='ASK', - become_method='sudo', - become_username='root', - become_password='ASK', - ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock='ASK', - created_by=self.user_sue, - ) - self.cred_sue_ask_many.admin_role.members.add(self.user_sue) - - self.cred_bob = Credential.objects.create( - username='bob', - password='ASK', - created_by=self.user_sue, - ) - self.cred_bob.use_role.members.add(self.user_bob) - - self.cred_chuck = Credential.objects.create( - username='chuck', - ssh_key_data=TEST_SSH_KEY_DATA, - created_by=self.user_sue, - ) - self.cred_chuck.use_role.members.add(self.user_chuck) - - self.cred_doug = Credential.objects.create( - username='doug', - password='doug doesn\'t mind his password being saved. this ' - 'is why we dont\'t let doug actually run jobs.', - created_by=self.user_sue, - ) - self.cred_doug.use_role.members.add(self.user_doug) - - self.cred_eve = Credential.objects.create( - username='eve', - password='ASK', - become_method='sudo', - become_username='root', - become_password='ASK', - created_by=self.user_sue, - ) - self.cred_eve.use_role.members.add(self.user_eve) - - self.cred_frank = Credential.objects.create( - username='frank', - password='fr@nk the t@nk', - created_by=self.user_sue, - ) - self.cred_frank.use_role.members.add(self.user_frank) - - self.cred_greg = Credential.objects.create( - username='greg', - ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock='ASK', - created_by=self.user_sue, - ) - self.cred_greg.use_role.members.add(self.user_greg) - - self.cred_holly = Credential.objects.create( - username='holly', - password='holly rocks', - created_by=self.user_sue, - ) - self.cred_holly.use_role.members.add(self.user_holly) - - self.cred_iris = Credential.objects.create( - username='iris', - password='ASK', - created_by=self.user_sue, - ) - self.cred_iris.use_role.members.add(self.user_iris) - - # Each operations team also has shared credentials they can use. - self.cred_ops_east = Credential.objects.create( - username='east', - ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK, - created_by = self.user_sue, - ) - self.team_ops_east.member_role.children.add(self.cred_ops_east.use_role) - - self.cred_ops_west = Credential.objects.create( - username='west', - password='Heading270', - created_by = self.user_sue, - ) - self.team_ops_west.member_role.children.add(self.cred_ops_west.use_role) - - - # FIXME: This code can be removed (probably) - # - this case has been removed as we've gotten rid of the active flag, keeping - # code around in case this has ramifications on some test failures.. if - # you find this message and all tests are passing, then feel free to remove this - # - anoek 2016-03-10 - #self.cred_ops_south = self.team_ops_south.credentials.create( - # username='south', - # password='Heading180', - # created_by = self.user_sue, - #) - - self.cred_ops_north = Credential.objects.create( - username='north', - password='Heading0', - created_by = self.user_sue, - ) - self.team_ops_north.member_role.children.add(self.cred_ops_north.admin_role) - - self.cred_ops_test = Credential.objects.create( - username='testers', - password='HeadingNone', - created_by = self.user_sue, - ) - self.team_ops_testers.member_role.children.add(self.cred_ops_test.use_role) - - # Engineering has job templates to check/run the dev project onto - # their own inventory. - self.jt_eng_check = JobTemplate.objects.create( - name='eng-dev-check', - job_type='check', - inventory= self.inv_eng, - project=self.proj_dev, - playbook=self.proj_dev.playbooks[0], - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_eng_check = self.jt_eng_check.create_job( - # created_by=self.user_sue, - # credential=self.cred_doug, - # ) - self.jt_eng_run = JobTemplate.objects.create( - name='eng-dev-run', - job_type='run', - inventory= self.inv_eng, - project=self.proj_dev, - playbook=self.proj_dev.playbooks[0], - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ask_credential_on_launch=True, - ) - # self.job_eng_run = self.jt_eng_run.create_job( - # created_by=self.user_sue, - # credential=self.cred_chuck, - # ) - - # Support has job templates to check/run the test project onto - # their own inventory. - self.jt_sup_check = JobTemplate.objects.create( - name='sup-test-check', - job_type='check', - inventory= self.inv_sup, - project=self.proj_test, - playbook=self.proj_test.playbooks[0], - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_sup_check = self.jt_sup_check.create_job( - # created_by=self.user_sue, - # credential=self.cred_frank, - # ) - self.jt_sup_run = JobTemplate.objects.create( - name='sup-test-run', - job_type='run', - inventory= self.inv_sup, - project=self.proj_test, - playbook=self.proj_test.playbooks[0], - host_config_key=uuid.uuid4().hex, - credential=self.cred_eve, - created_by=self.user_sue, - ) - # self.job_sup_run = self.jt_sup_run.create_job( - # created_by=self.user_sue, - # ) - - # Operations has job templates to check/run the prod project onto - # both east and west inventories, by default using the team credential. - self.jt_ops_east_check = JobTemplate.objects.create( - name='ops-east-prod-check', - job_type='check', - inventory= self.inv_ops_east, - project=self.proj_prod, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_east, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_ops_east_check = self.jt_ops_east_check.create_job( - # created_by=self.user_sue, - # ) - self.jt_ops_east_run = JobTemplate.objects.create( - name='ops-east-prod-run', - job_type='run', - inventory= self.inv_ops_east, - project=self.proj_prod, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_east, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - self.jt_ops_east_run_prod_east = JobTemplate.objects.create( - name='ops-east-prod-run-on-prod-east', - job_type='run', - inventory= self.inv_ops_east, - project=self.proj_prod_east, - playbook=self.proj_prod_east.playbooks[0], - credential=self.cred_ops_east, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_ops_east_run = self.jt_ops_east_run.create_job( - # created_by=self.user_sue, - # ) - self.jt_ops_west_check = JobTemplate.objects.create( - name='ops-west-prod-check', - job_type='check', - inventory= self.inv_ops_west, - project=self.proj_prod, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_west, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - self.jt_ops_west_check_test_team = JobTemplate.objects.create( - name='ops-west-prod-check-testers', - job_type='check', - inventory= self.inv_ops_west, - project=self.proj_prod, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_test, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_ops_west_check = self.jt_ops_west_check.create_job( - # created_by=self.user_sue, - # ) - self.jt_ops_west_run = JobTemplate.objects.create( - name='ops-west-prod-run', - job_type='run', - inventory= self.inv_ops_west, - project=self.proj_prod, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_west, - host_config_key=uuid.uuid4().hex, - created_by=self.user_sue, - ) - # self.job_ops_west_run = self.jt_ops_west_run.create_job( - # created_by=self.user_sue, - # ) - - def setUp(self): - super(BaseJobTestMixin, self).setUp() - self.start_rabbit() - self.setup_instances() - self.populate() - self.start_queue() - - def tearDown(self): - super(BaseJobTestMixin, self).tearDown() - self.stop_rabbit() - self.terminate_queue() From 996a5b20b0d93acb1507214ea53dcdda7b33044b Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 08:43:30 -0400 Subject: [PATCH 077/379] unit tests of cred field types --- awx/main/fields.py | 14 +- awx/main/tests/functional/test_credential.py | 135 -------------- awx/main/tests/unit/test_fields.py | 176 +++++++++++++++++++ 3 files changed, 186 insertions(+), 139 deletions(-) create mode 100644 awx/main/tests/unit/test_fields.py diff --git a/awx/main/fields.py b/awx/main/fields.py index e0af0674ac..8070a707f1 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -396,10 +396,16 @@ class JSONSchemaField(JSONBField): expected_type = error.validator_value if expected_type == 'object': expected_type = 'dict' - error.message = _( - '{type} provided in relative path {path}, expected {expected_type}' - ).format(path=list(error.path), type=type(error.instance).__name__, - expected_type=expected_type) + if error.path: + error.message = _( + '{type} provided in relative path {path}, expected {expected_type}' + ).format(path=list(error.path), type=type(error.instance).__name__, + expected_type=expected_type) + else: + error.message = _( + '{type} provided, expected {expected_type}' + ).format(path=list(error.path), type=type(error.instance).__name__, + expected_type=expected_type) errors.append(error) if errors: diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index 37609cd222..d445508b71 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -56,98 +56,6 @@ def test_cloud_kind_uniqueness(): assert CredentialType.defaults['aws']().unique_by_kind is False -@pytest.mark.django_db -@pytest.mark.parametrize('input_, valid', [ - ({}, True), - ({'fields': []}, True), - ({'fields': {}}, False), - ({'fields': 123}, False), - ({'fields': [{'id': 'username', 'label': 'Username', 'foo': 'bar'}]}, False), - ({'fields': [{'id': 'username', 'label': 'Username'}]}, True), - ({'fields': [{'id': 'username', 'label': 'Username', 'type': 'string'}]}, True), - ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 1}]}, False), - ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 'Help Text'}]}, True), # noqa - ({'fields': [{'id': 'username', 'label': 'Username'}, {'id': 'username', 'label': 'Username 2'}]}, False), # noqa - ({'fields': [{'id': '$invalid$', 'label': 'Invalid', 'type': 'string'}]}, False), # noqa - ({'fields': [{'id': 'password', 'label': 'Password', 'type': 'invalid-type'}]}, False), - ({'fields': [{'id': 'ssh_key', 'label': 'SSH Key', 'type': 'string', 'format': 'ssh_private_key'}]}, True), # noqa - ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean'}]}, True), - ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean', 'choices': ['a', 'b']}]}, False), - ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean', 'secret': True}]}, False), - ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': True}]}, True), - ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': True, 'type': 'boolean'}]}, False), # noqa - ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': 'bad'}]}, False), # noqa - ({'fields': [{'id': 'token', 'label': 'Token', 'secret': True}]}, True), - ({'fields': [{'id': 'token', 'label': 'Token', 'secret': 'bad'}]}, False), - ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': True}]}, True), - ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': 'bad'}]}, False), # noqa - ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': 'not-a-list'}]}, False), # noqa - ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': []}]}, False), - ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': ['su', 'sudo']}]}, True), # noqa - ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': ['dup', 'dup']}]}, False), # noqa - ({'fields': [{'id': 'tower', 'label': 'Reserved!', }]}, False), # noqa -]) -def test_cred_type_input_schema_validity(input_, valid): - type_ = CredentialType( - kind='cloud', - name='SomeCloud', - managed_by_tower=True, - inputs=input_ - ) - if valid is False: - with pytest.raises(Exception) as e: - type_.full_clean() - assert e.type in (ValidationError, serializers.ValidationError) - else: - type_.full_clean() - - -@pytest.mark.django_db -@pytest.mark.parametrize('injectors, valid', [ - ({}, True), - ({'invalid-injector': {}}, False), - ({'file': 123}, False), - ({'file': {}}, True), - ({'file': {'template': '{{username}}'}}, True), - ({'file': {'template.username': '{{username}}'}}, True), - ({'file': {'template.username': '{{username}}', 'template.password': '{{pass}}'}}, True), - ({'file': {'template': '{{username}}', 'template.password': '{{pass}}'}}, False), - ({'file': {'foo': 'bar'}}, False), - ({'env': 123}, False), - ({'env': {}}, True), - ({'env': {'AWX_SECRET': '{{awx_secret}}'}}, True), - ({'env': {'AWX_SECRET_99': '{{awx_secret}}'}}, True), - ({'env': {'99': '{{awx_secret}}'}}, False), - ({'env': {'AWX_SECRET=': '{{awx_secret}}'}}, False), - ({'extra_vars': 123}, False), - ({'extra_vars': {}}, True), - ({'extra_vars': {'hostname': '{{host}}'}}, True), - ({'extra_vars': {'hostname_99': '{{host}}'}}, True), - ({'extra_vars': {'99': '{{host}}'}}, False), - ({'extra_vars': {'99=': '{{host}}'}}, False), -]) -def test_cred_type_injectors_schema(injectors, valid): - type_ = CredentialType( - kind='cloud', - name='SomeCloud', - managed_by_tower=True, - inputs={ - 'fields': [ - {'id': 'username', 'type': 'string', 'label': '_'}, - {'id': 'pass', 'type': 'string', 'label': '_'}, - {'id': 'awx_secret', 'type': 'string', 'label': '_'}, - {'id': 'host', 'type': 'string', 'label': '_'}, - ] - }, - injectors=injectors - ) - if valid is False: - with pytest.raises(ValidationError): - type_.full_clean() - else: - type_.full_clean() - - @pytest.mark.django_db def test_credential_creation(organization_factory): org = organization_factory('test').organization @@ -174,49 +82,6 @@ def test_credential_creation(organization_factory): assert cred.inputs['username'] == cred.username == 'bob' -@pytest.mark.django_db -@pytest.mark.parametrize('inputs', [ - ['must-be-a-dict'], - {'user': 'wrong-key'}, - {'username': 1}, - {'username': 1.5}, - {'username': ['a', 'b', 'c']}, - {'username': {'a': 'b'}}, - {'username': False}, - {'flag': 1}, - {'flag': 1.5}, - {'flag': ['a', 'b', 'c']}, - {'flag': {'a': 'b'}}, - {'flag': 'some-string'}, -]) -def test_credential_creation_validation_failure(organization_factory, inputs): - org = organization_factory('test').organization - type_ = CredentialType( - kind='cloud', - name='SomeCloud', - managed_by_tower=True, - inputs={ - 'fields': [{ - 'id': 'username', - 'label': 'Username for SomeCloud', - 'type': 'string' - },{ - 'id': 'flag', - 'label': 'Some Boolean Flag', - 'type': 'boolean' - }] - } - ) - type_.save() - - with pytest.raises(Exception) as e: - cred = Credential(credential_type=type_, name="Bob's Credential", - inputs=inputs, organization=org) - cred.save() - cred.full_clean() - assert e.type in (ValidationError, serializers.ValidationError) - - @pytest.mark.django_db @pytest.mark.parametrize('kind', ['ssh', 'net', 'scm']) @pytest.mark.parametrize('ssh_key_data, ssh_key_unlock, valid', [ diff --git a/awx/main/tests/unit/test_fields.py b/awx/main/tests/unit/test_fields.py new file mode 100644 index 0000000000..bec0c4de2f --- /dev/null +++ b/awx/main/tests/unit/test_fields.py @@ -0,0 +1,176 @@ +import pytest + +from django.core.exceptions import ValidationError +from rest_framework.serializers import ValidationError as DRFValidationError + +from awx.main.models import Credential, CredentialType, BaseModel +from awx.main.fields import JSONSchemaField + + +@pytest.mark.parametrize('schema, given, message', [ + ( + { # immitates what the CredentialType injectors field is + "additionalProperties": False, + "type": "object", + "properties": { + "extra_vars": { + "additionalProperties": False, + "type": "object" + } + } + }, + {'extra_vars': ['duck', 'horse']}, + "list provided in relative path ['extra_vars'], expected dict" + ), + ( + { # immitates what the CredentialType injectors field is + "additionalProperties": False, + "type": "object", + }, + ['duck', 'horse'], + "list provided, expected dict" + ), +]) +def test_custom_error_messages(schema, given, message): + instance = BaseModel() + + class MockFieldSubclass(JSONSchemaField): + def schema(self, model_instance): + return schema + + field = MockFieldSubclass() + + with pytest.raises(ValidationError) as exc: + field.validate(given, instance) + + assert message == exc.value.error_list[0].message + + +@pytest.mark.parametrize('input_, valid', [ + ({}, True), + ({'fields': []}, True), + ({'fields': {}}, False), + ({'fields': 123}, False), + ({'fields': [{'id': 'username', 'label': 'Username', 'foo': 'bar'}]}, False), + ({'fields': [{'id': 'username', 'label': 'Username'}]}, True), + ({'fields': [{'id': 'username', 'label': 'Username', 'type': 'string'}]}, True), + ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 1}]}, False), + ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 'Help Text'}]}, True), # noqa + ({'fields': [{'id': 'username', 'label': 'Username'}, {'id': 'username', 'label': 'Username 2'}]}, False), # noqa + ({'fields': [{'id': '$invalid$', 'label': 'Invalid', 'type': 'string'}]}, False), # noqa + ({'fields': [{'id': 'password', 'label': 'Password', 'type': 'invalid-type'}]}, False), + ({'fields': [{'id': 'ssh_key', 'label': 'SSH Key', 'type': 'string', 'format': 'ssh_private_key'}]}, True), # noqa + ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean'}]}, True), + ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean', 'choices': ['a', 'b']}]}, False), + ({'fields': [{'id': 'flag', 'label': 'Some Flag', 'type': 'boolean', 'secret': True}]}, False), + ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': True}]}, True), + ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': True, 'type': 'boolean'}]}, False), # noqa + ({'fields': [{'id': 'certificate', 'label': 'Cert', 'multiline': 'bad'}]}, False), # noqa + ({'fields': [{'id': 'token', 'label': 'Token', 'secret': True}]}, True), + ({'fields': [{'id': 'token', 'label': 'Token', 'secret': 'bad'}]}, False), + ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': True}]}, True), + ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': 'bad'}]}, False), # noqa + ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': 'not-a-list'}]}, False), # noqa + ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': []}]}, False), + ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': ['su', 'sudo']}]}, True), # noqa + ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': ['dup', 'dup']}]}, False), # noqa + ({'fields': [{'id': 'tower', 'label': 'Reserved!', }]}, False), # noqa +]) +def test_cred_type_input_schema_validity(input_, valid): + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed_by_tower=True, + inputs=input_ + ) + field = CredentialType._meta.get_field('inputs') + if valid is False: + with pytest.raises(ValidationError): + field.clean(input_, type_) + else: + field.clean(input_, type_) + + +@pytest.mark.parametrize('injectors, valid', [ + ({}, True), + ({'invalid-injector': {}}, False), + ({'file': 123}, False), + ({'file': {}}, True), + ({'file': {'template': '{{username}}'}}, True), + ({'file': {'template.username': '{{username}}'}}, True), + ({'file': {'template.username': '{{username}}', 'template.password': '{{pass}}'}}, True), + ({'file': {'template': '{{username}}', 'template.password': '{{pass}}'}}, False), + ({'file': {'foo': 'bar'}}, False), + ({'env': 123}, False), + ({'env': {}}, True), + ({'env': {'AWX_SECRET': '{{awx_secret}}'}}, True), + ({'env': {'AWX_SECRET_99': '{{awx_secret}}'}}, True), + ({'env': {'99': '{{awx_secret}}'}}, False), + ({'env': {'AWX_SECRET=': '{{awx_secret}}'}}, False), + ({'extra_vars': 123}, False), + ({'extra_vars': {}}, True), + ({'extra_vars': {'hostname': '{{host}}'}}, True), + ({'extra_vars': {'hostname_99': '{{host}}'}}, True), + ({'extra_vars': {'99': '{{host}}'}}, False), + ({'extra_vars': {'99=': '{{host}}'}}, False), +]) +def test_cred_type_injectors_schema(injectors, valid): + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed_by_tower=True, + inputs={ + 'fields': [ + {'id': 'username', 'type': 'string', 'label': '_'}, + {'id': 'pass', 'type': 'string', 'label': '_'}, + {'id': 'awx_secret', 'type': 'string', 'label': '_'}, + {'id': 'host', 'type': 'string', 'label': '_'}, + ] + }, + injectors=injectors + ) + field = CredentialType._meta.get_field('injectors') + if valid is False: + with pytest.raises(ValidationError): + field.clean(injectors, type_) + else: + field.clean(injectors, type_) + + +@pytest.mark.parametrize('inputs', [ + ['must-be-a-dict'], + {'user': 'wrong-key'}, + {'username': 1}, + {'username': 1.5}, + {'username': ['a', 'b', 'c']}, + {'username': {'a': 'b'}}, + {'flag': 1}, + {'flag': 1.5}, + {'flag': ['a', 'b', 'c']}, + {'flag': {'a': 'b'}}, + {'flag': 'some-string'}, +]) +def test_credential_creation_validation_failure(inputs): + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed_by_tower=True, + inputs={ + 'fields': [{ + 'id': 'username', + 'label': 'Username for SomeCloud', + 'type': 'string' + },{ + 'id': 'flag', + 'label': 'Some Boolean Flag', + 'type': 'boolean' + }] + } + ) + cred = Credential(credential_type=type_, name="Bob's Credential", + inputs=inputs) + field = cred._meta.get_field('inputs') + + with pytest.raises(Exception) as e: + field.validate(inputs, cred) + assert e.type in (ValidationError, DRFValidationError) From 53139b109e87ae4b06e0625c6a23f9f6a8fd5dd5 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Tue, 3 Apr 2018 16:05:29 -0400 Subject: [PATCH 078/379] clean up application logic --- awx/main/access.py | 14 +++----- awx/main/tests/functional/api/test_oauth.py | 2 +- awx/main/tests/functional/test_rbac_oauth.py | 38 +++++++++++--------- awx/settings/defaults.py | 3 +- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 29d717791d..187aae3bc3 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -586,7 +586,7 @@ class OAuth2ApplicationAccess(BaseAccess): - I am a user in the organization of the application. I can create OAuth 2 applications when: - I am a superuser. - - I am the admin of the organization of the organization of the application. + - I am the admin of the organization of the application. ''' model = OAuth2Application @@ -596,15 +596,11 @@ class OAuth2ApplicationAccess(BaseAccess): return self.model.objects.filter(organization__in=self.user.organizations) def can_change(self, obj, data): - if obj.organization in self.user.admin_of_organizations or self.user.is_superuser: - if not self.check_related('organization', Organization, data, role_field='admin_role'): - return False - return True - else: - return False - + return self.user.is_superuser or self.check_related('organization', Organization, data, obj=obj, + role_field='admin_role', mandatory=True) + def can_delete(self, obj): - return obj.organization in self.user.admin_of_organizations or self.user.is_superuser + return self.user.is_superuser or obj.organization in self.user.admin_of_organizations def can_add(self, data): if self.user.is_superuser: diff --git a/awx/main/tests/functional/api/test_oauth.py b/awx/main/tests/functional/api/test_oauth.py index 6b462bbd58..a417eb539f 100644 --- a/awx/main/tests/functional/api/test_oauth.py +++ b/awx/main/tests/functional/api/test_oauth.py @@ -9,7 +9,7 @@ from oauth2_provider.models import RefreshToken @pytest.mark.django_db -def test_personal_access_token_creation(oauth_application, post, alice): # TODO: Update this test +def test_personal_access_token_creation(oauth_application, post, alice): url = drf_reverse('api:oauth_authorization_root_view') + 'token/' resp = post( url, diff --git a/awx/main/tests/functional/test_rbac_oauth.py b/awx/main/tests/functional/test_rbac_oauth.py index 6a99a284d0..8f673cab80 100644 --- a/awx/main/tests/functional/test_rbac_oauth.py +++ b/awx/main/tests/functional/test_rbac_oauth.py @@ -34,30 +34,34 @@ class TestOAuth2Application: assert access.can_read(app) is can_access - @pytest.mark.parametrize("user_for_access, can_access_list", [ - (0, [True, True]), - (1, [True, True]), - (2, [False, False]), - (3, [False, False]), - ]) - def test_can_edit_delete_app( - self, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization + def test_can_edit_delete_app_org_admin( + self, admin, org_admin, org_member, alice, organization ): - organization.admin_role.members.add(org_admin) - organization.member_role.members.add(org_member) user_list = [admin, org_admin, org_member, alice] - access = OAuth2ApplicationAccess(user_list[user_for_access]) - app_creation_user_list = [admin, org_admin] - for user, can_access in zip(app_creation_user_list, can_access_list): + can_access_list = [True, True, False, False] + for user, can_access in zip(user_list, can_access_list): app = Application.objects.create( - name='test app for {}'.format(user.username), user=user, + name='test app for {}'.format(org_admin.username), user=org_admin, client_type='confidential', authorization_grant_type='password', organization=organization ) + access = OAuth2ApplicationAccess(user) + assert access.can_change(app, {}) is can_access + assert access.can_delete(app) is can_access + + + def test_can_edit_delete_app_admin( + self, admin, org_admin, org_member, alice, organization + ): + user_list = [admin, org_admin, org_member, alice] + can_access_list = [True, True, False, False] + for user, can_access in zip(user_list, can_access_list): + app = Application.objects.create( + name='test app for {}'.format(admin.username), user=admin, + client_type='confidential', authorization_grant_type='password', organization=organization + ) + access = OAuth2ApplicationAccess(user) assert access.can_change(app, {}) is can_access assert access.can_delete(app) is can_access - - - def test_superuser_can_always_create(self, admin, org_admin, org_member, alice): diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 013f831492..645947eb60 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -289,8 +289,9 @@ REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'awx.api.pagination.Pagination', 'PAGE_SIZE': 25, 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'awx.api.authentication.LoggedOAuth2Authentication', 'awx.api.authentication.SessionAuthentication', + 'awx.api.authentication.LoggedOAuth2Authentication', + # 'awx.api.authentication.SessionAuthentication', 'awx.api.authentication.LoggedBasicAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( From c064195025c37269e204f771bebeba7b77d7d828 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 10:02:14 -0400 Subject: [PATCH 079/379] remove TOWER_HOST from job env vars --- awx/main/tasks.py | 1 - docs/CHANGELOG.md | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 0de7094100..51d93b9508 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1157,7 +1157,6 @@ class RunJob(BaseTask): if not kwargs.get('isolated'): env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_path env['ANSIBLE_STDOUT_CALLBACK'] = 'awx_display' - env['TOWER_HOST'] = settings.TOWER_URL_BASE env['AWX_HOST'] = settings.TOWER_URL_BASE env['CACHE'] = settings.CACHES['default']['LOCATION'] if 'LOCATION' in settings.CACHES['default'] else '' diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 78111dd8a2..592a9128e8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -62,3 +62,6 @@ * Saved Launch-time configurations feature - added WFJT node promptable fields to schedules, added `extra_data` to WFJT nodes, added "schedule this job" endpoint. [[#169](https://github.com/ansible/awx/issues/169)] +* Removed `TOWER_HOST` as a default environment variable in job running environment + due to conflict with tower credential type. Playbook authors should replace their + use with `AWX_HOST`. [[#1727](https://github.com/ansible/awx/issues/1727)] From 17c1ac4468ce1fe8479103de2c16332a4192c6ba Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 10:59:54 -0400 Subject: [PATCH 080/379] remove licenses for packes removed in 3.3 --- docs/licenses/cliff.txt | 202 ------------------- docs/licenses/cmd2.txt | 21 -- docs/licenses/django-transaction-hooks.txt | 28 --- docs/licenses/funcsigs.txt | 13 -- docs/licenses/gevent-websocket.txt | 216 --------------------- docs/licenses/gevent.txt | 24 --- docs/licenses/greenlet.txt | 30 --- docs/licenses/osc-lib.txt | 175 ----------------- docs/licenses/oslo.config.txt | 204 ------------------- docs/licenses/oslo.i18n.txt | 175 ----------------- docs/licenses/oslo.serialization.txt | 175 ----------------- docs/licenses/oslo.utils.txt | 175 ----------------- docs/licenses/positional.txt | 174 ----------------- docs/licenses/prettytable.txt | 30 --- docs/licenses/python-cinderclient.txt | 208 -------------------- docs/licenses/python-designateclient.txt | 175 ----------------- docs/licenses/python-glanceclient.txt | 175 ----------------- docs/licenses/python-ironicclient.txt | 176 ----------------- docs/licenses/python-keystoneclient.txt | 209 -------------------- docs/licenses/python-neutronclient.txt | 176 ----------------- docs/licenses/python-novaclient.txt | 208 -------------------- docs/licenses/python-openstackclient.txt | 176 ----------------- docs/licenses/python-social-auth.txt | 27 --- docs/licenses/rfc3986.txt | 13 -- docs/licenses/unicodecsv.txt | 25 --- docs/licenses/warlock.txt | 202 ------------------- docs/licenses/wrapt.txt | 24 --- 27 files changed, 3436 deletions(-) delete mode 100644 docs/licenses/cliff.txt delete mode 100644 docs/licenses/cmd2.txt delete mode 100644 docs/licenses/django-transaction-hooks.txt delete mode 100644 docs/licenses/funcsigs.txt delete mode 100644 docs/licenses/gevent-websocket.txt delete mode 100644 docs/licenses/gevent.txt delete mode 100644 docs/licenses/greenlet.txt delete mode 100644 docs/licenses/osc-lib.txt delete mode 100644 docs/licenses/oslo.config.txt delete mode 100644 docs/licenses/oslo.i18n.txt delete mode 100644 docs/licenses/oslo.serialization.txt delete mode 100644 docs/licenses/oslo.utils.txt delete mode 100644 docs/licenses/positional.txt delete mode 100644 docs/licenses/prettytable.txt delete mode 100644 docs/licenses/python-cinderclient.txt delete mode 100644 docs/licenses/python-designateclient.txt delete mode 100644 docs/licenses/python-glanceclient.txt delete mode 100644 docs/licenses/python-ironicclient.txt delete mode 100644 docs/licenses/python-keystoneclient.txt delete mode 100644 docs/licenses/python-neutronclient.txt delete mode 100644 docs/licenses/python-novaclient.txt delete mode 100644 docs/licenses/python-openstackclient.txt delete mode 100644 docs/licenses/python-social-auth.txt delete mode 100644 docs/licenses/rfc3986.txt delete mode 100644 docs/licenses/unicodecsv.txt delete mode 100644 docs/licenses/warlock.txt delete mode 100644 docs/licenses/wrapt.txt diff --git a/docs/licenses/cliff.txt b/docs/licenses/cliff.txt deleted file mode 100644 index d645695673..0000000000 --- a/docs/licenses/cliff.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/docs/licenses/cmd2.txt b/docs/licenses/cmd2.txt deleted file mode 100644 index 2eb890d746..0000000000 --- a/docs/licenses/cmd2.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2008-2016 Catherine Devlin and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/docs/licenses/django-transaction-hooks.txt b/docs/licenses/django-transaction-hooks.txt deleted file mode 100644 index e62887747a..0000000000 --- a/docs/licenses/django-transaction-hooks.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2014, Carl Meyer and contributors -All rights reserved. - -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 author nor the names of other - 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 -OWNER 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. diff --git a/docs/licenses/funcsigs.txt b/docs/licenses/funcsigs.txt deleted file mode 100644 index 3e563d6fbd..0000000000 --- a/docs/licenses/funcsigs.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2013 Aaron Iles - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/docs/licenses/gevent-websocket.txt b/docs/licenses/gevent-websocket.txt deleted file mode 100644 index 083e71cc4d..0000000000 --- a/docs/licenses/gevent-websocket.txt +++ /dev/null @@ -1,216 +0,0 @@ - Copyright 2011-2013 Jeffrey Gelens - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/docs/licenses/gevent.txt b/docs/licenses/gevent.txt deleted file mode 100644 index 190fb71b1f..0000000000 --- a/docs/licenses/gevent.txt +++ /dev/null @@ -1,24 +0,0 @@ -Except when otherwise stated (look at the beginning of each file) the software -and the documentation in this project are copyrighted by: - - Denis Bilenko and the contributors, http://www.gevent.org - -and licensed under the MIT license: - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. diff --git a/docs/licenses/greenlet.txt b/docs/licenses/greenlet.txt deleted file mode 100644 index 5e009e3c68..0000000000 --- a/docs/licenses/greenlet.txt +++ /dev/null @@ -1,30 +0,0 @@ -The following files are derived from Stackless Python and are subject to the -same license as Stackless Python: - - slp_platformselect.h - files in platform/ directory - -See LICENSE.PSF and http://www.stackless.com/ for details. - -Unless otherwise noted, the files in greenlet have been released under the -following MIT license: - -Copyright (c) Armin Rigo, Christian Tismer and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/docs/licenses/osc-lib.txt b/docs/licenses/osc-lib.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/osc-lib.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/oslo.config.txt b/docs/licenses/oslo.config.txt deleted file mode 100644 index 4143aac26d..0000000000 --- a/docs/licenses/oslo.config.txt +++ /dev/null @@ -1,204 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-keystoneclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project 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 OWNER 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. diff --git a/docs/licenses/oslo.i18n.txt b/docs/licenses/oslo.i18n.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/oslo.i18n.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/oslo.serialization.txt b/docs/licenses/oslo.serialization.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/oslo.serialization.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/oslo.utils.txt b/docs/licenses/oslo.utils.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/oslo.utils.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/positional.txt b/docs/licenses/positional.txt deleted file mode 100644 index dd5b3a58aa..0000000000 --- a/docs/licenses/positional.txt +++ /dev/null @@ -1,174 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/prettytable.txt b/docs/licenses/prettytable.txt deleted file mode 100644 index 7de41fb3cb..0000000000 --- a/docs/licenses/prettytable.txt +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2009-2013 Luke Maurits -# All rights reserved. -# With contributions from: -# * Chris Clark -# * Christoph Robbert -# * Klein Stephane -# * "maartendb" -# -# 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. -# * The name of the author may not 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 OWNER 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. diff --git a/docs/licenses/python-cinderclient.txt b/docs/licenses/python-cinderclient.txt deleted file mode 100644 index 3ecd073618..0000000000 --- a/docs/licenses/python-cinderclient.txt +++ /dev/null @@ -1,208 +0,0 @@ -Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) -Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) -All rights reserved. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-cinderclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project 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 OWNER 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. diff --git a/docs/licenses/python-designateclient.txt b/docs/licenses/python-designateclient.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/python-designateclient.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/python-glanceclient.txt b/docs/licenses/python-glanceclient.txt deleted file mode 100644 index 67db858821..0000000000 --- a/docs/licenses/python-glanceclient.txt +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/docs/licenses/python-ironicclient.txt b/docs/licenses/python-ironicclient.txt deleted file mode 100644 index 68c771a099..0000000000 --- a/docs/licenses/python-ironicclient.txt +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/docs/licenses/python-keystoneclient.txt b/docs/licenses/python-keystoneclient.txt deleted file mode 100644 index 32b66114fb..0000000000 --- a/docs/licenses/python-keystoneclient.txt +++ /dev/null @@ -1,209 +0,0 @@ -Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) -Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) -Copyright (c) 2011 Nebula, Inc - Keystone refactor (>= v2.7) -All rights reserved. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-keystoneclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project 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 OWNER 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. diff --git a/docs/licenses/python-neutronclient.txt b/docs/licenses/python-neutronclient.txt deleted file mode 100644 index 68c771a099..0000000000 --- a/docs/licenses/python-neutronclient.txt +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/docs/licenses/python-novaclient.txt b/docs/licenses/python-novaclient.txt deleted file mode 100644 index 1d8bc82cc8..0000000000 --- a/docs/licenses/python-novaclient.txt +++ /dev/null @@ -1,208 +0,0 @@ -Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) -Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) -All rights reserved. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-novaclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project 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 OWNER 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. diff --git a/docs/licenses/python-openstackclient.txt b/docs/licenses/python-openstackclient.txt deleted file mode 100644 index 68c771a099..0000000000 --- a/docs/licenses/python-openstackclient.txt +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/docs/licenses/python-social-auth.txt b/docs/licenses/python-social-auth.txt deleted file mode 100644 index b02084af50..0000000000 --- a/docs/licenses/python-social-auth.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012-2015, Matías Aguirre -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. 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. - - 3. Neither the name of this project 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 OWNER 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. diff --git a/docs/licenses/rfc3986.txt b/docs/licenses/rfc3986.txt deleted file mode 100644 index 72ce24cf02..0000000000 --- a/docs/licenses/rfc3986.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2014 Ian Cordasco, Rackspace - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/docs/licenses/unicodecsv.txt b/docs/licenses/unicodecsv.txt deleted file mode 100644 index 6d004c776d..0000000000 --- a/docs/licenses/unicodecsv.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2010 Jeremy Dunck. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - - 2. 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. - -THIS SOFTWARE IS PROVIDED BY JEREMY DUNCK ``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 JEREMY DUNCK 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. - -The views and conclusions contained in the software and documentation are those of the -authors and should not be interpreted as representing official policies, either expressed -or implied, of Jeremy Dunck. diff --git a/docs/licenses/warlock.txt b/docs/licenses/warlock.txt deleted file mode 100644 index d645695673..0000000000 --- a/docs/licenses/warlock.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/docs/licenses/wrapt.txt b/docs/licenses/wrapt.txt deleted file mode 100644 index 9570af0474..0000000000 --- a/docs/licenses/wrapt.txt +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013-2016, Graham Dumpleton -All rights reserved. - -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. - -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. From a6e9ed97d25ff1a03c4d363c9ab2865497ce982f Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 12:02:37 -0400 Subject: [PATCH 081/379] add the licenses obtained algorithmically --- docs/licenses/automat.txt | 21 +++ docs/licenses/boto3.txt | 12 ++ docs/licenses/botocore.txt | 12 ++ docs/licenses/colorama.txt | 28 ++++ docs/licenses/django-oauth-toolkit.txt | 26 ++++ docs/licenses/knack.txt | 21 +++ docs/licenses/pyOpenSSL.txt | 202 ++++++++++++++++++++++++ docs/licenses/requests-credssp.txt | 15 ++ docs/licenses/s3transfer.txt | 203 +++++++++++++++++++++++++ docs/licenses/scandir.txt | 27 ++++ 10 files changed, 567 insertions(+) create mode 100644 docs/licenses/automat.txt create mode 100644 docs/licenses/boto3.txt create mode 100644 docs/licenses/botocore.txt create mode 100644 docs/licenses/colorama.txt create mode 100644 docs/licenses/django-oauth-toolkit.txt create mode 100644 docs/licenses/knack.txt create mode 100644 docs/licenses/requests-credssp.txt create mode 100644 docs/licenses/s3transfer.txt create mode 100644 docs/licenses/scandir.txt diff --git a/docs/licenses/automat.txt b/docs/licenses/automat.txt new file mode 100644 index 0000000000..9773501b17 --- /dev/null +++ b/docs/licenses/automat.txt @@ -0,0 +1,21 @@ +Copyright (c) 2014 +Rackspace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/licenses/boto3.txt b/docs/licenses/boto3.txt new file mode 100644 index 0000000000..761ea7cfcd --- /dev/null +++ b/docs/licenses/boto3.txt @@ -0,0 +1,12 @@ +Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"). You +may not use this file except in compliance with the License. A copy of +the License is located at + + http://aws.amazon.com/apache2.0/ + +or in the "license" file accompanying this file. This file is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. diff --git a/docs/licenses/botocore.txt b/docs/licenses/botocore.txt new file mode 100644 index 0000000000..4c00dd5630 --- /dev/null +++ b/docs/licenses/botocore.txt @@ -0,0 +1,12 @@ +Copyright 2012-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"). You +may not use this file except in compliance with the License. A copy of +the License is located at + + http://aws.amazon.com/apache2.0/ + +or in the "license" file accompanying this file. This file is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. diff --git a/docs/licenses/colorama.txt b/docs/licenses/colorama.txt new file mode 100644 index 0000000000..5f567799f3 --- /dev/null +++ b/docs/licenses/colorama.txt @@ -0,0 +1,28 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +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 copyright holders, nor those 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. + diff --git a/docs/licenses/django-oauth-toolkit.txt b/docs/licenses/django-oauth-toolkit.txt new file mode 100644 index 0000000000..13606c9fbc --- /dev/null +++ b/docs/licenses/django-oauth-toolkit.txt @@ -0,0 +1,26 @@ +Copyright (c) 2013, Massimiliano Pippi, Federico Frenguelli and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. + +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 OWNER 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. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. diff --git a/docs/licenses/knack.txt b/docs/licenses/knack.txt new file mode 100644 index 0000000000..d1ca00f20a --- /dev/null +++ b/docs/licenses/knack.txt @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE \ No newline at end of file diff --git a/docs/licenses/pyOpenSSL.txt b/docs/licenses/pyOpenSSL.txt index d645695673..f335cc5938 100644 --- a/docs/licenses/pyOpenSSL.txt +++ b/docs/licenses/pyOpenSSL.txt @@ -189,6 +189,208 @@ Copyright [yyyy] [name of copyright owner] + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/docs/licenses/requests-credssp.txt b/docs/licenses/requests-credssp.txt new file mode 100644 index 0000000000..6f494085eb --- /dev/null +++ b/docs/licenses/requests-credssp.txt @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2016 Jordan Borean + +Permission to use, copy, modify and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS-IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/s3transfer.txt b/docs/licenses/s3transfer.txt new file mode 100644 index 0000000000..6b0b1270ff --- /dev/null +++ b/docs/licenses/s3transfer.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/docs/licenses/scandir.txt b/docs/licenses/scandir.txt new file mode 100644 index 0000000000..0759f503f2 --- /dev/null +++ b/docs/licenses/scandir.txt @@ -0,0 +1,27 @@ +Copyright (c) 2012, Ben Hoyt +All rights reserved. + +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 Ben Hoyt 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. From e48fa5c7bf6c839159713a5f205bebc06ec852c2 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 12:21:54 -0400 Subject: [PATCH 082/379] manual license updating --- docs/licenses/applicationinsights.txt | 21 + docs/licenses/azure-cli-core.txt | 21 + .../licenses/azure-data-lake-store-python.txt | 21 + docs/licenses/azure-sdk-for-python.txt | 21 + docs/licenses/azure-storage-python.txt | 21 + .../backports.functools-lru-cache.txt | 7 + docs/licenses/bcrypt.txt | 201 +++++++ docs/licenses/certifi.txt | 21 + docs/licenses/future.txt | 19 + docs/licenses/humanfriendly.txt | 20 + docs/licenses/hyperlink.txt | 28 + docs/licenses/m2crypt.txt | 26 + docs/licenses/os-service-types.txt | 175 ++++++ docs/licenses/ovirt-engine-sdk.txt | 177 ++++++ docs/licenses/paramiko.txt | 503 ++++++++++++++++++ docs/licenses/pathlib2.txt | 22 + docs/licenses/pyOpenSSL.txt | 204 +------ docs/licenses/pycurl.txt | 11 + docs/licenses/pygments.txt | 20 + docs/licenses/pynacl.txt | 174 ++++++ docs/licenses/service-fabric.txt | 21 + docs/licenses/social-auth-core.txt | 27 + docs/licenses/tabulate.txt | 20 + docs/licenses/uwsgitop.txt | 20 + 24 files changed, 1598 insertions(+), 203 deletions(-) create mode 100644 docs/licenses/applicationinsights.txt create mode 100644 docs/licenses/azure-cli-core.txt create mode 100644 docs/licenses/azure-data-lake-store-python.txt create mode 100644 docs/licenses/azure-sdk-for-python.txt create mode 100644 docs/licenses/azure-storage-python.txt create mode 100644 docs/licenses/backports.functools-lru-cache.txt create mode 100644 docs/licenses/bcrypt.txt create mode 100644 docs/licenses/certifi.txt create mode 100644 docs/licenses/future.txt create mode 100644 docs/licenses/humanfriendly.txt create mode 100644 docs/licenses/hyperlink.txt create mode 100644 docs/licenses/m2crypt.txt create mode 100644 docs/licenses/os-service-types.txt create mode 100644 docs/licenses/ovirt-engine-sdk.txt create mode 100644 docs/licenses/paramiko.txt create mode 100644 docs/licenses/pathlib2.txt create mode 100644 docs/licenses/pycurl.txt create mode 100644 docs/licenses/pygments.txt create mode 100644 docs/licenses/pynacl.txt create mode 100644 docs/licenses/service-fabric.txt create mode 100644 docs/licenses/social-auth-core.txt create mode 100644 docs/licenses/tabulate.txt create mode 100644 docs/licenses/uwsgitop.txt diff --git a/docs/licenses/applicationinsights.txt b/docs/licenses/applicationinsights.txt new file mode 100644 index 0000000000..cd7af0736c --- /dev/null +++ b/docs/licenses/applicationinsights.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/licenses/azure-cli-core.txt b/docs/licenses/azure-cli-core.txt new file mode 100644 index 0000000000..e9acabf9dc --- /dev/null +++ b/docs/licenses/azure-cli-core.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/azure-data-lake-store-python.txt b/docs/licenses/azure-data-lake-store-python.txt new file mode 100644 index 0000000000..a28827c2b8 --- /dev/null +++ b/docs/licenses/azure-data-lake-store-python.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/azure-sdk-for-python.txt b/docs/licenses/azure-sdk-for-python.txt new file mode 100644 index 0000000000..a28827c2b8 --- /dev/null +++ b/docs/licenses/azure-sdk-for-python.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/azure-storage-python.txt b/docs/licenses/azure-storage-python.txt new file mode 100644 index 0000000000..0313a903d7 --- /dev/null +++ b/docs/licenses/azure-storage-python.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/backports.functools-lru-cache.txt b/docs/licenses/backports.functools-lru-cache.txt new file mode 100644 index 0000000000..217d55c0d6 --- /dev/null +++ b/docs/licenses/backports.functools-lru-cache.txt @@ -0,0 +1,7 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/bcrypt.txt b/docs/licenses/bcrypt.txt new file mode 100644 index 0000000000..e4f139cb49 --- /dev/null +++ b/docs/licenses/bcrypt.txt @@ -0,0 +1,201 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/docs/licenses/certifi.txt b/docs/licenses/certifi.txt new file mode 100644 index 0000000000..03b38f486d --- /dev/null +++ b/docs/licenses/certifi.txt @@ -0,0 +1,21 @@ +This packge contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011# +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1# +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ \ No newline at end of file diff --git a/docs/licenses/future.txt b/docs/licenses/future.txt new file mode 100644 index 0000000000..9ad2094278 --- /dev/null +++ b/docs/licenses/future.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013-2016 Python Charmers Pty Ltd, Australia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/humanfriendly.txt b/docs/licenses/humanfriendly.txt new file mode 100644 index 0000000000..9b3290e3d2 --- /dev/null +++ b/docs/licenses/humanfriendly.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Peter Odding + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/hyperlink.txt b/docs/licenses/hyperlink.txt new file mode 100644 index 0000000000..ca90f4448c --- /dev/null +++ b/docs/licenses/hyperlink.txt @@ -0,0 +1,28 @@ +Copyright (c) 2017 +Glyph Lefkowitz +Itamar Turner-Trauring +Jean Paul Calderone +Adi Roiban +Amber Hawkie Brown +Mahmoud Hashemi + +and others that have contributed code to the public domain. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/m2crypt.txt b/docs/licenses/m2crypt.txt new file mode 100644 index 0000000000..d2f636fdde --- /dev/null +++ b/docs/licenses/m2crypt.txt @@ -0,0 +1,26 @@ +Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved. + +Portions copyright (c) 2004-2006 Open Source Applications Foundation. +All rights reserved. + +Portions copyright (c) 2005-2006 Vrije Universiteit Amsterdam. +All rights reserved. + +Copyright (c) 2008-2010 Heikki Toivonen. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation. + +THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED 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 AUTHOR 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. diff --git a/docs/licenses/os-service-types.txt b/docs/licenses/os-service-types.txt new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/docs/licenses/os-service-types.txt @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/docs/licenses/ovirt-engine-sdk.txt b/docs/licenses/ovirt-engine-sdk.txt new file mode 100644 index 0000000000..4947287f7b --- /dev/null +++ b/docs/licenses/ovirt-engine-sdk.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/docs/licenses/paramiko.txt b/docs/licenses/paramiko.txt new file mode 100644 index 0000000000..733f96c17a --- /dev/null +++ b/docs/licenses/paramiko.txt @@ -0,0 +1,503 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + + Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +GNU LESSER GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. + +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + +Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +a) Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable "work that +uses the Library", as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (1) uses at run time a +copy of the library already present on the user's computer system, +rather than copying library functions into the executable, and (2) +will operate properly with a modified version of the library, if +the user installs one, as long as the modified version is +interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. + +e) Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. + +b) Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + +Copyright (C) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the +library `Frob' (a library for tweaking knobs) written by James Random Hacker. + +, 1 April 1990 +Ty Coon, President of Vice + +That's all there is to it! + diff --git a/docs/licenses/pathlib2.txt b/docs/licenses/pathlib2.txt new file mode 100644 index 0000000000..1c31ac6b6b --- /dev/null +++ b/docs/licenses/pathlib2.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-2017 Matthias C. M. Troffaes +Copyright (c) 2012-2014 Antoine Pitrou and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/pyOpenSSL.txt b/docs/licenses/pyOpenSSL.txt index f335cc5938..7a4a3ea242 100644 --- a/docs/licenses/pyOpenSSL.txt +++ b/docs/licenses/pyOpenSSL.txt @@ -199,206 +199,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/docs/licenses/pycurl.txt b/docs/licenses/pycurl.txt new file mode 100644 index 0000000000..aea8911b01 --- /dev/null +++ b/docs/licenses/pycurl.txt @@ -0,0 +1,11 @@ +Copyright (C) 2001-2008 by Kjetil Jacobsen +Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer +Copyright (C) 2013-2017 by Oleg Pudeyev + +All rights reserved. + +PycURL is dual licensed under the LGPL and an MIT/X derivative license +based on the cURL license. A full copy of the LGPL license is included +in the file COPYING-LGPL. A full copy of the MIT/X derivative license is +included in the file COPYING-MIT. You can redistribute and/or modify PycURL +according to the terms of either license. \ No newline at end of file diff --git a/docs/licenses/pygments.txt b/docs/licenses/pygments.txt new file mode 100644 index 0000000000..721db633a4 --- /dev/null +++ b/docs/licenses/pygments.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Pavan Kumar Sunkara + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/pynacl.txt b/docs/licenses/pynacl.txt new file mode 100644 index 0000000000..f29e18a269 --- /dev/null +++ b/docs/licenses/pynacl.txt @@ -0,0 +1,174 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/docs/licenses/service-fabric.txt b/docs/licenses/service-fabric.txt new file mode 100644 index 0000000000..4b3ba9df30 --- /dev/null +++ b/docs/licenses/service-fabric.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE \ No newline at end of file diff --git a/docs/licenses/social-auth-core.txt b/docs/licenses/social-auth-core.txt new file mode 100644 index 0000000000..284c8ac165 --- /dev/null +++ b/docs/licenses/social-auth-core.txt @@ -0,0 +1,27 @@ +Copyright (c) 2012-2016, Matías Aguirre +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of this project 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 OWNER 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. \ No newline at end of file diff --git a/docs/licenses/tabulate.txt b/docs/licenses/tabulate.txt new file mode 100644 index 0000000000..7d6e69259c --- /dev/null +++ b/docs/licenses/tabulate.txt @@ -0,0 +1,20 @@ +Copyright (c) 2011-2017 Sergey Astanin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/licenses/uwsgitop.txt b/docs/licenses/uwsgitop.txt new file mode 100644 index 0000000000..473043462d --- /dev/null +++ b/docs/licenses/uwsgitop.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014-2015 unbit + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From 6e1e7d8426d9b460dac73068cc0016157e682114 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 14:35:28 -0400 Subject: [PATCH 083/379] remove shortcut for custom scripts copy --- awx/main/access.py | 2 +- .../tests/functional/test_rbac_inventory.py | 27 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 23f182177d..0855a6c146 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -395,7 +395,7 @@ class BaseAccess(object): elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob, CustomInventoryScript)): user_capabilities['delete'] = user_capabilities['edit'] continue - elif display_method == 'copy' and isinstance(obj, (Group, Host, CustomInventoryScript)): + elif display_method == 'copy' and isinstance(obj, (Group, Host)): user_capabilities['copy'] = user_capabilities['edit'] continue diff --git a/awx/main/tests/functional/test_rbac_inventory.py b/awx/main/tests/functional/test_rbac_inventory.py index 3258b9d0ae..508b2e0773 100644 --- a/awx/main/tests/functional/test_rbac_inventory.py +++ b/awx/main/tests/functional/test_rbac_inventory.py @@ -32,25 +32,40 @@ def test_custom_inv_script_access(organization, user): assert ou in custom_inv.admin_role -@pytest.mark.django_db -def test_modify_inv_script_foreign_org_admin(org_admin, organization, organization_factory, project): - custom_inv = CustomInventoryScript.objects.create(name='test', script='test', description='test', - organization=organization) +@pytest.fixture +def custom_inv(organization): + return CustomInventoryScript.objects.create( + name='test', script='test', description='test', organization=organization) + +@pytest.mark.django_db +def test_modify_inv_script_foreign_org_admin( + org_admin, organization, organization_factory, project, custom_inv): other_org = organization_factory('not-my-org').organization access = CustomInventoryScriptAccess(org_admin) assert not access.can_change(custom_inv, {'organization': other_org.pk, 'name': 'new-project'}) @pytest.mark.django_db -def test_org_member_inventory_script_permissions(org_member, organization): - custom_inv = CustomInventoryScript.objects.create(name='test', script='test', organization=organization) +def test_org_member_inventory_script_permissions(org_member, organization, custom_inv): access = CustomInventoryScriptAccess(org_member) assert access.can_read(custom_inv) assert not access.can_delete(custom_inv) assert not access.can_change(custom_inv, {'name': 'ed-test'}) +@pytest.mark.django_db +def test_copy_only_admin(org_member, organization, custom_inv): + custom_inv.admin_role.members.add(org_member) + access = CustomInventoryScriptAccess(org_member) + assert not access.can_copy(custom_inv) + assert access.get_user_capabilities(custom_inv, method_list=['edit', 'delete', 'copy']) == { + 'edit': True, + 'delete': True, + 'copy': False + } + + @pytest.mark.django_db @pytest.mark.parametrize("role", ["admin_role", "inventory_admin_role"]) def test_access_admin(role, organization, inventory, user): From 0f5f2804a716064e72edd1e61e46f6dd9b0b9740 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Tue, 27 Mar 2018 14:21:39 -0700 Subject: [PATCH 084/379] Reverts DeleteJob factory, fixes null provider to stdout pages and removing unnecessary console.logs in app.js --- awx/ui/client/src/app.js | 16 -- .../delete-job.factory.js | 145 ++++++++++++++++++ .../standard-out-factories/main.js | 4 +- 3 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 9a2e6ccc7d..7399b6d130 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -316,23 +316,7 @@ angular activateTab(); }); - $transitions.onCreate({}, function(trans) { - console.log('$onCreate ' +trans.to().name); - }); - - $transitions.onBefore({}, function(trans) { - console.log('$onBefore ' +trans.to().name); - }); - $transitions.onError({}, function(trans) { - - console.log('$onError ' +trans.to().name); - }); - $transitions.onExit({}, function(trans) { - console.log('$onExit ' +trans.to().name); - }); - $transitions.onSuccess({}, function(trans) { - console.log('$onSuccess ' +trans.to().name); if(trans.to() === trans.from()) { // check to see if something other than a search param has changed let toParamsWithoutSearchKeys = {}; diff --git a/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js b/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js new file mode 100644 index 0000000000..6e28362f3a --- /dev/null +++ b/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js @@ -0,0 +1,145 @@ +export default +function DeleteJob($state, Find, Rest, Wait, ProcessErrors, Prompt, Alert, + $filter, i18n) { + return function(params) { + var scope = params.scope, + id = params.id, + job = params.job, + callback = params.callback, + action, jobs, url, action_label, hdr; + + if (!job) { + if (scope.completed_jobs) { + jobs = scope.completed_jobs; + } + else if (scope.running_jobs) { + jobs = scope.running_jobs; + } + else if (scope.queued_jobs) { + jobs = scope.queued_jobs; + } + else if (scope.all_jobs) { + jobs = scope.all_jobs; + } + else if (scope.jobs) { + jobs = scope.jobs; + } + job = Find({list: jobs, key: 'id', val: id }); + } + + if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') { + url = job.related.cancel; + action_label = 'cancel'; + hdr = i18n._('Cancel'); + } else { + url = job.url; + action_label = 'delete'; + hdr = i18n._('Delete'); + } + + action = function () { + Wait('start'); + Rest.setUrl(url); + if (action_label === 'cancel') { + Rest.post() + .then(() => { + $('#prompt-modal').modal('hide'); + if (callback) { + scope.$emit(callback, action_label); + } + else { + $state.reload(); + Wait('stop'); + } + }) + .catch(({obj, status}) => { + Wait('stop'); + $('#prompt-modal').modal('hide'); + if (status === 403) { + Alert('Error', obj.detail); + } + // Ignore the error. The job most likely already finished. + // ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + + // ' failed. POST returned status: ' + status }); + }); + } else { + Rest.destroy() + .then(() => { + $('#prompt-modal').modal('hide'); + if (callback) { + scope.$emit(callback, action_label); + } + else { + let reloadListStateParams = null; + + if(scope.jobs.length === 1 && $state.params.job_search && !_.isEmpty($state.params.job_search.page) && $state.params.job_search.page !== '1') { + reloadListStateParams = _.cloneDeep($state.params); + reloadListStateParams.job_search.page = (parseInt(reloadListStateParams.job_search.page)-1).toString(); + } + + $state.go('.', reloadListStateParams, {reload: true}); + Wait('stop'); + } + }) + .catch(({obj, status}) => { + Wait('stop'); + $('#prompt-modal').modal('hide'); + if (status === 403) { + Alert('Error', obj.detail); + } + // Ignore the error. The job most likely already finished. + //ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + + // ' failed. DELETE returned status: ' + status }); + }); + } + }; + + if (scope.removeCancelNotAllowed) { + scope.removeCancelNotAllowed(); + } + scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() { + Wait('stop'); + Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info'); + }); + + if (scope.removeCancelJob) { + scope.removeCancelJob(); + } + scope.removeCancelJob = scope.$on('CancelJob', function() { + var cancelBody = "
" + i18n._("Are you sure you want to submit the request to cancel this job?") + "
"; + var deleteBody = "
" + i18n._("Are you sure you want to delete this job?") + "
"; + Prompt({ + hdr: hdr, + resourceName: `#${job.id} ` + $filter('sanitize')(job.name), + body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody, + action: action, + actionText: (action_label === 'cancel' || job.status === 'new') ? i18n._("OK") : i18n._("DELETE") + }); + }); + + if (action_label === 'cancel') { + Rest.setUrl(url); + Rest.get() + .then(({data}) => { + if (data.can_cancel) { + scope.$emit('CancelJob'); + } + else { + scope.$emit('CancelNotAllowed'); + } + }) + .catch(({data, status}) => { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + + ' failed. GET returned: ' + status }); + }); + } + else { + scope.$emit('CancelJob'); + } + }; +} + +DeleteJob.$inject = +[ '$state', 'Find', 'Rest', 'Wait', + 'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n' +]; diff --git a/awx/ui/client/src/standard-out/standard-out-factories/main.js b/awx/ui/client/src/standard-out/standard-out-factories/main.js index fdded8ab31..935c8dca37 100644 --- a/awx/ui/client/src/standard-out/standard-out-factories/main.js +++ b/awx/ui/client/src/standard-out/standard-out-factories/main.js @@ -5,7 +5,9 @@ *************************************************/ import lookUpName from './lookup-name.factory'; +import DeleteJob from './delete-job.factory'; export default angular.module('StandardOutHelper', []) - .factory('LookUpName', lookUpName); + .factory('LookUpName', lookUpName) + .factory('DeleteJob', DeleteJob); From 5f01d26224eb81400e5c8db44404b0d879926272 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 3 Apr 2018 16:51:34 -0400 Subject: [PATCH 085/379] automatically encrypt/decrypt main_oauth2application.client_secret see: https://github.com/ansible/awx/issues/1416 --- awx/main/fields.py | 14 +++++++++++ .../0029_v330_encrypt_oauth2_secret.py | 22 ++++++++++++++++++ awx/main/models/oauth.py | 7 ++++++ awx/main/tests/functional/api/test_oauth.py | 23 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 awx/main/migrations/0029_v330_encrypt_oauth2_secret.py diff --git a/awx/main/fields.py b/awx/main/fields.py index 29ccb71609..3d541d524a 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -42,6 +42,7 @@ from rest_framework import serializers # AWX from awx.main.utils.filters import SmartFilter +from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key from awx.main.validators import validate_ssh_private_key from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role from awx.main import utils @@ -821,3 +822,16 @@ class AskForField(models.BooleanField): # self.name will be set by the model metaclass, not this field raise Exception('Corresponding allows_field cannot be accessed until model is initialized.') return self._allows_field + + +class OAuth2ClientSecretField(models.CharField): + + def get_db_prep_value(self, value, connection, prepared=False): + return super(OAuth2ClientSecretField, self).get_db_prep_value( + encrypt_value(value), connection, prepared + ) + + def from_db_value(self, value, expression, connection, context): + if value.startswith('$encrypted$'): + return decrypt_value(get_encryption_key('value', pk=None), value) + return value diff --git a/awx/main/migrations/0029_v330_encrypt_oauth2_secret.py b/awx/main/migrations/0029_v330_encrypt_oauth2_secret.py new file mode 100644 index 0000000000..e49372144b --- /dev/null +++ b/awx/main/migrations/0029_v330_encrypt_oauth2_secret.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.11 on 2018-04-03 20:48 +from __future__ import unicode_literals + +import awx.main.fields +from django.db import migrations +import oauth2_provider.generators + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0028_v330_modify_application'), + ] + + operations = [ + migrations.AlterField( + model_name='oauth2application', + name='client_secret', + field=awx.main.fields.OAuth2ClientSecretField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, max_length=1024), + ), + ] diff --git a/awx/main/models/oauth.py b/awx/main/models/oauth.py index 8626ca51c3..c905aad3f4 100644 --- a/awx/main/models/oauth.py +++ b/awx/main/models/oauth.py @@ -9,6 +9,9 @@ from django.utils.translation import ugettext_lazy as _ # Django OAuth Toolkit from oauth2_provider.models import AbstractApplication, AbstractAccessToken +from oauth2_provider.generators import generate_client_secret + +from awx.main.fields import OAuth2ClientSecretField DATA_URI_RE = re.compile(r'.*') # FIXME @@ -39,6 +42,10 @@ class OAuth2Application(AbstractApplication): null=True, ) + client_secret = OAuth2ClientSecretField( + max_length=1024, blank=True, default=generate_client_secret, db_index=True + ) + class OAuth2AccessToken(AbstractAccessToken): diff --git a/awx/main/tests/functional/api/test_oauth.py b/awx/main/tests/functional/api/test_oauth.py index a417eb539f..3666165c6b 100644 --- a/awx/main/tests/functional/api/test_oauth.py +++ b/awx/main/tests/functional/api/test_oauth.py @@ -1,6 +1,9 @@ import pytest import base64 +from django.db import connection + +from awx.main.utils.encryption import decrypt_value, get_encryption_key from awx.api.versioning import reverse, drf_reverse from awx.main.models.oauth import (OAuth2Application as Application, OAuth2AccessToken as AccessToken, @@ -65,6 +68,26 @@ def test_oauth_application_update(oauth_application, organization, patch, admin, assert updated_app.organization == organization +@pytest.mark.django_db +def test_oauth_application_encryption(admin, organization, post): + response = post( + reverse('api:o_auth2_application_list'), { + 'name': 'test app', + 'organization': organization.pk, + 'client_type': 'confidential', + 'authorization_grant_type': 'password', + }, admin, expect=201 + ) + pk = response.data.get('id') + secret = response.data.get('client_secret') + with connection.cursor() as cursor: + encrypted = cursor.execute( + 'SELECT client_secret FROM main_oauth2application WHERE id={}'.format(pk) + ).fetchone()[0] + assert encrypted.startswith('$encrypted$') + assert decrypt_value(get_encryption_key('value', pk=None), encrypted) == secret + + @pytest.mark.django_db def test_oauth_token_create(oauth_application, get, post, admin): response = post( From 7084fe9a6df287b2fe98c06a0a8c498d0c0c17aa Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 4 Apr 2018 15:36:37 -0400 Subject: [PATCH 086/379] Fixed codemirror syntax highlighting --- awx/ui/client/src/vendor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awx/ui/client/src/vendor.js b/awx/ui/client/src/vendor.js index 7ad8681b41..c4263249f1 100644 --- a/awx/ui/client/src/vendor.js +++ b/awx/ui/client/src/vendor.js @@ -61,6 +61,8 @@ require('ng-toast-provider'); require('ng-toast-directives'); require('ng-toast'); require('lr-infinite-scroll'); +require('codemirror/mode/yaml/yaml'); +require('codemirror/mode/javascript/javascript'); // Network Visualization require('angular-mousewheel'); From 212ab96a31ef07925b8d92649685b3c8a81ec063 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 2 Nov 2017 17:01:12 -0400 Subject: [PATCH 087/379] Add structure for sandboxed job results --- awx/ui/client/features/index.js | 5 ++- .../features/output/index.controller.js | 4 +++ awx/ui/client/features/output/index.js | 35 +++++++++++++++++++ awx/ui/client/features/output/index.view.html | 3 ++ awx/ui/client/features/output/jobs.strings.js | 14 ++++++++ awx/ui/client/lib/models/Jobs.js | 19 ++++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 awx/ui/client/features/output/index.controller.js create mode 100644 awx/ui/client/features/output/index.js create mode 100644 awx/ui/client/features/output/index.view.html create mode 100644 awx/ui/client/features/output/jobs.strings.js create mode 100644 awx/ui/client/lib/models/Jobs.js diff --git a/awx/ui/client/features/index.js b/awx/ui/client/features/index.js index 763894c93c..0a6ec3864c 100644 --- a/awx/ui/client/features/index.js +++ b/awx/ui/client/features/index.js @@ -4,6 +4,7 @@ import atLibModels from '~models'; import atFeaturesApplications from '~features/applications'; import atFeaturesCredentials from '~features/credentials'; +import atFeaturesOutput from '~features/output'; import atFeaturesTemplates from '~features/templates'; import atFeaturesUsers from '~features/users'; import atFeaturesJobs from '~features/jobs'; @@ -18,7 +19,9 @@ angular.module(MODULE_NAME, [ atFeaturesCredentials, atFeaturesTemplates, atFeaturesUsers, - atFeaturesJobs + atFeaturesJobs, + atFeaturesOutput, + atFeaturesTemplates ]); export default MODULE_NAME; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js new file mode 100644 index 0000000000..8a730a99c8 --- /dev/null +++ b/awx/ui/client/features/output/index.controller.js @@ -0,0 +1,4 @@ +function JobsIndexController () { +} + +module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js new file mode 100644 index 0000000000..03b596e00a --- /dev/null +++ b/awx/ui/client/features/output/index.js @@ -0,0 +1,35 @@ +import JobsStrings from '~features/output/jobs.strings'; +import IndexController from '~features/output/index.controller'; + +const indexTemplate = require('~features/output/index.view.html'); + +const MODULE_NAME = 'at.features.output'; + +function JobsRun ($stateExtender, strings) { + $stateExtender.addState({ + name: 'jobz', + route: '/jobz', + ncyBreadcrumb: { + label: strings.get('state.TITLE') + }, + data: { + activityStream: true, + activityStreamTarget: 'jobs' + }, + views: { + templateUrl: indexTemplate, + controller: IndexController, + controllerAs: 'vm' + } + }); +} + +JobsRun.$inject = ['$stateExtender', 'JobsStrings']; + +angular + .module(MODULE_NAME, []) + .controller('indexController', IndexController) + .service('JobsStrings', JobsStrings) + .run(JobsRun); + +export default MODULE_NAME; diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html new file mode 100644 index 0000000000..36bd84a02d --- /dev/null +++ b/awx/ui/client/features/output/index.view.html @@ -0,0 +1,3 @@ +

+ test +

diff --git a/awx/ui/client/features/output/jobs.strings.js b/awx/ui/client/features/output/jobs.strings.js new file mode 100644 index 0000000000..aa1afcdfaf --- /dev/null +++ b/awx/ui/client/features/output/jobs.strings.js @@ -0,0 +1,14 @@ +function JobsStrings (BaseString) { + BaseString.call(this, 'jobs'); + + const { t } = this; + const ns = this.jobs; + + ns.state = { + TITLE: t.s('JOBZ') + }; +} + +JobsStrings.$inject = ['BaseStringService']; + +export default JobsStrings; diff --git a/awx/ui/client/lib/models/Jobs.js b/awx/ui/client/lib/models/Jobs.js new file mode 100644 index 0000000000..e82b3e04af --- /dev/null +++ b/awx/ui/client/lib/models/Jobs.js @@ -0,0 +1,19 @@ +let BaseModel; + +function JobsModel (method, resource, config) { + BaseModel.call(this, 'jobs'); + + this.Constructor = JobsModel; + + return this.create(method, resource, config); +} + +function JobsModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return JobsModel; +} + +JobsModelLoader.$inject = ['BaseModel']; + +export default JobsModelLoader; From 5b8d2e76592fef1b35c1cfd68cbbf43122ae77fc Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 3 Nov 2017 13:44:26 -0400 Subject: [PATCH 088/379] Add model and resolve block to sandbox view --- awx/ui/build/webpack.watch.js | 8 +++++++- .../client/features/output/index.controller.js | 3 ++- awx/ui/client/features/output/index.js | 17 ++++++++++++++--- awx/ui/client/features/output/index.view.html | 15 ++++++++++++--- awx/ui/client/lib/models/index.js | 2 ++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index d653707847..6ec25bed5a 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -26,7 +26,13 @@ const watch = { test: /\.js$/, enforce: 'pre', exclude: /node_modules/, - loader: 'eslint-loader' + loader: 'eslint-loader', + options: { + failOnWarning: false, + failOnError: false, + emitError: false, + emitWarning: false + } } ] }, diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 8a730a99c8..358750aefb 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,4 +1,5 @@ -function JobsIndexController () { +function JobsIndexController (resolved) { + console.log('test', resolved); } module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 03b596e00a..f7f0e9c768 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -17,9 +17,20 @@ function JobsRun ($stateExtender, strings) { activityStreamTarget: 'jobs' }, views: { - templateUrl: indexTemplate, - controller: IndexController, - controllerAs: 'vm' + '@': { + templateUrl: indexTemplate, + controller: IndexController, + controllerAs: 'vm' + } + }, + resolve: { + resolved: ['JobsModel', Jobs => { + const jobs = new Jobs(); + + return { + models: { jobs } + }; + }] } }); } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 36bd84a02d..de0177058e 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,3 +1,12 @@ -

- test -

+
+
+ +

left

+
+
+
+ +

right

+
+
+
diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index fb902fb91c..0e5ab2c6b3 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -13,6 +13,7 @@ import InventoryScript from '~models/InventoryScript'; import InventorySource from '~models/InventorySource'; import Job from '~models/Job'; import JobTemplate from '~models/JobTemplate'; +import Jobs from '~models/Jobs'; import Me from '~models/Me'; import ModelsStrings from '~models/models.strings'; import NotificationTemplate from '~models/NotificationTemplate'; @@ -44,6 +45,7 @@ angular .service('InventorySourceModel', InventorySource) .service('JobModel', Job) .service('JobTemplateModel', JobTemplate) + .service('JobsModel', Jobs) .service('MeModel', Me) .service('ModelsStrings', ModelsStrings) .service('NotificationTemplate', NotificationTemplate) From 0de5301c23f6dcd9a069bf74c6dbce2a44ade68b Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 7 Nov 2017 11:45:23 -0500 Subject: [PATCH 089/379] Fix eslint errors/warnings emitted to browser --- awx/ui/build/webpack.watch.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index 6ec25bed5a..0ae7d77ef5 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -26,13 +26,7 @@ const watch = { test: /\.js$/, enforce: 'pre', exclude: /node_modules/, - loader: 'eslint-loader', - options: { - failOnWarning: false, - failOnError: false, - emitError: false, - emitWarning: false - } + loader: 'eslint-loader' } ] }, @@ -60,6 +54,7 @@ const watch = { https: true, port: 3000, https: true, + clientLogLevel: 'none', proxy: { '/': { target: TARGET, From 946f3b5c9217bc709322f3a2eb6a53ffad0ad2ef Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 8 Nov 2017 16:08:37 -0500 Subject: [PATCH 090/379] Add ansi parsing libs --- awx/ui/client/features/_index.less | 1 + awx/ui/client/features/jobs/_index.less | 12 ++++++ .../features/output/index.controller.js | 39 ++++++++++++++++++- awx/ui/client/features/output/index.js | 10 ++--- awx/ui/client/features/output/index.view.html | 7 +++- 5 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 awx/ui/client/features/jobs/_index.less diff --git a/awx/ui/client/features/_index.less b/awx/ui/client/features/_index.less index e2339dc9e4..4a69120ed9 100644 --- a/awx/ui/client/features/_index.less +++ b/awx/ui/client/features/_index.less @@ -1,2 +1,3 @@ @import 'credentials/_index'; +@import 'jobs/_index'; @import 'users/tokens/_index'; diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less new file mode 100644 index 0000000000..9144f3c93c --- /dev/null +++ b/awx/ui/client/features/jobs/_index.less @@ -0,0 +1,12 @@ +.at-Stdout { + tr { + & > td:first-child { + padding-right: 5px; + border-right: 1px solid gray; + } + + & > td:last-child { + padding-left: 5px; + } + } +} diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 358750aefb..71c4bcf408 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,5 +1,40 @@ -function JobsIndexController (resolved) { - console.log('test', resolved); +import Ansi from 'ansi-to-html'; +import hasAnsi from 'has-ansi'; + +function JobsIndexController (job, $sce) { + const vm = this || {}; + const results = job.get('related.job_events.results'); + const ansi = new Ansi({}); + + /* + * const colors = []; + * + * for (let i = 0; i < 255; i++) { + * colors.push('#ababab'); + * } + * + */ + + let html = ''; + results.forEach((line, i) => { + if (!line.stdout) { + return; + } + + let output; + + if (hasAnsi(line.stdout)) { + output = ansi.toHtml(line.stdout); + } else { + output = line.stdout; // .replace(/(\n|\r)/g, ''); + } + + html += `${i}${output}`; + }); + + vm.html = $sce.trustAsHtml(html); } +JobsIndexController.$inject = ['job', '$sce']; + module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index f7f0e9c768..c5899b7964 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -24,13 +24,9 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - resolved: ['JobsModel', Jobs => { - const jobs = new Jobs(); - - return { - models: { jobs } - }; - }] + job: ['JobsModel', Jobs => new Jobs('get', 1002) + .then(job => job.extend('job_events')) + ] } }); } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index de0177058e..92d2594851 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -6,7 +6,12 @@
-

right

+
+                
+                  
+                  
+                
+
From 30c472c4991e237e1edaf97f4aa0f21ae6fd0981 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 15 Nov 2017 15:44:20 -0500 Subject: [PATCH 091/379] Add basic event output in sandbox --- awx/ui/client/features/jobs/_index.less | 22 ++-- .../features/output/index.controller.js | 114 ++++++++++++++---- awx/ui/client/features/output/index.js | 11 +- awx/ui/package.json | 2 + 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 9144f3c93c..50597f1b1a 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,12 +1,18 @@ .at-Stdout { - tr { - & > td:first-child { - padding-right: 5px; - border-right: 1px solid gray; - } + &-expand { + padding-right: 10px; + } - & > td:last-child { - padding-left: 5px; - } + &-lineNumber { + padding-right: 5px; + border-right: 1px solid gray; + } + + &-content { + padding-left: 5px; + } + + &-timestamp { + padding-left: 20px; } } diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 71c4bcf408..d94971ce7f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,40 +1,102 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; +let ansi; + +const EVENT_START_TASK = 'playbook_on_task_start'; +const EVENT_START_PLAY = 'playbook_on_play_start'; +const EVENT_STATS_PLAY = 'playbook_on_stats'; + +const EVENT_GROUPS = [ + EVENT_START_TASK, + EVENT_START_PLAY +]; + +const TIME_EVENTS = [ + EVENT_START_TASK, + EVENT_START_PLAY, + EVENT_STATS_PLAY +]; + function JobsIndexController (job, $sce) { const vm = this || {}; - const results = job.get('related.job_events.results'); - const ansi = new Ansi({}); + const events = job.get('related.job_events.results'); - /* - * const colors = []; - * - * for (let i = 0; i < 255; i++) { - * colors.push('#ababab'); - * } - * - */ + ansi = new Ansi(); - let html = ''; - results.forEach((line, i) => { - if (!line.stdout) { - return; - } - - let output; - - if (hasAnsi(line.stdout)) { - output = ansi.toHtml(line.stdout); - } else { - output = line.stdout; // .replace(/(\n|\r)/g, ''); - } - - html += `${i}${output}`; - }); + const html = parseEvents(events); vm.html = $sce.trustAsHtml(html); } +function parseEvents (events) { + events.sort((a, b) => a.start_line > b.start_line); + + return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); +} + +function parseLine (event) { + if (!event || !event.stdout) { + return ''; + } + + const { stdout } = event; + const lines = stdout.split('\r\n'); + + let ln = event.start_line; + + return lines.reduce((html, line, i) => { + ln++; + + const time = getTime(event, i); + const group = getGroup(event, i); + + return `${html}${createRow(ln, line, time, group)}`; + }, ''); +} + +function createRow (ln, content, time, group) { + content = hasAnsi(content) ? ansi.toHtml(content) : content; + + let expand = ''; + if (group.parent) { + expand = ''; + } + + return ` + + ${expand} + ${ln} + ${content} + ${time} + `; +} + +function getGroup (event, i) { + const group = {}; + + if (EVENT_GROUPS.includes(event.event) && i === 1) { + group.parent = true; + group.classList = `parent parent-${event.event_level}`; + } else { + group.classList = ''; + } + + group.level = event.event_level; + + return group; +} + +function getTime (event, i) { + if (!TIME_EVENTS.includes(event.event) || i !== 1) { + return ''; + } + + const date = new Date(event.created); + + return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; +} + JobsIndexController.$inject = ['job', '$sce']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index c5899b7964..558a1e4714 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -8,7 +8,7 @@ const MODULE_NAME = 'at.features.output'; function JobsRun ($stateExtender, strings) { $stateExtender.addState({ name: 'jobz', - route: '/jobz', + route: '/jobz/:id', ncyBreadcrumb: { label: strings.get('state.TITLE') }, @@ -24,9 +24,12 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - job: ['JobsModel', Jobs => new Jobs('get', 1002) - .then(job => job.extend('job_events')) - ] + job: ['JobsModel', '$stateParams', (Jobs, $stateParams) => { + const { id } = $stateParams; + + return new Jobs('get', id) + .then(job => job.extend('job_events')); + }] } }); } diff --git a/awx/ui/package.json b/awx/ui/package.json index 2d9f32f488..5f8ff0ad5c 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -107,12 +107,14 @@ "angular-sanitize": "~1.6.6", "angular-scheduler": "git+https://git@github.com/ansible/angular-scheduler#v0.3.2", "angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#v0.5.2", + "ansi-to-html": "^0.6.3", "babel-polyfill": "^6.26.0", "bootstrap": "^3.3.7", "bootstrap-datepicker": "^1.7.1", "codemirror": "^5.17.0", "components-font-awesome": "^4.6.1", "d3": "~3.3.13", + "has-ansi": "^3.0.0", "javascript-detect-element-resize": "^0.5.3", "jquery": "~2.2.4", "jquery-ui": "^1.12.1", From 5c10ce3082f421536cc3b09895cff529a0bfb394 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 17 Nov 2017 16:14:53 -0500 Subject: [PATCH 092/379] Update job output styling --- awx/ui/client/features/jobs/_index.less | 30 ++++++++++++++++++- .../features/output/index.controller.js | 27 +++++++++++++++-- awx/ui/client/features/output/index.view.html | 13 +++++--- awx/ui/client/lib/theme/_utility.less | 4 +++ 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 50597f1b1a..15acc8c90f 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,9 +1,27 @@ .at-Stdout { + font-family: monospace; + + &-controls { + border: 1px solid @at-gray-dark-2x; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: none; + } + + &-controlIcon { + font-size: 16px; + padding: 10px; + } + &-expand { - padding-right: 10px; + background-color: @at-gray-light-2x; + + padding: 0 10px; } &-lineNumber { + background-color: @at-gray-light-2x; + padding-right: 5px; border-right: 1px solid gray; } @@ -15,4 +33,14 @@ &-timestamp { padding-left: 20px; } + + &-output { + font-size: 14px; + border: 1px solid @at-gray-dark-2x; + background-color: @at-gray-light-3x; + padding: 0; + margin: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } } diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index d94971ce7f..01db5efeca 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -27,14 +27,27 @@ function JobsIndexController (job, $sce) { const html = parseEvents(events); vm.html = $sce.trustAsHtml(html); + vm.toggle = toggle; } function parseEvents (events) { - events.sort((a, b) => a.start_line > b.start_line); + events.sort(orderByLineNumber); return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); } +function orderByLineNumber (a, b) { + if (a.start_line > b.start_line) { + return 1; + } + + if (a.start_line < b.start_line) { + return -1; + } + + return 0; +} + function parseLine (event) { if (!event || !event.stdout) { return ''; @@ -60,7 +73,7 @@ function createRow (ln, content, time, group) { let expand = ''; if (group.parent) { - expand = ''; + expand = ''; } return ` @@ -97,6 +110,16 @@ function getTime (event, i) { return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; } +function toggle (id) { + console.log(id); +} + +/* + *function addDynamic (start) { + * document.getElementsByClassName('parent') + *} + */ + JobsIndexController.$inject = ['job', '$sce']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 92d2594851..8c43a4ae8a 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -5,11 +5,16 @@
- -
+        
+            
+
+
+
+
+ +
                 
-                  
-                  
+                  
diff --git a/awx/ui/client/lib/theme/_utility.less b/awx/ui/client/lib/theme/_utility.less index 1f47a481f3..8f95237fea 100644 --- a/awx/ui/client/lib/theme/_utility.less +++ b/awx/ui/client/lib/theme/_utility.less @@ -16,3 +16,7 @@ margin-left: 0; margin-right: 0; } + +.at-u-clear { + clear: both; +} From 3096a58272dfa5492fc4c3e518d8fb91a4fb87c8 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 30 Nov 2017 11:57:00 -0500 Subject: [PATCH 093/379] Update style to match latest mockup --- awx/ui/client/features/jobs/_index.less | 18 ++++++++----- .../features/output/index.controller.js | 18 ++++++++++++- awx/ui/client/features/output/index.view.html | 26 ++++++++++++++----- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 15acc8c90f..020ee3f46e 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -2,6 +2,7 @@ font-family: monospace; &-controls { + color: @at-gray-dark-4x; border: 1px solid @at-gray-dark-2x; border-top-left-radius: 4px; border-top-right-radius: 4px; @@ -9,29 +10,34 @@ } &-controlIcon { - font-size: 16px; + font-size: 12px; padding: 10px; } &-expand { - background-color: @at-gray-light-2x; + color: @at-gray-dark-4x; + background-color: @at-gray-light; + font-size: 12px; - padding: 0 10px; + padding: 0 20px 0 10px; } &-lineNumber { - background-color: @at-gray-light-2x; + color: @at-gray-dark-4x; + background-color: @at-gray-light; padding-right: 5px; - border-right: 1px solid gray; + border-right: 1px solid @at-gray-dark; } &-content { - padding-left: 5px; + padding-left: 20px; } &-timestamp { padding-left: 20px; + font-size: 12px; + text-align: right; } &-output { diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 01db5efeca..fa61c0df6c 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -55,6 +55,7 @@ function parseLine (event) { const { stdout } = event; const lines = stdout.split('\r\n'); + const isTruncated = (event.end_line - event.start_line) > lines.length; let ln = event.start_line; @@ -63,17 +64,32 @@ function parseLine (event) { const time = getTime(event, i); const group = getGroup(event, i); + const isLastLine = i === lines.length - 1; + + if (isTruncated && isLastLine) { + return `${html}${createRow(ln, line, time, group)}${createTruncatedRow()}`; + } return `${html}${createRow(ln, line, time, group)}`; }, ''); } +function createTruncatedRow () { + return ` + + + + + + `; +} + function createRow (ln, content, time, group) { content = hasAnsi(content) ? ansi.toHtml(content) : content; let expand = ''; if (group.parent) { - expand = ''; + expand = ''; } return ` diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 8c43a4ae8a..6b51b5f548 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -7,16 +7,28 @@
-
-
+
+
-
-                
-                  
-                
-
+

+                
+                    
+                        
+                        
+                        
+                    
+                
+                
+                
+                    
+                        
+                        
+                        
+                        
+                    
+                
 
  Back to Top
From aaec3474b0cb861956d337f09aab62158c049cda Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 30 Nov 2017 17:25:53 -0500 Subject: [PATCH 094/379] Add support for params to BaseModel.extend --- awx/ui/client/features/jobs/_index.less | 29 ++++++++++++----- .../features/output/index.controller.js | 31 +++++++++++-------- awx/ui/client/features/output/index.js | 7 ++++- awx/ui/client/features/output/index.view.html | 20 ++++++------ awx/ui/client/lib/models/Base.js | 18 +++-------- 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 020ee3f46e..5b66ff8366 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,7 +1,7 @@ .at-Stdout { font-family: monospace; - &-controls { + &-menu { color: @at-gray-dark-4x; border: 1px solid @at-gray-dark-2x; border-top-left-radius: 4px; @@ -9,12 +9,12 @@ border-bottom: none; } - &-controlIcon { + &-menuIcon { font-size: 12px; padding: 10px; } - &-expand { + &-toggle { color: @at-gray-dark-4x; background-color: @at-gray-light; font-size: 12px; @@ -22,25 +22,38 @@ padding: 0 20px 0 10px; } - &-lineNumber { + &-line { color: @at-gray-dark-4x; background-color: @at-gray-light; + text-align: right; + padding-right: 5px; border-right: 1px solid @at-gray-dark; } - &-content { - padding-left: 20px; + &-event { + padding-left: 2ch; + width: 83ch; } - &-timestamp { + &-time { padding-left: 20px; font-size: 12px; text-align: right; } - &-output { + &-container { + & > table { + table-layout: fixed; + + td { + vertical-align: top; + word-wrap: break-word; + white-space: pre-wrap; + } + } + font-size: 14px; border: 1px solid @at-gray-dark-2x; background-color: @at-gray-light-3x; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index fa61c0df6c..c571062641 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -33,6 +33,7 @@ function JobsIndexController (job, $sce) { function parseEvents (events) { events.sort(orderByLineNumber); + console.log(events); return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); } @@ -67,20 +68,20 @@ function parseLine (event) { const isLastLine = i === lines.length - 1; if (isTruncated && isLastLine) { - return `${html}${createRow(ln, line, time, group)}${createTruncatedRow()}`; + return `${html}${createRow(ln, line, time, group)}${createTruncatedRow(event.id)}`; } return `${html}${createRow(ln, line, time, group)}`; }, ''); } -function createTruncatedRow () { +function createTruncatedRow (id) { return ` - - - - - + + + ... + + `; } @@ -94,10 +95,10 @@ function createRow (ln, content, time, group) { return ` - ${expand} - ${ln} - ${content} - ${time} + ${expand} + ${ln} + ${content} + ${time} `; } @@ -129,13 +130,17 @@ function getTime (event, i) { function toggle (id) { console.log(id); } - /* + * + *function getTruncatedEvent () { + * + *} + * *function addDynamic (start) { * document.getElementsByClassName('parent') *} + * */ - JobsIndexController.$inject = ['job', '$sce']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 558a1e4714..abe9a5edec 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -28,7 +28,12 @@ function JobsRun ($stateExtender, strings) { const { id } = $stateParams; return new Jobs('get', id) - .then(job => job.extend('job_events')); + .then(job => job.extend('job_events', { + params: { + page_size: 200, + order_by: 'start_line' + } + })); }] } }); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 6b51b5f548..5a585c7321 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -6,26 +6,26 @@
-
-
-
+
+
+
-

+            
- - - + + + - - - + + +
  
   Back to Top
diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index a2c202aa79..ee63b97076 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -327,25 +327,17 @@ function has (method, keys) { return value !== undefined && value !== null; } -function extend (method, related, config = {}) { - if (!related) { - related = method; - method = 'GET'; - } else { - method = method.toUpperCase(); - } +function extend (related, config) { + const req = this.parseRequestConfig('GET', config); - if (this.has(method, `related.${related}`)) { - const req = { - method, - url: this.get(`related.${related}`) - }; + if (this.has(req.method, `related.${related}`)) { + req.url = this.get(`related.${related}`); Object.assign(req, config); return $http(req) .then(({ data }) => { - this.set(method, `related.${related}`, data); + this.set(req.method, `related.${related}`, data); return this; }); From 0a66d1c3fc9a765df043e6fcab8ccc5a20f27c5d Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 4 Dec 2017 15:57:39 -0500 Subject: [PATCH 095/379] Add dynamic angular interaction after HTML insertion --- awx/ui/client/features/jobs/_index.less | 35 +++++++++------ .../features/output/index.controller.js | 45 ++++++++++--------- awx/ui/client/features/output/index.js | 2 +- awx/ui/client/features/output/index.view.html | 18 +------- 4 files changed, 48 insertions(+), 52 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 5b66ff8366..8c3a7195e1 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -20,6 +20,7 @@ font-size: 12px; padding: 0 20px 0 10px; + user-select: none; } &-line { @@ -30,30 +31,24 @@ padding-right: 5px; border-right: 1px solid @at-gray-dark; + user-select: none; } &-event { - padding-left: 2ch; - width: 83ch; + padding: 0 10px; } &-time { - padding-left: 20px; + padding-right: 2ch; font-size: 12px; text-align: right; + user-select: none; + width: 11ch; + border-left: 1px dashed @at-gray-dark; } &-container { - & > table { - table-layout: fixed; - - td { - vertical-align: top; - word-wrap: break-word; - white-space: pre-wrap; - } - } - + max-height: 80vh; font-size: 14px; border: 1px solid @at-gray-dark-2x; background-color: @at-gray-light-3x; @@ -61,5 +56,19 @@ margin: 0; border-top-left-radius: 0; border-top-right-radius: 0; + + & > table { + table-layout: fixed; + + tr:hover > td { + background: white; + } + + td { + vertical-align: top; + word-wrap: break-word; + white-space: pre-wrap; + } + } } } diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index c571062641..d88d1e915f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -2,6 +2,10 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; let ansi; +let $timeout; +let $sce; +let $compile; +let $scope; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; @@ -18,22 +22,30 @@ const TIME_EVENTS = [ EVENT_STATS_PLAY ]; -function JobsIndexController (job, $sce) { +function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { + ansi = new Ansi(); + $timeout = _$timeout_; + $sce = _$sce_; + $compile = _$compile_; + $scope = _$scope_; + const vm = this || {}; const events = job.get('related.job_events.results'); + const html = $sce.trustAsHtml(parseEvents(events)); - ansi = new Ansi(); - - const html = parseEvents(events); - - vm.html = $sce.trustAsHtml(html); vm.toggle = toggle; + + $timeout(() => { + const table = $('#result-table'); + + table.html($sce.getTrustedHtml(html)); + $compile(table.contents())($scope); + }); } function parseEvents (events) { events.sort(orderByLineNumber); - console.log(events); return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); } @@ -90,12 +102,12 @@ function createRow (ln, content, time, group) { let expand = ''; if (group.parent) { - expand = ''; + expand = ''; } return ` - ${expand} + ${expand} ${ln} ${content} ${time} @@ -108,6 +120,7 @@ function getGroup (event, i) { if (EVENT_GROUPS.includes(event.event) && i === 1) { group.parent = true; group.classList = `parent parent-${event.event_level}`; + group.id = i; } else { group.classList = ''; } @@ -130,17 +143,7 @@ function getTime (event, i) { function toggle (id) { console.log(id); } -/* - * - *function getTruncatedEvent () { - * - *} - * - *function addDynamic (start) { - * document.getElementsByClassName('parent') - *} - * - */ -JobsIndexController.$inject = ['job', '$sce']; + +JobsIndexController.$inject = ['job', '$sce', '$timeout', '$scope', '$compile']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index abe9a5edec..aa98a2359a 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -30,7 +30,7 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { params: { - page_size: 200, + page_size: 10000, order_by: 'start_line' } })); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 5a585c7321..34fd7dc368 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -12,23 +12,7 @@
-

-                
-                    
-                        
-                        
-                        
-                    
-                
-                
-                
-                    
-                        
-                        
-                        
-                        
-                    
-                
 
  Back to Top
+
 
From 6f7841a9206a8c2cdcdc5dc79b6cbd3069960920 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 5 Dec 2017 16:50:00 -0500 Subject: [PATCH 096/379] Add record object to maintain event meta info --- .../features/output/index.controller.js | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index d88d1e915f..4b44fc2c0f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -7,6 +7,8 @@ let $sce; let $compile; let $scope; +const record = {}; + const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -41,6 +43,8 @@ function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); }); + + console.log(record); } function parseEvents (events) { @@ -68,22 +72,46 @@ function parseLine (event) { const { stdout } = event; const lines = stdout.split('\r\n'); - const isTruncated = (event.end_line - event.start_line) > lines.length; - let ln = event.start_line; + let eventLn = event.start_line; + let ln = event.start_line + 1; + + if (lines[0] === '') { + ln++; + } + + record[ln] = { + line: ln, + id: event.id, + uuid: event.uuid, + level: event.event_level, + start: event.start_line, + end: event.end_line, + isTruncated: (event.end_line - event.start_line) > lines.length + }; + + if (record[ln].isTruncated) { + record[ln].truncatedAt = event.start_line + lines.length; + } + + if (EVENT_GROUPS.includes(event.event)) { + record[ln].parent = true; + } + + const current = record[ln]; return lines.reduce((html, line, i) => { - ln++; + eventLn++; const time = getTime(event, i); const group = getGroup(event, i); const isLastLine = i === lines.length - 1; - if (isTruncated && isLastLine) { - return `${html}${createRow(ln, line, time, group)}${createTruncatedRow(event.id)}`; + if (current.isTruncated && isLastLine) { + return `${html}${createRow(eventLn, line, time, group)}${createTruncatedRow(event.id)}`; } - return `${html}${createRow(ln, line, time, group)}`; + return `${html}${createRow(eventLn, line, time, group)}`; }, ''); } From dbf1fd2d4f1387489f3d6b4df89f95b3c6c1fc0e Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 6 Dec 2017 13:10:43 -0500 Subject: [PATCH 097/379] Use event record with output template --- .../features/output/index.controller.js | 117 +++++++++--------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 4b44fc2c0f..76047c1078 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -25,11 +25,11 @@ const TIME_EVENTS = [ ]; function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { - ansi = new Ansi(); $timeout = _$timeout_; $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; + ansi = new Ansi(); const vm = this || {}; const events = job.get('related.job_events.results'); @@ -73,103 +73,104 @@ function parseLine (event) { const { stdout } = event; const lines = stdout.split('\r\n'); - let eventLn = event.start_line; - let ln = event.start_line + 1; + let eventLine = event.start_line; + let displayLine = event.start_line + 1; if (lines[0] === '') { - ln++; + displayLine++; } - record[ln] = { - line: ln, + record[displayLine] = { + line: displayLine, id: event.id, uuid: event.uuid, level: event.event_level, start: event.start_line, end: event.end_line, - isTruncated: (event.end_line - event.start_line) > lines.length + isTruncated: (event.end_line - event.start_line) > lines.length, }; - if (record[ln].isTruncated) { - record[ln].truncatedAt = event.start_line + lines.length; + if (record[displayLine].isTruncated) { + record[displayLine].truncatedAt = event.start_line + lines.length; } if (EVENT_GROUPS.includes(event.event)) { - record[ln].parent = true; + record[displayLine].isParent = true; } - const current = record[ln]; + if (TIME_EVENTS.includes(event.event)) { + record[displayLine].time = getTime(event.created); + } + + const current = record[displayLine]; return lines.reduce((html, line, i) => { - eventLn++; + eventLine++; - const time = getTime(event, i); - const group = getGroup(event, i); const isLastLine = i === lines.length - 1; + let append = createRow(eventLine, line, current); if (current.isTruncated && isLastLine) { - return `${html}${createRow(eventLn, line, time, group)}${createTruncatedRow(event.id)}`; + append += createRow(); } - return `${html}${createRow(eventLn, line, time, group)}`; + return `${html}${append}`; }, ''); } -function createTruncatedRow (id) { - return ` - - - ... - - - `; -} - -function createRow (ln, content, time, group) { - content = hasAnsi(content) ? ansi.toHtml(content) : content; - +function createRow (ln, content, current) { let expand = ''; - if (group.parent) { - expand = ''; + let timestamp = ''; + let toggleRow = ''; + let classList = ''; + + content = content || ''; + + if (hasAnsi(content)) { + content = ansi.toHtml(content); + } + + if (current) { + if (current.line === ln) { + if (current.isParent) { + expand = ''; + toggleRow = `${expand}`; + } + + if (current.time) { + timestamp = current.time; + } + } else { + classList += `child-of-${current.line}`; + } + } + + if (!toggleRow) { + toggleRow = ''; + } + + if (!ln) { + ln = '...'; } return ` - - ${expand} + + ${toggleRow} ${ln} ${content} - ${time} + ${timestamp} `; } -function getGroup (event, i) { - const group = {}; - - if (EVENT_GROUPS.includes(event.event) && i === 1) { - group.parent = true; - group.classList = `parent parent-${event.event_level}`; - group.id = i; - } else { - group.classList = ''; - } - - group.level = event.event_level; - - return group; -} - -function getTime (event, i) { - if (!TIME_EVENTS.includes(event.event) || i !== 1) { - return ''; - } - - const date = new Date(event.created); +function getTime (created) { + const date = new Date(created); return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; } -function toggle (id) { - console.log(id); +function toggle (line) { + const lines = document.getElementsByClassName(`child-of-${line}`); + console.log(lines); } JobsIndexController.$inject = ['job', '$sce', '$timeout', '$scope', '$compile']; From d914b70bb61b80d0fe048590c226ca216f645abc Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 6 Dec 2017 15:41:52 -0500 Subject: [PATCH 098/379] Add expand/collapse to parent events --- awx/ui/client/features/jobs/_index.less | 13 +- .../features/output/index.controller.js | 118 +++++++++++------- 2 files changed, 77 insertions(+), 54 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 8c3a7195e1..0763f0c42d 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -19,6 +19,10 @@ background-color: @at-gray-light; font-size: 12px; + & > i { + cursor: pointer; + } + padding: 0 20px 0 10px; user-select: none; } @@ -29,6 +33,7 @@ text-align: right; + vertical-align: top; padding-right: 5px; border-right: 1px solid @at-gray-dark; user-select: none; @@ -36,6 +41,8 @@ &-event { padding: 0 10px; + word-wrap: break-word; + white-space: pre-wrap; } &-time { @@ -63,12 +70,6 @@ tr:hover > td { background: white; } - - td { - vertical-align: top; - word-wrap: break-word; - white-space: pre-wrap; - } } } } diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 76047c1078..0c28ce968f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -73,56 +73,66 @@ function parseLine (event) { const { stdout } = event; const lines = stdout.split('\r\n'); - let eventLine = event.start_line; - let displayLine = event.start_line + 1; + let ln = event.start_line; - if (lines[0] === '') { - displayLine++; - } - - record[displayLine] = { - line: displayLine, - id: event.id, - uuid: event.uuid, - level: event.event_level, - start: event.start_line, - end: event.end_line, - isTruncated: (event.end_line - event.start_line) > lines.length, - }; - - if (record[displayLine].isTruncated) { - record[displayLine].truncatedAt = event.start_line + lines.length; - } - - if (EVENT_GROUPS.includes(event.event)) { - record[displayLine].isParent = true; - } - - if (TIME_EVENTS.includes(event.event)) { - record[displayLine].time = getTime(event.created); - } - - const current = record[displayLine]; + const current = createRecord(ln, lines, event); return lines.reduce((html, line, i) => { - eventLine++; + ln++; const isLastLine = i === lines.length - 1; - let append = createRow(eventLine, line, current); + let append = createRow(current, ln, line); - if (current.isTruncated && isLastLine) { - append += createRow(); + if (current && current.isTruncated && isLastLine) { + append += createRow(current); } return `${html}${append}`; }, ''); } -function createRow (ln, content, current) { +function createRecord (ln, lines, event) { + if (!event.uuid) { + return null; + } + + const info = { + line: ln + 1, + uuid: event.uuid, + level: event.event_level, + start: event.start_line, + end: event.end_line, + isTruncated: (event.end_line - event.start_line) > lines.length + }; + + if (event.parent_uuid) { + info.childOf = event.parent_uuid; + } + + if (info.isTruncated) { + info.truncatedAt = event.start_line + lines.length; + } + + if (EVENT_GROUPS.includes(event.event)) { + info.isParent = true; + } + + if (TIME_EVENTS.includes(event.event)) { + info.time = getTime(event.created); + info.line++; + } + + record[event.uuid] = info; + + return info; +} + +function createRow (current, ln, content) { let expand = ''; let timestamp = ''; let toggleRow = ''; let classList = ''; + let id = ''; content = content || ''; @@ -131,17 +141,18 @@ function createRow (ln, content, current) { } if (current) { - if (current.line === ln) { - if (current.isParent) { - expand = ''; - toggleRow = `${expand}`; - } + if (current.isParent && current.line === ln) { + id = current.uuid; + expand = ''; + toggleRow = `${expand}`; + } - if (current.time) { - timestamp = current.time; - } - } else { - classList += `child-of-${current.line}`; + if (current.time && current.line === ln) { + timestamp = current.time; + } + + if (!classList) { + classList += `child-of-${current.childOf}`; } } @@ -154,7 +165,7 @@ function createRow (ln, content, current) { } return ` - + ${toggleRow} ${ln} ${content} @@ -168,9 +179,20 @@ function getTime (created) { return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; } -function toggle (line) { - const lines = document.getElementsByClassName(`child-of-${line}`); - console.log(lines); +function toggle (uuid) { + const i = $(`#${uuid} .at-Stdout-toggle > i`); + + if (i.hasClass('fa-chevron-down')) { + i.addClass('fa-chevron-right'); + i.removeClass('fa-chevron-down'); + + $(`.child-of-${uuid}`).addClass('hidden'); + } else { + i.addClass('fa-chevron-down'); + i.removeClass('fa-chevron-right'); + + $(`.child-of-${uuid}`).removeClass('hidden'); + } } JobsIndexController.$inject = ['job', '$sce', '$timeout', '$scope', '$compile']; From 56b6d7e85d6d33a256f7277870b5ba2b7d6aebfd Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 7 Dec 2017 15:31:57 -0500 Subject: [PATCH 099/379] Add scrollTo for top and bottom, add better expand/collapse --- awx/ui/client/features/jobs/_index.less | 15 +++- .../features/output/index.controller.js | 84 ++++++++++++++++--- awx/ui/client/features/output/index.view.html | 20 ++++- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 0763f0c42d..32ef05dbf7 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,7 +1,7 @@ .at-Stdout { font-family: monospace; - &-menu { + &-menuTop { color: @at-gray-dark-4x; border: 1px solid @at-gray-dark-2x; border-top-left-radius: 4px; @@ -9,9 +9,18 @@ border-bottom: none; } + &-menuBottom { + color: @at-gray-dark-4x; + border: 1px solid @at-gray-dark-2x; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top: none; + } + &-menuIcon { font-size: 12px; padding: 10px; + cursor: pointer; } &-toggle { @@ -51,7 +60,6 @@ text-align: right; user-select: none; width: 11ch; - border-left: 1px dashed @at-gray-dark; } &-container { @@ -61,8 +69,7 @@ background-color: @at-gray-light-3x; padding: 0; margin: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; + border-radius: 0; & > table { table-layout: fixed; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0c28ce968f..e6976a2ab1 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,6 +1,7 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; +let vm; let ansi; let $timeout; let $sce; @@ -8,6 +9,7 @@ let $compile; let $scope; const record = {}; +const meta = {}; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; @@ -31,11 +33,20 @@ function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { $scope = _$scope_; ansi = new Ansi(); - const vm = this || {}; + vm = this || {}; const events = job.get('related.job_events.results'); const html = $sce.trustAsHtml(parseEvents(events)); vm.toggle = toggle; + vm.menu = { + expand: menuExpand, + scrollToBottom: menuScrollToBottom, + scrollToTop: menuScrollToTop + }; + + vm.state = { + expand: true + }; $timeout(() => { const table = $('#result-table'); @@ -43,8 +54,23 @@ function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); }); +} - console.log(record); +function menuExpand () { + vm.state.expand = !vm.state.expand; + vm.toggle(meta.parent); +} + +function menuScrollToBottom () { + const container = $('.at-Stdout-container')[0]; + + container.scrollTo(0, container.scrollHeight); +} + +function menuScrollToTop () { + const container = $('.at-Stdout-container')[0]; + + container.scrollTo(0, 0); } function parseEvents (events) { @@ -106,7 +132,7 @@ function createRecord (ln, lines, event) { }; if (event.parent_uuid) { - info.childOf = event.parent_uuid; + info.parents = getParentEvents(event.parent_uuid); } if (info.isTruncated) { @@ -115,6 +141,19 @@ function createRecord (ln, lines, event) { if (EVENT_GROUPS.includes(event.event)) { info.isParent = true; + + if (event.event_level === 1) { + meta.parent = event.uuid; + } + + if (event.parent_uuid) { + if (record[event.parent_uuid].children && + !record[event.parent_uuid].children.includes(event.uuid)) { + record[event.parent_uuid].children.push(event.uuid); + } else { + record[event.parent_uuid].children = [event.uuid]; + } + } } if (TIME_EVENTS.includes(event.event)) { @@ -127,6 +166,20 @@ function createRecord (ln, lines, event) { return info; } +function getParentEvents (uuid, list) { + list = list || []; + + if (record[uuid]) { + list.push(uuid); + } + + if (record[uuid].parents) { + list = list.concat(record[uuid].parents); + } + + return list; +} + function createRow (current, ln, content) { let expand = ''; let timestamp = ''; @@ -151,8 +204,8 @@ function createRow (current, ln, content) { timestamp = current.time; } - if (!classList) { - classList += `child-of-${current.childOf}`; + if (current.parents) { + classList = current.parents.reduce((list, uuid) => `${list} child-of-${uuid}`, ''); } } @@ -180,18 +233,23 @@ function getTime (created) { } function toggle (uuid) { - const i = $(`#${uuid} .at-Stdout-toggle > i`); + const lines = $(`.child-of-${uuid}`); + let icon = $(`#${uuid} .at-Stdout-toggle > i`); - if (i.hasClass('fa-chevron-down')) { - i.addClass('fa-chevron-right'); - i.removeClass('fa-chevron-down'); + if (record[uuid].children) { + icon = icon.add($(`#${record[uuid].children.join(', #')}`).find('.at-Stdout-toggle > i')); + } - $(`.child-of-${uuid}`).addClass('hidden'); + if (icon.hasClass('fa-chevron-down')) { + icon.addClass('fa-chevron-right'); + icon.removeClass('fa-chevron-down'); + + lines.addClass('hidden'); } else { - i.addClass('fa-chevron-down'); - i.removeClass('fa-chevron-right'); + icon.addClass('fa-chevron-down'); + icon.removeClass('fa-chevron-right'); - $(`.child-of-${uuid}`).removeClass('hidden'); + lines.removeClass('hidden'); } } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 34fd7dc368..2da5753b03 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -6,13 +6,27 @@
-
-
-
+
+
+ +
+
+ +
+
 
+ +
+
+ +
+ +
+
From 21e74fc5eb8d9181a2cf479039084a80041e9678 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 11 Dec 2017 16:10:29 -0500 Subject: [PATCH 100/379] Add click to launch host event detail modal --- awx/ui/client/features/jobs/_index.less | 17 ++++- .../features/output/index.controller.js | 71 +++++++++++++------ awx/ui/client/features/output/index.view.html | 3 + .../lib/components/modal/modal.directive.js | 3 +- 4 files changed, 68 insertions(+), 26 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 32ef05dbf7..78e1de6b33 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -49,9 +49,13 @@ } &-event { - padding: 0 10px; - word-wrap: break-word; - white-space: pre-wrap; + .at-mixin-event(); + } + + &-event--host { + .at-mixin-event(); + + cursor: pointer; } &-time { @@ -80,3 +84,10 @@ } } } + +.at-mixin-event() { + padding: 0 10px; + word-wrap: break-word; + white-space: pre-wrap; + +} diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index e6976a2ab1..3139c3a6f8 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -33,11 +33,19 @@ function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { $scope = _$scope_; ansi = new Ansi(); - vm = this || {}; const events = job.get('related.job_events.results'); const html = $sce.trustAsHtml(parseEvents(events)); + vm = this || {}; + + $scope.ns = 'jobs'; + $scope.jobs = { + modal: {} + }; + vm.toggle = toggle; + vm.showHostDetails = showHostDetails; + vm.menu = { expand: menuExpand, scrollToBottom: menuScrollToBottom, @@ -64,13 +72,13 @@ function menuExpand () { function menuScrollToBottom () { const container = $('.at-Stdout-container')[0]; - container.scrollTo(0, container.scrollHeight); + container.scrollTop = container.scrollHeight; } function menuScrollToTop () { const container = $('.at-Stdout-container')[0]; - container.scrollTo(0, 0); + container.scrollTop = 0; } function parseEvents (events) { @@ -123,14 +131,20 @@ function createRecord (ln, lines, event) { } const info = { + id: event.id, line: ln + 1, uuid: event.uuid, level: event.event_level, start: event.start_line, end: event.end_line, - isTruncated: (event.end_line - event.start_line) > lines.length + isTruncated: (event.end_line - event.start_line) > lines.length, + isHost: typeof event.host === 'number' }; + if (info.isHost) { + console.log(event); + } + if (event.parent_uuid) { info.parents = getParentEvents(event.parent_uuid); } @@ -147,11 +161,13 @@ function createRecord (ln, lines, event) { } if (event.parent_uuid) { - if (record[event.parent_uuid].children && - !record[event.parent_uuid].children.includes(event.uuid)) { - record[event.parent_uuid].children.push(event.uuid); - } else { - record[event.parent_uuid].children = [event.uuid]; + if (record[event.parent_uuid]) { + if (record[event.parent_uuid].children && + !record[event.parent_uuid].children.includes(event.uuid)) { + record[event.parent_uuid].children.push(event.uuid); + } else { + record[event.parent_uuid].children = [event.uuid]; + } } } } @@ -171,21 +187,21 @@ function getParentEvents (uuid, list) { if (record[uuid]) { list.push(uuid); - } - if (record[uuid].parents) { - list = list.concat(record[uuid].parents); + if (record[uuid].parents) { + list = list.concat(record[uuid].parents); + } } return list; } function createRow (current, ln, content) { - let expand = ''; - let timestamp = ''; - let toggleRow = ''; - let classList = ''; let id = ''; + let timestamp = ''; + let tdToggle = ''; + let tdEvent = ''; + let classList = ''; content = content || ''; @@ -196,8 +212,11 @@ function createRow (current, ln, content) { if (current) { if (current.isParent && current.line === ln) { id = current.uuid; - expand = ''; - toggleRow = `${expand}`; + tdToggle = ``; + } + + if (current.isHost) { + tdEvent = `${content}`; } if (current.time && current.line === ln) { @@ -209,8 +228,12 @@ function createRow (current, ln, content) { } } - if (!toggleRow) { - toggleRow = ''; + if (!tdEvent) { + tdEvent = `${content}`; + } + + if (!tdToggle) { + tdToggle = ''; } if (!ln) { @@ -219,9 +242,9 @@ function createRow (current, ln, content) { return ` - ${toggleRow} + ${tdToggle} ${ln} - ${content} + ${tdEvent} ${timestamp} `; } @@ -232,6 +255,10 @@ function getTime (created) { return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; } +function showHostDetails (id) { + $scope.jobs.modal.show('title', `test${id}`); +} + function toggle (uuid) { const lines = $(`.child-of-${uuid}`); let icon = $(`#${uuid} .at-Stdout-toggle > i`); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 2da5753b03..ba1256c5cc 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -4,6 +4,7 @@

left

+
@@ -29,4 +30,6 @@
+ + diff --git a/awx/ui/client/lib/components/modal/modal.directive.js b/awx/ui/client/lib/components/modal/modal.directive.js index 302ff92a03..f3def99885 100644 --- a/awx/ui/client/lib/components/modal/modal.directive.js +++ b/awx/ui/client/lib/components/modal/modal.directive.js @@ -12,7 +12,7 @@ function atModalLink (scope, el, attrs, controllers) { }); } -function AtModalController (eventService, strings) { +function AtModalController ($timeout, eventService, strings) { const vm = this; let overlay; @@ -58,6 +58,7 @@ function AtModalController (eventService, strings) { } AtModalController.$inject = [ + '$timeout', 'EventService', 'ComponentsStrings' ]; From a7f29aac3af98ca841f91696d27ba350115e02c0 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 12 Dec 2017 16:26:12 -0500 Subject: [PATCH 101/379] Add component-based stdout for host modal --- awx/ui/client/features/_index.less | 2 +- .../features/output/index.controller.js | 23 +++- awx/ui/client/features/output/index.js | 12 +- awx/ui/client/features/output/index.view.html | 7 +- awx/ui/client/lib/components/index.js | 2 + .../lib/components/output/stdout.directive.js | 105 ++++++++++++++++++ .../lib/components/output/stdout.partial.html | 19 ++++ awx/ui/client/lib/models/Job.js | 8 +- awx/ui/client/lib/models/JobEvent.js | 19 ++++ awx/ui/client/lib/models/Jobs.js | 19 ---- awx/ui/client/lib/models/index.js | 10 +- 11 files changed, 189 insertions(+), 37 deletions(-) create mode 100644 awx/ui/client/lib/components/output/stdout.directive.js create mode 100644 awx/ui/client/lib/components/output/stdout.partial.html create mode 100644 awx/ui/client/lib/models/JobEvent.js delete mode 100644 awx/ui/client/lib/models/Jobs.js diff --git a/awx/ui/client/features/_index.less b/awx/ui/client/features/_index.less index 4a69120ed9..59e8e4630b 100644 --- a/awx/ui/client/features/_index.less +++ b/awx/ui/client/features/_index.less @@ -1,3 +1,3 @@ @import 'credentials/_index'; -@import 'jobs/_index'; +@import 'output/_index'; @import 'users/tokens/_index'; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 3139c3a6f8..238adc11f4 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -3,6 +3,7 @@ import hasAnsi from 'has-ansi'; let vm; let ansi; +let jobEvent; let $timeout; let $sce; let $compile; @@ -26,19 +27,19 @@ const TIME_EVENTS = [ EVENT_STATS_PLAY ]; -function JobsIndexController (job, _$sce_, _$timeout_, _$scope_, _$compile_) { +function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, _$compile_) { $timeout = _$timeout_; $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; + ansi = new Ansi(); + jobEvent = new JobEventModel(); const events = job.get('related.job_events.results'); const html = $sce.trustAsHtml(parseEvents(events)); - vm = this || {}; - - $scope.ns = 'jobs'; + vm = this || {}; $scope.ns = 'jobs'; $scope.jobs = { modal: {} }; @@ -256,7 +257,17 @@ function getTime (created) { } function showHostDetails (id) { - $scope.jobs.modal.show('title', `test${id}`); + jobEvent.request('get', id) + .then(() => { + const title = jobEvent.get('host_name'); + + vm.host = { + menu: true, + stdout: jobEvent.get('stdout') + }; + + $scope.jobs.modal.show(title); + }); } function toggle (uuid) { @@ -280,6 +291,6 @@ function toggle (uuid) { } } -JobsIndexController.$inject = ['job', '$sce', '$timeout', '$scope', '$compile']; +JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index aa98a2359a..e989329f45 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -1,3 +1,8 @@ +import JobsStrings from '~features/output/jobs.strings'; +import IndexController from '~features/output/index.controller'; +import atLibModels from '~models'; +import atLibComponents from '~components'; + import JobsStrings from '~features/output/jobs.strings'; import IndexController from '~features/output/index.controller'; @@ -24,7 +29,7 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - job: ['JobsModel', '$stateParams', (Jobs, $stateParams) => { + job: ['JobModel', '$stateParams', (Jobs, $stateParams) => { const { id } = $stateParams; return new Jobs('get', id) @@ -42,7 +47,10 @@ function JobsRun ($stateExtender, strings) { JobsRun.$inject = ['$stateExtender', 'JobsStrings']; angular - .module(MODULE_NAME, []) + .module(MODULE_NAME, [ + atLibModels, + atLibComponents + ]) .controller('indexController', IndexController) .service('JobsStrings', JobsStrings) .run(JobsRun); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index ba1256c5cc..0d92977ee5 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,7 +1,7 @@
-

left

+

@@ -31,5 +31,8 @@
- + +
+ +
diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index 9ac933628c..339747ad67 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -20,6 +20,7 @@ import launchTemplate from '~components/launchTemplateButton/launchTemplateButto import layout from '~components/layout/layout.directive'; import list from '~components/list/list.directive'; import modal from '~components/modal/modal.directive'; +import outputStdout from '~components/output/stdout.directive'; import panel from '~components/panel/panel.directive'; import panelBody from '~components/panel/body.directive'; import panelHeading from '~components/panel/heading.directive'; @@ -68,6 +69,7 @@ angular .directive('atRowItem', rowItem) .directive('atRowAction', rowAction) .directive('atModal', modal) + .directive('atOutputStdout', outputStdout) .directive('atPanel', panel) .directive('atPanelBody', panelBody) .directive('atPanelHeading', panelHeading) diff --git a/awx/ui/client/lib/components/output/stdout.directive.js b/awx/ui/client/lib/components/output/stdout.directive.js new file mode 100644 index 0000000000..880e2c6eb6 --- /dev/null +++ b/awx/ui/client/lib/components/output/stdout.directive.js @@ -0,0 +1,105 @@ +import Ansi from 'ansi-to-html'; +import hasAnsi from 'has-ansi'; + +const templateUrl = require('~components/output/stdout.partial.html'); + +let $sce; +let $timeout; +let ansi; + +function atOutputStdoutLink (scope, element, attrs, controller) { + controller.init(scope, element); +} + +function AtOutputStdoutController (_$sce_, _$timeout_) { + const vm = this || {}; + + $timeout = _$timeout_; + $sce = _$sce_; + ansi = new Ansi(); + + let scope; + let element; + + vm.init = (_scope_, _element_) => { + scope = _scope_; + element = _element_; + + scope.$watch('state.stdout', curr => { + if (!curr) { + return; + } + + render(scope.state.stdout); + }); + }; + + vm.scroll = position => { + const container = element.find('.at-Stdout-container')[0]; + + if (position === 'bottom') { + container.scrollTop = container.scrollHeight; + } else { + container.scrollTop = 0; + } + }; +} + +AtOutputStdoutController.$inject = [ + '$sce', + '$timeout', +]; + +function render (stdout) { + console.log('render'); + const html = $sce.trustAsHtml(parseStdout(stdout)); + + $timeout(() => { + const table = $('#atStdoutTBody'); + + table.html($sce.getTrustedHtml(html)); + }); +} + +function parseStdout (stdout) { + const lines = stdout.split('\r\n'); + + let ln = 0; + + return lines.reduce((html, line) => { + ln++; + + return `${html}${createRow(ln, line)}`; + }, ''); +} + +function createRow (ln, content) { + content = content || ''; + + if (hasAnsi(content)) { + content = ansi.toHtml(content); + } + + return ` + + ${ln} + ${content} + `; +} +function atOutputStdout () { + return { + restrict: 'E', + transclude: true, + replace: true, + require: 'atOutputStdout', + templateUrl, + controller: AtOutputStdoutController, + controllerAs: 'vm', + link: atOutputStdoutLink, + scope: { + state: '=', + } + }; +} + +export default atOutputStdout; diff --git a/awx/ui/client/lib/components/output/stdout.partial.html b/awx/ui/client/lib/components/output/stdout.partial.html new file mode 100644 index 0000000000..ca38d736f4 --- /dev/null +++ b/awx/ui/client/lib/components/output/stdout.partial.html @@ -0,0 +1,19 @@ +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
diff --git a/awx/ui/client/lib/models/Job.js b/awx/ui/client/lib/models/Job.js index 7d87f82330..ef80c5dafb 100644 --- a/awx/ui/client/lib/models/Job.js +++ b/awx/ui/client/lib/models/Job.js @@ -1,5 +1,5 @@ -let Base; let $http; +let BaseModel; function getRelaunch (params) { const req = { @@ -24,7 +24,7 @@ function postRelaunch (params) { } function JobModel (method, resource, config) { - Base.call(this, 'jobs'); + BaseModel.call(this, 'jobs'); this.Constructor = JobModel; this.postRelaunch = postRelaunch.bind(this); @@ -33,8 +33,8 @@ function JobModel (method, resource, config) { return this.create(method, resource, config); } -function JobModelLoader (BaseModel, _$http_) { - Base = BaseModel; +function JobModelLoader (_BaseModel_, _$http_) { + BaseModel = _BaseModel_; $http = _$http_; return JobModel; diff --git a/awx/ui/client/lib/models/JobEvent.js b/awx/ui/client/lib/models/JobEvent.js new file mode 100644 index 0000000000..1c71ba9c54 --- /dev/null +++ b/awx/ui/client/lib/models/JobEvent.js @@ -0,0 +1,19 @@ +let BaseModel; + +function JobEventModel (method, resource, config) { + BaseModel.call(this, 'job_events'); + + this.Constructor = JobEventModel; + + return this.create(method, resource, config); +} + +function JobEventModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return JobEventModel; +} + +JobEventModel.$inject = ['BaseModel']; + +export default JobEventModelLoader; diff --git a/awx/ui/client/lib/models/Jobs.js b/awx/ui/client/lib/models/Jobs.js deleted file mode 100644 index e82b3e04af..0000000000 --- a/awx/ui/client/lib/models/Jobs.js +++ /dev/null @@ -1,19 +0,0 @@ -let BaseModel; - -function JobsModel (method, resource, config) { - BaseModel.call(this, 'jobs'); - - this.Constructor = JobsModel; - - return this.create(method, resource, config); -} - -function JobsModelLoader (_BaseModel_) { - BaseModel = _BaseModel_; - - return JobsModel; -} - -JobsModelLoader.$inject = ['BaseModel']; - -export default JobsModelLoader; diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 0e5ab2c6b3..91eb3742ee 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -12,10 +12,10 @@ import Inventory from '~models/Inventory'; import InventoryScript from '~models/InventoryScript'; import InventorySource from '~models/InventorySource'; import Job from '~models/Job'; +import JobEvent from '~models/JobEvent'; import JobTemplate from '~models/JobTemplate'; import Jobs from '~models/Jobs'; import Me from '~models/Me'; -import ModelsStrings from '~models/models.strings'; import NotificationTemplate from '~models/NotificationTemplate'; import Organization from '~models/Organization'; import Project from '~models/Project'; @@ -26,6 +26,8 @@ import WorkflowJobTemplate from '~models/WorkflowJobTemplate'; import WorkflowJobTemplateNode from '~models/WorkflowJobTemplateNode'; import UnifiedJob from '~models/UnifiedJob'; +import ModelsStrings from '~models/models.strings'; + const MODULE_NAME = 'at.lib.models'; angular @@ -43,11 +45,11 @@ angular .service('InventoryModel', Inventory) .service('InventoryScriptModel', InventoryScript) .service('InventorySourceModel', InventorySource) + .service('JobEventModel', JobEvent) .service('JobModel', Job) .service('JobTemplateModel', JobTemplate) .service('JobsModel', Jobs) .service('MeModel', Me) - .service('ModelsStrings', ModelsStrings) .service('NotificationTemplate', NotificationTemplate) .service('OrganizationModel', Organization) .service('ProjectModel', Project) @@ -56,6 +58,8 @@ angular .service('UnifiedJobTemplateModel', UnifiedJobTemplate) .service('WorkflowJobModel', WorkflowJob) .service('WorkflowJobTemplateModel', WorkflowJobTemplate) - .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode); + .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) + .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) + .service('ModelsStrings', ModelsStrings); export default MODULE_NAME; From 81dac1d1b89347a1861920a9f27c9813a4729ff7 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 13 Dec 2017 13:02:42 -0500 Subject: [PATCH 102/379] Update code/output components --- .../features/output/index.controller.js | 24 +--- awx/ui/client/features/output/index.view.html | 12 +- .../lib/components/code/events.directive.js | 104 ++++++++++++++++++ .../lib/components/code/events.partial.html | 0 .../components/code/menu-bottom.directive.js | 41 +++++++ .../components/code/menu-bottom.partial.html | 7 ++ .../lib/components/code/menu-top.directive.js | 52 +++++++++ .../lib/components/code/menu-top.partial.html | 12 ++ .../{output => code}/stdout.directive.js | 3 +- .../{output => code}/stdout.partial.html | 0 awx/ui/client/lib/components/index.js | 10 +- 11 files changed, 230 insertions(+), 35 deletions(-) create mode 100644 awx/ui/client/lib/components/code/events.directive.js create mode 100644 awx/ui/client/lib/components/code/events.partial.html create mode 100644 awx/ui/client/lib/components/code/menu-bottom.directive.js create mode 100644 awx/ui/client/lib/components/code/menu-bottom.partial.html create mode 100644 awx/ui/client/lib/components/code/menu-top.directive.js create mode 100644 awx/ui/client/lib/components/code/menu-top.partial.html rename awx/ui/client/lib/components/{output => code}/stdout.directive.js (95%) rename awx/ui/client/lib/components/{output => code}/stdout.partial.html (100%) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 238adc11f4..0b87c47552 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -48,13 +48,10 @@ function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, vm.showHostDetails = showHostDetails; vm.menu = { - expand: menuExpand, - scrollToBottom: menuScrollToBottom, - scrollToTop: menuScrollToTop - }; - - vm.state = { - expand: true + top: { + expand: menuExpand, + isExpanded: false + } }; $timeout(() => { @@ -66,22 +63,9 @@ function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, } function menuExpand () { - vm.state.expand = !vm.state.expand; vm.toggle(meta.parent); } -function menuScrollToBottom () { - const container = $('.at-Stdout-container')[0]; - - container.scrollTop = container.scrollHeight; -} - -function menuScrollToTop () { - const container = $('.at-Stdout-container')[0]; - - container.scrollTop = 0; -} - function parseEvents (events) { events.sort(orderByLineNumber); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 0d92977ee5..7850adacb3 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -7,17 +7,7 @@
-
-
- -
-
- -
- -
-
+
 
diff --git a/awx/ui/client/lib/components/code/events.directive.js b/awx/ui/client/lib/components/code/events.directive.js new file mode 100644 index 0000000000..8426bf3ae6 --- /dev/null +++ b/awx/ui/client/lib/components/code/events.directive.js @@ -0,0 +1,104 @@ +import Ansi from 'ansi-to-html'; +import hasAnsi from 'has-ansi'; + +const templateUrl = require('~components/code/events.partial.html'); + +let $sce; +let $timeout; +let ansi; + +function atOutputEventLink (scope, element, attrs, controller) { + controller.init(scope, element); +} + +function AtOutputEventController (_$sce_, _$timeout_) { + const vm = this || {}; + + $timeout = _$timeout_; + $sce = _$sce_; + ansi = new Ansi(); + + let scope; + let element; + + vm.init = (_scope_, _element_) => { + scope = _scope_; + element = _element_; + + scope.$watch('state.stdout', curr => { + if (!curr) { + return; + } + + render(scope.state.stdout); + }); + }; + + vm.scroll = position => { + const container = element.find('.at-Stdout-container')[0]; + + if (position === 'bottom') { + container.scrollTop = container.scrollHeight; + } else { + container.scrollTop = 0; + } + }; +} + +AtOutputEventController.$inject = [ + '$sce', + '$timeout', +]; + +function render (stdout) { + const html = $sce.trustAsHtml(parseStdout(stdout)); + + $timeout(() => { + const table = $('#atStdoutTBody'); + + table.html($sce.getTrustedHtml(html)); + }); +} + +function parseStdout (stdout) { + const lines = stdout.split('\r\n'); + + let ln = 0; + + return lines.reduce((html, line) => { + ln++; + + return `${html}${createRow(ln, line)}`; + }, ''); +} + +function createRow (ln, content) { + content = content || ''; + + if (hasAnsi(content)) { + content = ansi.toHtml(content); + } + + return ` + + ${ln} + ${content} + `; +} +function atOutputEvent () { + return { + restrict: 'E', + transclude: true, + replace: true, + require: 'atOutputEvent', + templateUrl, + controller: AtOutputEventController, + controllerAs: 'vm', + link: atOutputEventLink, + scope: { + state: '=', + } + }; +} + +export default atOutputEvent; diff --git a/awx/ui/client/lib/components/code/events.partial.html b/awx/ui/client/lib/components/code/events.partial.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/awx/ui/client/lib/components/code/menu-bottom.directive.js b/awx/ui/client/lib/components/code/menu-bottom.directive.js new file mode 100644 index 0000000000..5b15a7bc10 --- /dev/null +++ b/awx/ui/client/lib/components/code/menu-bottom.directive.js @@ -0,0 +1,41 @@ +const templateUrl = require('~components/code/menu-bottom.partial.html'); + +function atCodeMenuBottomLink (scope, element, attrs, controller) { + controller.init(scope, element); +} + +function AtCodeMenuBottomController () { + const vm = this || {}; + + let element; + + vm.init = (_scope_, _element_) => { + element = _element_; + }; + + vm.scroll = () => { + const container = element.find('.at-Stdout-container')[0]; + + container.scrollTop = container.scrollHeight; + }; +} + +AtCodeMenuBottomController.$inject = []; + +function atCodeMenuBottom () { + return { + restrict: 'E', + transclude: true, + replace: true, + require: 'atCodeMenuBottom', + templateUrl, + controller: AtCodeMenuBottomController, + controllerAs: 'vm', + link: atCodeMenuBottomLink, + scope: { + state: '=', + } + }; +} + +export default atCodeMenuBottom; diff --git a/awx/ui/client/lib/components/code/menu-bottom.partial.html b/awx/ui/client/lib/components/code/menu-bottom.partial.html new file mode 100644 index 0000000000..1a0717a360 --- /dev/null +++ b/awx/ui/client/lib/components/code/menu-bottom.partial.html @@ -0,0 +1,7 @@ +
+
+ +
+ +
+
diff --git a/awx/ui/client/lib/components/code/menu-top.directive.js b/awx/ui/client/lib/components/code/menu-top.directive.js new file mode 100644 index 0000000000..824e24123f --- /dev/null +++ b/awx/ui/client/lib/components/code/menu-top.directive.js @@ -0,0 +1,52 @@ +const templateUrl = require('~components/code/menu-top.partial.html'); + +function atCodeMenuTopLink (scope, element, attrs, controller) { + controller.init(scope, element); +} + +function AtCodeMenuTopController () { + const vm = this || {}; + + let element; + let scope; + + vm.init = (_scope_, _element_) => { + scope = _scope_; + element = _element_; + + scope.state.isExpanded = scope.state.isExpanded || false; + }; + + vm.scroll = () => { + const container = element.parent().find('.at-Stdout-container')[0]; + + console.log(container); + + container.scrollTop = 0; + }; + + vm.expand = () => { + scope.state.isExpanded = !scope.state.isExpanded; + scope.state.expand(); + }; +} + +AtCodeMenuTopController.$inject = []; + +function atCodeMenuTop () { + return { + restrict: 'E', + transclude: true, + replace: true, + require: 'atCodeMenuTop', + templateUrl, + controller: AtCodeMenuTopController, + controllerAs: 'vm', + link: atCodeMenuTopLink, + scope: { + state: '=', + } + }; +} + +export default atCodeMenuTop; diff --git a/awx/ui/client/lib/components/code/menu-top.partial.html b/awx/ui/client/lib/components/code/menu-top.partial.html new file mode 100644 index 0000000000..2e21dc1b8b --- /dev/null +++ b/awx/ui/client/lib/components/code/menu-top.partial.html @@ -0,0 +1,12 @@ +
+
+ +
+ +
+ +
+ +
+
diff --git a/awx/ui/client/lib/components/output/stdout.directive.js b/awx/ui/client/lib/components/code/stdout.directive.js similarity index 95% rename from awx/ui/client/lib/components/output/stdout.directive.js rename to awx/ui/client/lib/components/code/stdout.directive.js index 880e2c6eb6..1f32b5ca7f 100644 --- a/awx/ui/client/lib/components/output/stdout.directive.js +++ b/awx/ui/client/lib/components/code/stdout.directive.js @@ -1,7 +1,7 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; -const templateUrl = require('~components/output/stdout.partial.html'); +const templateUrl = require('~components/code/stdout.partial.html'); let $sce; let $timeout; @@ -51,7 +51,6 @@ AtOutputStdoutController.$inject = [ ]; function render (stdout) { - console.log('render'); const html = $sce.trustAsHtml(parseStdout(stdout)); $timeout(() => { diff --git a/awx/ui/client/lib/components/output/stdout.partial.html b/awx/ui/client/lib/components/code/stdout.partial.html similarity index 100% rename from awx/ui/client/lib/components/output/stdout.partial.html rename to awx/ui/client/lib/components/code/stdout.partial.html diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index 339747ad67..bf45c63a6d 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -1,6 +1,10 @@ import atLibServices from '~services'; import actionGroup from '~components/action/action-group.directive'; +import codeMenuBottom from '~components/code/menu-bottom.directive'; +import codeMenuTop from '~components/code/menu-top.directive'; +import codeEvents from '~components/code/events.directive'; +import codeStdout from '~components/code/stdout.directive'; import divider from '~components/utility/divider.directive'; import form from '~components/form/form.directive'; import formAction from '~components/form/action.directive'; @@ -20,7 +24,6 @@ import launchTemplate from '~components/launchTemplateButton/launchTemplateButto import layout from '~components/layout/layout.directive'; import list from '~components/list/list.directive'; import modal from '~components/modal/modal.directive'; -import outputStdout from '~components/output/stdout.directive'; import panel from '~components/panel/panel.directive'; import panelBody from '~components/panel/body.directive'; import panelHeading from '~components/panel/heading.directive'; @@ -46,6 +49,10 @@ angular atLibServices ]) .directive('atActionGroup', actionGroup) + .directive('atCodeEvents', codeEvents) + .directive('atCodeMenuBottom', codeMenuBottom) + .directive('atCodeMenuTop', codeMenuTop) + .directive('atCodeStdout', codeStdout) .directive('atDivider', divider) .directive('atForm', form) .directive('atFormAction', formAction) @@ -69,7 +76,6 @@ angular .directive('atRowItem', rowItem) .directive('atRowAction', rowAction) .directive('atModal', modal) - .directive('atOutputStdout', outputStdout) .directive('atPanel', panel) .directive('atPanelBody', panelBody) .directive('atPanelHeading', panelHeading) From e26c977b36586d9806c079121aadf1501f42973b Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 4 Jan 2018 11:06:54 -0500 Subject: [PATCH 103/379] Add partial implementation of model.next --- .../features/output/index.controller.js | 33 ++++++++++++++++--- awx/ui/client/features/output/index.js | 2 +- awx/ui/client/features/output/index.view.html | 21 ++++++++++-- .../lib/components/code/menu-top.directive.js | 2 -- .../lib/components/code/stdout.directive.js | 17 +++++----- awx/ui/client/lib/models/Base.js | 19 +++++++++++ 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0b87c47552..c94660d298 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -3,6 +3,7 @@ import hasAnsi from 'has-ansi'; let vm; let ansi; +let job; let jobEvent; let $timeout; let $sce; @@ -27,11 +28,12 @@ const TIME_EVENTS = [ EVENT_STATS_PLAY ]; -function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, _$compile_) { +function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_, _$compile_) { $timeout = _$timeout_; $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; + job = _job_; ansi = new Ansi(); jobEvent = new JobEventModel(); @@ -39,7 +41,9 @@ function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, const events = job.get('related.job_events.results'); const html = $sce.trustAsHtml(parseEvents(events)); - vm = this || {}; $scope.ns = 'jobs'; + vm = this || {}; + + $scope.ns = 'jobs'; $scope.jobs = { modal: {} }; @@ -48,9 +52,13 @@ function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, vm.showHostDetails = showHostDetails; vm.menu = { + scroll, top: { - expand: menuExpand, + expand, isExpanded: false + }, + bottom: { + next } }; @@ -62,10 +70,27 @@ function JobsIndexController (job, JobEventModel, _$sce_, _$timeout_, _$scope_, }); } -function menuExpand () { +function next () { + job.next('job_events') + .then(data => { + console.log(data); + }); +} + +function expand () { vm.toggle(meta.parent); } +function scroll (direction) { + const container = $('.at-Stdout-container')[0]; + + if (direction === 'top') { + container.scrollTop = 0; + } else { + container.scrollTop = container.scrollHeight; + } +} + function parseEvents (events) { events.sort(orderByLineNumber); diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index e989329f45..6ff616c6ba 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -35,7 +35,7 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { params: { - page_size: 10000, + page_size: 10, order_by: 'start_line' } })); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 7850adacb3..89862f9cba 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -7,12 +7,27 @@
- +
+
+ +
+ +
+ +
+ +
+
 
-
+
+ +
+ +
@@ -23,6 +38,6 @@
- +
diff --git a/awx/ui/client/lib/components/code/menu-top.directive.js b/awx/ui/client/lib/components/code/menu-top.directive.js index 824e24123f..e78ead1bae 100644 --- a/awx/ui/client/lib/components/code/menu-top.directive.js +++ b/awx/ui/client/lib/components/code/menu-top.directive.js @@ -20,8 +20,6 @@ function AtCodeMenuTopController () { vm.scroll = () => { const container = element.parent().find('.at-Stdout-container')[0]; - console.log(container); - container.scrollTop = 0; }; diff --git a/awx/ui/client/lib/components/code/stdout.directive.js b/awx/ui/client/lib/components/code/stdout.directive.js index 1f32b5ca7f..c73e90b7e8 100644 --- a/awx/ui/client/lib/components/code/stdout.directive.js +++ b/awx/ui/client/lib/components/code/stdout.directive.js @@ -7,11 +7,11 @@ let $sce; let $timeout; let ansi; -function atOutputStdoutLink (scope, element, attrs, controller) { +function atCodeStdoutLink (scope, element, attrs, controller) { controller.init(scope, element); } -function AtOutputStdoutController (_$sce_, _$timeout_) { +function AtCodeStdoutController (_$sce_, _$timeout_) { const vm = this || {}; $timeout = _$timeout_; @@ -45,7 +45,7 @@ function AtOutputStdoutController (_$sce_, _$timeout_) { }; } -AtOutputStdoutController.$inject = [ +AtCodeStdoutController.$inject = [ '$sce', '$timeout', ]; @@ -85,20 +85,21 @@ function createRow (ln, content) { ${content} `; } -function atOutputStdout () { + +function atCodeStdout () { return { restrict: 'E', transclude: true, replace: true, - require: 'atOutputStdout', + require: 'atCodeStdout', templateUrl, - controller: AtOutputStdoutController, + controller: AtCodeStdoutController, controllerAs: 'vm', - link: atOutputStdoutLink, + link: atCodeStdoutLink, scope: { state: '=', } }; } -export default atOutputStdout; +export default atCodeStdout; diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index ee63b97076..cfd84c6a1f 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -346,6 +346,24 @@ function extend (related, config) { return Promise.reject(new Error(`No related property, ${related}, exists`)); } +function next (related) { + related = related || this.resource; + + if (!this.has(`related.${related}.next`)) { + return Promise.resolve(null); + } + + const req = { + method: 'GET', + url: this.get(`related.${related}.next`) + }; + + return $http(req) + .then(({ data }) => { + console.log(data); + }); +} + function normalizePath (resource) { const version = '/api/v2/'; @@ -523,6 +541,7 @@ function BaseModel (resource, settings) { this.isCacheable = isCacheable; this.isCreatable = isCreatable; this.match = match; + this.next = next; this.normalizePath = normalizePath; this.options = options; this.parseRequestConfig = parseRequestConfig; From 3006caffe17fd816d5435c59a8de7e2076e1390d Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 5 Jan 2018 15:14:19 -0500 Subject: [PATCH 104/379] Update style to be inline with mockups --- awx/ui/client/features/jobs/_index.less | 50 ++++++++--- .../features/output/index.controller.js | 86 +++++++++++++++---- awx/ui/client/features/output/index.js | 2 +- awx/ui/client/features/output/index.view.html | 13 ++- awx/ui/client/lib/models/Base.js | 43 ++++++++-- 5 files changed, 150 insertions(+), 44 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 78e1de6b33..1ebec50483 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -11,10 +11,28 @@ &-menuBottom { color: @at-gray-dark-4x; - border: 1px solid @at-gray-dark-2x; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-top: none; + font-size: 10px; + text-transform: uppercase; + font-weight: bold; + position: absolute; + right: 60px; + bottom: 24px; + cursor: pointer; + } + + &-menuIconGroup { + & > p { + margin: 0; + } + + & > p:first-child { + font-size: 20px; + margin-right: 8px; + } + + & > p:last-child { + margin-top: 9px; + } } &-menuIcon { @@ -26,25 +44,24 @@ &-toggle { color: @at-gray-dark-4x; background-color: @at-gray-light; - font-size: 12px; + font-size: 18px; + line-height: 12px; & > i { cursor: pointer; } - padding: 0 20px 0 10px; + padding: 0 10px 0 10px; user-select: none; } &-line { - color: @at-gray-dark-4x; + color: @at-gray-dark-6x; background-color: @at-gray-light; - text-align: right; - vertical-align: top; padding-right: 5px; - border-right: 1px solid @at-gray-dark; + border-right: 1px solid @at-gray-dark-2x; user-select: none; } @@ -64,16 +81,25 @@ text-align: right; user-select: none; width: 11ch; + + & > span { + background-color: white; + border-radius: 4px; + padding: 1px 2px; + } } &-container { max-height: 80vh; - font-size: 14px; + font-size: 15px; border: 1px solid @at-gray-dark-2x; - background-color: @at-gray-light-3x; + background-color: @at-gray-light-2x; + color: @at-gray-dark-6x; padding: 0; margin: 0; border-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; & > table { table-layout: fixed; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index c94660d298..0926846e42 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -5,13 +5,16 @@ let vm; let ansi; let job; let jobEvent; +let container; let $timeout; let $sce; let $compile; let $scope; const record = {}; -const meta = {}; +const meta = { + scroll: {} +}; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; @@ -52,10 +55,13 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ vm.showHostDetails = showHostDetails; vm.menu = { - scroll, + scroll: { + display: false, + to: scrollTo + }, top: { expand, - isExpanded: false + isExpanded: true }, bottom: { next @@ -64,9 +70,15 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ $timeout(() => { const table = $('#result-table'); + container = $('.at-Stdout-container'); table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); + + meta.scroll.height = container[0].scrollHeight; + meta.scroll.buffer = 100; + + container.scroll(onScroll); }); } @@ -78,16 +90,14 @@ function next () { } function expand () { - vm.toggle(meta.parent); + vm.toggle(meta.parent, true); } -function scroll (direction) { - const container = $('.at-Stdout-container')[0]; - +function scrollTo (direction) { if (direction === 'top') { - container.scrollTop = 0; + container[0].scrollTop = 0; } else { - container.scrollTop = container.scrollHeight; + container[0].scrollTop = container[0].scrollHeight; } } @@ -222,7 +232,7 @@ function createRow (current, ln, content) { if (current) { if (current.isParent && current.line === ln) { id = current.uuid; - tdToggle = ``; + tdToggle = ``; } if (current.isHost) { @@ -230,7 +240,7 @@ function createRow (current, ln, content) { } if (current.time && current.line === ln) { - timestamp = current.time; + timestamp = `${current.time}`; } if (current.parents) { @@ -261,8 +271,11 @@ function createRow (current, ln, content) { function getTime (created) { const date = new Date(created); + const hour = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours(); + const minute = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes(); + const second = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds(); - return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; + return `${hour}:${minute}:${second}`; } function showHostDetails (id) { @@ -279,27 +292,64 @@ function showHostDetails (id) { }); } -function toggle (uuid) { +function toggle (uuid, menu) { const lines = $(`.child-of-${uuid}`); let icon = $(`#${uuid} .at-Stdout-toggle > i`); + if (menu || record[uuid].level === 1) { + vm.menu.top.isExpanded = !vm.menu.top.isExpanded; + } + if (record[uuid].children) { icon = icon.add($(`#${record[uuid].children.join(', #')}`).find('.at-Stdout-toggle > i')); } - if (icon.hasClass('fa-chevron-down')) { - icon.addClass('fa-chevron-right'); - icon.removeClass('fa-chevron-down'); + if (icon.hasClass('fa-angle-down')) { + icon.addClass('fa-angle-right'); + icon.removeClass('fa-angle-down'); lines.addClass('hidden'); } else { - icon.addClass('fa-chevron-down'); - icon.removeClass('fa-chevron-right'); + icon.addClass('fa-angle-down'); + icon.removeClass('fa-angle-right'); lines.removeClass('hidden'); } } +function onScroll () { + if (meta.scroll.inProgress) { + return; + } + + meta.scroll.inProgress = true; + + $timeout(() => { + const top = container[0].scrollTop; + const bottom = top + meta.scroll.buffer + container[0].offsetHeight; + + meta.scroll.inProgress = false; + + if (top === 0) { + vm.menu.scroll.display = false; + + return; + } + + vm.menu.scroll.display = true; + + if (bottom >= meta.scroll.height) { + // fetch more lines + } + }, 500); +} + +/* + *function approximateLineNumber () { + * + *} + */ + JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 6ff616c6ba..62e7309115 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -35,7 +35,7 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { params: { - page_size: 10, + page_size: 1000, order_by: 'start_line' } })); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 89862f9cba..e45303ccec 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -13,7 +13,7 @@ ng-class="{ 'fa-minus': vm.menu.top.isExpanded, 'fa-plus': !vm.menu.top.isExpanded }">
-
+
@@ -22,13 +22,10 @@
 
-
-
- -
- -
- +
+
+

+

Back to Top

diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index cfd84c6a1f..0efa02b329 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -346,21 +346,53 @@ function extend (related, config) { return Promise.reject(new Error(`No related property, ${related}, exists`)); } -function next (related) { - related = related || this.resource; +function next (related, config = {}) { + const url = this.get(`related.${related}.next`); - if (!this.has(`related.${related}.next`)) { + if (!url) { return Promise.resolve(null); } const req = { method: 'GET', - url: this.get(`related.${related}.next`) + url }; return $http(req) .then(({ data }) => { - console.log(data); + const results = this.get(`related.${related}.results`) || []; + + data.results = results.concat(data.results); + this.set('get', `related.${related}`, data); + + if (config.limit < results.length) { + console.log(results); + } + }); +} + +function prev (related, config = {}) { + const url = this.get(`related.${related}.previous`); + + if (!url) { + return Promise.resolve(null); + } + + const req = { + method: 'GET', + url + }; + + return $http(req) + .then(({ data }) => { + const results = this.get(`related.${related}.results`) || []; + + data.results = data.results.concat(results); + this.set('get', `related.${related}`, data); + + if (config.limit < results.length) { + console.log(results); + } }); } @@ -545,6 +577,7 @@ function BaseModel (resource, settings) { this.normalizePath = normalizePath; this.options = options; this.parseRequestConfig = parseRequestConfig; + this.prev = prev; this.request = request; this.requestWithCache = requestWithCache; this.search = search; From b88ad50a75a3f65dd8a4563567a48fd96281b458 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 9 Jan 2018 09:15:54 -0500 Subject: [PATCH 105/379] Update style of stdout container --- awx/ui/client/features/jobs/_index.less | 3 ++- awx/ui/client/features/output/index.controller.js | 14 ++++++++++---- awx/ui/client/features/output/index.js | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 1ebec50483..66725e86eb 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -90,7 +90,8 @@ } &-container { - max-height: 80vh; + height: calc(~"100vh - 240px"); + overflow-y: scroll; font-size: 15px; border: 1px solid @at-gray-dark-2x; background-color: @at-gray-light-2x; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0926846e42..b00404b398 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -77,6 +77,9 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ meta.scroll.height = container[0].scrollHeight; meta.scroll.buffer = 100; + meta.next = job.get('related.job_events.next'); + meta.prev = job.get('related.job_events.previous'); + meta.cursor = job.get('related.job_events.results').length - 1; container.scroll(onScroll); }); @@ -84,8 +87,11 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ function next () { job.next('job_events') - .then(data => { - console.log(data); + .then(() => { + meta.next = job.get('related.job_events.next'); + meta.prev = job.get('related.job_events.previous'); + + console.log(job.get('related.job_events.results')); }); } @@ -338,8 +344,8 @@ function onScroll () { vm.menu.scroll.display = true; - if (bottom >= meta.scroll.height) { - // fetch more lines + if (bottom >= meta.scroll.height && meta.next) { + next(); } }, 500); } diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 62e7309115..630d84a442 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -35,7 +35,7 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { params: { - page_size: 1000, + page_size: 13, order_by: 'start_line' } })); From 5a75059c865b4bda8c8e1c18cc25ebf42234b2bb Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 9 Jan 2018 16:01:20 -0500 Subject: [PATCH 106/379] Add load on scroll and max results to base model --- .../features/output/index.controller.js | 27 ++++++++------- awx/ui/client/lib/models/Base.js | 34 +++++++++++++++---- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index b00404b398..6aae149d69 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -79,22 +79,31 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ meta.scroll.buffer = 100; meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); - meta.cursor = job.get('related.job_events.results').length - 1; + meta.cursor = job.get('related.job_events.results').length; container.scroll(onScroll); }); } function next () { - job.next('job_events') + job.next({ related: 'job_events', limit: 5 }) .then(() => { meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); - console.log(job.get('related.job_events.results')); + append(); }); } +function append () { + const events = job.get('related.job_events.results').slice(meta.cursor); + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); + const table = $('#result-table'); + + table.append(rows); + $compile(rows.contents())($scope); +} + function expand () { vm.toggle(meta.parent, true); } @@ -141,13 +150,13 @@ function parseLine (event) { ln++; const isLastLine = i === lines.length - 1; - let append = createRow(current, ln, line); + let row = createRow(current, ln, line); if (current && current.isTruncated && isLastLine) { - append += createRow(current); + row += createRow(current); } - return `${html}${append}`; + return `${html}${row}`; }, ''); } @@ -350,12 +359,6 @@ function onScroll () { }, 500); } -/* - *function approximateLineNumber () { - * - *} - */ - JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; module.exports = JobsIndexController; diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 0efa02b329..20746ed8aa 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -104,6 +104,10 @@ function httpGet (config = {}) { if (config.params) { req.params = config.params; + + if (config.params.page_size) { + this.pageSize = config.params.page_size; + } } if (typeof config.resource === 'object') { @@ -330,6 +334,10 @@ function has (method, keys) { function extend (related, config) { const req = this.parseRequestConfig('GET', config); + if (config.params.page_size) { + this.pageSize = config.params.page_size; + } + if (this.has(req.method, `related.${related}`)) { req.url = this.get(`related.${related}`); @@ -346,8 +354,17 @@ function extend (related, config) { return Promise.reject(new Error(`No related property, ${related}, exists`)); } -function next (related, config = {}) { - const url = this.get(`related.${related}.next`); +function next (config = {}) { + let url; + let results; + + if (config.related) { + url = this.get(`related.${config.related}.next`); + results = this.get(`related.${config.related}.results`) || []; + } else { + url = this.get('next'); + results = this.get('results'); + } if (!url) { return Promise.resolve(null); @@ -360,13 +377,18 @@ function next (related, config = {}) { return $http(req) .then(({ data }) => { - const results = this.get(`related.${related}.results`) || []; + results = results || []; data.results = results.concat(data.results); - this.set('get', `related.${related}`, data); - if (config.limit < results.length) { - console.log(results); + if ((config.limit * this.pageSize) < data.results.length) { + data.results.splice(-config.limit * this.pageSize); + } + + if (config.related) { + this.set('get', `related.${config.related}`, data); + } else { + this.set('get', data); } }); } From c08538b8f09cbd1db6c4462f524d041680eb77f6 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 10 Jan 2018 13:29:32 -0500 Subject: [PATCH 107/379] Fix model pagination behavior, limit, and cache --- .../features/output/index.controller.js | 23 ++++------ awx/ui/client/features/output/index.js | 6 ++- awx/ui/client/lib/models/Base.js | 45 +++++++------------ 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 6aae149d69..380aed1a95 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -16,6 +16,7 @@ const meta = { scroll: {} }; +const SCROLL_BUFFER = 250; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -75,8 +76,6 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); - meta.scroll.height = container[0].scrollHeight; - meta.scroll.buffer = 100; meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); meta.cursor = job.get('related.job_events.results').length; @@ -86,17 +85,17 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ } function next () { - job.next({ related: 'job_events', limit: 5 }) - .then(() => { + job.next({ related: 'job_events' }) + .then(cursor => { meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); - append(); + append(cursor); }); } -function append () { - const events = job.get('related.job_events.results').slice(meta.cursor); +function append (cursor) { + const events = job.get('related.job_events.results').slice(cursor); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); const table = $('#result-table'); @@ -176,10 +175,6 @@ function createRecord (ln, lines, event) { isHost: typeof event.host === 'number' }; - if (info.isHost) { - console.log(event); - } - if (event.parent_uuid) { info.parents = getParentEvents(event.parent_uuid); } @@ -341,7 +336,7 @@ function onScroll () { $timeout(() => { const top = container[0].scrollTop; - const bottom = top + meta.scroll.buffer + container[0].offsetHeight; + const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; meta.scroll.inProgress = false; @@ -353,10 +348,10 @@ function onScroll () { vm.menu.scroll.display = true; - if (bottom >= meta.scroll.height && meta.next) { + if (bottom >= container[0].scrollHeight && meta.next) { next(); } - }, 500); + }, 250); } JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 630d84a442..b29adebb48 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -34,10 +34,12 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { + pageCache: true, + pageLimit: 2, params: { - page_size: 13, + page_size: 25, order_by: 'start_line' - } + }, })); }] } diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 20746ed8aa..ac0f44081f 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -107,6 +107,8 @@ function httpGet (config = {}) { if (config.params.page_size) { this.pageSize = config.params.page_size; + this.pageLimit = config.pageLimit || false; + this.pageCache = config.pageCache || false; } } @@ -336,6 +338,8 @@ function extend (related, config) { if (config.params.page_size) { this.pageSize = config.params.page_size; + this.pageLimit = config.pageLimit || false; + this.pageCache = config.pageCache || false; } if (this.has(req.method, `related.${related}`)) { @@ -377,12 +381,19 @@ function next (config = {}) { return $http(req) .then(({ data }) => { - results = results || []; + let cursor = 0; - data.results = results.concat(data.results); + if (this.pageCache) { + results = results || []; + data.results = results.concat(data.results); + cursor = results.length; - if ((config.limit * this.pageSize) < data.results.length) { - data.results.splice(-config.limit * this.pageSize); + if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { + const removeCount = data.results.length - this.pageLimit * this.pageSize; + + data.results.splice(0, removeCount); + cursor -= removeCount; + } } if (config.related) { @@ -390,31 +401,8 @@ function next (config = {}) { } else { this.set('get', data); } - }); -} -function prev (related, config = {}) { - const url = this.get(`related.${related}.previous`); - - if (!url) { - return Promise.resolve(null); - } - - const req = { - method: 'GET', - url - }; - - return $http(req) - .then(({ data }) => { - const results = this.get(`related.${related}.results`) || []; - - data.results = data.results.concat(results); - this.set('get', `related.${related}`, data); - - if (config.limit < results.length) { - console.log(results); - } + return cursor <= 0 ? 0 : cursor; }); } @@ -599,7 +587,6 @@ function BaseModel (resource, settings) { this.normalizePath = normalizePath; this.options = options; this.parseRequestConfig = parseRequestConfig; - this.prev = prev; this.request = request; this.requestWithCache = requestWithCache; this.search = search; From fa59f46f2bae3a6306ffd64792985be237bb223c Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 15 Jan 2018 09:36:59 -0500 Subject: [PATCH 108/379] Update less variable names --- awx/ui/client/features/jobs/_index.less | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 66725e86eb..356ad91076 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -2,15 +2,15 @@ font-family: monospace; &-menuTop { - color: @at-gray-dark-4x; - border: 1px solid @at-gray-dark-2x; + color: @at-gray-848992; + border: 1px solid @at-gray-f2; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: none; } &-menuBottom { - color: @at-gray-dark-4x; + color: @at-gray-848992; font-size: 10px; text-transform: uppercase; font-weight: bold; @@ -42,8 +42,8 @@ } &-toggle { - color: @at-gray-dark-4x; - background-color: @at-gray-light; + color: @at-gray-848992; + background-color: @at-gray-eb; font-size: 18px; line-height: 12px; @@ -56,12 +56,12 @@ } &-line { - color: @at-gray-dark-6x; - background-color: @at-gray-light; + color: @at-gray-161b1f; + background-color: @at-gray-eb; text-align: right; vertical-align: top; padding-right: 5px; - border-right: 1px solid @at-gray-dark-2x; + border-right: 1px solid @at-gray-b7; user-select: none; } @@ -93,9 +93,9 @@ height: calc(~"100vh - 240px"); overflow-y: scroll; font-size: 15px; - border: 1px solid @at-gray-dark-2x; - background-color: @at-gray-light-2x; - color: @at-gray-dark-6x; + border: 1px solid @at-gray-b7; + background-color: @at-gray-f2; + color: @at-gray-161b1f; padding: 0; margin: 0; border-radius: 0; From ab8651eab645223ef840ce6dd48597586df681ec Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 17 Jan 2018 15:32:06 -0500 Subject: [PATCH 109/379] Add functions to calc number of rows in view --- awx/ui/client/features/jobs/_index.less | 2 +- .../features/output/index.controller.js | 63 +++++++++++++++++-- awx/ui/client/features/output/index.js | 2 +- awx/ui/client/features/output/index.view.html | 2 +- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 356ad91076..750491edff 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -3,7 +3,7 @@ &-menuTop { color: @at-gray-848992; - border: 1px solid @at-gray-f2; + border: 1px solid @at-gray-b7; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: none; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 380aed1a95..8b1f2b84f4 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -16,10 +16,14 @@ const meta = { scroll: {} }; +const ROW_LIMIT = 200; const SCROLL_BUFFER = 250; +const SCROLL_LOAD_DELAY = 100; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; +const ELEMENT_TBODY = '#atStdoutResultTable'; +const ELEMENT_CONTAINER = '.at-Stdout-container'; const EVENT_GROUPS = [ EVENT_START_TASK, @@ -70,8 +74,8 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ }; $timeout(() => { - const table = $('#result-table'); - container = $('.at-Stdout-container'); + const table = $(ELEMENT_TBODY); + container = $(ELEMENT_CONTAINER); table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); @@ -90,14 +94,65 @@ function next () { meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); + console.log(ROW_LIMIT); + console.log(getRowCount()); + console.log(getRowHeight()); + console.log(getRowsInView()); + console.log(getScrollPosition()); + + console.log('above:', getRowsAbove()); + console.log('below:', getRowsBelow()); append(cursor); }); } +function getRowCount () { + return $(ELEMENT_TBODY).children().length; +} + +function getRowHeight () { + return $(ELEMENT_TBODY).children()[0].offsetHeight; +} + +function getViewHeight () { + return $(ELEMENT_CONTAINER)[0].offsetHeight; +} + +function getScrollPosition () { + return $(ELEMENT_CONTAINER)[0].scrollTop; +} + +function getScrollHeight () { + return $(ELEMENT_CONTAINER)[0].scrollHeight; +} + +function getRowsAbove () { + const top = getScrollPosition(); + + if (top === 0) { + return 0; + } + + return Math.floor(top / getRowHeight()); +} + +function getRowsBelow () { + const bottom = getScrollPosition() + getViewHeight(); + + return Math.floor((getScrollHeight() - bottom) / getRowHeight()); +} + +function getRowsInView () { + const rowHeight = getRowHeight(); + const viewHeight = getViewHeight(); + + return Math.floor(viewHeight / rowHeight); +} + function append (cursor) { const events = job.get('related.job_events.results').slice(cursor); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); - const table = $('#result-table'); + const table = $(ELEMENT_TBODY); table.append(rows); $compile(rows.contents())($scope); @@ -351,7 +406,7 @@ function onScroll () { if (bottom >= container[0].scrollHeight && meta.next) { next(); } - }, 250); + }, SCROLL_LOAD_DELAY); } JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index b29adebb48..c3049bdfb7 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -37,7 +37,7 @@ function JobsRun ($stateExtender, strings) { pageCache: true, pageLimit: 2, params: { - page_size: 25, + page_size: 1000, order_by: 'start_line' }, })); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index e45303ccec..2ebf9f3285 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -20,7 +20,7 @@
-
 
+
 
From 4b81d8d494db499de7e2a2509725b25fcf212b02 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 19 Jan 2018 15:07:25 -0500 Subject: [PATCH 110/379] Add WIP implementation of pagination with cache --- .../features/output/index.controller.js | 38 +++- awx/ui/client/lib/models/Base.js | 182 ++++++++++++++---- 2 files changed, 181 insertions(+), 39 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 8b1f2b84f4..2b0919ea4b 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -89,20 +89,36 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ } function next () { - job.next({ related: 'job_events' }) + const config = { + related: 'job_events', + params: { + order_by: 'start_line' + } + }; + + job.next(config) .then(cursor => { meta.next = job.get('related.job_events.next'); meta.prev = job.get('related.job_events.previous'); console.log(ROW_LIMIT); console.log(getRowCount()); - console.log(getRowHeight()); console.log(getRowsInView()); console.log(getScrollPosition()); console.log('above:', getRowsAbove()); console.log('below:', getRowsBelow()); append(cursor); + shift(); + + debugger; + }); +} + +function prev () { + job.prev({ related: 'job_events' }) + .then(cursor => { + console.log(cursor); }); } @@ -158,6 +174,22 @@ function append (cursor) { $compile(rows.contents())($scope); } +/* + *function prepend (cursor) { + * + *} + * + */ +function shift () { + const count = getRowCount() - ROW_LIMIT; + const rows = $(ELEMENT_TBODY).children().slice(0, count); + + console.log(count, rows); + + rows.empty(); + rows.remove(); +} + function expand () { vm.toggle(meta.parent, true); } @@ -398,6 +430,8 @@ function onScroll () { if (top === 0) { vm.menu.scroll.display = false; + prev(); + return; } diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index ac0f44081f..def0deff11 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -106,9 +106,10 @@ function httpGet (config = {}) { req.params = config.params; if (config.params.page_size) { - this.pageSize = config.params.page_size; - this.pageLimit = config.pageLimit || false; - this.pageCache = config.pageCache || false; + this.page.size = config.params.page_size; + this.page.limit = config.pageLimit || false; + this.page.cache = config.pageCache || false; + this.page.current = 1; } } @@ -120,6 +121,7 @@ function httpGet (config = {}) { req.url = `${this.path}${config.resource}/`; } + console.log(req, this.path); return $http(req) .then(res => { this.model.GET = res.data; @@ -337,9 +339,11 @@ function extend (related, config) { const req = this.parseRequestConfig('GET', config); if (config.params.page_size) { - this.pageSize = config.params.page_size; - this.pageLimit = config.pageLimit || false; - this.pageCache = config.pageCache || false; + this.page.size = config.params.page_size; + this.page.limit = config.pageLimit || false; + this.page.cache = config.pageCache || false; + this.page.current = 1; + this.page.cursor = 0; } if (this.has(req.method, `related.${related}`)) { @@ -358,54 +362,146 @@ function extend (related, config) { return Promise.reject(new Error(`No related property, ${related}, exists`)); } -function next (config = {}) { +function goToPage (config, page) { + const params = config.params || {}; + let url; - let results; + // let results; + let cursor; + let pageNumber; if (config.related) { - url = this.get(`related.${config.related}.next`); - results = this.get(`related.${config.related}.results`) || []; + // results = this.get(`related.${config.related}.results`) || []; + url = `${this.endpoint}${config.related}/`; } else { - url = this.get('next'); - results = this.get('results'); + // results = this.get('results'); + url = this.endpoint; } - if (!url) { - return Promise.resolve(null); + params.page_size = this.page.size; + + if (page === 'next') { + // if (at max) + + pageNumber = this.page.current + 1; + cursor = this.page.cursor + this.page.size; + } else if (page === 'prev') { + // if (at min) + + pageNumber = this.page.current - 1; + cursor = this.page.cursor - this.page.size; + } else { + pageNumber = page; + cursor = this.page.size * (pageNumber - 1); } + if (cursor !== 0 && cursor !== (this.page.limit * this.page.size)) { + this.page.cursor = cursor; + this.page.current = pageNumber; + + return Promise.resolve(cursor); + } + + params.page_size = this.page.size; + params.page = this.page.current; + const req = { method: 'GET', - url + url, + params }; return $http(req) .then(({ data }) => { - let cursor = 0; - - if (this.pageCache) { - results = results || []; - data.results = results.concat(data.results); - cursor = results.length; - - if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { - const removeCount = data.results.length - this.pageLimit * this.pageSize; - - data.results.splice(0, removeCount); - cursor -= removeCount; - } - } - - if (config.related) { - this.set('get', `related.${config.related}`, data); - } else { - this.set('get', data); - } - - return cursor <= 0 ? 0 : cursor; + console.log(data); }); } +function next (config = {}) { + return this.goToPage(config, 'next'); +/* + * .then(({ data }) => { + * let cursor = 0; + * + * if (this.pageCache) { + * results = results || []; + * data.results = results.concat(data.results); + * cursor = results.length; + * + * if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { + * const removeCount = data.results.length - this.pageLimit * this.pageSize; + * + * data.results.splice(0, removeCount); + * cursor -= removeCount; + * } + * } + * + * if (config.related) { + * this.set('get', `related.${config.related}`, data); + * } else { + * this.set('get', data); + * } + * + * this.page.current++; + * + * return cursor <= 0 ? 0 : cursor; + * }); + */ +} + +function prev (config = {}) { + return this.goToPage(config, 'next'); +} +/* + * let url; + * let results; + * + * console.log(config, config.cursor) + * if (config.related) { + * url = this.get(`related.${config.related}.next`); + * results = this.get(`related.${config.related}.results`) || []; + * } else { + * url = this.get('next'); + * results = this.get('results'); + * } + * + * if (!url) { + * return Promise.resolve(null); + * } + * + * const req = { + * method: 'GET', + * url + * }; + * + * return $http(req) + * .then(({ data }) => { + * let cursor = 0; + * + * if (this.pageCache) { + * results = results || []; + * data.results = results.concat(data.results); + * cursor = results.length; + * + * if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { + * const removeCount = data.results.length - this.pageLimit * this.pageSize; + * + * data.results.splice(0, removeCount); + * cursor -= removeCount; + * } + * } + * + * if (config.related) { + * this.set('get', `related.${config.related}`, data); + * } else { + * this.set('get', data); + * } + * + * return cursor <= 0 ? 0 : cursor; + * }); + *} + */ + function normalizePath (resource) { const version = '/api/v2/'; @@ -515,6 +611,10 @@ function create (method, resource, config) { return this; } + if (req.resource) { + this.setEndpoint(req.resource); + } + this.promise = this.request(req); if (req.graft) { @@ -525,6 +625,10 @@ function create (method, resource, config) { .then(() => this); } +function setEndpoint (resource) { + this.endpoint = `${this.path}${resource}/`; +} + function parseRequestConfig (method, resource, config) { if (!method) { return null; @@ -577,6 +681,7 @@ function BaseModel (resource, settings) { this.create = create; this.find = find; this.get = get; + this.goToPage = goToPage; this.graft = graft; this.has = has; this.isEditable = isEditable; @@ -587,10 +692,12 @@ function BaseModel (resource, settings) { this.normalizePath = normalizePath; this.options = options; this.parseRequestConfig = parseRequestConfig; + this.prev = prev; this.request = request; this.requestWithCache = requestWithCache; this.search = search; this.set = set; + this.setEndpoint = setEndpoint; this.unset = unset; this.extend = extend; this.copy = copy; @@ -605,6 +712,7 @@ function BaseModel (resource, settings) { delete: httpDelete.bind(this) }; + this.page = {}; this.model = {}; this.path = this.normalizePath(resource); this.label = strings.get(`${resource}.LABEL`); From 745e547e346146364a34343004f7fdebfd39d2c9 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 22 Jan 2018 15:01:11 -0500 Subject: [PATCH 111/379] Add nested page cache --- .../features/output/index.controller.js | 2 - awx/ui/client/lib/models/Base.js | 71 ++++++++++++------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 2b0919ea4b..dcfabe4d7a 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -110,8 +110,6 @@ function next () { console.log('below:', getRowsBelow()); append(cursor); shift(); - - debugger; }); } diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index def0deff11..77400855ff 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -107,9 +107,15 @@ function httpGet (config = {}) { if (config.params.page_size) { this.page.size = config.params.page_size; - this.page.limit = config.pageLimit || false; - this.page.cache = config.pageCache || false; this.page.current = 1; + + if (config.pageCache) { + this.page.cache = { + root: {} + }; + + this.page.limit = config.pageLimit || false; + } } } @@ -121,11 +127,14 @@ function httpGet (config = {}) { req.url = `${this.path}${config.resource}/`; } - console.log(req, this.path); return $http(req) .then(res => { this.model.GET = res.data; + if (config.pageCache) { + this.page.cache[this.page.current] = res.data.results; + } + return res; }); } @@ -340,10 +349,12 @@ function extend (related, config) { if (config.params.page_size) { this.page.size = config.params.page_size; - this.page.limit = config.pageLimit || false; - this.page.cache = config.pageCache || false; this.page.current = 1; - this.page.cursor = 0; + + if (config.pageCache) { + this.page.cache = this.page.cache || {}; + this.page.limit = config.pageLimit || false; + } } if (this.has(req.method, `related.${related}`)) { @@ -355,6 +366,12 @@ function extend (related, config) { .then(({ data }) => { this.set(req.method, `related.${related}`, data); + if (config.pageCache) { + const key = `related.${related}.${this.page.current}`; + + _.set(this.page.cache, key, data.results); + } + return this; }); } @@ -366,40 +383,42 @@ function goToPage (config, page) { const params = config.params || {}; let url; - // let results; - let cursor; + let count; + let key; let pageNumber; if (config.related) { - // results = this.get(`related.${config.related}.results`) || []; + count = this.get(`related.${config.related}.count`); url = `${this.endpoint}${config.related}/`; + key = `related.${config.related}`; } else { - // results = this.get('results'); + count = this.get('count'); url = this.endpoint; + key = 'root'; } params.page_size = this.page.size; if (page === 'next') { // if (at max) - pageNumber = this.page.current + 1; - cursor = this.page.cursor + this.page.size; - } else if (page === 'prev') { + } else if (page === 'previous') { + // if (at min) + pageNumber = this.page.current - 1; + } else if (page === 'first') { // if (at min) - pageNumber = this.page.current - 1; - cursor = this.page.cursor - this.page.size; - } else { + pageNumber = 1; + } else if (page === 'last') { + // if (at min) + + pageNumber = Math.floor(count / this.page.size); + } else if (typeof pageNumber === 'number') { pageNumber = page; - cursor = this.page.size * (pageNumber - 1); } - if (cursor !== 0 && cursor !== (this.page.limit * this.page.size)) { - this.page.cursor = cursor; - this.page.current = pageNumber; - - return Promise.resolve(cursor); + if (this.page.cache && this.page.cache[pageNumber]) { + return Promise.resolve(this.page.cache[pageNumber]); } params.page_size = this.page.size; @@ -413,7 +432,11 @@ function goToPage (config, page) { return $http(req) .then(({ data }) => { - console.log(data); + if (this.page.cache) { + _.set(this.page.cache, `${key}.${pageNumber}`, data.results); + } + + console.log('cache', this.page.cache); }); } @@ -450,7 +473,7 @@ function next (config = {}) { } function prev (config = {}) { - return this.goToPage(config, 'next'); + return this.goToPage(config, 'prev'); } /* * let url; From cc36ee6bedd03fac1814b1fbbc361d905b37817c Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 23 Jan 2018 17:05:59 -0500 Subject: [PATCH 112/379] Add WIP prepend/previous on scroll --- .../features/output/index.controller.js | 161 +++++++++++++----- awx/ui/client/features/output/index.js | 4 +- awx/ui/client/lib/models/Base.js | 159 ++++++----------- 3 files changed, 172 insertions(+), 152 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index dcfabe4d7a..3fa2686c08 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -10,6 +10,7 @@ let $timeout; let $sce; let $compile; let $scope; +let $q; const record = {}; const meta = { @@ -18,7 +19,7 @@ const meta = { const ROW_LIMIT = 200; const SCROLL_BUFFER = 250; -const SCROLL_LOAD_DELAY = 100; +// const SCROLL_LOAD_DELAY = 100; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -36,11 +37,20 @@ const TIME_EVENTS = [ EVENT_STATS_PLAY ]; -function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_, _$compile_) { +function JobsIndexController ( + _job_, + JobEventModel, + _$sce_, + _$timeout_, + _$scope_, + _$compile_, + _$q_ +) { $timeout = _$timeout_; $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; + $q = _$q_; job = _job_; ansi = new Ansi(); @@ -80,10 +90,6 @@ function JobsIndexController (_job_, JobEventModel, _$sce_, _$timeout_, _$scope_ table.html($sce.getTrustedHtml(html)); $compile(table.contents())($scope); - meta.next = job.get('related.job_events.next'); - meta.prev = job.get('related.job_events.previous'); - meta.cursor = job.get('related.job_events.results').length; - container.scroll(onScroll); }); } @@ -96,10 +102,13 @@ function next () { } }; - job.next(config) - .then(cursor => { - meta.next = job.get('related.job_events.next'); - meta.prev = job.get('related.job_events.previous'); + console.log('[2] getting next page'); + return job.next(config) + .then(events => { + console.log(events); + if (!events) { + return $q.resolve(); + } console.log(ROW_LIMIT); console.log(getRowCount()); @@ -108,15 +117,21 @@ function next () { console.log('above:', getRowsAbove()); console.log('below:', getRowsBelow()); - append(cursor); - shift(); + return shift() + .then(() => append(events)); }); } function prev () { - job.prev({ related: 'job_events' }) - .then(cursor => { - console.log(cursor); + console.log('[2] getting previous page'); + return job.prev({ related: 'job_events' }) + .then(events => { + if (!events) { + return $q.resolve(); + } + + return pop() + .then(() => prepend(events)); }); } @@ -163,29 +178,66 @@ function getRowsInView () { return Math.floor(viewHeight / rowHeight); } -function append (cursor) { - const events = job.get('related.job_events.results').slice(cursor); - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); - const table = $(ELEMENT_TBODY); +function append (events) { + console.log('[4] appending next page'); - table.append(rows); - $compile(rows.contents())($scope); + return $q(resolve => { + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); + const table = $(ELEMENT_TBODY); + + table.append(rows); + $compile(rows.contents())($scope); + + $timeout(() => { + resolve(); + }); + }); +} + +function prepend (events) { + console.log('[4] prepending next page'); + + return $q(resolve => { + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); + const table = $(ELEMENT_TBODY); + + table.prepend(rows); + $compile(rows.contents())($scope); + + $timeout(() => { + resolve(); + }); + }); +} + +function pop () { + console.log('[3] popping old page'); + return $q(resolve => { + const count = getRowCount() - ROW_LIMIT; + const rows = $(ELEMENT_TBODY).children().slice(-count); + + rows.empty(); + rows.remove(); + + $timeout(() => { + resolve(); + }); + }); } -/* - *function prepend (cursor) { - * - *} - * - */ function shift () { - const count = getRowCount() - ROW_LIMIT; - const rows = $(ELEMENT_TBODY).children().slice(0, count); + console.log('[3] shifting old page'); + return $q(resolve => { + const count = getRowCount() - ROW_LIMIT; + const rows = $(ELEMENT_TBODY).children().slice(0, count); - console.log(count, rows); + rows.empty(); + rows.remove(); - rows.empty(); - rows.remove(); + $timeout(() => { + resolve(); + }); + }); } function expand () { @@ -419,28 +471,43 @@ function onScroll () { meta.scroll.inProgress = true; - $timeout(() => { - const top = container[0].scrollTop; - const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; + const top = container[0].scrollTop; + const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; - meta.scroll.inProgress = false; + if (top === 0) { + console.log('[1] scroll to top'); + vm.menu.scroll.display = false; - if (top === 0) { - vm.menu.scroll.display = false; + prev() + .then(() => { + console.log('[5] scroll reset'); + meta.scroll.inProgress = false; + }); - prev(); + return; + } - return; - } + vm.menu.scroll.display = true; - vm.menu.scroll.display = true; + if (bottom >= container[0].scrollHeight) { + console.log('[1] scroll to bottom'); - if (bottom >= container[0].scrollHeight && meta.next) { - next(); - } - }, SCROLL_LOAD_DELAY); + next() + .then(() => { + console.log('[5] scroll reset'); + meta.scroll.inProgress = false; + }); + } } -JobsIndexController.$inject = ['job', 'JobEventModel', '$sce', '$timeout', '$scope', '$compile']; +JobsIndexController.$inject = [ + 'job', + 'JobEventModel', + '$sce', + '$timeout', + '$scope', + '$compile', + '$q' +]; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index c3049bdfb7..1e8fc4e23e 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -35,9 +35,9 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { pageCache: true, - pageLimit: 2, + pageLimit: 4, params: { - page_size: 1000, + page_size: 100, order_by: 'start_line' }, })); diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 77400855ff..e9723655f4 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -110,11 +110,17 @@ function httpGet (config = {}) { this.page.current = 1; if (config.pageCache) { - this.page.cache = { - root: {} - }; - + this.page.cachedPages = this.page.cachedPages || {}; + this.page.cache = this.page.cache || {}; this.page.limit = config.pageLimit || false; + + if (!_.has(this.page.cachedPages, 'root')) { + this.page.cachedPages.root = []; + } + + if (!_.has(this.page.cache, 'root')) { + this.page.cache.root = {}; + } } } } @@ -132,7 +138,10 @@ function httpGet (config = {}) { this.model.GET = res.data; if (config.pageCache) { - this.page.cache[this.page.current] = res.data.results; + this.page.cache.root[this.page.current] = res.data.results; + this.page.cachedPages.root.push(this.page.current); + this.page.count = res.data.count; + this.page.last = Math.ceil(res.data.count / this.page.size); } return res; @@ -352,8 +361,17 @@ function extend (related, config) { this.page.current = 1; if (config.pageCache) { + this.page.cachedPages = this.page.cachedPages || {}; this.page.cache = this.page.cache || {}; this.page.limit = config.pageLimit || false; + + if (!_.has(this.page.cachedPages, `related.${related}`)) { + _.set(this.page.cachedPages, `related.${related}`, []); + } + + if (!_.has(this.page.cache, `related.${related}`)) { + _.set(this.page.cache, `related.${related}`, []); + } } } @@ -367,9 +385,10 @@ function extend (related, config) { this.set(req.method, `related.${related}`, data); if (config.pageCache) { - const key = `related.${related}.${this.page.current}`; - - _.set(this.page.cache, key, data.results); + this.page.cache.related[related][this.page.current] = data.results; + this.page.cachedPages.related[related].push(this.page.current); + this.page.count = data.count; + this.page.last = Math.ceil(data.count / this.page.size); } return this; @@ -383,16 +402,15 @@ function goToPage (config, page) { const params = config.params || {}; let url; - let count; let key; let pageNumber; + let pageCache; + let pagesInCache; if (config.related) { - count = this.get(`related.${config.related}.count`); url = `${this.endpoint}${config.related}/`; key = `related.${config.related}`; } else { - count = this.get('count'); url = this.endpoint; key = 'root'; } @@ -400,29 +418,34 @@ function goToPage (config, page) { params.page_size = this.page.size; if (page === 'next') { - // if (at max) pageNumber = this.page.current + 1; } else if (page === 'previous') { - // if (at min) pageNumber = this.page.current - 1; } else if (page === 'first') { - // if (at min) - pageNumber = 1; } else if (page === 'last') { - // if (at min) - - pageNumber = Math.floor(count / this.page.size); - } else if (typeof pageNumber === 'number') { + pageNumber = this.page.last; + } else { pageNumber = page; } - if (this.page.cache && this.page.cache[pageNumber]) { - return Promise.resolve(this.page.cache[pageNumber]); + this.page.current = pageNumber; + + if (pageNumber < 1 || pageNumber > this.page.last) { + return Promise.resolve(null); + } + + if (this.page.cache) { + pageCache = _.get(this.page.cache, key); + pagesInCache = _.get(this.page.cachedPages, key); + + if (_.has(pageCache, pageNumber)) { + return Promise.resolve(pageCache[pageNumber]); + } } params.page_size = this.page.size; - params.page = this.page.current; + params.page = pageNumber; const req = { method: 'GET', @@ -432,98 +455,28 @@ function goToPage (config, page) { return $http(req) .then(({ data }) => { - if (this.page.cache) { - _.set(this.page.cache, `${key}.${pageNumber}`, data.results); + if (pageCache) { + pageCache[pageNumber] = data.results; + pagesInCache.push(pageNumber); + + if (pagesInCache.length > this.page.limit) { + const pageToDelete = pagesInCache.shift(); + + delete pageCache[pageToDelete]; + } } - console.log('cache', this.page.cache); + return data.results; }); } function next (config = {}) { return this.goToPage(config, 'next'); -/* - * .then(({ data }) => { - * let cursor = 0; - * - * if (this.pageCache) { - * results = results || []; - * data.results = results.concat(data.results); - * cursor = results.length; - * - * if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { - * const removeCount = data.results.length - this.pageLimit * this.pageSize; - * - * data.results.splice(0, removeCount); - * cursor -= removeCount; - * } - * } - * - * if (config.related) { - * this.set('get', `related.${config.related}`, data); - * } else { - * this.set('get', data); - * } - * - * this.page.current++; - * - * return cursor <= 0 ? 0 : cursor; - * }); - */ } function prev (config = {}) { - return this.goToPage(config, 'prev'); + return this.goToPage(config, 'previous'); } -/* - * let url; - * let results; - * - * console.log(config, config.cursor) - * if (config.related) { - * url = this.get(`related.${config.related}.next`); - * results = this.get(`related.${config.related}.results`) || []; - * } else { - * url = this.get('next'); - * results = this.get('results'); - * } - * - * if (!url) { - * return Promise.resolve(null); - * } - * - * const req = { - * method: 'GET', - * url - * }; - * - * return $http(req) - * .then(({ data }) => { - * let cursor = 0; - * - * if (this.pageCache) { - * results = results || []; - * data.results = results.concat(data.results); - * cursor = results.length; - * - * if (this.pageLimit && this.pageLimit * this.pageSize < data.results.length) { - * const removeCount = data.results.length - this.pageLimit * this.pageSize; - * - * data.results.splice(0, removeCount); - * cursor -= removeCount; - * } - * } - * - * if (config.related) { - * this.set('get', `related.${config.related}`, data); - * } else { - * this.set('get', data); - * } - * - * return cursor <= 0 ? 0 : cursor; - * }); - *} - */ function normalizePath (resource) { const version = '/api/v2/'; From e5187e4ac8bd1567111d25e47d37c22691e34a10 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 24 Jan 2018 15:52:31 -0500 Subject: [PATCH 113/379] Adjust pagination/scrolling --- awx/ui/build/webpack.watch.js | 22 +-- .../features/output/index.controller.js | 139 +++++++++++------- awx/ui/client/features/output/index.js | 2 +- awx/ui/client/lib/models/Base.js | 28 +++- 4 files changed, 123 insertions(+), 68 deletions(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index 0ae7d77ef5..a68c2eee25 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -20,16 +20,18 @@ const watch = { output: { filename: OUTPUT }, - module: { - rules: [ - { - test: /\.js$/, - enforce: 'pre', - exclude: /node_modules/, - loader: 'eslint-loader' - } - ] - }, + /* + *module: { + * rules: [ + * { + * test: /\.js$/, + * enforce: 'pre', + * exclude: /node_modules/, + * loader: 'eslint-loader' + * } + * ] + *}, + */ plugins: [ new HtmlWebpackHarddiskPlugin(), new HardSourceWebpackPlugin({ diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 3fa2686c08..9021108a30 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -14,12 +14,14 @@ let $q; const record = {}; const meta = { - scroll: {} + scroll: {}, + page: {} }; const ROW_LIMIT = 200; +const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; -// const SCROLL_LOAD_DELAY = 100; +const SCROLL_LOAD_DELAY = 250; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -83,6 +85,11 @@ function JobsIndexController ( } }; + meta.page.cache = [{ + page: 1, + count: events.length + }]; + $timeout(() => { const table = $(ELEMENT_TBODY); container = $(ELEMENT_CONTAINER); @@ -97,41 +104,54 @@ function JobsIndexController ( function next () { const config = { related: 'job_events', + page: meta.page.cache[meta.page.cache.length - 1].page + 1, params: { order_by: 'start_line' } }; - console.log('[2] getting next page'); - return job.next(config) - .then(events => { - console.log(events); - if (!events) { + console.log('[2] getting next page', config.page, meta.page.cache); + return job.goToPage(config) + .then(data => { + if (!data || !data.results) { return $q.resolve(); } - console.log(ROW_LIMIT); - console.log(getRowCount()); - console.log(getRowsInView()); - console.log(getScrollPosition()); + meta.page.cache.push({ + page: data.page, + count: data.results.length + }); - console.log('above:', getRowsAbove()); - console.log('below:', getRowsBelow()); + console.log(data.results); return shift() - .then(() => append(events)); + .then(() => append(data.results)); }); } function prev () { - console.log('[2] getting previous page'); - return job.prev({ related: 'job_events' }) - .then(events => { - if (!events) { + const config = { + related: 'job_events', + page: meta.page.cache[0].page - 1, + params: { + order_by: 'start_line' + } + }; + + console.log('[2] getting previous page', config.page, meta.page.cache); + + return job.goToPage(config) + .then(data => { + if (!data || !data.results) { return $q.resolve(); } + meta.page.cache.unshift({ + page: data.page, + count: data.results.length + }); + return pop() - .then(() => prepend(events)); + .then(() => prepend(data.results)); }); } @@ -187,7 +207,6 @@ function append (events) { table.append(rows); $compile(rows.contents())($scope); - $timeout(() => { resolve(); }); @@ -201,6 +220,10 @@ function prepend (events) { const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); const table = $(ELEMENT_TBODY); + // Set row count added + // pop number of rows (not number of events) + // meta.page.cache[0].rows = rows.length; + table.prepend(rows); $compile(rows.contents())($scope); @@ -213,14 +236,20 @@ function prepend (events) { function pop () { console.log('[3] popping old page'); return $q(resolve => { - const count = getRowCount() - ROW_LIMIT; - const rows = $(ELEMENT_TBODY).children().slice(-count); + if (meta.page.cache.length <= PAGE_LIMIT) { + console.log('[3.1] nothing to pop'); + return resolve(); + } + + const ejected = meta.page.cache.pop(); + console.log('[3.1] popping', ejected); + const rows = $(ELEMENT_TBODY).children().slice(-ejected.count); rows.empty(); rows.remove(); $timeout(() => { - resolve(); + return resolve(); }); }); } @@ -228,14 +257,20 @@ function pop () { function shift () { console.log('[3] shifting old page'); return $q(resolve => { - const count = getRowCount() - ROW_LIMIT; - const rows = $(ELEMENT_TBODY).children().slice(0, count); + if (meta.page.cache.length <= PAGE_LIMIT) { + console.log('[3.1] nothing to shift'); + return resolve(); + } + + const ejected = meta.page.cache.shift(); + console.log('[3.1] shifting', ejected); + const rows = $(ELEMENT_TBODY).children().slice(0, ejected.count); rows.empty(); rows.remove(); $timeout(() => { - resolve(); + return resolve(); }); }); } @@ -471,33 +506,37 @@ function onScroll () { meta.scroll.inProgress = true; - const top = container[0].scrollTop; - const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; + $timeout(() => { + const top = container[0].scrollTop; + const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; - if (top === 0) { - console.log('[1] scroll to top'); - vm.menu.scroll.display = false; + if (top <= SCROLL_BUFFER) { + console.log('[1] scroll to top'); + vm.menu.scroll.display = false; - prev() - .then(() => { - console.log('[5] scroll reset'); + prev() + .then(() => { + console.log('[5] scroll reset'); + meta.scroll.inProgress = false; + }); + + return; + } else { + vm.menu.scroll.display = true; + + if (bottom >= container[0].scrollHeight) { + console.log('[1] scroll to bottom'); + + next() + .then(() => { + console.log('[5] scroll reset'); + meta.scroll.inProgress = false; + }); + } else { meta.scroll.inProgress = false; - }); - - return; - } - - vm.menu.scroll.display = true; - - if (bottom >= container[0].scrollHeight) { - console.log('[1] scroll to bottom'); - - next() - .then(() => { - console.log('[5] scroll reset'); - meta.scroll.inProgress = false; - }); - } + } + } + }, SCROLL_LOAD_DELAY); } JobsIndexController.$inject = [ diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 1e8fc4e23e..e8daf39557 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -35,7 +35,7 @@ function JobsRun ($stateExtender, strings) { return new Jobs('get', id) .then(job => job.extend('job_events', { pageCache: true, - pageLimit: 4, + pageLimit: 3, params: { page_size: 100, order_by: 'start_line' diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index e9723655f4..0c04964a5f 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -398,8 +398,9 @@ function extend (related, config) { return Promise.reject(new Error(`No related property, ${related}, exists`)); } -function goToPage (config, page) { +function goToPage (config) { const params = config.params || {}; + const page = config.page; let url; let key; @@ -429,18 +430,22 @@ function goToPage (config, page) { pageNumber = page; } - this.page.current = pageNumber; - + console.log('pageNumber', page, pageNumber); if (pageNumber < 1 || pageNumber > this.page.last) { return Promise.resolve(null); } + this.page.current = pageNumber; + if (this.page.cache) { pageCache = _.get(this.page.cache, key); pagesInCache = _.get(this.page.cachedPages, key); if (_.has(pageCache, pageNumber)) { - return Promise.resolve(pageCache[pageNumber]); + return Promise.resolve({ + results: pageCache[pageNumber], + page: pageNumber + }); } } @@ -455,6 +460,8 @@ function goToPage (config, page) { return $http(req) .then(({ data }) => { + let ejected; + if (pageCache) { pageCache[pageNumber] = data.results; pagesInCache.push(pageNumber); @@ -466,16 +473,23 @@ function goToPage (config, page) { } } - return data.results; + return { + results: data.results, + page: pageNumber + } }); } function next (config = {}) { - return this.goToPage(config, 'next'); + config.page = 'next'; + + return this.goToPage(config); } function prev (config = {}) { - return this.goToPage(config, 'previous'); + config.page = 'previous'; + + return this.goToPage(config); } function normalizePath (resource) { From 52a6cca20650e0ca0557df6b61926b31ad4e9091 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 1 Feb 2018 09:59:10 -0500 Subject: [PATCH 114/379] Add models for remaining event types --- .../features/output/index.controller.js | 58 ++++++++++------- awx/ui/client/features/output/index.js | 62 ++++++++++++++----- awx/ui/client/lib/models/AdHocCommand.js | 10 +-- awx/ui/client/lib/models/Base.js | 4 +- awx/ui/client/lib/models/ProjectUpdate.js | 19 ++++++ awx/ui/client/lib/models/SystemJob.js | 19 ++++++ awx/ui/client/lib/models/index.js | 7 ++- 7 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 awx/ui/client/lib/models/ProjectUpdate.js create mode 100644 awx/ui/client/lib/models/SystemJob.js diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 9021108a30..d826fdbcc2 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -4,7 +4,6 @@ import hasAnsi from 'has-ansi'; let vm; let ansi; let job; -let jobEvent; let container; let $timeout; let $sce; @@ -18,7 +17,6 @@ const meta = { page: {} }; -const ROW_LIMIT = 200; const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; const SCROLL_LOAD_DELAY = 250; @@ -41,7 +39,6 @@ const TIME_EVENTS = [ function JobsIndexController ( _job_, - JobEventModel, _$sce_, _$timeout_, _$scope_, @@ -56,10 +53,10 @@ function JobsIndexController ( job = _job_; ansi = new Ansi(); - jobEvent = new JobEventModel(); const events = job.get('related.job_events.results'); - const html = $sce.trustAsHtml(parseEvents(events)); + const parsed = parseEvents(events); + const html = $sce.trustAsHtml(parsed.html); vm = this || {}; @@ -87,7 +84,7 @@ function JobsIndexController ( meta.page.cache = [{ page: 1, - count: events.length + lines: parsed.lines }]; $timeout(() => { @@ -118,11 +115,9 @@ function next () { } meta.page.cache.push({ - page: data.page, - count: data.results.length + page: data.page }); - console.log(data.results); return shift() .then(() => append(data.results)); }); @@ -138,7 +133,6 @@ function prev () { }; console.log('[2] getting previous page', config.page, meta.page.cache); - return job.goToPage(config) .then(data => { if (!data || !data.results) { @@ -146,8 +140,7 @@ function prev () { } meta.page.cache.unshift({ - page: data.page, - count: data.results.length + page: data.page }); return pop() @@ -202,8 +195,12 @@ function append (events) { console.log('[4] appending next page'); return $q(resolve => { - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); + const parsed = parseEvents(events); + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); + const index = meta.page.cache.length - 1; + + meta.page.cache[index].lines = parsed.lines; table.append(rows); $compile(rows.contents())($scope); @@ -217,12 +214,11 @@ function prepend (events) { console.log('[4] prepending next page'); return $q(resolve => { - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parseEvents(events)))); + const parsed = parseEvents(events); + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); - // Set row count added - // pop number of rows (not number of events) - // meta.page.cache[0].rows = rows.length; + meta.page.cache[0].lines = parsed.lines; table.prepend(rows); $compile(rows.contents())($scope); @@ -243,7 +239,7 @@ function pop () { const ejected = meta.page.cache.pop(); console.log('[3.1] popping', ejected); - const rows = $(ELEMENT_TBODY).children().slice(-ejected.count); + const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); rows.empty(); rows.remove(); @@ -264,7 +260,7 @@ function shift () { const ejected = meta.page.cache.shift(); console.log('[3.1] shifting', ejected); - const rows = $(ELEMENT_TBODY).children().slice(0, ejected.count); + const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); rows.empty(); rows.remove(); @@ -288,9 +284,22 @@ function scrollTo (direction) { } function parseEvents (events) { + let lines = 0; + let html = ''; + events.sort(orderByLineNumber); - return events.reduce((html, event) => `${html}${parseLine(event)}`, ''); + events.forEach(event => { + const line = parseLine(event); + + html += line.html; + lines += line.count; + }); + + return { + html, + lines + }; } function orderByLineNumber (a, b) { @@ -307,17 +316,18 @@ function orderByLineNumber (a, b) { function parseLine (event) { if (!event || !event.stdout) { - return ''; + return { html: '', count: 0 }; } const { stdout } = event; const lines = stdout.split('\r\n'); + let count = lines.length; let ln = event.start_line; const current = createRecord(ln, lines, event); - return lines.reduce((html, line, i) => { + const html = lines.reduce((html, line, i) => { ln++; const isLastLine = i === lines.length - 1; @@ -325,10 +335,13 @@ function parseLine (event) { if (current && current.isTruncated && isLastLine) { row += createRow(current); + count++; } return `${html}${row}`; }, ''); + + return { html, count }; } function createRecord (ln, lines, event) { @@ -541,7 +554,6 @@ function onScroll () { JobsIndexController.$inject = [ 'job', - 'JobEventModel', '$sce', '$timeout', '$scope', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index e8daf39557..d447ed2817 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -10,10 +10,48 @@ const indexTemplate = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; +function resolveJob (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { + const { id } = $stateParams; + const { type } = $stateParams; + + let Resource; + + switch (type) { + case 'project': + Resource = ProjectUpdate; + break; + case 'playbook': + Resource = Job; + break; + case 'command': + Resource = AdHocCommand; + break; + case 'system': + Resource = SystemJob; + break; + case 'workflow': + Resource = WorkflowJob; + break; + default: + // Redirect + return null; + } + + return new Resource('get', id) + .then(resource => resource.extend('job_events', { + pageCache: true, + pageLimit: 3, + params: { + page_size: 100, + order_by: 'start_line' + } + })); +} + function JobsRun ($stateExtender, strings) { $stateExtender.addState({ name: 'jobz', - route: '/jobz/:id', + route: '/jobz/:type/:id', ncyBreadcrumb: { label: strings.get('state.TITLE') }, @@ -29,19 +67,15 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - job: ['JobModel', '$stateParams', (Jobs, $stateParams) => { - const { id } = $stateParams; - - return new Jobs('get', id) - .then(job => job.extend('job_events', { - pageCache: true, - pageLimit: 3, - params: { - page_size: 100, - order_by: 'start_line' - }, - })); - }] + job: [ + 'JobModel', + 'ProjectUpdateModel', + 'AdHocCommandModel', + 'SystemJobModel', + 'WorkflowJobModel', + '$stateParams', + resolveJob + ] } }); } diff --git a/awx/ui/client/lib/models/AdHocCommand.js b/awx/ui/client/lib/models/AdHocCommand.js index c398219531..9f259a929a 100644 --- a/awx/ui/client/lib/models/AdHocCommand.js +++ b/awx/ui/client/lib/models/AdHocCommand.js @@ -1,5 +1,5 @@ -let Base; let $http; +let BaseModel; function getRelaunch (params) { const req = { @@ -20,7 +20,7 @@ function postRelaunch (params) { } function AdHocCommandModel (method, resource, config) { - Base.call(this, 'ad_hoc_commands'); + BaseModel.call(this, 'ad_hoc_commands'); this.Constructor = AdHocCommandModel; this.postRelaunch = postRelaunch.bind(this); @@ -29,16 +29,16 @@ function AdHocCommandModel (method, resource, config) { return this.create(method, resource, config); } -function AdHocCommandModelLoader (BaseModel, _$http_) { - Base = BaseModel; +function AdHocCommandModelLoader (_$http_, _BaseModel_) { $http = _$http_; + BaseModel = _BaseModel_; return AdHocCommandModel; } AdHocCommandModelLoader.$inject = [ + '$http', 'BaseModel', - '$http' ]; export default AdHocCommandModelLoader; diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 0c04964a5f..9985de8799 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -460,8 +460,6 @@ function goToPage (config) { return $http(req) .then(({ data }) => { - let ejected; - if (pageCache) { pageCache[pageNumber] = data.results; pagesInCache.push(pageNumber); @@ -469,7 +467,9 @@ function goToPage (config) { if (pagesInCache.length > this.page.limit) { const pageToDelete = pagesInCache.shift(); + console.log(pageCache); delete pageCache[pageToDelete]; + console.log(this.page.cache); } } diff --git a/awx/ui/client/lib/models/ProjectUpdate.js b/awx/ui/client/lib/models/ProjectUpdate.js new file mode 100644 index 0000000000..3de76790df --- /dev/null +++ b/awx/ui/client/lib/models/ProjectUpdate.js @@ -0,0 +1,19 @@ +let BaseModel; + +function ProjectUpdateModel (method, resource, config) { + BaseModel.call(this, 'jobs'); + + this.Constructor = ProjectUpdateModel; + + return this.create(method, resource, config); +} + +function ProjectUpdateModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return ProjectUpdateModel; +} + +ProjectUpdateModelLoader.$inject = ['BaseModel']; + +export default ProjectUpdateModelLoader; diff --git a/awx/ui/client/lib/models/SystemJob.js b/awx/ui/client/lib/models/SystemJob.js new file mode 100644 index 0000000000..ec41941046 --- /dev/null +++ b/awx/ui/client/lib/models/SystemJob.js @@ -0,0 +1,19 @@ +let BaseModel; + +function SystemJobModel (method, resource, config) { + BaseModel.call(this, 'jobs'); + + this.Constructor = SystemJobModel; + + return this.create(method, resource, config); +} + +function SystemJobModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return SystemJobModel; +} + +SystemJobModelLoader.$inject = ['BaseModel']; + +export default SystemJobModelLoader; diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 91eb3742ee..3875975d4d 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -14,12 +14,13 @@ import InventorySource from '~models/InventorySource'; import Job from '~models/Job'; import JobEvent from '~models/JobEvent'; import JobTemplate from '~models/JobTemplate'; -import Jobs from '~models/Jobs'; import Me from '~models/Me'; import NotificationTemplate from '~models/NotificationTemplate'; import Organization from '~models/Organization'; import Project from '~models/Project'; import Schedule from '~models/Schedule'; +import ProjectUpdate from '~models/ProjectUpdate'; +import SystemJob from '~models/SystemJob'; import UnifiedJobTemplate from '~models/UnifiedJobTemplate'; import WorkflowJob from '~models/WorkflowJob'; import WorkflowJobTemplate from '~models/WorkflowJobTemplate'; @@ -48,18 +49,18 @@ angular .service('JobEventModel', JobEvent) .service('JobModel', Job) .service('JobTemplateModel', JobTemplate) - .service('JobsModel', Jobs) .service('MeModel', Me) .service('NotificationTemplate', NotificationTemplate) .service('OrganizationModel', Organization) .service('ProjectModel', Project) .service('ScheduleModel', Schedule) .service('UnifiedJobModel', UnifiedJob) + .service('ProjectUpdateModel', ProjectUpdate) + .service('SystemJobModel', SystemJob) .service('UnifiedJobTemplateModel', UnifiedJobTemplate) .service('WorkflowJobModel', WorkflowJob) .service('WorkflowJobTemplateModel', WorkflowJobTemplate) .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) - .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) .service('ModelsStrings', ModelsStrings); export default MODULE_NAME; From 41d3d29ae84a03541d2c866852261a8a0c120ceb Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 2 Feb 2018 10:43:43 -0500 Subject: [PATCH 115/379] Fix project update model --- .../client/features/output/index.controller.js | 16 ++++++++++++++++ awx/ui/client/features/output/index.js | 10 ++++++++-- awx/ui/client/lib/models/ProjectUpdate.js | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index d826fdbcc2..14d086ff4b 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -473,6 +473,22 @@ function getTime (created) { return `${hour}:${minute}:${second}`; } +function pageUp () { + +} + +function pageDown () { + +} + +function jumpToStart () { + +} + +function jumpToEnd () { + +} + function showHostDetails (id) { jobEvent.request('get', id) .then(() => { diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index d447ed2817..bb515e226b 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -15,13 +15,16 @@ function resolveJob (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $ const { type } = $stateParams; let Resource; + let related; switch (type) { case 'project': Resource = ProjectUpdate; + related = 'events'; break; case 'playbook': Resource = Job; + related = 'job_events'; break; case 'command': Resource = AdHocCommand; @@ -38,14 +41,17 @@ function resolveJob (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $ } return new Resource('get', id) - .then(resource => resource.extend('job_events', { + .then(resource => resource.extend(related, { pageCache: true, pageLimit: 3, params: { page_size: 100, order_by: 'start_line' } - })); + })) + .catch(err => { + console.error(err); + }); } function JobsRun ($stateExtender, strings) { diff --git a/awx/ui/client/lib/models/ProjectUpdate.js b/awx/ui/client/lib/models/ProjectUpdate.js index 3de76790df..84fae23f50 100644 --- a/awx/ui/client/lib/models/ProjectUpdate.js +++ b/awx/ui/client/lib/models/ProjectUpdate.js @@ -1,7 +1,7 @@ let BaseModel; function ProjectUpdateModel (method, resource, config) { - BaseModel.call(this, 'jobs'); + BaseModel.call(this, 'project_updates'); this.Constructor = ProjectUpdateModel; From 07ff25a241090b025bf4c69860f0e19898947830 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 6 Feb 2018 15:55:44 -0500 Subject: [PATCH 116/379] Add generalized resource to job results view --- .../features/output/index.controller.js | 31 +++++++++++++------ awx/ui/client/features/output/index.js | 6 ++-- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 14d086ff4b..d2bdedeb6d 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -3,7 +3,8 @@ import hasAnsi from 'has-ansi'; let vm; let ansi; -let job; +let resource; +let related; let container; let $timeout; let $sce; @@ -38,7 +39,7 @@ const TIME_EVENTS = [ ]; function JobsIndexController ( - _job_, + _resource_, _$sce_, _$timeout_, _$scope_, @@ -50,11 +51,12 @@ function JobsIndexController ( $compile = _$compile_; $scope = _$scope_; $q = _$q_; - job = _job_; + resource = _resource_; ansi = new Ansi(); + related = getRelated(); - const events = job.get('related.job_events.results'); + const events = resource.get(`related.${related}.results`); const parsed = parseEvents(events); const html = $sce.trustAsHtml(parsed.html); @@ -98,9 +100,20 @@ function JobsIndexController ( }); } +function getRelated () { + const name = resource.constructor.name; + + switch (name) { + case 'ProjectUpdateModel': + return 'events'; + case 'JobModel': + return 'job_events'; + } +} + function next () { const config = { - related: 'job_events', + related, page: meta.page.cache[meta.page.cache.length - 1].page + 1, params: { order_by: 'start_line' @@ -108,7 +121,7 @@ function next () { }; console.log('[2] getting next page', config.page, meta.page.cache); - return job.goToPage(config) + return resource.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); @@ -125,7 +138,7 @@ function next () { function prev () { const config = { - related: 'job_events', + related, page: meta.page.cache[0].page - 1, params: { order_by: 'start_line' @@ -133,7 +146,7 @@ function prev () { }; console.log('[2] getting previous page', config.page, meta.page.cache); - return job.goToPage(config) + return resource.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); @@ -569,7 +582,7 @@ function onScroll () { } JobsIndexController.$inject = [ - 'job', + 'resource', '$sce', '$timeout', '$scope', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index bb515e226b..7c082c1c0e 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -10,7 +10,7 @@ const indexTemplate = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; -function resolveJob (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { +function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { const { id } = $stateParams; const { type } = $stateParams; @@ -73,14 +73,14 @@ function JobsRun ($stateExtender, strings) { } }, resolve: { - job: [ + resource: [ 'JobModel', 'ProjectUpdateModel', 'AdHocCommandModel', 'SystemJobModel', 'WorkflowJobModel', '$stateParams', - resolveJob + resolveResource ] } }); From 2e07fee39f97a06282795c08e95994d8b1a64b71 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 7 Feb 2018 16:08:11 -0500 Subject: [PATCH 117/379] Add more robust stdout navigation --- awx/ui/client/features/jobs/_index.less | 8 + .../features/output/index.controller.js | 145 +++++++++++++----- awx/ui/client/features/output/index.view.html | 17 +- 3 files changed, 131 insertions(+), 39 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 750491edff..abd2352fb1 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -41,6 +41,14 @@ cursor: pointer; } + &-menuIcon--lg { + font-size: 22px; + line-height: 12px; + font-weight: bold; + padding: 10px; + cursor: pointer; + } + &-toggle { color: @at-gray-848992; background-color: @at-gray-eb; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index d2bdedeb6d..dd0c28f845 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -73,7 +73,10 @@ function JobsIndexController ( vm.menu = { scroll: { display: false, - to: scrollTo + home: scrollHome, + end: scrollEnd, + down: scrollPageDown, + up: scrollPageUp }, top: { expand, @@ -137,6 +140,8 @@ function next () { } function prev () { + const container = $(ELEMENT_CONTAINER)[0]; + const config = { related, page: meta.page.cache[0].page - 1, @@ -157,7 +162,10 @@ function prev () { }); return pop() - .then(() => prepend(data.results)); + .then(() => prepend(data.results)) + .then(lines => { + container.scrollTop = (getRowHeight() * lines) + }); }); } @@ -236,9 +244,7 @@ function prepend (events) { table.prepend(rows); $compile(rows.contents())($scope); - $timeout(() => { - resolve(); - }); + $timeout(() => resolve(parsed.lines)); }); } @@ -257,9 +263,7 @@ function pop () { rows.empty(); rows.remove(); - $timeout(() => { - return resolve(); - }); + $timeout(() => resolve(ejected)); }); } @@ -278,9 +282,19 @@ function shift () { rows.empty(); rows.remove(); - $timeout(() => { - return resolve(); - }); + $timeout(() => resolve()); + }); +} + +function clear () { + console.log('[3] clearing pages'); + return $q(resolve => { + const rows = $(ELEMENT_TBODY).children(); + + rows.empty(); + rows.remove(); + + $timeout(() => resolve()); }); } @@ -288,14 +302,6 @@ function expand () { vm.toggle(meta.parent, true); } -function scrollTo (direction) { - if (direction === 'top') { - container[0].scrollTop = 0; - } else { - container[0].scrollTop = container[0].scrollHeight; - } -} - function parseEvents (events) { let lines = 0; let html = ''; @@ -486,22 +492,6 @@ function getTime (created) { return `${hour}:${minute}:${second}`; } -function pageUp () { - -} - -function pageDown () { - -} - -function jumpToStart () { - -} - -function jumpToEnd () { - -} - function showHostDetails (id) { jobEvent.request('get', id) .then(() => { @@ -581,6 +571,91 @@ function onScroll () { }, SCROLL_LOAD_DELAY); } +function scrollHome () { + const config = { + related, + page: 'first', + params: { + order_by: 'start_line' + } + }; + + meta.scroll.inProgress = true; + + console.log('[2] getting first page', config.page, meta.page.cache); + return resource.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + meta.page.cache = [{ + page: data.page + }] + + return clear() + .then(() => prepend(data.results)) + .then(() => { + meta.scroll.inProgress = false; + }); + }); +} + +function scrollEnd () { + const config = { + related, + page: 'last', + params: { + order_by: 'start_line' + } + }; + + meta.scroll.inProgress = true; + + console.log('[2] getting last page', config.page, meta.page.cache); + return resource.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + meta.page.cache = [{ + page: data.page + }] + + return clear() + .then(() => append(data.results)) + .then(() => { + const container = $(ELEMENT_CONTAINER)[0]; + + container.scrollTop = getScrollHeight(); + meta.scroll.inProgress = false; + }); + }); +} + +function scrollPageUp () { + const container = $(ELEMENT_CONTAINER)[0]; + const jump = container.scrollTop - container.offsetHeight; + + if (jump < 0) { + container.scrollTop = 0; + } else { + container.scrollTop = jump; + } +} + +function scrollPageDown () { + const container = $(ELEMENT_CONTAINER)[0]; + const jump = container.scrollTop + container.offsetHeight; + + if (jump > container.scrollHeight) { + container.scrollTop = container.scrollHeight; + } else { + container.scrollTop = jump; + } +} + JobsIndexController.$inject = [ 'resource', '$sce', diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 2ebf9f3285..dd938739b0 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -13,8 +13,17 @@ ng-class="{ 'fa-minus': vm.menu.top.isExpanded, 'fa-plus': !vm.menu.top.isExpanded }">
-
- +
+ +
+
+ +
+
+ +
+
+
@@ -23,8 +32,8 @@
 
-
-

+
+

Back to Top

From a6ee7b6aac3235af77e18bc973788cb78690081a Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 8 Feb 2018 13:58:31 -0500 Subject: [PATCH 118/379] Remove unused functionality from controller --- awx/ui/client/features/jobs/_index.less | 16 +++++ .../features/output/index.controller.js | 65 +++---------------- awx/ui/client/features/output/index.view.html | 6 +- 3 files changed, 28 insertions(+), 59 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index abd2352fb1..a72c05a6be 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -7,6 +7,10 @@ border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: none; + + & > div { + user-select: none; + } } &-menuBottom { @@ -18,6 +22,10 @@ right: 60px; bottom: 24px; cursor: pointer; + + &:hover { + color: @at-blue; + } } &-menuIconGroup { @@ -39,6 +47,10 @@ font-size: 12px; padding: 10px; cursor: pointer; + + &:hover { + color: @at-blue; + } } &-menuIcon--lg { @@ -47,6 +59,10 @@ font-weight: bold; padding: 10px; cursor: pointer; + + &:hover { + color: @at-blue; + } } &-toggle { diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index dd0c28f845..305f0dd8d9 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -20,7 +20,7 @@ const meta = { const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; -const SCROLL_LOAD_DELAY = 250; +const SCROLL_LOAD_DELAY = 50; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -161,57 +161,18 @@ function prev () { page: data.page }); + const previousHeight = container.scrollHeight; + return pop() .then(() => prepend(data.results)) .then(lines => { - container.scrollTop = (getRowHeight() * lines) + const currentHeight = container.scrollHeight; + + container.scrollTop = currentHeight - previousHeight; }); }); } -function getRowCount () { - return $(ELEMENT_TBODY).children().length; -} - -function getRowHeight () { - return $(ELEMENT_TBODY).children()[0].offsetHeight; -} - -function getViewHeight () { - return $(ELEMENT_CONTAINER)[0].offsetHeight; -} - -function getScrollPosition () { - return $(ELEMENT_CONTAINER)[0].scrollTop; -} - -function getScrollHeight () { - return $(ELEMENT_CONTAINER)[0].scrollHeight; -} - -function getRowsAbove () { - const top = getScrollPosition(); - - if (top === 0) { - return 0; - } - - return Math.floor(top / getRowHeight()); -} - -function getRowsBelow () { - const bottom = getScrollPosition() + getViewHeight(); - - return Math.floor((getScrollHeight() - bottom) / getRowHeight()); -} - -function getRowsInView () { - const rowHeight = getRowHeight(); - const viewHeight = getViewHeight(); - - return Math.floor(viewHeight / rowHeight); -} - function append (events) { console.log('[4] appending next page'); @@ -628,7 +589,7 @@ function scrollEnd () { .then(() => { const container = $(ELEMENT_CONTAINER)[0]; - container.scrollTop = getScrollHeight(); + container.scrollTop = container.scrollHeight; meta.scroll.inProgress = false; }); }); @@ -638,22 +599,14 @@ function scrollPageUp () { const container = $(ELEMENT_CONTAINER)[0]; const jump = container.scrollTop - container.offsetHeight; - if (jump < 0) { - container.scrollTop = 0; - } else { - container.scrollTop = jump; - } + container.scrollTop = jump; } function scrollPageDown () { const container = $(ELEMENT_CONTAINER)[0]; const jump = container.scrollTop + container.offsetHeight; - if (jump > container.scrollHeight) { - container.scrollTop = container.scrollHeight; - } else { - container.scrollTop = jump; - } + container.scrollTop = jump; } JobsIndexController.$inject = [ diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index dd938739b0..4b460e4bc1 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -16,15 +16,15 @@
+
+ +
-
- -
From 2eef16632512f72fc47a2a2169b0fdfb05ad7085 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 9 Feb 2018 14:30:17 -0500 Subject: [PATCH 119/379] Remove git merge conflict artifacts --- awx/ui/client/lib/models/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 3875975d4d..809b6a72ba 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -60,7 +60,6 @@ angular .service('UnifiedJobTemplateModel', UnifiedJobTemplate) .service('WorkflowJobModel', WorkflowJob) .service('WorkflowJobTemplateModel', WorkflowJobTemplate) - .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) - .service('ModelsStrings', ModelsStrings); + .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode); export default MODULE_NAME; From ad1764c7f234e1357b59b08b388021c7ac105e55 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 9 Feb 2018 16:01:29 -0500 Subject: [PATCH 120/379] Add ws subscription to job results --- .../features/output/index.controller.js | 4 ++ awx/ui/client/features/output/index.js | 52 ++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 305f0dd8d9..0e1a76b65a 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -5,6 +5,7 @@ let vm; let ansi; let resource; let related; +let socket; let container; let $timeout; let $sce; @@ -40,6 +41,7 @@ const TIME_EVENTS = [ function JobsIndexController ( _resource_, + _socket_, _$sce_, _$timeout_, _$scope_, @@ -52,6 +54,7 @@ function JobsIndexController ( $scope = _$scope_; $q = _$q_; resource = _resource_; + socket = _socket_; ansi = new Ansi(); related = getRelated(); @@ -611,6 +614,7 @@ function scrollPageDown () { JobsIndexController.$inject = [ 'resource', + 'socket', '$sce', '$timeout', '$scope', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 7c082c1c0e..4d395b17a9 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -54,13 +54,38 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ }); } -function JobsRun ($stateExtender, strings) { - $stateExtender.addState({ +function resolveSocket (SocketService, $stateParams) { + const { id } = $stateParams; + const { type } = $stateParams; + + // TODO: accommodate other result types (management, scm_update, etc) + const state = { + data: { + socket: { + groups: { + jobs: ['status_changed', 'summary'], + job_events: [] + } + } + } + }; + + SocketService.addStateResolve(state, id); + + return SocketService; +} + +function resolveBreadcrumb (strings) { + return { + label: strings.get('state.TITLE') + }; +} + +function JobsRun ($stateRegistry) { + const state = { name: 'jobz', + url: '/jobz/:type/:id', route: '/jobz/:type/:id', - ncyBreadcrumb: { - label: strings.get('state.TITLE') - }, data: { activityStream: true, activityStreamTarget: 'jobs' @@ -81,12 +106,23 @@ function JobsRun ($stateExtender, strings) { 'WorkflowJobModel', '$stateParams', resolveResource + ], + ncyBreadcrumb: [ + 'JobsStrings', + resolveBreadcrumb + ], + socket: [ + 'SocketService', + '$stateParams', + resolveSocket ] - } - }); + }, + }; + + $stateRegistry.register(state); } -JobsRun.$inject = ['$stateExtender', 'JobsStrings']; +JobsRun.$inject = ['$stateRegistry']; angular .module(MODULE_NAME, [ From 3d02ef820903de5d197c91cb2ef621588ddafc67 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 14 Feb 2018 14:23:14 -0500 Subject: [PATCH 121/379] Add basic (no optimization) real-time implementation --- .../features/output/index.controller.js | 27 ++++++++++++++++--- awx/ui/client/features/output/index.js | 22 ++++++++++----- awx/ui/client/features/output/index.view.html | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0e1a76b65a..2d95f596e1 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -5,7 +5,6 @@ let vm; let ansi; let resource; let related; -let socket; let container; let $timeout; let $sce; @@ -18,6 +17,7 @@ const meta = { scroll: {}, page: {} }; +const current = {}; const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; @@ -41,7 +41,7 @@ const TIME_EVENTS = [ function JobsIndexController ( _resource_, - _socket_, + webSocketNamespace, _$sce_, _$timeout_, _$scope_, @@ -54,7 +54,6 @@ function JobsIndexController ( $scope = _$scope_; $q = _$q_; resource = _resource_; - socket = _socket_; ansi = new Ansi(); related = getRelated(); @@ -72,6 +71,9 @@ function JobsIndexController ( vm.toggle = toggle; vm.showHostDetails = showHostDetails; + vm.clear = clear; + + $scope.$on(webSocketNamespace, processWebSocketEvents); vm.menu = { scroll: { @@ -95,6 +97,7 @@ function JobsIndexController ( lines: parsed.lines }]; + $timeout(() => { const table = $(ELEMENT_TBODY); container = $(ELEMENT_CONTAINER); @@ -106,6 +109,22 @@ function JobsIndexController ( }); } +function clear () { + const rows = $(ELEMENT_TBODY).children(); + + rows.empty(); + rows.remove(); +} + +function processWebSocketEvents (scope, data) { + meta.scroll.inProgress = true; + + append([data]) + .then(() => { + container[0].scrollTop = container[0].scrollHeight; + }); +} + function getRelated () { const name = resource.constructor.name; @@ -614,7 +633,7 @@ function scrollPageDown () { JobsIndexController.$inject = [ 'resource', - 'socket', + 'webSocketNamespace', '$sce', '$timeout', '$scope', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 4d395b17a9..6ff6e61012 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -54,17 +54,27 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ }); } -function resolveSocket (SocketService, $stateParams) { +function resolveWebSocket (SocketService, $stateParams) { + const prefix = 'ws'; const { id } = $stateParams; const { type } = $stateParams; - // TODO: accommodate other result types (management, scm_update, etc) + let name; + + switch (type) { + case 'playbook': + name = 'job_events'; + break; + default: + name = 'events'; + } + const state = { data: { socket: { groups: { jobs: ['status_changed', 'summary'], - job_events: [] + [name]: [] } } } @@ -72,7 +82,7 @@ function resolveSocket (SocketService, $stateParams) { SocketService.addStateResolve(state, id); - return SocketService; + return `${prefix}-${name}-${id}`; } function resolveBreadcrumb (strings) { @@ -111,10 +121,10 @@ function JobsRun ($stateRegistry) { 'JobsStrings', resolveBreadcrumb ], - socket: [ + webSocketNamespace: [ 'SocketService', '$stateParams', - resolveSocket + resolveWebSocket ] }, }; diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 4b460e4bc1..11c903dfb0 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,7 +1,7 @@
-

+

From d6e705894759e9847e80782c437a3cec1ff5c793 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 15 Feb 2018 13:31:56 -0500 Subject: [PATCH 122/379] Remove extraneous model import --- awx/ui/client/features/output/index.controller.js | 4 ++-- awx/ui/client/lib/models/index.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 2d95f596e1..a0e9b6bd1d 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -129,10 +129,10 @@ function getRelated () { const name = resource.constructor.name; switch (name) { - case 'ProjectUpdateModel': - return 'events'; case 'JobModel': return 'job_events'; + default: + return 'events'; } } diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 809b6a72ba..3875975d4d 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -60,6 +60,7 @@ angular .service('UnifiedJobTemplateModel', UnifiedJobTemplate) .service('WorkflowJobModel', WorkflowJob) .service('WorkflowJobTemplateModel', WorkflowJobTemplate) - .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode); + .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode) + .service('ModelsStrings', ModelsStrings); export default MODULE_NAME; From e143698484c8ad25d9b9f97b6742dcb46bbb2097 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 19 Feb 2018 10:03:06 -0500 Subject: [PATCH 123/379] Fix resource references in models --- awx/ui/client/features/output/index.js | 3 +-- awx/ui/client/lib/models/SystemJob.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 6ff6e61012..927217f350 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -15,12 +15,11 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ const { type } = $stateParams; let Resource; - let related; + let related = 'events'; switch (type) { case 'project': Resource = ProjectUpdate; - related = 'events'; break; case 'playbook': Resource = Job; diff --git a/awx/ui/client/lib/models/SystemJob.js b/awx/ui/client/lib/models/SystemJob.js index ec41941046..cc092ff8f4 100644 --- a/awx/ui/client/lib/models/SystemJob.js +++ b/awx/ui/client/lib/models/SystemJob.js @@ -1,7 +1,7 @@ let BaseModel; function SystemJobModel (method, resource, config) { - BaseModel.call(this, 'jobs'); + BaseModel.call(this, 'system_jobs'); this.Constructor = SystemJobModel; From 83897d43a75e89a4c7385466157f2c04409416af Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 19 Feb 2018 13:33:40 -0500 Subject: [PATCH 124/379] Add websocket connection info for remaining job types --- .../features/output/index.controller.js | 1 + awx/ui/client/features/output/index.js | 38 ++++++++++++------- .../src/shared/socket/socket.service.js | 12 ++++-- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index a0e9b6bd1d..bad00f5542 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -119,6 +119,7 @@ function clear () { function processWebSocketEvents (scope, data) { meta.scroll.inProgress = true; + console.log(data); append([data]) .then(() => { container[0].scrollTop = container[0].scrollHeight; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 927217f350..c67122d148 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -47,33 +47,45 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ page_size: 100, order_by: 'start_line' } - })) - .catch(err => { - console.error(err); - }); + })); } function resolveWebSocket (SocketService, $stateParams) { + const { type, id } = $stateParams; const prefix = 'ws'; - const { id } = $stateParams; - const { type } = $stateParams; let name; + let events; switch (type) { - case 'playbook': - name = 'job_events'; + case 'system': + name = 'system_jobs'; + events = 'system_job_events'; + break; + case 'project': + name = 'project_updates'; + events = 'project_update_events'; + break; + case 'command': + name = 'ad_hoc_commands'; + events = 'ad_hoc_command_events'; + break; + case 'inventory': + name = 'inventory_updates'; + events = 'inventory_update_events'; + break; + case 'playbook': + name = 'jobs'; + events = 'job_events'; break; - default: - name = 'events'; } const state = { data: { socket: { groups: { - jobs: ['status_changed', 'summary'], - [name]: [] + [name]: ['status_changed', 'summary'], + [events]: [] } } } @@ -81,7 +93,7 @@ function resolveWebSocket (SocketService, $stateParams) { SocketService.addStateResolve(state, id); - return `${prefix}-${name}-${id}`; + return `${prefix}-${events}-${id}`; } function resolveBreadcrumb (strings) { diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js index 7c390d0cba..b26dae58d7 100644 --- a/awx/ui/client/src/shared/socket/socket.service.js +++ b/awx/ui/client/src/shared/socket/socket.service.js @@ -90,12 +90,18 @@ export default // ex: 'ws-jobs-' str = `ws-${data.group_name}-${data.job}`; } + else if(data.group_name==="project_update_events"){ + str = `ws-${data.group_name}-${data.project_update}`; + } else if(data.group_name==="ad_hoc_command_events"){ - // The naming scheme is "ws" then a - // dash (-) and the group_name, then the job ID - // ex: 'ws-jobs-' str = `ws-${data.group_name}-${data.ad_hoc_command}`; } + else if(data.group_name==="system_job_events"){ + str = `ws-${data.group_name}-${data.system_job}`; + } + else if(data.group_name==="inventory_update_events"){ + str = `ws-${data.group_name}-${data.inventory_update}`; + } else if(data.group_name==="control"){ // As of v. 3.1.0, there is only 1 "control" // message, which is for expiring the session if the From d48f69317fe108000e69e33e2199e17d2525a156 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 20 Feb 2018 16:44:51 -0500 Subject: [PATCH 125/379] Add scroll lock for real-time display --- awx/ui/client/features/jobs/_index.less | 9 + .../features/output/index.controller.js | 177 +++++++++--------- awx/ui/client/features/output/index.js | 91 +++++---- awx/ui/client/features/output/index.view.html | 24 +-- 4 files changed, 164 insertions(+), 137 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index a72c05a6be..623db07cc6 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -65,6 +65,15 @@ } } + &-menuIcon--active { + font-size: 22px; + line-height: 12px; + font-weight: bold; + padding: 10px; + cursor: pointer; + color: @at-blue; + } + &-toggle { color: @at-gray-848992; background-color: @at-gray-eb; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index bad00f5542..48f223235e 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -3,8 +3,8 @@ import hasAnsi from 'has-ansi'; let vm; let ansi; +let model; let resource; -let related; let container; let $timeout; let $sce; @@ -13,11 +13,9 @@ let $scope; let $q; const record = {}; -const meta = { - scroll: {}, - page: {} -}; -const current = {}; + +let parent = null; +let cache = []; const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; @@ -27,6 +25,8 @@ const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; const ELEMENT_TBODY = '#atStdoutResultTable'; const ELEMENT_CONTAINER = '.at-Stdout-container'; +const JOB_START = 'playbook_on_start'; +const JOB_END = 'playbook_on_stats'; const EVENT_GROUPS = [ EVENT_START_TASK, @@ -48,55 +48,48 @@ function JobsIndexController ( _$compile_, _$q_ ) { + vm = this || {}; + $timeout = _$timeout_; $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; $q = _$q_; resource = _resource_; + model = resource.model; ansi = new Ansi(); - related = getRelated(); - const events = resource.get(`related.${related}.results`); + const events = model.get(`related.${resource.related}.results`); const parsed = parseEvents(events); const html = $sce.trustAsHtml(parsed.html); - vm = this || {}; + cache.push({ page: 1, lines: parsed.lines }); - $scope.ns = 'jobs'; - $scope.jobs = { - modal: {} - }; - - vm.toggle = toggle; - vm.showHostDetails = showHostDetails; + // Development helper(s) vm.clear = clear; - $scope.$on(webSocketNamespace, processWebSocketEvents); - - vm.menu = { - scroll: { - display: false, - home: scrollHome, - end: scrollEnd, - down: scrollPageDown, - up: scrollPageUp - }, - top: { - expand, - isExpanded: true - }, - bottom: { - next - } + // Stdout Navigation + vm.scroll = { + lock: false, + display: false, + active: false, + home: scrollHome, + end: scrollEnd, + down: scrollPageDown, + up: scrollPageUp }; - meta.page.cache = [{ - page: 1, - lines: parsed.lines - }]; + // Expand/collapse + vm.toggle = toggle; + vm.expand = expand; + vm.isExpanded = true; + // Real-time (active between JOB_START and JOB_END events only) + $scope.$on(webSocketNamespace, processWebSocketEvents); + vm.stream = { + active: false + }; $timeout(() => { const table = $(ELEMENT_TBODY); @@ -117,43 +110,41 @@ function clear () { } function processWebSocketEvents (scope, data) { - meta.scroll.inProgress = true; + vm.scroll.active = true; + + if (data.event === JOB_START) { + vm.stream.active = true; + vm.scroll.lock = true; + } else if (data.event === JOB_END) { + vm.stream.active = false; + vm.scroll.lock = false; + } - console.log(data); append([data]) .then(() => { - container[0].scrollTop = container[0].scrollHeight; + if (vm.scroll.lock) { + container[0].scrollTop = container[0].scrollHeight; + } }); } -function getRelated () { - const name = resource.constructor.name; - - switch (name) { - case 'JobModel': - return 'job_events'; - default: - return 'events'; - } -} - function next () { const config = { - related, - page: meta.page.cache[meta.page.cache.length - 1].page + 1, + related: resource.related, + page: cache[cache.length - 1].page + 1, params: { order_by: 'start_line' } }; - console.log('[2] getting next page', config.page, meta.page.cache); - return resource.goToPage(config) + console.log('[2] getting next page', config.page, cache); + return model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - meta.page.cache.push({ + cache.push({ page: data.page }); @@ -166,21 +157,21 @@ function prev () { const container = $(ELEMENT_CONTAINER)[0]; const config = { - related, - page: meta.page.cache[0].page - 1, + related: resource.related, + page: cache[0].page - 1, params: { order_by: 'start_line' } }; - console.log('[2] getting previous page', config.page, meta.page.cache); - return resource.goToPage(config) + console.log('[2] getting previous page', config.page, cache); + return model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - meta.page.cache.unshift({ + cache.unshift({ page: data.page }); @@ -203,9 +194,9 @@ function append (events) { const parsed = parseEvents(events); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); - const index = meta.page.cache.length - 1; + const index = cache.length - 1; - meta.page.cache[index].lines = parsed.lines; + cache[index].lines = parsed.lines; table.append(rows); $compile(rows.contents())($scope); @@ -223,7 +214,7 @@ function prepend (events) { const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); - meta.page.cache[0].lines = parsed.lines; + cache[0].lines = parsed.lines; table.prepend(rows); $compile(rows.contents())($scope); @@ -235,12 +226,12 @@ function prepend (events) { function pop () { console.log('[3] popping old page'); return $q(resolve => { - if (meta.page.cache.length <= PAGE_LIMIT) { + if (cache.length <= PAGE_LIMIT) { console.log('[3.1] nothing to pop'); return resolve(); } - const ejected = meta.page.cache.pop(); + const ejected = cache.pop(); console.log('[3.1] popping', ejected); const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); @@ -254,12 +245,12 @@ function pop () { function shift () { console.log('[3] shifting old page'); return $q(resolve => { - if (meta.page.cache.length <= PAGE_LIMIT) { + if (cache.length <= PAGE_LIMIT) { console.log('[3.1] nothing to shift'); return resolve(); } - const ejected = meta.page.cache.shift(); + const ejected = cache.shift(); console.log('[3.1] shifting', ejected); const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); @@ -283,7 +274,7 @@ function clear () { } function expand () { - vm.toggle(meta.parent, true); + vm.toggle(parent, true); } function parseEvents (events) { @@ -375,7 +366,7 @@ function createRecord (ln, lines, event) { info.isParent = true; if (event.event_level === 1) { - meta.parent = event.uuid; + parent = event.uuid; } if (event.parent_uuid) { @@ -495,7 +486,7 @@ function toggle (uuid, menu) { let icon = $(`#${uuid} .at-Stdout-toggle > i`); if (menu || record[uuid].level === 1) { - vm.menu.top.isExpanded = !vm.menu.top.isExpanded; + vm.isExpanded = !vm.isExpanded; } if (record[uuid].children) { @@ -516,11 +507,11 @@ function toggle (uuid, menu) { } function onScroll () { - if (meta.scroll.inProgress) { + if (vm.scroll.active) { return; } - meta.scroll.inProgress = true; + vm.scroll.active = true; $timeout(() => { const top = container[0].scrollTop; @@ -528,17 +519,17 @@ function onScroll () { if (top <= SCROLL_BUFFER) { console.log('[1] scroll to top'); - vm.menu.scroll.display = false; + vm.scroll.display = false; prev() .then(() => { console.log('[5] scroll reset'); - meta.scroll.inProgress = false; + vm.scroll.active = false; }); return; } else { - vm.menu.scroll.display = true; + vm.scroll.display = true; if (bottom >= container[0].scrollHeight) { console.log('[1] scroll to bottom'); @@ -546,10 +537,10 @@ function onScroll () { next() .then(() => { console.log('[5] scroll reset'); - meta.scroll.inProgress = false; + vm.scroll.active = false; }); } else { - meta.scroll.inProgress = false; + vm.scroll.active = false; } } }, SCROLL_LOAD_DELAY); @@ -557,53 +548,59 @@ function onScroll () { function scrollHome () { const config = { - related, + related: resource.related, page: 'first', params: { order_by: 'start_line' } }; - meta.scroll.inProgress = true; + vm.scroll.active = true; - console.log('[2] getting first page', config.page, meta.page.cache); - return resource.goToPage(config) + console.log('[2] getting first page', config.page, cache); + return model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - meta.page.cache = [{ + cache = [{ page: data.page }] return clear() .then(() => prepend(data.results)) .then(() => { - meta.scroll.inProgress = false; + vm.scroll.active = false; }); }); } function scrollEnd () { + if (vm.scroll.lock) { + vm.scroll.lock = false; + + return; + } + const config = { - related, + related: resource.related, page: 'last', params: { order_by: 'start_line' } }; - meta.scroll.inProgress = true; + vm.scroll.active = true; - console.log('[2] getting last page', config.page, meta.page.cache); - return resource.goToPage(config) + console.log('[2] getting last page', config.page, cache); + return model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - meta.page.cache = [{ + cache = [{ page: data.page }] @@ -613,7 +610,7 @@ function scrollEnd () { const container = $(ELEMENT_CONTAINER)[0]; container.scrollTop = container.scrollHeight; - meta.scroll.inProgress = false; + vm.scroll.active = false; }); }); } diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index c67122d148..a23e8cbbc0 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -9,10 +9,12 @@ import IndexController from '~features/output/index.controller'; const indexTemplate = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; +const PAGE_CACHE = true; +const PAGE_LIMIT = 3; +const PAGE_SIZE = 100; function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { - const { id } = $stateParams; - const { type } = $stateParams; + const { id, type } = $stateParams; let Resource; let related = 'events'; @@ -40,52 +42,44 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ } return new Resource('get', id) - .then(resource => resource.extend(related, { - pageCache: true, - pageLimit: 3, + .then(model => model.extend(related, { + pageCache: PAGE_CACHE, + pageLimit: PAGE_LIMIT, params: { - page_size: 100, + page_size: PAGE_SIZE, order_by: 'start_line' } - })); + })) + .then(model => { + return { + id, + type, + model, + related, + ws: getWebSocketResource(type), + page: { + cache: PAGE_CACHE, + limit: PAGE_LIMIT, + size: PAGE_SIZE + } + }; + }); } function resolveWebSocket (SocketService, $stateParams) { const { type, id } = $stateParams; const prefix = 'ws'; + const resource = getWebSocketResource(type); let name; let events; - switch (type) { - case 'system': - name = 'system_jobs'; - events = 'system_job_events'; - break; - case 'project': - name = 'project_updates'; - events = 'project_update_events'; - break; - case 'command': - name = 'ad_hoc_commands'; - events = 'ad_hoc_command_events'; - break; - case 'inventory': - name = 'inventory_updates'; - events = 'inventory_update_events'; - break; - case 'playbook': - name = 'jobs'; - events = 'job_events'; - break; - } - const state = { data: { socket: { groups: { - [name]: ['status_changed', 'summary'], - [events]: [] + [resource.name]: ['status_changed', 'summary'], + [resource.key]: [] } } } @@ -93,7 +87,7 @@ function resolveWebSocket (SocketService, $stateParams) { SocketService.addStateResolve(state, id); - return `${prefix}-${events}-${id}`; + return `${prefix}-${resource.key}-${id}`; } function resolveBreadcrumb (strings) { @@ -102,6 +96,37 @@ function resolveBreadcrumb (strings) { }; } +function getWebSocketResource (type) { + let name; + let key; + + switch (type) { + case 'system': + name = 'system_jobs'; + key = 'system_job_events'; + break; + case 'project': + name = 'project_updates'; + key = 'project_update_events'; + break; + case 'command': + name = 'ad_hoc_commands'; + key = 'ad_hoc_command_events'; + break; + case 'inventory': + name = 'inventory_updates'; + key = 'inventory_update_events'; + break; + case 'playbook': + name = 'jobs'; + key = 'job_events'; + break; + } + + return { name, key }; +} + + function JobsRun ($stateRegistry) { const state = { name: 'jobz', diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 11c903dfb0..71898ae4d1 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -8,21 +8,22 @@
-
+
+ ng-class="{ 'fa-minus': vm.isExpanded, 'fa-plus': !vm.isExpanded }">
-
- +
+
-
+
-
+
-
+
@@ -31,8 +32,8 @@
 
-
-
+
+

Back to Top

@@ -41,9 +42,4 @@
- - -
- -
From 5c3cf83d081a4e64c6c70c762c74fd5bac33156d Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 21 Feb 2018 15:54:08 -0500 Subject: [PATCH 126/379] Implement memory max (NodeList ejection) in real-time mode --- .../features/output/index.controller.js | 225 +++++++++++------- awx/ui/client/features/output/index.view.html | 4 +- awx/ui/client/lib/models/Base.js | 1 - 3 files changed, 142 insertions(+), 88 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 48f223235e..71db68d5b4 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -16,8 +16,8 @@ const record = {}; let parent = null; let cache = []; +let buffer = []; -const PAGE_LIMIT = 3; const SCROLL_BUFFER = 250; const SCROLL_LOAD_DELAY = 50; const EVENT_START_TASK = 'playbook_on_task_start'; @@ -67,13 +67,13 @@ function JobsIndexController ( cache.push({ page: 1, lines: parsed.lines }); // Development helper(s) - vm.clear = clear; + vm.clear = devClear; // Stdout Navigation vm.scroll = { - lock: false, - display: false, - active: false, + isLocked: false, + showBackToTop: false, + isActive: false, home: scrollHome, end: scrollEnd, down: scrollPageDown, @@ -88,10 +88,13 @@ function JobsIndexController ( // Real-time (active between JOB_START and JOB_END events only) $scope.$on(webSocketNamespace, processWebSocketEvents); vm.stream = { - active: false + isActive: false, + isRendering: false, + count: 0, + page: 1 }; - $timeout(() => { + window.requestAnimationFrame(() => { const table = $(ELEMENT_TBODY); container = $(ELEMENT_CONTAINER); @@ -102,32 +105,72 @@ function JobsIndexController ( }); } -function clear () { - const rows = $(ELEMENT_TBODY).children(); - - rows.empty(); - rows.remove(); -} - function processWebSocketEvents (scope, data) { - vm.scroll.active = true; + vm.scroll.isActive = true; if (data.event === JOB_START) { - vm.stream.active = true; - vm.scroll.lock = true; + vm.stream.isActive = true; + vm.scroll.isLocked = true; } else if (data.event === JOB_END) { - vm.stream.active = false; - vm.scroll.lock = false; + vm.stream.isActive = false; } - append([data]) + if (vm.stream.count % resource.page.size === 0) { + cache.push({ + page: vm.stream.page + }); + + vm.stream.page++; + } + + vm.stream.count++; + buffer.push(data); + + if (vm.stream.isRendering) { + return; + } + + vm.stream.isRendering = true; + + const events = buffer.slice(0, buffer.length); + + buffer = []; + + return render(events); +} + +function render (events) { + return shift() + .then(() => append(events)) .then(() => { - if (vm.scroll.lock) { - container[0].scrollTop = container[0].scrollHeight; + if (vm.scroll.isLocked) { + const height = container[0].scrollHeight; + container[0].scrollTop = height; + } + + if (!vm.stream.isActive) { + if (buffer.length) { + events = buffer.slice(0, buffer.length); + buffer = []; + + return render(events) + .then(() => { + vm.stream.isRendering = false; + vm.scroll.isLocked = false; + vm.scroll.isActive = false; + }); + } + } else { + vm.stream.isRendering = false; } }); } +function devClear () { + cache = []; + clear(); +} + function next () { const config = { related: resource.related, @@ -137,7 +180,7 @@ function next () { } }; - console.log('[2] getting next page', config.page, cache); + // console.log('[2] getting next page', config.page, cache); return model.goToPage(config) .then(data => { if (!data || !data.results) { @@ -164,7 +207,7 @@ function prev () { } }; - console.log('[2] getting previous page', config.page, cache); + // console.log('[2] getting previous page', config.page, cache); return model.goToPage(config) .then(data => { if (!data || !data.results) { @@ -188,88 +231,100 @@ function prev () { } function append (events) { - console.log('[4] appending next page'); - + // console.log('[4] appending next page'); return $q(resolve => { - const parsed = parseEvents(events); - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); - const table = $(ELEMENT_TBODY); - const index = cache.length - 1; + window.requestAnimationFrame(() => { + const parsed = parseEvents(events); + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); + const table = $(ELEMENT_TBODY); + const index = cache.length - 1; - cache[index].lines = parsed.lines; + if (cache[index].lines) { + cache[index].lines += parsed.lines; + } else { + cache[index].lines = parsed.lines; + } - table.append(rows); - $compile(rows.contents())($scope); - $timeout(() => { - resolve(); + table.append(rows); + $compile(rows.contents())($scope); + + return resolve(); }); }); } function prepend (events) { - console.log('[4] prepending next page'); + // console.log('[4] prepending next page'); return $q(resolve => { - const parsed = parseEvents(events); - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); - const table = $(ELEMENT_TBODY); + window.requestAnimationFrame(() => { + const parsed = parseEvents(events); + const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); + const table = $(ELEMENT_TBODY); - cache[0].lines = parsed.lines; + cache[0].lines = parsed.lines; - table.prepend(rows); - $compile(rows.contents())($scope); + table.prepend(rows); + $compile(rows.contents())($scope); - $timeout(() => resolve(parsed.lines)); + return resolve(parsed.lines); + }); }); } function pop () { - console.log('[3] popping old page'); + // console.log('[3] popping old page'); return $q(resolve => { - if (cache.length <= PAGE_LIMIT) { - console.log('[3.1] nothing to pop'); + if (cache.length <= resource.page.limit) { + // console.log('[3.1] nothing to pop'); return resolve(); } - const ejected = cache.pop(); - console.log('[3.1] popping', ejected); - const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); + window.requestAnimationFrame(() => { + const ejected = cache.pop(); + // console.log('[3.1] popping', ejected); + const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); - rows.empty(); - rows.remove(); + rows.empty(); + rows.remove(); - $timeout(() => resolve(ejected)); + return resolve(ejected); + }); }); } function shift () { - console.log('[3] shifting old page'); + // console.log('[3] shifting old page'); return $q(resolve => { - if (cache.length <= PAGE_LIMIT) { - console.log('[3.1] nothing to shift'); + if (cache.length <= resource.page.limit) { + // console.log('[3.1] nothing to shift'); return resolve(); } - const ejected = cache.shift(); - console.log('[3.1] shifting', ejected); - const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); + window.requestAnimationFrame(() => { + const ejected = cache.shift(); + // console.log('[3.1] shifting', ejected); + const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); - rows.empty(); - rows.remove(); + rows.empty(); + rows.remove(); - $timeout(() => resolve()); + return resolve(); + }); }); } function clear () { - console.log('[3] clearing pages'); + // console.log('[3] clearing pages'); return $q(resolve => { - const rows = $(ELEMENT_TBODY).children(); + window.requestAnimationFrame(() => { + const rows = $(ELEMENT_TBODY).children(); - rows.empty(); - rows.remove(); + rows.empty(); + rows.remove(); - $timeout(() => resolve()); + return resolve(); + }); }); } @@ -507,40 +562,40 @@ function toggle (uuid, menu) { } function onScroll () { - if (vm.scroll.active) { + if (vm.scroll.isActive) { return; } - vm.scroll.active = true; + vm.scroll.isActive = true; $timeout(() => { const top = container[0].scrollTop; const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; if (top <= SCROLL_BUFFER) { - console.log('[1] scroll to top'); - vm.scroll.display = false; + // console.log('[1] scroll to top'); + vm.scroll.showBackToTop = false; prev() .then(() => { - console.log('[5] scroll reset'); - vm.scroll.active = false; + // console.log('[5] scroll reset'); + vm.scroll.isActive = false; }); return; } else { - vm.scroll.display = true; + vm.scroll.showBackToTop = true; if (bottom >= container[0].scrollHeight) { - console.log('[1] scroll to bottom'); + // console.log('[1] scroll to bottom'); next() .then(() => { - console.log('[5] scroll reset'); - vm.scroll.active = false; + // console.log('[5] scroll reset'); + vm.scroll.isActive = false; }); } else { - vm.scroll.active = false; + vm.scroll.isActive = false; } } }, SCROLL_LOAD_DELAY); @@ -555,9 +610,9 @@ function scrollHome () { } }; - vm.scroll.active = true; + vm.scroll.isActive = true; - console.log('[2] getting first page', config.page, cache); + // console.log('[2] getting first page', config.page, cache); return model.goToPage(config) .then(data => { if (!data || !data.results) { @@ -571,14 +626,14 @@ function scrollHome () { return clear() .then(() => prepend(data.results)) .then(() => { - vm.scroll.active = false; + vm.scroll.isActive = false; }); }); } function scrollEnd () { - if (vm.scroll.lock) { - vm.scroll.lock = false; + if (vm.scroll.isLocked) { + vm.scroll.isLocked = false; return; } @@ -591,9 +646,9 @@ function scrollEnd () { } }; - vm.scroll.active = true; + vm.scroll.isActive = true; - console.log('[2] getting last page', config.page, cache); + // console.log('[2] getting last page', config.page, cache); return model.goToPage(config) .then(data => { if (!data || !data.results) { @@ -610,7 +665,7 @@ function scrollEnd () { const container = $(ELEMENT_CONTAINER)[0]; container.scrollTop = container.scrollHeight; - vm.scroll.active = false; + vm.scroll.isActive = false; }); }); } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 71898ae4d1..3c1a276826 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -15,7 +15,7 @@
+ ng-class=" { 'at-Stdout-menuIcon--active': vm.scroll.isLocked }">
@@ -32,7 +32,7 @@
 
-
+

Back to Top

diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 9985de8799..b2e91f97df 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -430,7 +430,6 @@ function goToPage (config) { pageNumber = page; } - console.log('pageNumber', page, pageNumber); if (pageNumber < 1 || pageNumber > this.page.last) { return Promise.resolve(null); } From 60a43015e2d5452af92001a44ce530403d7455ed Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 22 Feb 2018 15:45:09 -0500 Subject: [PATCH 127/379] Update when scroll,stream flags are flipped --- awx/ui/client/features/jobs/_index.less | 3 +-- .../client/features/output/index.controller.js | 17 +++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/jobs/_index.less index 623db07cc6..d661afae97 100644 --- a/awx/ui/client/features/jobs/_index.less +++ b/awx/ui/client/features/jobs/_index.less @@ -1,6 +1,4 @@ .at-Stdout { - font-family: monospace; - &-menuTop { color: @at-gray-848992; border: 1px solid @at-gray-b7; @@ -123,6 +121,7 @@ } &-container { + font-family: monospace; height: calc(~"100vh - 240px"); overflow-y: scroll; font-size: 15px; diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 71db68d5b4..66f3171dd7 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -115,6 +115,12 @@ function processWebSocketEvents (scope, data) { vm.stream.isActive = false; } + if (!vm.scroll.isLocked) { + vm.scroll.isActive = false; + + return; + } + if (vm.stream.count % resource.page.size === 0) { cache.push({ page: vm.stream.page @@ -153,12 +159,11 @@ function render (events) { events = buffer.slice(0, buffer.length); buffer = []; - return render(events) - .then(() => { - vm.stream.isRendering = false; - vm.scroll.isLocked = false; - vm.scroll.isActive = false; - }); + return render(events); + } else { + vm.stream.isRendering = false; + vm.scroll.isLocked = false; + vm.scroll.isActive = false; } } else { vm.stream.isRendering = false; From a5bd905f18ca6cea99f2cb4cd902ed130c03e8b7 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 26 Feb 2018 11:18:02 -0500 Subject: [PATCH 128/379] [WIP] Add event buffering on scroll/resume --- .../features/output/index.controller.js | 37 +++++++++++-------- awx/ui/client/features/output/index.js | 5 ++- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 66f3171dd7..c6ceacea41 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -106,33 +106,32 @@ function JobsIndexController ( } function processWebSocketEvents (scope, data) { - vm.scroll.isActive = true; - if (data.event === JOB_START) { + vm.scroll.isActive = true; vm.stream.isActive = true; vm.scroll.isLocked = true; } else if (data.event === JOB_END) { vm.stream.isActive = false; } - if (!vm.scroll.isLocked) { - vm.scroll.isActive = false; - - return; - } + // TODO: Determine how to manage buffered events (store in page cache vs. separate) + // Leaning towards keeping separate (same as they come in over WS). On resume of scroll, + // Clear/reset cache, append buffered events, then back to normal render cycle if (vm.stream.count % resource.page.size === 0) { - cache.push({ - page: vm.stream.page - }); + cache.push({ page: vm.stream.page }); vm.stream.page++; + + if (buffer.length > (resource.page.resultLimit - resource.page.size)) { + buffer.splice(0, (buffer.length - resource.page.resultLimit) + resource.page.size); + } } vm.stream.count++; buffer.push(data); - if (vm.stream.isRendering) { + if (vm.stream.isRendering || !vm.scroll.isLocked) { return; } @@ -280,7 +279,7 @@ function prepend (events) { function pop () { // console.log('[3] popping old page'); return $q(resolve => { - if (cache.length <= resource.page.limit) { + if (cache.length <= resource.page.pageLimit) { // console.log('[3.1] nothing to pop'); return resolve(); } @@ -299,16 +298,16 @@ function pop () { } function shift () { - // console.log('[3] shifting old page'); + console.log('[3] shifting old page', cache.length); return $q(resolve => { - if (cache.length <= resource.page.limit) { + if (cache.length <= resource.page.pageLimit) { // console.log('[3.1] nothing to shift'); return resolve(); } window.requestAnimationFrame(() => { const ejected = cache.shift(); - // console.log('[3.1] shifting', ejected); + console.log('[3.1] shifting', ejected); const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); rows.empty(); @@ -639,8 +638,16 @@ function scrollHome () { function scrollEnd () { if (vm.scroll.isLocked) { vm.scroll.isLocked = false; + vm.scroll.isActive = false; return; + } else if (!vm.scroll.isLocked && vm.stream.isActive) { + vm.scroll.isActive = true; + + return clear() + .then(() => { + vm.scroll.isLocked = true; + }); } const config = { diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index a23e8cbbc0..7467c0b079 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -59,8 +59,9 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ ws: getWebSocketResource(type), page: { cache: PAGE_CACHE, - limit: PAGE_LIMIT, - size: PAGE_SIZE + size: PAGE_SIZE, + pageLimit: PAGE_LIMIT, + resultLimit: PAGE_SIZE * PAGE_LIMIT } }; }); From df84f822f6780d0b3cf062c1885ee0392f4de86e Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 27 Feb 2018 17:12:55 -0500 Subject: [PATCH 129/379] [WIP] Move page-related functionality into separate service --- .../features/{jobs => output}/_index.less | 0 .../features/output/index.controller.js | 115 +++++++------- awx/ui/client/features/output/index.js | 34 ++--- awx/ui/client/features/output/page.service.js | 140 ++++++++++++++++++ .../client/features/output/render.service.js | 0 5 files changed, 218 insertions(+), 71 deletions(-) rename awx/ui/client/features/{jobs => output}/_index.less (100%) create mode 100644 awx/ui/client/features/output/page.service.js create mode 100644 awx/ui/client/features/output/render.service.js diff --git a/awx/ui/client/features/jobs/_index.less b/awx/ui/client/features/output/_index.less similarity index 100% rename from awx/ui/client/features/jobs/_index.less rename to awx/ui/client/features/output/_index.less diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index c6ceacea41..ba1bc2252e 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -5,6 +5,7 @@ let vm; let ansi; let model; let resource; +let page; let container; let $timeout; let $sce; @@ -41,7 +42,7 @@ const TIME_EVENTS = [ function JobsIndexController ( _resource_, - webSocketNamespace, + _page_, _$sce_, _$timeout_, _$scope_, @@ -56,6 +57,7 @@ function JobsIndexController ( $scope = _$scope_; $q = _$q_; resource = _resource_; + page = _page_; model = resource.model; ansi = new Ansi(); @@ -64,7 +66,9 @@ function JobsIndexController ( const parsed = parseEvents(events); const html = $sce.trustAsHtml(parsed.html); - cache.push({ page: 1, lines: parsed.lines }); + page.init(resource); + + page.add({ number: 1, lines: parsed.lines }); // Development helper(s) vm.clear = devClear; @@ -86,10 +90,12 @@ function JobsIndexController ( vm.isExpanded = true; // Real-time (active between JOB_START and JOB_END events only) - $scope.$on(webSocketNamespace, processWebSocketEvents); + $scope.$on(resource.ws.namespace, processWebSocketEvents); vm.stream = { isActive: false, isRendering: false, + isPaused: false, + buffered: 0, count: 0, page: 1 }; @@ -105,7 +111,13 @@ function JobsIndexController ( }); } +// TODO: Determine how to manage buffered events (store in page cache vs. separate) +// Leaning towards keeping separate (same as they come in over WS). On resume of scroll, +// Clear/reset cache, append buffered events, then back to normal render cycle + function processWebSocketEvents (scope, data) { + let done; + if (data.event === JOB_START) { vm.scroll.isActive = true; vm.stream.isActive = true; @@ -114,37 +126,28 @@ function processWebSocketEvents (scope, data) { vm.stream.isActive = false; } - // TODO: Determine how to manage buffered events (store in page cache vs. separate) - // Leaning towards keeping separate (same as they come in over WS). On resume of scroll, - // Clear/reset cache, append buffered events, then back to normal render cycle + const pageAdded = page.addToBuffer(data); - if (vm.stream.count % resource.page.size === 0) { - cache.push({ page: vm.stream.page }); - - vm.stream.page++; - - if (buffer.length > (resource.page.resultLimit - resource.page.size)) { - buffer.splice(0, (buffer.length - resource.page.resultLimit) + resource.page.size); - } + if (pageAdded && !vm.scroll.isLocked) { + vm.stream.isPaused = true; } - vm.stream.count++; - buffer.push(data); + if (vm.stream.isPaused && vm.scroll.isLocked) { + vm.stream.isPaused = false; + } - if (vm.stream.isRendering || !vm.scroll.isLocked) { + if (vm.stream.isRendering || vm.stream.isPaused) { return; } - vm.stream.isRendering = true; - - const events = buffer.slice(0, buffer.length); - - buffer = []; + const events = page.emptyBuffer(); return render(events); } function render (events) { + vm.stream.isRendering = true; + return shift() .then(() => append(events)) .then(() => { @@ -154,16 +157,15 @@ function render (events) { } if (!vm.stream.isActive) { - if (buffer.length) { - events = buffer.slice(0, buffer.length); - buffer = []; + const buffer = page.emptyBuffer(); - return render(events); - } else { - vm.stream.isRendering = false; - vm.scroll.isLocked = false; - vm.scroll.isActive = false; + if (buffer.length) { + return render(buffer); } + + vm.stream.isRendering = false; + vm.scroll.isLocked = false; + vm.scroll.isActive = false; } else { vm.stream.isRendering = false; } @@ -172,13 +174,14 @@ function render (events) { function devClear () { cache = []; + page.init(resource); clear(); } function next () { const config = { related: resource.related, - page: cache[cache.length - 1].page + 1, + page: vm.scroll.lastPage + 1, params: { order_by: 'start_line' } @@ -191,9 +194,9 @@ function next () { return $q.resolve(); } - cache.push({ - page: data.page - }); + cache.push({ page: data.page, events: [] }); + + vm.scroll.lastPage = data.page; return shift() .then(() => append(data.results)); @@ -205,12 +208,13 @@ function prev () { const config = { related: resource.related, - page: cache[0].page - 1, + page: vm.scroll.firstPage - 1, params: { order_by: 'start_line' } }; + console.log(cache); // console.log('[2] getting previous page', config.page, cache); return model.goToPage(config) .then(data => { @@ -218,12 +222,13 @@ function prev () { return $q.resolve(); } - cache.unshift({ - page: data.page - }); + cache.unshift({ page: data.page, events: [] }); + + vm.scroll.firstPage = data.page; const previousHeight = container.scrollHeight; + console.log(cache); return pop() .then(() => prepend(data.results)) .then(lines => { @@ -241,13 +246,8 @@ function append (events) { const parsed = parseEvents(events); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); - const index = cache.length - 1; - if (cache[index].lines) { - cache[index].lines += parsed.lines; - } else { - cache[index].lines = parsed.lines; - } + page.updateLineCount('current', parsed.lines); table.append(rows); $compile(rows.contents())($scope); @@ -289,6 +289,8 @@ function pop () { // console.log('[3.1] popping', ejected); const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); + vm.scroll.firstPage = cache[0].page; + rows.empty(); rows.remove(); @@ -298,17 +300,19 @@ function pop () { } function shift () { - console.log('[3] shifting old page', cache.length); + // console.log('[3] shifting old page', cache.length); return $q(resolve => { - if (cache.length <= resource.page.pageLimit) { + if (!page.isOverCapacity()) { // console.log('[3.1] nothing to shift'); return resolve(); } window.requestAnimationFrame(() => { - const ejected = cache.shift(); - console.log('[3.1] shifting', ejected); - const rows = $(ELEMENT_TBODY).children().slice(0, ejected.lines); + const lines = page.trim(); + //console.log('[3.1] shifting', lines); + const rows = $(ELEMENT_TBODY).children().slice(0, lines); + + vm.scroll.firstPage = page.getPageNumber('first'); rows.empty(); rows.remove(); @@ -637,17 +641,20 @@ function scrollHome () { function scrollEnd () { if (vm.scroll.isLocked) { + // Make note of current page when unlocked -- keep buffered events for that page for + // continuity + + vm.scroll.firstPage = cache[0].page; + vm.scroll.lastPage = cache[cache.length - 1].page; vm.scroll.isLocked = false; vm.scroll.isActive = false; return; } else if (!vm.scroll.isLocked && vm.stream.isActive) { vm.scroll.isActive = true; + vm.scroll.isLocked = true; - return clear() - .then(() => { - vm.scroll.isLocked = true; - }); + return; } const config = { @@ -698,7 +705,7 @@ function scrollPageDown () { JobsIndexController.$inject = [ 'resource', - 'webSocketNamespace', + 'JobPageService', '$sce', '$timeout', '$scope', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 7467c0b079..6d35022cf4 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -3,15 +3,17 @@ import IndexController from '~features/output/index.controller'; import atLibModels from '~models'; import atLibComponents from '~components'; -import JobsStrings from '~features/output/jobs.strings'; -import IndexController from '~features/output/index.controller'; +import Strings from '~features/output/jobs.strings'; +import Controller from '~features/output/index.controller'; +import PageService from '~features/output/page.service'; -const indexTemplate = require('~features/output/index.view.html'); +const Template = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; const PAGE_CACHE = true; const PAGE_LIMIT = 3; const PAGE_SIZE = 100; +const WS_PREFIX = 'ws'; function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { const { id, type } = $stateParams; @@ -56,20 +58,20 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ type, model, related, - ws: getWebSocketResource(type), + ws: { + namespace: `${WS_PREFIX}-${getWebSocketResource(type).key}-${id}` + }, page: { cache: PAGE_CACHE, size: PAGE_SIZE, - pageLimit: PAGE_LIMIT, - resultLimit: PAGE_SIZE * PAGE_LIMIT + pageLimit: PAGE_LIMIT } }; }); } -function resolveWebSocket (SocketService, $stateParams) { +function resolveWebSocketConnection (SocketService, $stateParams) { const { type, id } = $stateParams; - const prefix = 'ws'; const resource = getWebSocketResource(type); let name; @@ -87,8 +89,6 @@ function resolveWebSocket (SocketService, $stateParams) { }; SocketService.addStateResolve(state, id); - - return `${prefix}-${resource.key}-${id}`; } function resolveBreadcrumb (strings) { @@ -139,8 +139,8 @@ function JobsRun ($stateRegistry) { }, views: { '@': { - templateUrl: indexTemplate, - controller: IndexController, + templateUrl: Template, + controller: Controller, controllerAs: 'vm' } }, @@ -155,13 +155,13 @@ function JobsRun ($stateRegistry) { resolveResource ], ncyBreadcrumb: [ - 'JobsStrings', + 'JobStrings', resolveBreadcrumb ], - webSocketNamespace: [ + webSocketConnection: [ 'SocketService', '$stateParams', - resolveWebSocket + resolveWebSocketConnection ] }, }; @@ -176,8 +176,8 @@ angular atLibModels, atLibComponents ]) - .controller('indexController', IndexController) - .service('JobsStrings', JobsStrings) + .service('JobStrings', Strings) + .service('JobPageService', PageService) .run(JobsRun); export default MODULE_NAME; diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js new file mode 100644 index 0000000000..1d1227c68d --- /dev/null +++ b/awx/ui/client/features/output/page.service.js @@ -0,0 +1,140 @@ +function JobPageService () { + this.page = null; + this.resource = null; + this.result = null; + this.buffer = null; + this.cache = null; + + this.init = resource => { + this.resource = resource; + + this.page = { + limit: resource.page.pageLimit, + size: resource.page.size, + current: 0, + index: -1, + count: 0 + }; + + this.result = { + limit: this.page.limit * this.page.size, + count: 0 + }; + + this.buffer = { + count: 0 + }; + + this.cache = []; + }; + + this.add = (page, position) => { + page.events = page.events || []; + page.lines = page.lines || 0; + + if (!position) { + this.cache.push(page); + } + + this.page.count++; + }; + + this.addToBuffer = event => { + let pageAdded = false; + + if (this.result.count % this.page.size === 0) { + pageAdded = true; + + this.add({ number: this.page.count + 1, events: [event] }); + + this.trimBuffer(); + } else { + this.cache[this.cache.length - 1].events.push(event); + } + + this.buffer.count++; + this.result.count++; + + return pageAdded; + }; + + this.trimBuffer = () => { + const diff = this.cache.length - this.page.limit; + + if (diff <= 0) { + return; + } + + for (let i = 0; i < diff; i++) { + if (this.cache[i].events) { + this.buffer.count -= this.cache[i].events.length; + this.cache[i].events = []; + } + } + }; + + this.emptyBuffer = () => { + let data = []; + + for (let i = 0; i < this.cache.length; i++) { + const events = this.cache[i].events; + + if (events.length > 0) { + this.buffer.count -= events.length; + data = data.concat(this.cache[i].events.splice(0, events.length)); + } + } + + return data; + }; + + this.isOverCapacity = () => { + return (this.cache.length - this.page.limit) > 0; + }; + + this.trim = () => { + const count = this.cache.length - this.page.limit; + const ejected = this.cache.splice(0, count); + const linesRemoved = ejected.reduce((total, page) => total + page.lines, 0); + + return linesRemoved; + }; + + this.getPageNumber = (page) => { + let index; + + if (page === 'first') { + index = 0; + } + + return this.cache[index].number; + }; + + this.updateLineCount = (page, lines) => { + let index; + + if (page === 'current') { + index = this.cache.length - 1; + } + + if (this.cache[index].lines) { + this.cache[index].lines += lines; + } else { + this.cache[index].lines = lines; + } + } + + this.next = () => { + + }; + + this.prev = () => { + + }; + + this.current = () => { + return this.resource.model.get(`related.${this.resource.related}.results`); + }; +} + +export default JobPageService; diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js new file mode 100644 index 0000000000..e69de29bb2 From b16d9a89e38041a897c7a614d9d909f8ac314486 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 1 Mar 2018 09:15:43 -0500 Subject: [PATCH 130/379] Refactor page handling --- .../features/output/index.controller.js | 232 +++++++----------- awx/ui/client/features/output/page.service.js | 164 +++++++++++-- awx/ui/client/lib/models/Base.js | 2 - 3 files changed, 238 insertions(+), 160 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index ba1bc2252e..ce8a047e7f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -16,11 +16,9 @@ let $q; const record = {}; let parent = null; -let cache = []; -let buffer = []; -const SCROLL_BUFFER = 250; -const SCROLL_LOAD_DELAY = 50; +const SCROLL_THRESHOLD = 0.1; +const SCROLL_DELAY = 1000; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; @@ -78,6 +76,8 @@ function JobsIndexController ( isLocked: false, showBackToTop: false, isActive: false, + position: 0, + time: 0, home: scrollHome, end: scrollEnd, down: scrollPageDown, @@ -111,10 +111,6 @@ function JobsIndexController ( }); } -// TODO: Determine how to manage buffered events (store in page cache vs. separate) -// Leaning towards keeping separate (same as they come in over WS). On resume of scroll, -// Clear/reset cache, append buffered events, then back to normal render cycle - function processWebSocketEvents (scope, data) { let done; @@ -173,74 +169,47 @@ function render (events) { } function devClear () { - cache = []; page.init(resource); clear(); } function next () { - const config = { - related: resource.related, - page: vm.scroll.lastPage + 1, - params: { - order_by: 'start_line' - } - }; - - // console.log('[2] getting next page', config.page, cache); - return model.goToPage(config) - .then(data => { - if (!data || !data.results) { - return $q.resolve(); + return page.next() + .then(events => { + if (!events) { + return; } - cache.push({ page: data.page, events: [] }); - - vm.scroll.lastPage = data.page; - return shift() - .then(() => append(data.results)); - }); + .then(() => append(events)); + }) } -function prev () { +function previous () { const container = $(ELEMENT_CONTAINER)[0]; - const config = { - related: resource.related, - page: vm.scroll.firstPage - 1, - params: { - order_by: 'start_line' - } - }; + let previousHeight; - console.log(cache); - // console.log('[2] getting previous page', config.page, cache); - return model.goToPage(config) - .then(data => { - if (!data || !data.results) { - return $q.resolve(); + return page.previous() + .then(events => { + if (!events) { + return; } - cache.unshift({ page: data.page, events: [] }); - - vm.scroll.firstPage = data.page; - - const previousHeight = container.scrollHeight; - - console.log(cache); return pop() - .then(() => prepend(data.results)) - .then(lines => { - const currentHeight = container.scrollHeight; + .then(() => { + previousHeight = container.scrollHeight; + return prepend(events); + }) + .then(() => { + const currentHeight = container.scrollHeight; container.scrollTop = currentHeight - previousHeight; }); }); } function append (events) { - // console.log('[4] appending next page'); return $q(resolve => { window.requestAnimationFrame(() => { const parsed = parseEvents(events); @@ -258,62 +227,52 @@ function append (events) { } function prepend (events) { - // console.log('[4] prepending next page'); - return $q(resolve => { window.requestAnimationFrame(() => { const parsed = parseEvents(events); const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); const table = $(ELEMENT_TBODY); - cache[0].lines = parsed.lines; + page.updateLineCount('current', parsed.lines); table.prepend(rows); $compile(rows.contents())($scope); - return resolve(parsed.lines); + $scope.$apply(() => { + return resolve(parsed.lines); + }); }); }); } function pop () { - // console.log('[3] popping old page'); return $q(resolve => { - if (cache.length <= resource.page.pageLimit) { - // console.log('[3.1] nothing to pop'); + if (!page.isOverCapacity()) { return resolve(); } window.requestAnimationFrame(() => { - const ejected = cache.pop(); - // console.log('[3.1] popping', ejected); - const rows = $(ELEMENT_TBODY).children().slice(-ejected.lines); - - vm.scroll.firstPage = cache[0].page; + const lines = page.trim('right'); + const rows = $(ELEMENT_TBODY).children().slice(-lines); rows.empty(); rows.remove(); - return resolve(ejected); + return resolve(); }); }); } function shift () { - // console.log('[3] shifting old page', cache.length); return $q(resolve => { if (!page.isOverCapacity()) { - // console.log('[3.1] nothing to shift'); return resolve(); } window.requestAnimationFrame(() => { - const lines = page.trim(); - //console.log('[3.1] shifting', lines); + const lines = page.trim('left'); const rows = $(ELEMENT_TBODY).children().slice(0, lines); - vm.scroll.firstPage = page.getPageNumber('first'); - rows.empty(); rows.remove(); @@ -323,7 +282,6 @@ function shift () { } function clear () { - // console.log('[3] clearing pages'); return $q(resolve => { window.requestAnimationFrame(() => { const rows = $(ELEMENT_TBODY).children(); @@ -574,65 +532,71 @@ function onScroll () { return; } + if (vm.scroll.register) { + $timeout.cancel(vm.scroll.register); + } + + vm.scroll.register = $timeout(registerScrollEvent, SCROLL_DELAY); +} + +function registerScrollEvent () { vm.scroll.isActive = true; - $timeout(() => { - const top = container[0].scrollTop; - const bottom = top + SCROLL_BUFFER + container[0].offsetHeight; + const position = container[0].scrollTop; + const height = container[0].offsetHeight; + const downward = position > vm.scroll.position; - if (top <= SCROLL_BUFFER) { - // console.log('[1] scroll to top'); - vm.scroll.showBackToTop = false; + let promise; - prev() - .then(() => { - // console.log('[5] scroll reset'); - vm.scroll.isActive = false; - }); + if (position !== 0 ) { + vm.scroll.showBackToTop = true; + } else { + vm.scroll.showBackToTop = false; + } - return; - } else { - vm.scroll.showBackToTop = true; - if (bottom >= container[0].scrollHeight) { - // console.log('[1] scroll to bottom'); - - next() - .then(() => { - // console.log('[5] scroll reset'); - vm.scroll.isActive = false; - }); - } else { - vm.scroll.isActive = false; - } + console.log('downward', downward); + if (downward) { + if (((height - position) / height) < SCROLL_THRESHOLD) { + promise = next; } - }, SCROLL_LOAD_DELAY); + } else { + if ((position / height) < SCROLL_THRESHOLD) { + console.log('previous'); + promise = previous; + } + } + + vm.scroll.position = position; + + if (!promise) { + vm.scroll.isActive = false; + + return $q.resolve(); + } + + return promise() + .then(() => { + console.log('done'); + vm.scroll.isActive = false; + /* + *$timeout(() => { + * vm.scroll.isActive = false; + *}, SCROLL_DELAY); + */ + }); + } function scrollHome () { - const config = { - related: resource.related, - page: 'first', - params: { - order_by: 'start_line' - } - }; - - vm.scroll.isActive = true; - - // console.log('[2] getting first page', config.page, cache); - return model.goToPage(config) - .then(data => { - if (!data || !data.results) { - return $q.resolve(); + return page.first() + .then(events => { + if (!events) { + return; } - cache = [{ - page: data.page - }] - return clear() - .then(() => prepend(data.results)) + .then(() => prepend(events)) .then(() => { vm.scroll.isActive = false; }); @@ -641,45 +605,31 @@ function scrollHome () { function scrollEnd () { if (vm.scroll.isLocked) { - // Make note of current page when unlocked -- keep buffered events for that page for - // continuity + page.bookmark(); - vm.scroll.firstPage = cache[0].page; - vm.scroll.lastPage = cache[cache.length - 1].page; vm.scroll.isLocked = false; vm.scroll.isActive = false; return; } else if (!vm.scroll.isLocked && vm.stream.isActive) { + page.bookmark(); + vm.scroll.isActive = true; vm.scroll.isLocked = true; return; } - const config = { - related: resource.related, - page: 'last', - params: { - order_by: 'start_line' - } - }; - vm.scroll.isActive = true; - // console.log('[2] getting last page', config.page, cache); - return model.goToPage(config) - .then(data => { - if (!data || !data.results) { - return $q.resolve(); + return page.last() + .then(events => { + if (!events) { + return; } - cache = [{ - page: data.page - }] - return clear() - .then(() => append(data.results)) + .then(() => append(events)) .then(() => { const container = $(ELEMENT_CONTAINER)[0]; diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index 1d1227c68d..b82eba4705 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -1,4 +1,4 @@ -function JobPageService () { +function JobPageService ($q) { this.page = null; this.resource = null; this.result = null; @@ -13,7 +13,14 @@ function JobPageService () { size: resource.page.size, current: 0, index: -1, - count: 0 + count: 0, + first: 0, + last: 0, + bookmark: { + first: 0, + last: 0, + current: 0 + } }; this.result = { @@ -28,14 +35,25 @@ function JobPageService () { this.cache = []; }; - this.add = (page, position) => { + this.add = (page, position, bookmark) => { page.events = page.events || []; page.lines = page.lines || 0; - if (!position) { + if (position === 'first') { + this.cache.unshift(page); + this.page.first = page.number; + this.page.last = this.cache[this.cache.length -1].number; + } else { this.cache.push(page); + this.page.last = page.number; + this.page.first = this.cache[0].number; } + if (bookmark) { + this.page.bookmark.current = page.number; + } + + this.page.current = page.number; this.page.count++; }; @@ -88,19 +106,33 @@ function JobPageService () { return data; }; + this.emptyCache = () => { + this.page.first = this.page.current; + this.page.last = this.page.current; + this.cache = []; + }; + this.isOverCapacity = () => { return (this.cache.length - this.page.limit) > 0; }; - this.trim = () => { + this.trim = side => { const count = this.cache.length - this.page.limit; - const ejected = this.cache.splice(0, count); - const linesRemoved = ejected.reduce((total, page) => total + page.lines, 0); - return linesRemoved; + let ejected; + + if (side === 'left') { + ejected = this.cache.splice(0, count); + this.page.first = this.cache[0].number; + } else { + ejected = this.cache.splice(-count); + this.page.last = this.cache[this.cache.length - 1].number; + } + + return ejected.reduce((total, page) => total + page.lines, 0); }; - this.getPageNumber = (page) => { + this.getPageNumber = page => { let index; if (page === 'first') { @@ -114,22 +146,118 @@ function JobPageService () { let index; if (page === 'current') { - index = this.cache.length - 1; + index = this.cache.findIndex(item => item.number === this.page.current); } - if (this.cache[index].lines) { - this.cache[index].lines += lines; - } else { - this.cache[index].lines = lines; - } + this.cache[index].lines += lines; } - this.next = () => { + this.bookmark = () => { + console.log('b,current', this.page.current); + if (!this.page.bookmark.active) { + this.page.bookmark.first = this.page.first; + this.page.bookmark.last = this.page.last; + this.page.bookmark.current = this.page.current; + console.log('b,bookmark', this.page.bookmark.current); + this.page.bookmark.active = true; + } else { + this.page.bookmark.active = false; + } }; - this.prev = () => { + this.next = () => { + let page; + let bookmark; + if (this.page.bookmark.active) { + page = this.page.bookmark.current + 1; + bookmark = true; + } else { + page = this.page.last + 1; + } + + const config = this.buildRequestConfig(page); + + return this.resource.model.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + this.add({ number: data.page, events: [], lines: 0 }, 'last', bookmark); + + return data.results; + }); + }; + + this.previous = () => { + let page; + let bookmark; + + if (this.page.bookmark.active) { + page = this.page.bookmark.current - 1; + bookmark = true; + } else { + page = this.page.first - 1; + } + + const config = this.buildRequestConfig(page); + + return this.resource.model.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + this.add({ number: data.page, events: [], lines: 0 }, 'first', bookmark); + + return data.results; + }); + }; + + this.last = () => { + const config = this.buildRequestConfig('last'); + + this.emptyCache(); + + return this.resource.model.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + this.add({ number: data.page, events: [], lines: 0 }, 'last'); + + return data.results; + }); + }; + + this.first = () => { + const config = this.buildRequestConfig('first'); + + this.emptyCache(); + + return this.resource.model.goToPage(config) + .then(data => { + if (!data || !data.results) { + return $q.resolve(); + } + + this.add({ number: data.page, events: [], lines: 0 }, 'first'); + + return data.results; + }); + }; + + this.buildRequestConfig = (page) => { + return { + page, + related: this.resource.related, + params: { + order_by: 'start_line' + } + }; }; this.current = () => { @@ -137,4 +265,6 @@ function JobPageService () { }; } +JobPageService.$inject = ['$q']; + export default JobPageService; diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index b2e91f97df..98f2b25007 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -466,9 +466,7 @@ function goToPage (config) { if (pagesInCache.length > this.page.limit) { const pageToDelete = pagesInCache.shift(); - console.log(pageCache); delete pageCache[pageToDelete]; - console.log(this.page.cache); } } From 0c09447f2d2cb2cd77bf181ab425a331c76b1619 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 1 Mar 2018 16:29:32 -0500 Subject: [PATCH 131/379] Refactor scroll handling into independent service --- .../features/output/index.controller.js | 553 ++++-------------- awx/ui/client/features/output/index.js | 4 +- awx/ui/client/features/output/page.service.js | 13 +- .../client/features/output/render.service.js | 297 ++++++++++ .../client/features/output/scroll.service.js | 172 ++++++ 5 files changed, 578 insertions(+), 461 deletions(-) create mode 100644 awx/ui/client/features/output/scroll.service.js diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index ce8a047e7f..25ebb9b531 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,83 +1,41 @@ -import Ansi from 'ansi-to-html'; -import hasAnsi from 'has-ansi'; - -let vm; -let ansi; -let model; -let resource; -let page; -let container; -let $timeout; -let $sce; -let $compile; -let $scope; -let $q; - -const record = {}; - -let parent = null; - -const SCROLL_THRESHOLD = 0.1; -const SCROLL_DELAY = 1000; -const EVENT_START_TASK = 'playbook_on_task_start'; -const EVENT_START_PLAY = 'playbook_on_play_start'; -const EVENT_STATS_PLAY = 'playbook_on_stats'; -const ELEMENT_TBODY = '#atStdoutResultTable'; -const ELEMENT_CONTAINER = '.at-Stdout-container'; const JOB_START = 'playbook_on_start'; const JOB_END = 'playbook_on_stats'; -const EVENT_GROUPS = [ - EVENT_START_TASK, - EVENT_START_PLAY -]; - -const TIME_EVENTS = [ - EVENT_START_TASK, - EVENT_START_PLAY, - EVENT_STATS_PLAY -]; +let vm; +let $compile; +let $scope; +let $q; +let page; +let render; +let scroll; +let resource; function JobsIndexController ( _resource_, _page_, - _$sce_, - _$timeout_, + _scroll_, + _render_, _$scope_, _$compile_, _$q_ ) { vm = this || {}; - $timeout = _$timeout_; - $sce = _$sce_; $compile = _$compile_; $scope = _$scope_; $q = _$q_; resource = _resource_; + page = _page_; - model = resource.model; - - ansi = new Ansi(); - - const events = model.get(`related.${resource.related}.results`); - const parsed = parseEvents(events); - const html = $sce.trustAsHtml(parsed.html); - - page.init(resource); - - page.add({ number: 1, lines: parsed.lines }); + scroll = _scroll_; + render = _render_; // Development helper(s) vm.clear = devClear; // Stdout Navigation vm.scroll = { - isLocked: false, showBackToTop: false, - isActive: false, - position: 0, - time: 0, home: scrollHome, end: scrollEnd, down: scrollPageDown, @@ -90,87 +48,93 @@ function JobsIndexController ( vm.isExpanded = true; // Real-time (active between JOB_START and JOB_END events only) - $scope.$on(resource.ws.namespace, processWebSocketEvents); vm.stream = { - isActive: false, - isRendering: false, - isPaused: false, - buffered: 0, - count: 0, - page: 1 + active: false, + rendering: false, + paused: false }; - window.requestAnimationFrame(() => { - const table = $(ELEMENT_TBODY); - container = $(ELEMENT_CONTAINER); + const stream = false; // TODO: Set in route - table.html($sce.getTrustedHtml(html)); - $compile(table.contents())($scope); - - container.scroll(onScroll); - }); + render.requestAnimationFrame(() => init()); } -function processWebSocketEvents (scope, data) { - let done; +function init (stream) { + page.init(resource); + render.init({ + get: () => resource.model.get(`related.${resource.related}.results`), + compile: html => $compile(html)($scope) + }); + + scroll.init({ + isAtRest: scrollIsAtRest, + previous, + next + }); + + if (stream) { + $scope.$on(resource.ws.namespace, process); + } else { + next(); + } +} + +function process (scope, data) { if (data.event === JOB_START) { - vm.scroll.isActive = true; - vm.stream.isActive = true; - vm.scroll.isLocked = true; + vm.stream.active = true; + scroll.lock(); } else if (data.event === JOB_END) { - vm.stream.isActive = false; + vm.stream.active = false; } const pageAdded = page.addToBuffer(data); - if (pageAdded && !vm.scroll.isLocked) { - vm.stream.isPaused = true; + if (pageAdded && !scroll.isLocked()) { + vm.stream.paused = true; } - if (vm.stream.isPaused && vm.scroll.isLocked) { - vm.stream.isPaused = false; + if (vm.stream.paused && scroll.isLocked()) { + vm.stream.paused = false; } - if (vm.stream.isRendering || vm.stream.isPaused) { + if (vm.stream.rendering || vm.stream.paused) { return; } const events = page.emptyBuffer(); - return render(events); + return renderStream(events); } -function render (events) { - vm.stream.isRendering = true; +function renderStream (events) { + vm.stream.rendering = true; return shift() .then(() => append(events)) .then(() => { - if (vm.scroll.isLocked) { - const height = container[0].scrollHeight; - container[0].scrollTop = height; + if (scroll.isLocked()) { + scroll.setScrollPosition(scroll.getScrollHeight()); } - if (!vm.stream.isActive) { + if (!vm.stream.active) { const buffer = page.emptyBuffer(); if (buffer.length) { - return render(buffer); + return renderStream(buffer); + } else { + vm.stream.rendering = false; + scroll.unlock(); } - - vm.stream.isRendering = false; - vm.scroll.isLocked = false; - vm.scroll.isActive = false; } else { - vm.stream.isRendering = false; + vm.stream.rendering = false; } }); } function devClear () { - page.init(resource); - clear(); + init(true); + render.clear(); } function next () { @@ -182,13 +146,12 @@ function next () { return shift() .then(() => append(events)); - }) + }); } function previous () { - const container = $(ELEMENT_CONTAINER)[0]; - - let previousHeight; + let initialPosition = scroll.getScrollPosition(); + let postPopHeight; return page.previous() .then(events => { @@ -198,296 +161,56 @@ function previous () { return pop() .then(() => { - previousHeight = container.scrollHeight; + postPopHeight = scroll.getScrollHeight(); return prepend(events); }) .then(() => { - const currentHeight = container.scrollHeight; - container.scrollTop = currentHeight - previousHeight; + const currentHeight = scroll.getScrollHeight(); + + scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition); }); }); } function append (events) { - return $q(resolve => { - window.requestAnimationFrame(() => { - const parsed = parseEvents(events); - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); - const table = $(ELEMENT_TBODY); - - page.updateLineCount('current', parsed.lines); - - table.append(rows); - $compile(rows.contents())($scope); - - return resolve(); + return render.append(events) + .then(count => { + page.updateLineCount('current', count); }); - }); } function prepend (events) { - return $q(resolve => { - window.requestAnimationFrame(() => { - const parsed = parseEvents(events); - const rows = $($sce.getTrustedHtml($sce.trustAsHtml(parsed.html))); - const table = $(ELEMENT_TBODY); - - page.updateLineCount('current', parsed.lines); - - table.prepend(rows); - $compile(rows.contents())($scope); - - $scope.$apply(() => { - return resolve(parsed.lines); - }); + return render.prepend(events) + .then(count => { + page.updateLineCount('current', count); }); - }); } function pop () { - return $q(resolve => { - if (!page.isOverCapacity()) { - return resolve(); - } + if (!page.isOverCapacity()) { + return $q.resolve(); + } - window.requestAnimationFrame(() => { - const lines = page.trim('right'); - const rows = $(ELEMENT_TBODY).children().slice(-lines); + const lines = page.trim('right'); - rows.empty(); - rows.remove(); - - return resolve(); - }); - }); + return render.pop(lines); } function shift () { - return $q(resolve => { - if (!page.isOverCapacity()) { - return resolve(); - } + if (!page.isOverCapacity()) { + return $q.resolve(); + } - window.requestAnimationFrame(() => { - const lines = page.trim('left'); - const rows = $(ELEMENT_TBODY).children().slice(0, lines); + const lines = page.trim('left'); - rows.empty(); - rows.remove(); - - return resolve(); - }); - }); -} - -function clear () { - return $q(resolve => { - window.requestAnimationFrame(() => { - const rows = $(ELEMENT_TBODY).children(); - - rows.empty(); - rows.remove(); - - return resolve(); - }); - }); + return render.shift(lines); } function expand () { vm.toggle(parent, true); } -function parseEvents (events) { - let lines = 0; - let html = ''; - - events.sort(orderByLineNumber); - - events.forEach(event => { - const line = parseLine(event); - - html += line.html; - lines += line.count; - }); - - return { - html, - lines - }; -} - -function orderByLineNumber (a, b) { - if (a.start_line > b.start_line) { - return 1; - } - - if (a.start_line < b.start_line) { - return -1; - } - - return 0; -} - -function parseLine (event) { - if (!event || !event.stdout) { - return { html: '', count: 0 }; - } - - const { stdout } = event; - const lines = stdout.split('\r\n'); - - let count = lines.length; - let ln = event.start_line; - - const current = createRecord(ln, lines, event); - - const html = lines.reduce((html, line, i) => { - ln++; - - const isLastLine = i === lines.length - 1; - let row = createRow(current, ln, line); - - if (current && current.isTruncated && isLastLine) { - row += createRow(current); - count++; - } - - return `${html}${row}`; - }, ''); - - return { html, count }; -} - -function createRecord (ln, lines, event) { - if (!event.uuid) { - return null; - } - - const info = { - id: event.id, - line: ln + 1, - uuid: event.uuid, - level: event.event_level, - start: event.start_line, - end: event.end_line, - isTruncated: (event.end_line - event.start_line) > lines.length, - isHost: typeof event.host === 'number' - }; - - if (event.parent_uuid) { - info.parents = getParentEvents(event.parent_uuid); - } - - if (info.isTruncated) { - info.truncatedAt = event.start_line + lines.length; - } - - if (EVENT_GROUPS.includes(event.event)) { - info.isParent = true; - - if (event.event_level === 1) { - parent = event.uuid; - } - - if (event.parent_uuid) { - if (record[event.parent_uuid]) { - if (record[event.parent_uuid].children && - !record[event.parent_uuid].children.includes(event.uuid)) { - record[event.parent_uuid].children.push(event.uuid); - } else { - record[event.parent_uuid].children = [event.uuid]; - } - } - } - } - - if (TIME_EVENTS.includes(event.event)) { - info.time = getTime(event.created); - info.line++; - } - - record[event.uuid] = info; - - return info; -} - -function getParentEvents (uuid, list) { - list = list || []; - - if (record[uuid]) { - list.push(uuid); - - if (record[uuid].parents) { - list = list.concat(record[uuid].parents); - } - } - - return list; -} - -function createRow (current, ln, content) { - let id = ''; - let timestamp = ''; - let tdToggle = ''; - let tdEvent = ''; - let classList = ''; - - content = content || ''; - - if (hasAnsi(content)) { - content = ansi.toHtml(content); - } - - if (current) { - if (current.isParent && current.line === ln) { - id = current.uuid; - tdToggle = ``; - } - - if (current.isHost) { - tdEvent = `${content}`; - } - - if (current.time && current.line === ln) { - timestamp = `${current.time}`; - } - - if (current.parents) { - classList = current.parents.reduce((list, uuid) => `${list} child-of-${uuid}`, ''); - } - } - - if (!tdEvent) { - tdEvent = `${content}`; - } - - if (!tdToggle) { - tdToggle = ''; - } - - if (!ln) { - ln = '...'; - } - - return ` - - ${tdToggle} - ${ln} - ${tdEvent} - ${timestamp} - `; -} - -function getTime (created) { - const date = new Date(created); - const hour = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours(); - const minute = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes(); - const second = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds(); - - return `${hour}:${minute}:${second}`; -} - function showHostDetails (id) { jobEvent.request('get', id) .then(() => { @@ -527,100 +250,38 @@ function toggle (uuid, menu) { } } -function onScroll () { - if (vm.scroll.isActive) { - return; - } - - if (vm.scroll.register) { - $timeout.cancel(vm.scroll.register); - } - - vm.scroll.register = $timeout(registerScrollEvent, SCROLL_DELAY); -} - -function registerScrollEvent () { - vm.scroll.isActive = true; - - const position = container[0].scrollTop; - const height = container[0].offsetHeight; - const downward = position > vm.scroll.position; - - let promise; - - if (position !== 0 ) { - vm.scroll.showBackToTop = true; - } else { - vm.scroll.showBackToTop = false; - } - - - console.log('downward', downward); - if (downward) { - if (((height - position) / height) < SCROLL_THRESHOLD) { - promise = next; - } - } else { - if ((position / height) < SCROLL_THRESHOLD) { - console.log('previous'); - promise = previous; - } - } - - vm.scroll.position = position; - - if (!promise) { - vm.scroll.isActive = false; - - return $q.resolve(); - } - - return promise() - .then(() => { - console.log('done'); - vm.scroll.isActive = false; - /* - *$timeout(() => { - * vm.scroll.isActive = false; - *}, SCROLL_DELAY); - */ - }); - -} - function scrollHome () { + scroll.pause(); + return page.first() .then(events => { if (!events) { return; } - return clear() - .then(() => prepend(events)) + return render.clear() + .then(() => render.prepend(events)) .then(() => { - vm.scroll.isActive = false; + scroll.setScrollPosition(0); + scroll.resume(); }); }); } function scrollEnd () { - if (vm.scroll.isLocked) { + if (scroll.isLocked()) { page.bookmark(); - - vm.scroll.isLocked = false; - vm.scroll.isActive = false; + scroll.unlock(); return; - } else if (!vm.scroll.isLocked && vm.stream.isActive) { + } else if (!scroll.isLocked() && vm.stream.active) { page.bookmark(); - - vm.scroll.isActive = true; - vm.scroll.isLocked = true; + scroll.lock(); return; } - vm.scroll.isActive = true; + scroll.pause(); return page.last() .then(events => { @@ -628,36 +289,32 @@ function scrollEnd () { return; } - return clear() - .then(() => append(events)) + return render.clear() + .then(() => render.append(events)) .then(() => { - const container = $(ELEMENT_CONTAINER)[0]; - - container.scrollTop = container.scrollHeight; - vm.scroll.isActive = false; + scroll.setScrollPosition(scroll.getScrollHeight()); + scroll.resume(); }); }); } function scrollPageUp () { - const container = $(ELEMENT_CONTAINER)[0]; - const jump = container.scrollTop - container.offsetHeight; - - container.scrollTop = jump; + scroll.pageUp(); } function scrollPageDown () { - const container = $(ELEMENT_CONTAINER)[0]; - const jump = container.scrollTop + container.offsetHeight; + scroll.pageDown(); +} - container.scrollTop = jump; +function scrollIsAtRest (isAtRest) { + vm.scroll.showBackToTop = !isAtRest; } JobsIndexController.$inject = [ 'resource', 'JobPageService', - '$sce', - '$timeout', + 'JobScrollService', + 'JobRenderService', '$scope', '$compile', '$q' diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 6d35022cf4..a0e0b70123 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -1,11 +1,10 @@ -import JobsStrings from '~features/output/jobs.strings'; -import IndexController from '~features/output/index.controller'; import atLibModels from '~models'; import atLibComponents from '~components'; import Strings from '~features/output/jobs.strings'; import Controller from '~features/output/index.controller'; import PageService from '~features/output/page.service'; +import ScrollService from '~features/output/scroll.service'; const Template = require('~features/output/index.view.html'); @@ -178,6 +177,7 @@ angular ]) .service('JobStrings', Strings) .service('JobPageService', PageService) + .service('JobScrollService', ScrollService) .run(JobsRun); export default MODULE_NAME; diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index b82eba4705..e91b80191e 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -1,16 +1,10 @@ function JobPageService ($q) { - this.page = null; - this.resource = null; - this.result = null; - this.buffer = null; - this.cache = null; - this.init = resource => { this.resource = resource; this.page = { - limit: resource.page.pageLimit, - size: resource.page.size, + limit: this.resource.page.pageLimit, + size: this.resource.page.size, current: 0, index: -1, count: 0, @@ -153,13 +147,10 @@ function JobPageService ($q) { } this.bookmark = () => { - console.log('b,current', this.page.current); if (!this.page.bookmark.active) { this.page.bookmark.first = this.page.first; this.page.bookmark.last = this.page.last; this.page.bookmark.current = this.page.current; - - console.log('b,bookmark', this.page.bookmark.current); this.page.bookmark.active = true; } else { this.page.bookmark.active = false; diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index e69de29bb2..235de94198 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -0,0 +1,297 @@ +import Ansi from 'ansi-to-html'; +import hasAnsi from 'has-ansi'; + +const ELEMENT_TBODY = '#atStdoutResultTable'; +const EVENT_START_TASK = 'playbook_on_task_start'; +const EVENT_START_PLAY = 'playbook_on_play_start'; +const EVENT_STATS_PLAY = 'playbook_on_stats'; +const JOB_START = 'playbook_on_start'; +const JOB_END = 'playbook_on_stats'; + +const EVENT_GROUPS = [ + EVENT_START_TASK, + EVENT_START_PLAY +]; + +const TIME_EVENTS = [ + EVENT_START_TASK, + EVENT_START_PLAY, + EVENT_STATS_PLAY +]; + +const ansi = new Ansi(); + +function JobRenderService ($q, $sce, $window) { + this.init = ({ compile, apply, get }) => { + this.parent = null; + this.record = {}; + this.el = $(ELEMENT_TBODY); + this.hooks = { get, compile, apply }; + }; + + this.sortByLineNumber = (a, b) => { + if (a.start_line > b.start_line) { + return 1; + } + + if (a.start_line < b.start_line) { + return -1; + } + + return 0; + }; + + this.transformEventGroup = events => { + let lines = 0; + let html = ''; + + events.sort(this.sortByLineNumber); + + events.forEach(event => { + const line = this.transformEvent(event); + + html += line.html; + lines += line.count; + }); + + return { html, lines }; + }; + + this.transformEvent = event => { + if (!event || !event.stdout) { + return { html: '', count: 0 }; + } + + const { stdout } = event; + const lines = stdout.split('\r\n'); + + let count = lines.length; + let ln = event.start_line; + + const current = this.createRecord(ln, lines, event); + + const html = lines.reduce((html, line, i) => { + ln++; + + const isLastLine = i === lines.length - 1; + let row = this.createRow(current, ln, line); + + if (current && current.isTruncated && isLastLine) { + row += this.createRow(current); + count++; + } + + return `${html}${row}`; + }, ''); + + return { html, count }; + }; + + this.createRecord = event => { + if (!event.uuid) { + return null; + } + + const info = { + id: event.id, + line: ln + 1, + uuid: event.uuid, + level: event.event_level, + start: event.start_line, + end: event.end_line, + isTruncated: (event.end_line - event.start_line) > lines.length, + isHost: typeof event.host === 'number' + }; + + if (event.parent_uuid) { + info.parents = getParentEvents(event.parent_uuid); + } + + if (info.isTruncated) { + info.truncatedAt = event.start_line + lines.length; + } + + if (EVENT_GROUPS.includes(event.event)) { + info.isParent = true; + + if (event.event_level === 1) { + this.parent = event.uuid; + } + + if (event.parent_uuid) { + if (this.record[event.parent_uuid]) { + if (this.record[event.parent_uuid].children && + !this.record[event.parent_uuid].children.includes(event.uuid)) { + this.record[event.parent_uuid].children.push(event.uuid); + } else { + this.record[event.parent_uuid].children = [event.uuid]; + } + } + } + } + + if (TIME_EVENTS.includes(event.event)) { + info.time = this.getTimestamp(event.created); + info.line++; + } + + this.record[event.uuid] = info; + + return info; + }; + + this.createRow = (current, ln, content) => { + let id = ''; + let timestamp = ''; + let tdToggle = ''; + let tdEvent = ''; + let classList = ''; + + content = content || ''; + + if (hasAnsi(content)) { + content = ansi.toHtml(content); + } + + if (current) { + if (current.isParent && current.line === ln) { + id = current.uuid; + tdToggle = ``; + } + + if (current.isHost) { + tdEvent = `${content}`; + } + + if (current.time && current.line === ln) { + timestamp = `${current.time}`; + } + + if (current.parents) { + classList = current.parents.reduce((list, uuid) => `${list} child-of-${uuid}`, ''); + } + } + + if (!tdEvent) { + tdEvent = `${content}`; + } + + if (!tdToggle) { + tdToggle = ''; + } + + if (!ln) { + ln = '...'; + } + + return ` + + ${tdToggle} + ${ln} + ${tdEvent} + ${timestamp} + `; + } + + this.getTimestamp = (created) => { + const date = new Date(created); + const hour = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours(); + const minute = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes(); + const second = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds(); + + return `${hour}:${minute}:${second}`; + } + + this.getParentEvents = (uuid, list) => { + list = list || []; + + if (this.record[uuid]) { + list.push(uuid); + + if (this.record[uuid].parents) { + list = list.concat(record[uuid].parents); + } + } + + return list; + }; + + this.getEvents = () => { + return this.hooks.get(); + }; + + this.insert = (events, insert) => { + const result = this.transformEventGroup(events); + const html = this.sanitize(result.html); + + return this.requestAnimationFrame(() => insert(html)) + .then(() => this.compile(html)) + .then(() => result.lines); + }; + + this.remove = elements => { + return this.requestAnimationFrame(() => { + elements.empty(); + elements.remove(); + }); + }; + + this.requestAnimationFrame = fn => { + return $q(resolve => { + $window.requestAnimationFrame(() => { + if (fn) { + fn(); + } + + return resolve(); + }); + }); + }; + + this.compile = html => { + this.hooks.compile(html); + + return this.requestAnimationFrame(); + }; + + this.build = () => { + + }; + + this.clear = () => { + const elements = this.el.children(); + + return this.remove(elements); + }; + + this.shift = lines => { + const elements = this.el.children().slice(0, lines); + + return this.remove(elements); + }; + + this.pop = lines => { + const elements = this.el.children().slice(-lines); + + return this.remove(elements); + }; + + this.prepend = events => { + return this.insert(events, html => this.el.prepend(html)) + }; + + this.append = events => { + return this.insert(events, html => this.el.append(html)) + }; + + // TODO: stdout from the API should not be trusted. + this.sanitize = html => { + html = $sce.trustAsHtml(html); + + return $sce.getTrustedHtml(html); + }; +} + +JobRenderService.$inject = ['$q', '$sce', '$window']; + +export default JobRenderService; diff --git a/awx/ui/client/features/output/scroll.service.js b/awx/ui/client/features/output/scroll.service.js new file mode 100644 index 0000000000..489871e354 --- /dev/null +++ b/awx/ui/client/features/output/scroll.service.js @@ -0,0 +1,172 @@ +const ELEMENT_CONTAINER = '.at-Stdout-container'; +const DELAY = 100; +const THRESHOLD = 0.1; + +function JobScrollService ($q, $timeout) { + this.init = (hooks) => { + this.el = $(ELEMENT_CONTAINER); + this.timer = null; + + this.position = { + previous: 0, + current: 0 + }; + + this.hooks = { + isAtRest: hooks.isAtRest, + next: hooks.next, + previous: hooks.previous + }; + + this.state = { + locked: false, + paused: false, + top: true + }; + + this.el.scroll(this.listen); + }; + + this.listen = () => { + if (this.isPaused()) { + return; + } + + if (this.timer) { + $timeout.cancel(this.timer); + } + + this.timer = $timeout(this.register, DELAY); + }; + + this.register = () => { + this.pause(); + + const height = this.getScrollHeight(); + const current = this.getScrollPosition(); + const downward = current > this.position.previous; + + let promise; + + if (downward && this.isBeyondThreshold(downward, current)) { + promise = this.hooks.next; + } else if (!downward && this.isBeyondThreshold(downward, current)) { + promise = this.hooks.previous; + } + + if (!promise) { + this.setScrollPosition(current); + this.isAtRest(); + this.resume(); + + return $q.resolve(); + } + + return promise() + .then(() => { + this.setScrollPosition(this.getScrollPosition()); + this.isAtRest(); + this.resume(); + }); + }; + + this.isBeyondThreshold = (downward, current) => { + const previous = this.position.previous; + const height = this.getScrollHeight(); + + if (downward) { + current += this.getViewableHeight(); + + if (current >= height || ((height - current) / height) < THRESHOLD) { + return true; + } + } else { + if (current <= 0 || (current / height) < THRESHOLD) { + return true; + } + } + + return false; + }; + + this.pageUp = () => { + if (this.isPaused()) { + return; + } + + const top = this.getScrollPosition(); + const height = this.getViewableHeight(); + + this.setScrollPosition(top - height); + }; + + this.pageDown = () => { + if (this.isPaused()) { + return; + } + + const top = this.getScrollPosition(); + const height = this.getViewableHeight(); + + this.setScrollPosition(top + height); + }; + + this.getScrollHeight = () => { + return this.el[0].scrollHeight; + }; + + this.getViewableHeight = () => { + return this.el[0].offsetHeight; + }; + + this.getScrollPosition = () => { + return this.el[0].scrollTop; + }; + + this.setScrollPosition = position => { + this.position.previous = this.position.current; + this.position.current = position; + this.el[0].scrollTop = position; + this.isAtRest(); + }; + + this.isAtRest = () => { + if (this.position.current === 0 && !this.state.top) { + this.state.top = true; + this.hooks.isAtRest(true); + } else if (this.position.current > 0 && this.state.top) { + this.state.top = false; + this.hooks.isAtRest(false); + } + }; + + this.resume = () => { + this.state.paused = false; + }; + + this.pause = () => { + this.state.paused = true; + }; + + this.isPaused = () => { + return this.state.paused; + }; + + this.lock = () => { + this.state.locked = true; + this.state.paused = true; + }; + + this.unlock = () => { + this.state.locked = false; + this.state.paused = false; + }; + + this.isLocked = () => { + return this.state.locked; + }; +} + +JobScrollService.$inject = ['$q', '$timeout']; + +export default JobScrollService; From 3705169de0e25ba25ec0e62544279f2be0c21cec Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 2 Mar 2018 16:23:44 -0500 Subject: [PATCH 132/379] Remove stream service from job index includes --- awx/ui/client/features/output/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index a0e0b70123..bc73ca83c7 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -4,6 +4,7 @@ import atLibComponents from '~components'; import Strings from '~features/output/jobs.strings'; import Controller from '~features/output/index.controller'; import PageService from '~features/output/page.service'; +import RenderService from '~features/output/render.service'; import ScrollService from '~features/output/scroll.service'; const Template = require('~features/output/index.view.html'); From 60a19246ae44c2bf038abf9dc97d1fb102b8a6da Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 6 Mar 2018 17:47:22 -0500 Subject: [PATCH 133/379] Add promise-based event processesing for consistency --- .../features/output/index.controller.js | 180 +++++++------ awx/ui/client/features/output/page.service.js | 245 ++++++++++-------- .../client/features/output/scroll.service.js | 7 + 3 files changed, 244 insertions(+), 188 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 25ebb9b531..e2aeae2d90 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -10,6 +10,8 @@ let render; let scroll; let resource; +let chain; + function JobsIndexController ( _resource_, _page_, @@ -56,6 +58,7 @@ function JobsIndexController ( const stream = false; // TODO: Set in route + chain = $q.resolve(); render.requestAnimationFrame(() => init()); } @@ -81,37 +84,39 @@ function init (stream) { } function process (scope, data) { - if (data.event === JOB_START) { - vm.stream.active = true; - scroll.lock(); - } else if (data.event === JOB_END) { - vm.stream.active = false; - } + chain = chain.then(() => { + if (data.event === JOB_START) { + vm.stream.active = true; + scroll.lock(); + } else if (data.event === JOB_END) { + vm.stream.active = false; + } - const pageAdded = page.addToBuffer(data); + const pageAdded = page.addToBuffer(data); - if (pageAdded && !scroll.isLocked()) { - vm.stream.paused = true; - } + if (pageAdded && !scroll.isLocked()) { + vm.stream.paused = true; + } - if (vm.stream.paused && scroll.isLocked()) { - vm.stream.paused = false; - } + if (vm.stream.paused && scroll.isLocked()) { + vm.stream.paused = false; + } - if (vm.stream.rendering || vm.stream.paused) { - return; - } + if (vm.stream.rendering || vm.stream.paused) { + return; + } - const events = page.emptyBuffer(); + const events = page.emptyBuffer(); - return renderStream(events); + return renderStream(events); + }) } function renderStream (events) { vm.stream.rendering = true; return shift() - .then(() => append(events)) + .then(() => append(events, true)) .then(() => { if (scroll.isLocked()) { scroll.setScrollPosition(scroll.getScrollHeight()); @@ -135,6 +140,12 @@ function renderStream (events) { function devClear () { init(true); render.clear(); + + vm.stream = { + active: false, + rendering: false, + paused: false + }; } function next () { @@ -167,23 +178,22 @@ function previous () { }) .then(() => { const currentHeight = scroll.getScrollHeight(); - scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition); }); }); } -function append (events) { +function append (events, stream) { return render.append(events) .then(count => { - page.updateLineCount('current', count); + page.updateLineCount(count, stream); }); } function prepend (events) { return render.prepend(events) .then(count => { - page.updateLineCount('current', count); + page.updateLineCount(count); }); } @@ -192,7 +202,7 @@ function pop () { return $q.resolve(); } - const lines = page.trim('right'); + const lines = page.trim(); return render.pop(lines); } @@ -202,11 +212,71 @@ function shift () { return $q.resolve(); } - const lines = page.trim('left'); + const lines = page.trim(true); return render.shift(lines); } +function scrollHome () { + scroll.pause(); + + return page.first() + .then(events => { + if (!events) { + return; + } + + return render.clear() + .then(() => prepend(events)) + .then(() => { + scroll.resetScrollPosition(); + scroll.resume(); + }); + }); +} + +function scrollEnd () { + if (scroll.isLocked()) { + page.setBookmark(); + scroll.unlock(); + + return; + } else if (!scroll.isLocked() && vm.stream.active) { + page.removeBookmark(); + scroll.lock(); + + return; + } + + scroll.pause(); + + return page.last() + .then(events => { + if (!events) { + return; + } + + return render.clear() + .then(() => append(events)) + .then(() => { + scroll.setScrollPosition(scroll.getScrollHeight()); + scroll.resume(); + }); + }); +} + +function scrollPageUp () { + scroll.pageUp(); +} + +function scrollPageDown () { + scroll.pageDown(); +} + +function scrollIsAtRest (isAtRest) { + vm.scroll.showBackToTop = !isAtRest; +} + function expand () { vm.toggle(parent, true); } @@ -250,66 +320,6 @@ function toggle (uuid, menu) { } } -function scrollHome () { - scroll.pause(); - - return page.first() - .then(events => { - if (!events) { - return; - } - - return render.clear() - .then(() => render.prepend(events)) - .then(() => { - scroll.setScrollPosition(0); - scroll.resume(); - }); - }); -} - -function scrollEnd () { - if (scroll.isLocked()) { - page.bookmark(); - scroll.unlock(); - - return; - } else if (!scroll.isLocked() && vm.stream.active) { - page.bookmark(); - scroll.lock(); - - return; - } - - scroll.pause(); - - return page.last() - .then(events => { - if (!events) { - return; - } - - return render.clear() - .then(() => render.append(events)) - .then(() => { - scroll.setScrollPosition(scroll.getScrollHeight()); - scroll.resume(); - }); - }); -} - -function scrollPageUp () { - scroll.pageUp(); -} - -function scrollPageDown () { - scroll.pageDown(); -} - -function scrollIsAtRest (isAtRest) { - vm.scroll.showBackToTop = !isAtRest; -} - JobsIndexController.$inject = [ 'resource', 'JobPageService', diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index e91b80191e..a89d56d6b8 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -5,12 +5,21 @@ function JobPageService ($q) { this.page = { limit: this.resource.page.pageLimit, size: this.resource.page.size, - current: 0, - index: -1, - count: 0, - first: 0, - last: 0, - bookmark: { + cache: [], + state: { + count: 0, + current: 0, + first: 0, + last: 0 + } + }; + + this.bookmark = { + pending: false, + set: false, + cache: [], + state: { + count: 0, first: 0, last: 0, current: 0 @@ -25,43 +34,45 @@ function JobPageService ($q) { this.buffer = { count: 0 }; - - this.cache = []; }; - this.add = (page, position, bookmark) => { - page.events = page.events || []; - page.lines = page.lines || 0; + this.addPage = (number, events, push, reference) => { + const page = { number, events, lines: 0 }; + reference = reference || this.getActiveReference(); - if (position === 'first') { - this.cache.unshift(page); - this.page.first = page.number; - this.page.last = this.cache[this.cache.length -1].number; + if (push) { + reference.cache.push(page); + reference.state.last = page.number; + reference.state.first = reference.cache[0].number; } else { - this.cache.push(page); - this.page.last = page.number; - this.page.first = this.cache[0].number; + reference.cache.unshift(page); + reference.state.first = page.number; + reference.state.last = reference.cache[reference.cache.length -1].number; } - if (bookmark) { - this.page.bookmark.current = page.number; - } + reference.state.current = page.number; + reference.state.count++; + }; - this.page.current = page.number; - this.page.count++; + this.addToPageCache = (index, event, reference) => { + reference.cache[index].events.push(event); }; this.addToBuffer = event => { + const reference = this.getReference(); let pageAdded = false; if (this.result.count % this.page.size === 0) { - pageAdded = true; + this.addPage(reference.state.current + 1, [event], true, reference); - this.add({ number: this.page.count + 1, events: [event] }); + if (this.isBookmarkPending()) { + this.setBookmark(); + } this.trimBuffer(); + pageAdded = true; } else { - this.cache[this.cache.length - 1].events.push(event); + this.addToPageCache(reference.cache.length - 1, event, reference); } this.buffer.count++; @@ -71,104 +82,127 @@ function JobPageService ($q) { }; this.trimBuffer = () => { - const diff = this.cache.length - this.page.limit; + const reference = this.getReference(); + const diff = reference.cache.length - this.page.limit; if (diff <= 0) { return; } for (let i = 0; i < diff; i++) { - if (this.cache[i].events) { - this.buffer.count -= this.cache[i].events.length; - this.cache[i].events = []; + if (reference.cache[i].events) { + this.buffer.count -= reference.cache[i].events.length; + reference.cache[i].events.splice(0, reference.cache[i].events.length); } } }; this.emptyBuffer = () => { + const reference = this.getReference(); let data = []; - for (let i = 0; i < this.cache.length; i++) { - const events = this.cache[i].events; + for (let i = 0; i < reference.cache.length; i++) { + const count = reference.cache[i].events.length; - if (events.length > 0) { - this.buffer.count -= events.length; - data = data.concat(this.cache[i].events.splice(0, events.length)); + if (count > 0) { + this.buffer.count -= count; + data = data.concat(reference.cache[i].events.splice(0, count)); } } return data; }; - this.emptyCache = () => { - this.page.first = this.page.current; - this.page.last = this.page.current; - this.cache = []; + this.emptyCache = number => { + const reference = this.getActiveReference(); + + number = number || reference.state.current; + + reference.state.first = number; + reference.state.last = number; + reference.state.current = number; + reference.cache.splice(0, reference.cache.length); }; this.isOverCapacity = () => { - return (this.cache.length - this.page.limit) > 0; + const reference = this.getActiveReference(); + + return (reference.cache.length - this.page.limit) > 0; }; - this.trim = side => { - const count = this.cache.length - this.page.limit; - + this.trim = left => { + let reference = this.getActiveReference(); + let excess = reference.cache.length - this.page.limit; let ejected; - if (side === 'left') { - ejected = this.cache.splice(0, count); - this.page.first = this.cache[0].number; + if (left) { + ejected = reference.cache.splice(0, excess); + reference.state.first = reference.cache[0].number; } else { - ejected = this.cache.splice(-count); - this.page.last = this.cache[this.cache.length - 1].number; + ejected = reference.cache.splice(-excess); + reference.state.last = reference.cache[reference.cache.length - 1].number; } return ejected.reduce((total, page) => total + page.lines, 0); }; - this.getPageNumber = page => { - let index; - - if (page === 'first') { - index = 0; - } - - return this.cache[index].number; + this.isPageBookmarked = number => { + return number >= this.page.bookmark.first && number <= this.page.bookmark.last; }; - this.updateLineCount = (page, lines) => { - let index; + this.updateLineCount = (lines, stream) => { + let reference; - if (page === 'current') { - index = this.cache.findIndex(item => item.number === this.page.current); + if (stream) { + reference = this.getReference(); + } else { + reference = this.getActiveReference(); } - this.cache[index].lines += lines; + const index = reference.cache.findIndex(item => item.number === reference.state.current); + + reference.cache[index].lines += lines; } - this.bookmark = () => { - if (!this.page.bookmark.active) { - this.page.bookmark.first = this.page.first; - this.page.bookmark.last = this.page.last; - this.page.bookmark.current = this.page.current; - this.page.bookmark.active = true; - } else { - this.page.bookmark.active = false; + this.isBookmarkPending = () => { + return this.bookmark.pending; + }; + + this.isBookmarkSet = () => { + return this.bookmark.set; + }; + + this.setBookmark = () => { + if (this.isBookmarkSet()) { + return; } + + if (!this.isBookmarkPending()) { + this.bookmark.pending = true; + + return; + } + + this.bookmark.state.first = this.page.state.first; + this.bookmark.state.last = this.page.state.last; + this.bookmark.state.current = this.page.state.current; + this.bookmark.cache = JSON.parse(JSON.stringify(this.page.cache)); + this.bookmark.set = true; + this.bookmark.pending = false; + }; + + this.removeBookmark = () => { + this.bookmark.set = false; + this.bookmark.pending = false; + this.bookmark.cache.splice(0, this.bookmark.cache.length); + this.bookmark.state.first = 0; + this.bookmark.state.last = 0; + this.bookmark.state.current = 0; }; this.next = () => { - let page; - let bookmark; - - if (this.page.bookmark.active) { - page = this.page.bookmark.current + 1; - bookmark = true; - } else { - page = this.page.last + 1; - } - - const config = this.buildRequestConfig(page); + const reference = this.getActiveReference(); + const config = this.buildRequestConfig(reference.state.last + 1); return this.resource.model.goToPage(config) .then(data => { @@ -176,24 +210,15 @@ function JobPageService ($q) { return $q.resolve(); } - this.add({ number: data.page, events: [], lines: 0 }, 'last', bookmark); + this.addPage(data.page, [], true); return data.results; }); }; this.previous = () => { - let page; - let bookmark; - - if (this.page.bookmark.active) { - page = this.page.bookmark.current - 1; - bookmark = true; - } else { - page = this.page.first - 1; - } - - const config = this.buildRequestConfig(page); + const reference = this.getActiveReference(); + const config = this.buildRequestConfig(reference.state.first - 1); return this.resource.model.goToPage(config) .then(data => { @@ -201,7 +226,7 @@ function JobPageService ($q) { return $q.resolve(); } - this.add({ number: data.page, events: [], lines: 0 }, 'first', bookmark); + this.addPage(data.page, [], false); return data.results; }); @@ -210,15 +235,14 @@ function JobPageService ($q) { this.last = () => { const config = this.buildRequestConfig('last'); - this.emptyCache(); - return this.resource.model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - this.add({ number: data.page, events: [], lines: 0 }, 'last'); + this.emptyCache(data.page); + this.addPage(data.page, [], true); return data.results; }); @@ -227,23 +251,22 @@ function JobPageService ($q) { this.first = () => { const config = this.buildRequestConfig('first'); - this.emptyCache(); - return this.resource.model.goToPage(config) .then(data => { if (!data || !data.results) { return $q.resolve(); } - this.add({ number: data.page, events: [], lines: 0 }, 'first'); + this.emptyCache(data.page); + this.addPage(data.page, [], false); return data.results; }); }; - this.buildRequestConfig = (page) => { + this.buildRequestConfig = number => { return { - page, + page: number, related: this.resource.related, params: { order_by: 'start_line' @@ -251,8 +274,24 @@ function JobPageService ($q) { }; }; - this.current = () => { - return this.resource.model.get(`related.${this.resource.related}.results`); + this.getActiveReference = () => { + return this.isBookmarkSet() ? this.getReference(true) : this.getReference(); + }; + + this.getReference = (bookmark) => { + if (bookmark) { + return { + bookmark: true, + cache: this.bookmark.cache, + state: this.bookmark.state + }; + } + + return { + bookmark: false, + cache: this.page.cache, + state: this.page.state + }; }; } diff --git a/awx/ui/client/features/output/scroll.service.js b/awx/ui/client/features/output/scroll.service.js index 489871e354..ef80655bf5 100644 --- a/awx/ui/client/features/output/scroll.service.js +++ b/awx/ui/client/features/output/scroll.service.js @@ -130,6 +130,13 @@ function JobScrollService ($q, $timeout) { this.isAtRest(); }; + this.resetScrollPosition = () => { + this.position.previous = 0; + this.position.current = 0; + this.el[0].scrollTop = 0; + this.isAtRest(); + }; + this.isAtRest = () => { if (this.position.current === 0 && !this.state.top) { this.state.top = true; From f0862cd413bbf9f7c0dd9cb7b932f6690fdb7819 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 16:30:44 -0400 Subject: [PATCH 134/379] avoid v1 cruft fields in JobCancelSerializer --- awx/api/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 0fc9baa6e5..aaeefad579 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3248,11 +3248,12 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): return summary_fields -class JobCancelSerializer(JobSerializer): +class JobCancelSerializer(BaseSerializer): can_cancel = serializers.BooleanField(read_only=True) class Meta: + model = Job fields = ('can_cancel',) From 02b7424b29bd6259612c1aac565f5343566f4567 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 4 Apr 2018 13:54:14 -0400 Subject: [PATCH 135/379] check for existence and type of all spec items --- awx/api/views.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/awx/api/views.py b/awx/api/views.py index 82e017f0ec..462a8946b4 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -3128,16 +3128,22 @@ class JobTemplateSurveySpec(GenericAPIView): return Response() def _validate_spec_data(self, new_spec, old_spec): - if "name" not in new_spec: - return Response(dict(error=_("'name' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST) - if "description" not in new_spec: - return Response(dict(error=_("'description' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST) - if "spec" not in new_spec: - return Response(dict(error=_("'spec' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST) - if not isinstance(new_spec["spec"], list): - return Response(dict(error=_("'spec' must be a list of items.")), status=status.HTTP_400_BAD_REQUEST) - if len(new_spec["spec"]) < 1: - return Response(dict(error=_("'spec' doesn't contain any items.")), status=status.HTTP_400_BAD_REQUEST) + schema_errors = {} + for field, expect_type, type_label in [ + ('name', six.string_types, 'string'), + ('description', six.string_types, 'string'), + ('spec', list, 'list of items')]: + if field not in new_spec: + schema_errors['error'] = _("Field '{}' is missing from survey spec.").format(field) + elif not isinstance(new_spec[field], expect_type): + schema_errors['error'] = _("Expected {} for field '{}', received {} type.").format( + type_label, field, type(new_spec[field]).__name__) + + if isinstance(new_spec.get('spec', None), list) and len(new_spec["spec"]) < 1: + schema_errors['error'] = _("'spec' doesn't contain any items.") + + if schema_errors: + return Response(schema_errors, status=status.HTTP_400_BAD_REQUEST) variable_set = set() old_spec_dict = JobTemplate.pivot_spec(old_spec) From a0948b410e8a8c52cd0ba15a0bb87c7ed27bee00 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Wed, 4 Apr 2018 16:43:32 -0400 Subject: [PATCH 136/379] embed license panel within settings pane --- awx/ui/client/legacy/styles/forms.less | 4 ++++ .../client/src/configuration/configuration.partial.html | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/awx/ui/client/legacy/styles/forms.less b/awx/ui/client/legacy/styles/forms.less index 9cc5779d36..e3cfe1e765 100644 --- a/awx/ui/client/legacy/styles/forms.less +++ b/awx/ui/client/legacy/styles/forms.less @@ -102,6 +102,10 @@ flex-wrap:wrap; } +.Form-tabHolder--licenseSelected { + margin-bottom: -20px; +} + .Form-tabs { flex: 1 0 auto; display: flex; diff --git a/awx/ui/client/src/configuration/configuration.partial.html b/awx/ui/client/src/configuration/configuration.partial.html index a567e6bde2..c7f65fd05e 100644 --- a/awx/ui/client/src/configuration/configuration.partial.html +++ b/awx/ui/client/src/configuration/configuration.partial.html @@ -5,13 +5,13 @@
-
+
-
CONFIGURE {{BRAND_NAME}}
+
CONFIGURE {{BRAND_NAME}}
-
+
Authentication
Jobs
System
@@ -24,8 +24,7 @@
+
- -
From e49204381919fbbb3ed2770169d58add1b5f1305 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 2 Apr 2018 13:57:56 -0400 Subject: [PATCH 137/379] utilize new list on projects template list --- .../features/templates/index.controller.js | 12 ++ awx/ui/client/features/templates/index.js | 2 - .../client/features/templates/index.view.html | 10 ++ .../routes/projectsTemplatesList.route.js | 55 +++++++++ .../templatesList.route.js} | 23 ++-- ...troller.js => templatesList.controller.js} | 0 .../templates/templatesList.view.html | 114 ++++++++++++++++++ awx/ui/client/src/projects/main.js | 3 +- .../templates/labels/labelsList.directive.js | 6 +- awx/ui/client/src/templates/main.js | 2 +- 10 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 awx/ui/client/features/templates/index.controller.js create mode 100644 awx/ui/client/features/templates/index.view.html create mode 100644 awx/ui/client/features/templates/routes/projectsTemplatesList.route.js rename awx/ui/client/features/templates/{list.route.js => routes/templatesList.route.js} (72%) rename awx/ui/client/features/templates/{list-templates.controller.js => templatesList.controller.js} (100%) create mode 100644 awx/ui/client/features/templates/templatesList.view.html diff --git a/awx/ui/client/features/templates/index.controller.js b/awx/ui/client/features/templates/index.controller.js new file mode 100644 index 0000000000..fd0850d582 --- /dev/null +++ b/awx/ui/client/features/templates/index.controller.js @@ -0,0 +1,12 @@ +function IndexTemplatesController (strings, dataset) { + let vm = this; + vm.strings = strings; + vm.count = dataset.data.count; +} + +IndexTemplatesController.$inject = [ + 'TemplatesStrings', + 'Dataset' +]; + +export default IndexTemplatesController; diff --git a/awx/ui/client/features/templates/index.js b/awx/ui/client/features/templates/index.js index a2f6ab25a4..fd0a49b45a 100644 --- a/awx/ui/client/features/templates/index.js +++ b/awx/ui/client/features/templates/index.js @@ -1,11 +1,9 @@ import TemplatesStrings from './templates.strings'; -import ListController from './list-templates.controller'; const MODULE_NAME = 'at.features.templates'; angular .module(MODULE_NAME, []) - .controller('ListController', ListController) .service('TemplatesStrings', TemplatesStrings); export default MODULE_NAME; diff --git a/awx/ui/client/features/templates/index.view.html b/awx/ui/client/features/templates/index.view.html new file mode 100644 index 0000000000..6323fd8129 --- /dev/null +++ b/awx/ui/client/features/templates/index.view.html @@ -0,0 +1,10 @@ +
+ + + {{:: vm.strings.get('list.PANEL_TITLE') }} +
+ {{ vm.count }} +
+
+
+
diff --git a/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js b/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js new file mode 100644 index 0000000000..2fa97411b8 --- /dev/null +++ b/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js @@ -0,0 +1,55 @@ +import { N_ } from '../../../src/i18n'; +import templatesListController from '../templatesList.controller'; + +const templatesListTemplate = require('~features/templates/templatesList.view.html'); + +export default { + url: "/templates", + name: 'projects.edit.templates', + params: { + template_search: { + dynamic: true, + value: { + }, + } + }, + ncyBreadcrumb: { + label: N_("JOB TEMPLATES") + }, + views: { + 'related': { + controller: templatesListController, + templateUrl: templatesListTemplate, + controllerAs: 'vm' + } + }, + resolve: { + resolvedModels: [ + 'JobTemplateModel', + 'WorkflowJobTemplateModel', + (JobTemplate, WorkflowJobTemplate) => { + const models = [ + new JobTemplate(['options']), + new WorkflowJobTemplate(['options']), + ]; + return Promise.all(models); + }, + ], + Dataset: [ + '$stateParams', + 'Wait', + 'GetBasePath', + 'QuerySet', + ($stateParams, Wait, GetBasePath, qs) => { + const searchPath = GetBasePath('unified_job_templates'); + + const searchParam = _.assign($stateParams.template_search, { + jobtemplate__project: $stateParams.project_id }); + + Wait('start'); + return qs.search(searchPath, searchParam) + .finally(() => Wait('stop')); + } + ], + } +}; diff --git a/awx/ui/client/features/templates/list.route.js b/awx/ui/client/features/templates/routes/templatesList.route.js similarity index 72% rename from awx/ui/client/features/templates/list.route.js rename to awx/ui/client/features/templates/routes/templatesList.route.js index e08b2fc863..8a912776cc 100644 --- a/awx/ui/client/features/templates/list.route.js +++ b/awx/ui/client/features/templates/routes/templatesList.route.js @@ -1,16 +1,14 @@ -import ListController from './list-templates.controller'; -const listTemplate = require('~features/templates/list.view.html'); -import { N_ } from '../../src/i18n'; +import { N_ } from '../../../src/i18n'; +import templatesListController from '../templatesList.controller'; +import indexController from '../index.controller'; + +const indexTemplate = require('~features/templates/index.view.html'); +const templatesListTemplate = require('~features/templates/templatesList.view.html'); export default { name: 'templates', route: '/templates', ncyBreadcrumb: { - // TODO: this would be best done with our - // strings file pattern, but it's not possible to - // get a handle on this route within a DI based - // on the state tree generation as present in - // src/templates currently label: N_("TEMPLATES") }, data: { @@ -33,8 +31,13 @@ export default { searchPrefix: 'template', views: { '@': { - controller: ListController, - templateUrl: listTemplate, + templateUrl: indexTemplate, + controller: indexController, + controllerAs: 'vm' + }, + 'templatesList@templates': { + controller: templatesListController, + templateUrl: templatesListTemplate, controllerAs: 'vm', } }, diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/templatesList.controller.js similarity index 100% rename from awx/ui/client/features/templates/list-templates.controller.js rename to awx/ui/client/features/templates/templatesList.controller.js diff --git a/awx/ui/client/features/templates/templatesList.view.html b/awx/ui/client/features/templates/templatesList.view.html new file mode 100644 index 0000000000..9b344955a9 --- /dev/null +++ b/awx/ui/client/features/templates/templatesList.view.html @@ -0,0 +1,114 @@ + +
+ + +
+ + +
+
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
+
+ + +
diff --git a/awx/ui/client/src/projects/main.js b/awx/ui/client/src/projects/main.js index 94eee1de34..8f186e5458 100644 --- a/awx/ui/client/src/projects/main.js +++ b/awx/ui/client/src/projects/main.js @@ -13,7 +13,8 @@ import { N_ } from '../i18n'; import GetProjectPath from './factories/get-project-path.factory'; import GetProjectIcon from './factories/get-project-icon.factory'; import GetProjectToolTip from './factories/get-project-tool-tip.factory'; -import ProjectsTemplatesRoute from './projects-templates.route'; + +import ProjectsTemplatesRoute from '~features/templates/routes/projectsTemplatesList.route'; import ProjectsStrings from './projects.strings'; export default diff --git a/awx/ui/client/src/templates/labels/labelsList.directive.js b/awx/ui/client/src/templates/labels/labelsList.directive.js index 8402a14dd2..3763998427 100644 --- a/awx/ui/client/src/templates/labels/labelsList.directive.js +++ b/awx/ui/client/src/templates/labels/labelsList.directive.js @@ -93,8 +93,10 @@ export default }; if (scope.$parent.$parent.template) { - scope.labels = scope.$parent.$parent.template.summary_fields.labels.results.slice(0, 5); - scope.count = scope.$parent.$parent.template.summary_fields.labels.count; + if (_.has(scope, '$parent.$parent.template.summary_fields.labels.results')) { + scope.labels = scope.$parent.$parent.template.summary_fields.labels.results.slice(0, 5); + scope.count = scope.$parent.$parent.template.summary_fields.labels.count; + } } else if (scope.$parent.$parent.job) { if (_.has(scope, '$parent.$parent.job.summary_fields.labels.results')) { scope.labels = scope.$parent.$parent.job.summary_fields.labels.results.slice(0, 5); diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index f9280252a0..35f8cd5965 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -19,7 +19,7 @@ import WorkflowForm from './workflows.form'; import InventorySourcesList from './inventory-sources.list'; import TemplateList from './templates.list'; import TemplatesStrings from './templates.strings'; -import listRoute from '~features/templates/list.route.js'; +import listRoute from '~features/templates/routes/templatesList.route.js'; import templateCompletedJobsRoute from '~features/jobs/routes/templateCompletedJobs.route.js'; export default From a1b7d8698135830bdac571314f26bb91449c91fb Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 2 Apr 2018 17:09:08 -0400 Subject: [PATCH 138/379] remove the rest of templates list code and update organization tempaltes list to using new code --- .../organizationsTemplatesList.route.js | 65 +++++++++++++++ .../routes/projectsTemplatesList.route.js | 1 + .../linkout/organizations-linkout.route.js | 81 ++----------------- .../src/projects/projects-templates.route.js | 60 -------------- 4 files changed, 73 insertions(+), 134 deletions(-) create mode 100644 awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js delete mode 100644 awx/ui/client/src/projects/projects-templates.route.js diff --git a/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js b/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js new file mode 100644 index 0000000000..3bdd705f1e --- /dev/null +++ b/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js @@ -0,0 +1,65 @@ +import { N_ } from '../../../src/i18n'; +import templatesListController from '../templatesList.controller'; +import indexController from '../index.controller'; + +const indexTemplate = require('~features/templates/index.view.html'); +const templatesListTemplate = require('~features/templates/templatesList.view.html'); + +export default { + url: "/:organization_id/job_templates", + name: 'organizations.job_templates', + params: { + template_search: { + dynamic: true, + value: { + type: 'workflow_job_template,job_template', + }, + } + }, + ncyBreadcrumb: { + label: N_("JOB TEMPLATES") + }, + views: { + 'form': { + templateUrl: indexTemplate, + controller: indexController, + controllerAs: 'vm' + }, + 'templatesList@organizations.job_templates': { + controller: templatesListController, + templateUrl: templatesListTemplate, + controllerAs: 'vm', + } + }, + resolve: { + resolvedModels: [ + 'JobTemplateModel', + 'WorkflowJobTemplateModel', + (JobTemplate, WorkflowJobTemplate) => { + const models = [ + new JobTemplate(['options']), + new WorkflowJobTemplate(['options']), + ]; + return Promise.all(models); + }, + ], + Dataset: [ + '$stateParams', + 'Wait', + 'GetBasePath', + 'QuerySet', + ($stateParams, Wait, GetBasePath, qs) => { + const searchPath = GetBasePath('unified_job_templates'); + + const searchParam = _.assign($stateParams.template_search, { + or__project__organization: $stateParams.organization_id, + or__jobtemplate__inventory__organization: $stateParams.organization_id, + }); + + Wait('start'); + return qs.search(searchPath, searchParam) + .finally(() => Wait('stop')); + } + ], + } +}; diff --git a/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js b/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js index 2fa97411b8..8f6d916dbc 100644 --- a/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js +++ b/awx/ui/client/features/templates/routes/projectsTemplatesList.route.js @@ -10,6 +10,7 @@ export default { template_search: { dynamic: true, value: { + type: 'workflow_job_template,job_template', }, } }, diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index 43165dbf9d..20f8b6a6c3 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -4,15 +4,16 @@ * All Rights Reserved *************************************************/ +import OrganizationsJobTemplatesRoute from '~features/templates/routes/organizationsTemplatesList.route'; + import OrganizationsAdmins from './controllers/organizations-admins.controller'; import OrganizationsInventories from './controllers/organizations-inventories.controller'; -import OrganizationsJobTemplates from './controllers/organizations-job-templates.controller'; import OrganizationsProjects from './controllers/organizations-projects.controller'; import OrganizationsTeams from './controllers/organizations-teams.controller'; import OrganizationsUsers from './controllers/organizations-users.controller'; import { N_ } from '../../i18n'; -export default [{ +let lists = [{ name: 'organizations.users', url: '/:organization_id/users', searchPrefix: 'user', @@ -215,78 +216,6 @@ export default [{ } ] } -}, { - name: 'organizations.job_templates', - url: '/:organization_id/job_templates', - searchPrefix: 'job_template', - views: { - 'form': { - controller: OrganizationsJobTemplates, - templateProvider: function(OrgJobTemplateList, generateList) { - let html = generateList.build({ - list: OrgJobTemplateList, - mode: 'edit', - cancelButton: true - }); - return generateList.wrapPanel(html); - }, - }, - }, - params: { - template_search: { - value: { - or__project__organization: null, - or__inventory__organization: null, - page_size: 20 - }, - dynamic: true - } - }, - data: { - activityStream: true, - activityStreamTarget: 'organization', - socket: { - "groups": { - "jobs": ["status_changed"] - } - } - }, - ncyBreadcrumb: { - parent: "organizations.edit", - label: N_("JOB TEMPLATES") - }, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }], - OrgJobTemplateList: ['TemplateList', 'GetBasePath', '$stateParams', function(TemplateList) { - let list = _.cloneDeep(TemplateList); - delete list.actions; - // @issue Why is the delete action unavailable in this view? - delete list.fieldActions.delete; - delete list.fields.type; - list.listTitle = N_('Job Templates') + ` | {{ name }}`; - list.emptyListText = "This list is populated by job templates added from the Job Templates section"; - list.iterator = 'template'; - list.name = 'job_templates'; - list.basePath = "job_templates"; - list.fields.smart_status.ngInclude = "'/static/partials/organizations-job-template-smart-status.html'"; - list.fields.name.ngHref = '#/templates/job_template/{{template.id}}'; - list.fieldActions.schedule.ngClick = 'scheduleJob(template.id)'; - list.fieldActions.copy.ngClick = 'copyTemplate(template.id)'; - list.fieldActions.edit.ngClick = "editJobTemplate(template.id)"; - list.fieldActions.view.ngClick = "editJobTemplate(template.id)"; - return list; - }], - OrgJobTemplateDataset: ['OrgJobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath', - function(list, qs, $stateParams, GetBasePath) { - let path = GetBasePath(list.name); - $stateParams.template_search.or__project__organization = $stateParams.organization_id; - $stateParams.template_search.or__inventory__organization = $stateParams.organization_id; - return qs.search(path, $stateParams.template_search); - } - ] - } }, { name: 'organizations.admins', url: '/:organization_id/admins', @@ -355,3 +284,7 @@ export default [{ }] } }]; + +lists.push(OrganizationsJobTemplatesRoute); + +export default lists; diff --git a/awx/ui/client/src/projects/projects-templates.route.js b/awx/ui/client/src/projects/projects-templates.route.js deleted file mode 100644 index f15c229edd..0000000000 --- a/awx/ui/client/src/projects/projects-templates.route.js +++ /dev/null @@ -1,60 +0,0 @@ -import { N_ } from '../i18n'; - -export default { - url: "/templates", - name: 'projects.edit.templates', - params: { - template_search: { - value: { - page_size: '20', - project: '', - order_by: "-id" - } - } - }, - ncyBreadcrumb: { - label: N_("JOB TEMPLATES") - }, - views: { - // TODO: this controller was removed and replaced - // with the new features/templates controller - // this view should be updated with the new - // expanded list - 'related': { - templateProvider: function(FormDefinition, GenerateForm) { - let html = GenerateForm.buildCollection({ - mode: 'edit', - related: 'templates', - form: typeof(FormDefinition) === 'function' ? - FormDefinition() : FormDefinition - }); - return html; - }, - controller: 'TemplatesListController' - } - }, - resolve: { - ListDefinition: ['TemplateList', '$transition$', (TemplateList, $transition$) => { - let id = $transition$.params().project_id; - TemplateList.actions.add.ngClick = `$state.go('templates.addJobTemplate', {project_id: ${id}})`; - TemplateList.basePath = 'job_templates'; - return TemplateList; - }], - Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope', - (list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => { - // allow related list definitions to use interpolated $rootScope / $stateParams in basePath field - let path, interpolator; - if (GetBasePath(list.basePath)) { - path = GetBasePath(list.basePath); - } else { - interpolator = $interpolate(list.basePath); - path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams }); - } - let project_id = $stateParams.project_id; - $stateParams[`${list.iterator}_search`].project = project_id; - path = GetBasePath('job_templates'); - return qs.search(path, $stateParams[`${list.iterator}_search`]); - } - ] - } -}; From 1cc7d5535e6c284eb7e433c69321d970e414ce7e Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 4 Mar 2018 16:04:16 -0500 Subject: [PATCH 139/379] hacking together some basic bootstrapping for job results view - not yet an actual test --- awx/ui/test/e2e/tests/test-jobz.js | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 awx/ui/test/e2e/tests/test-jobz.js diff --git a/awx/ui/test/e2e/tests/test-jobz.js b/awx/ui/test/e2e/tests/test-jobz.js new file mode 100644 index 0000000000..9f76fc7bec --- /dev/null +++ b/awx/ui/test/e2e/tests/test-jobz.js @@ -0,0 +1,118 @@ +import uuid from 'uuid'; + +import { + get, + post, +} from '../api'; +import { + getAdminMachineCredential, + getInventory, + getOrCreate, + getOrganization, + waitForJob, +} from '../fixtures'; + +// AWX_E2E_URL='https://localhost:3000' npm --prefix awx/ui run e2e -- --filter="*jobz*" + +const session = `e2e-${uuid().substr(0, 8)}`; + +const SCM_URL = 'https://github.com/jakemcdermott/ansible-playbooks'; +const PLAYBOOK = 'setfact_50.yml'; +const PARAMS = '?job_event_search=page_size:200;order_by:start_line;not__event__in:playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats;task:set'; + +let data; + +const waitForJobz = endpoint => { + const interval = 2000; + const statuses = ['successful', 'failed', 'error', 'canceled']; + + let attempts = 20; + + return new Promise((resolve, reject) => { + (function pollStatus () { + get(endpoint).then(update => { + const completed = statuses.indexOf(update.data.status) > -1; + + if (completed) { + return resolve(update.data); + } + + if (--attempts <= 0) { + return reject(new Error('Retry limit exceeded.')); + } + + return setTimeout(pollStatus, interval); + }); + }()); + }); +}; + +const getProject = (namespace = session) => getOrganization(namespace) + .then(organization => getOrCreate('/projects/', { + name: `${namespace}-project`, + description: namespace, + organization: organization.id, + scm_url: SCM_URL, + scm_type: 'git' + }) + .then(project => { + if (project.related.current_update) { + return waitForJobz(project.related.current_update) + .then(() => project); + } + return project; + })); + +const getJobTemplate = (namespace = session) => { + const promises = [ + getInventory(namespace), + getAdminMachineCredential(namespace), + getProject(namespace) + ]; + + return Promise.all(promises) + .then(([inventory, credential, project]) => getOrCreate('/job_templates/', { + name: `${namespace}-job-template`, + description: namespace, + inventory: inventory.id, + credential: credential.id, + project: project.id, + playbook: PLAYBOOK, + })); +}; + +const getJob = (namespace = session) => getJobTemplate(namespace) + .then(template => { + if (template.related.last_job) { + return waitForJobz(template.related.last_job); + } + + return post(template.related.launch, {}) + .then(res => waitForJobz(res.data.url)); + }); + +module.exports = { + before: (client, done) => { + getJob() + .then(job => { + data = { job }; + done(); + }) + }, + 'test jobz': client => { + const location = `${client.globals.launch_url}/#/jobz/playbook/${data.job.id}`; + const templates = client.page.templates(); + + client.useCss(); + client.resizeWindow(1200, 800); + client.login(); + client.waitForAngular(); + + // client.url(location); + client.url(`${location}${PARAMS}`); + + client.pause(); + + client.end(); + }, +}; From c12173233b74cea537c77328ab1b8af7130c767d Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 4 Mar 2018 16:11:45 -0500 Subject: [PATCH 140/379] add initial test and sanity check for search tags --- awx/ui/test/e2e/tests/test-searchez.js | 125 +++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 awx/ui/test/e2e/tests/test-searchez.js diff --git a/awx/ui/test/e2e/tests/test-searchez.js b/awx/ui/test/e2e/tests/test-searchez.js new file mode 100644 index 0000000000..9442ac66c0 --- /dev/null +++ b/awx/ui/test/e2e/tests/test-searchez.js @@ -0,0 +1,125 @@ +import { range } from 'lodash'; + +import { getAdminMachineCredential } from '../fixtures'; + +// AWX_E2E_URL='https://localhost:3000' npm --prefix awx/ui run e2e -- --filter="*jobz*" + +const spinny = 'div.spinny'; +const searchInput = 'smart-search input'; +const searchSubmit = 'smart-search i[class*="search"]'; +const searchTags = 'smart-search .SmartSearch-tagContainer'; +const searchClearAll = 'smart-search .SmartSearch-clearAll'; +const searchTagDelete = 'i[class*="fa-times"]'; + +const createTagSelector = n => `${searchTags}:nth-of-type(${n})`; +const createTagDeleteSelector = n => `${searchTags}:nth-of-type(${n}) ${searchTagDelete}`; + +const checkTags = (client, tags) => { + const strategy = 'css selector'; + + const countReached = createTagSelector(tags.length); + const countExceeded = createTagSelector(tags.length + 1); + + if (tags.length > 0) { + client.waitForElementVisible(countReached); + client.waitForElementNotPresent(countExceeded); + } + + client.elements(strategy, searchTags, tagElements => { + client.assert.equal(tagElements.value.length, tags.length); + + let n = -1; + tagElements.value.map(o => o.ELEMENT).forEach(id => { + client.elementIdText(id, ({ value }) => { + client.assert.equal(value, tags[++n]); + }); + }); + }); +}; + +module.exports = { + before: (client, done) => { + const resources = range(25).map(n => getAdminMachineCredential(`test-search-${n}`)); + Promise.all(resources).then(done); + }, + 'add and remove search tags': client => { + const credentials = client.page.credentials(); + + client.login(); + client.waitForAngular(); + + credentials.section.navigation.waitForElementVisible('@credentials'); + credentials.section.navigation.click('@credentials'); + + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + client.waitForElementVisible(searchInput); + client.waitForElementVisible(searchSubmit); + + client.expect.element(searchInput).enabled; + client.expect.element(searchSubmit).enabled; + + checkTags(client, []); + + client.setValue(searchInput, 'foo'); + client.click(searchSubmit); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['foo']); + + client.setValue(searchInput, 'bar e2e'); + client.click(searchSubmit); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['foo', 'bar', 'e2e']); + + client.click(searchClearAll); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, []); + + client.setValue(searchInput, 'fiz name:foo'); + client.click(searchSubmit); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['fiz', 'name:foo']); + + client.click(searchClearAll); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, []); + + client.setValue(searchInput, 'hello name:world fiz'); + client.click(searchSubmit); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['hello', 'fiz', 'name:world']); + + client.click(createTagDeleteSelector(2)); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['hello', 'name:world']); + + client.click(createTagDeleteSelector(1)); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, ['name:world']); + + client.click(createTagDeleteSelector(1)); + client.waitForElementVisible(spinny); + client.waitForElementNotVisible(spinny); + + checkTags(client, []); + + client.end(); + }, +}; \ No newline at end of file From 0adf671de4b5f2fa82e334ed2ef1fb34cd137d85 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 4 Mar 2018 16:25:10 -0500 Subject: [PATCH 141/379] refactor, lint, separate data transformation logic from display logic --- .../shared/smart-search/queryset.service.js | 54 +- .../smart-search/smart-search.controller.js | 768 +++++++++--------- .../smart-search/smart-search.partial.html | 4 +- 3 files changed, 424 insertions(+), 402 deletions(-) diff --git a/awx/ui/client/src/shared/smart-search/queryset.service.js b/awx/ui/client/src/shared/smart-search/queryset.service.js index 943f63818b..7d07139c4c 100644 --- a/awx/ui/client/src/shared/smart-search/queryset.service.js +++ b/awx/ui/client/src/shared/smart-search/queryset.service.js @@ -44,19 +44,16 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear key = this.replaceDefaultFlags(key); if (!Array.isArray(values)) { - values = this.replaceEncodedTokens(values); - - return `${key}=${values}`; + values = [values]; } return values .map(value => { value = this.replaceDefaultFlags(value); value = this.replaceEncodedTokens(value); - - return `${key}=${value}`; + return [key, value] }) - .join('&'); + }, // encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL encodeQueryset(params) { @@ -69,15 +66,33 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear result += '&'; } - return result += this.encodeTerms(value, key); + const encodedTermString = this.encodeTerms(value, key) + .map(([key, value]) => `${key}=${value}`) + .join('&'); + + return result += encodedTermString; }, '?'); }, + // like encodeQueryset, but return an actual unstringified API-consumable http param object + encodeQuerysetObject(params) { + return _.reduce(params, (obj, value, key) => { + const encodedTerms = this.encodeTerms(value, key); + + for (let encodedIndex in encodedTerms) { + const [encodedKey, encodedValue] = encodedTerms[encodedIndex]; + obj[encodedKey] = obj[encodedKey] || []; + obj[encodedKey].push(encodedValue) + } + + return obj; + }, {}); + }, // encodes a ui smart-search param to a django-friendly param // operand:key:comparator:value => {operand__key__comparator: value} - encodeParam(params){ + encodeParam({ term, relatedSearchTerm, searchTerm, singleSearchParam }){ // Assumption here is that we have a key and a value so the length // of the paramParts array will be 2. [0] is the key and [1] the value - let paramParts = SmartSearchService.splitTermIntoParts(params.term); + let paramParts = SmartSearchService.splitTermIntoParts(term); let keySplit = paramParts[0].split('.'); let exclude = false; let lessThanGreaterThan = paramParts[1].match(/^(>|<).*$/) ? true : false; @@ -88,16 +103,16 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear let paramString = exclude ? "not__" : ""; let valueString = paramParts[1]; if(keySplit.length === 1) { - if(params.searchTerm && !lessThanGreaterThan) { - if(params.singleSearchParam) { + if(searchTerm && !lessThanGreaterThan) { + if(singleSearchParam) { paramString += keySplit[0] + '__icontains'; } else { paramString += keySplit[0] + '__icontains_DEFAULT'; } } - else if(params.relatedSearchTerm) { - if(params.singleSearchParam) { + else if(relatedSearchTerm) { + if(singleSearchParam) { paramString += keySplit[0]; } else { @@ -131,8 +146,8 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear } } - if(params.singleSearchParam) { - return {[params.singleSearchParam]: paramString + "=" + valueString}; + if(singleSearchParam) { + return {[singleSearchParam]: paramString + "=" + valueString}; } else { return {[paramString] : encodeURIComponent(valueString)}; @@ -189,7 +204,14 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear return decodeParamString(value); } }, - + convertToSearchTags(obj) { + const tags = []; + for (let key in obj) { + const value = obj[key]; + tags.push(this.decodeParam(value, key)); + } + return tags; + }, // encodes a django queryset for ui-router's URLMatcherFactory // {operand__key__comparator: value, } => 'operand:key:comparator:value;...' // value.isArray expands to: diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index e7dd435307..5a59bbde81 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -1,422 +1,422 @@ -export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', 'SmartSearchService', 'i18n', 'ConfigService', '$transitions', - function($stateParams, $scope, $state, GetBasePath, qs, SmartSearchService, i18n, configService, $transitions) { +function SmartSearchController ( + $scope, + $state, + $stateParams, + $transitions, + configService, + GetBasePath, + i18n, + qs, + SmartSearchService +) { + const searchKey = `${$scope.iterator}_search`; + const optionsKey = `${$scope.list.iterator}_options`; - let path, - defaults, - queryset, - transitionSuccessListener; + let path; + let defaults; + let queryset; + let transitionSuccessListener; - configService.getConfig() - .then(config => init(config)); + configService.getConfig() + .then(config => init(config)); - function init(config) { - let version; + function init (config) { + let version; - try { - version = config.version.split('-')[0]; - } catch (err) { - version = 'latest'; - } + try { + [version] = config.version.split('-'); + } catch (err) { + version = 'latest'; + } - $scope.documentationLink = `http://docs.ansible.com/ansible-tower/${version}/html/userguide/search_sort.html`; + $scope.documentationLink = `http://docs.ansible.com/ansible-tower/${version}/html/userguide/search_sort.html`; + $scope.searchPlaceholder = i18n._('Search'); - if($scope.defaultParams) { - defaults = $scope.defaultParams; - } - else { - // steps through the current tree of $state configurations, grabs default search params - defaults = _.find($state.$current.path, (step) => { - if(step && step.params && step.params.hasOwnProperty(`${$scope.iterator}_search`)){ - return step.params.hasOwnProperty(`${$scope.iterator}_search`); - } - }).params[`${$scope.iterator}_search`].config.value; - } + if ($scope.defaultParams) { + defaults = $scope.defaultParams; + } else { + // steps through the current tree of $state configurations, grabs default search params + const stateConfig = _.find($state.$current.path, step => _.has(step, `params.${searchKey}`)); + defaults = stateConfig.params[searchKey].config.value; + } - if($scope.querySet) { - queryset = _.cloneDeep($scope.querySet); - } - else { - queryset = $state.params[`${$scope.iterator}_search`]; - } + if ($scope.querySet) { + queryset = _.cloneDeep($scope.querySet); + } else { + queryset = $state.params[searchKey]; + } - path = GetBasePath($scope.basePath) || $scope.basePath; - generateSearchTags(); - qs.initFieldset(path, $scope.djangoModel).then((data) => { + path = GetBasePath($scope.basePath) || $scope.basePath; + generateSearchTags(); + + qs.initFieldset(path, $scope.djangoModel) + .then((data) => { $scope.models = data.models; $scope.options = data.options.data; if ($scope.list) { - $scope.$emit(`${$scope.list.iterator}_options`, data.options); - } - }); - $scope.searchPlaceholder = $scope.disableSearch ? i18n._('Cannot search running job') : i18n._('Search'); - - function compareParams(a, b) { - for (let key in a) { - if (!(key in b) || a[key].toString() !== b[key].toString()) { - return false; - } - } - for (let key in b) { - if (!(key in a)) { - return false; - } - } - return true; - } - - if(transitionSuccessListener) { - transitionSuccessListener(); - } - - transitionSuccessListener = $transitions.onSuccess({}, function(trans) { - // State has changed - check to see if this is a param change - if(trans.from().name === trans.to().name) { - if(!compareParams(trans.params('from')[`${$scope.iterator}_search`], trans.params('to')[`${$scope.iterator}_search`])) { - // Params are not the same - we need to update the search. This should only happen when the user - // hits the forward/back navigation buttons in their browser. - queryset = trans.params('to')[`${$scope.iterator}_search`]; - qs.search(path, queryset).then((res) => { - $scope.dataset = res.data; - $scope.collection = res.data.results; - $scope.$emit('updateDataset', res.data); - }); - - $scope.searchTerm = null; - generateSearchTags(); - } + $scope.$emit(optionsKey, data.options); } }); - $scope.$on('$destroy', transitionSuccessListener); + function compareParams(a, b) { + for (let key in a) { + if (!(key in b) || a[key].toString() !== b[key].toString()) { + return false; + } + } + for (let key in b) { + if (!(key in a)) { + return false; + } + } + return true; + } - $scope.$watch('disableSearch', function(disableSearch){ - if(disableSearch) { - $scope.searchPlaceholder = i18n._('Cannot search running job'); - } - else { - $scope.searchPlaceholder = i18n._('Search'); + if (transitionSuccessListener) { + transitionSuccessListener(); + } + + transitionSuccessListener = $transitions.onSuccess({}, trans => { + // State has changed - check to see if this is a param change + if (trans.from().name === trans.to().name) { + if (!compareParams(trans.params('from')[searchKey], trans.params('to')[searchKey])) { + // Params are not the same - we need to update the search. This should only + // happen when the user hits the forward/back browser navigation buttons. + queryset = trans.params('to')[searchKey]; + qs.search(path, queryset).then((res) => { + $scope.dataset = res.data; + $scope.collection = res.data.results; + $scope.$emit('updateDataset', res.data); + }); + + $scope.searchTerm = null; + generateSearchTags(); } + } + }); + + $scope.$on('$destroy', transitionSuccessListener); + $scope.$watch('disableSearch', disableSearch => { + if (disableSearch) { + $scope.searchPlaceholder = i18n._('Cannot search running job'); + } else { + $scope.searchPlaceholder = i18n._('Search'); + } + }); + } + + function generateSearchTags () { + $scope.searchTags = []; + + const querysetCopy = angular.copy(queryset); + + if ($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) { + const searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20'); + delete querysetCopy[$scope.singleSearchParam]; + + $.each(searchParam, (index, param) => { + const paramParts = decodeURIComponent(param).split(/=(.+)/); + const reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]); + $scope.searchTags.push(reconstructedSearchString); }); } - function generateSearchTags() { - $scope.searchTags = []; + $scope.searchTags = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults)); + } - let querysetCopy = angular.copy(queryset); - - if($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) { - let searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20'); - delete querysetCopy[$scope.singleSearchParam]; - - $.each(searchParam, function(index, param) { - let paramParts = decodeURIComponent(param).split(/=(.+)/); - let reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]); - $scope.searchTags.push(reconstructedSearchString); - }); + function revertSearch (queryToBeRestored) { + queryset = queryToBeRestored; + // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic + // This transition will not reload controllers/resolves/views + // but will register new $stateParams[$scope.iterator + '_search'] terms + if (!$scope.querySet) { + $state.go('.', { [searchKey]: queryset }); + } + qs.search(path, queryset).then((res) => { + if ($scope.querySet) { + $scope.querySet = queryset; } + $scope.dataset = res.data; + $scope.collection = res.data.results; + }); - $scope.searchTags = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults)); + $scope.searchTerm = null; + + generateSearchTags(); + } + + $scope.toggleKeyPane = () => { + $scope.showKeyPane = !$scope.showKeyPane; + }; + + function searchWithoutKey (term, singleSearchParam = null) { + if (singleSearchParam) { + return { [singleSearchParam]: `search=${encodeURIComponent(term)}` }; + } + return { search: encodeURIComponent(term) }; + } + + function isAnsibleFactSearchTerm (termParts) { + const rootField = termParts[0].split('.')[0].replace(/^-/, ''); + return rootField === 'ansible_facts'; + } + + function isRelatedField (termParts) { + const rootField = termParts[0].split('.')[0].replace(/^-/, ''); + const listName = $scope.list.name; + const baseRelatedTypePath = `models.${listName}.base.${rootField}.type`; + + const isRelatedSearchTermField = (_.contains($scope.models[listName].related, rootField)); + const isBaseModelRelatedSearchTermField = (_.get($scope, baseRelatedTypePath) === 'field'); + + return (isRelatedSearchTermField || isBaseModelRelatedSearchTermField); + } + + function getSearchInputQueryset ({ terms, singleSearchParam }) { + let params = {}; + + // remove leading/trailing whitespace + terms = (terms) ? terms.trim() : ''; + let splitTerms; + + if (singleSearchParam === 'host_filter') { + splitTerms = SmartSearchService.splitFilterIntoTerms(terms); + } else { + splitTerms = SmartSearchService.splitSearchIntoTerms(terms); } - function revertSearch(queryToBeRestored) { - queryset = queryToBeRestored; - // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic - // This transition will not reload controllers/resolves/views - // but will register new $stateParams[$scope.iterator + '_search'] terms - if(!$scope.querySet) { - $state.go('.', { - [$scope.iterator + '_search']: queryset }); + const combineSameSearches = (a, b) => { + if (!a) { + return undefined; } - qs.search(path, queryset).then((res) => { - if($scope.querySet) { - $scope.querySet = queryset; - } - $scope.dataset = res.data; - $scope.collection = res.data.results; - }); - $scope.searchTerm = null; - - generateSearchTags(); - } - - function searchWithoutKey(term) { - if($scope.singleSearchParam) { - return { - [$scope.singleSearchParam]: "search=" + encodeURIComponent(term) - }; + if (_.isArray(a)) { + return a.concat(b); } - return { - search: encodeURIComponent(term) - }; - } - $scope.toggleKeyPane = function() { - $scope.showKeyPane = !$scope.showKeyPane; + if (singleSearchParam) { + return `${a}%20and%20${b}`; + } + + return [a, b]; }; - // add a search tag, merge new queryset, $state.go() - $scope.addTerm = function(terms) { - let params = {}, - origQueryset = _.clone(queryset); - - // Remove leading/trailing whitespace if there is any - terms = (terms) ? terms.trim() : ""; - - if(terms && terms !== '') { - let splitTerms; - - if ($scope.singleSearchParam === 'host_filter') { - splitTerms = SmartSearchService.splitFilterIntoTerms(terms); - } else { - splitTerms = SmartSearchService.splitSearchIntoTerms(terms); - } - - _.forEach(splitTerms, (term) => { - let termParts = SmartSearchService.splitTermIntoParts(term); - - function combineSameSearches(a,b){ - if (_.isArray(a)) { - return a.concat(b); - } - else { - if(a) { - if($scope.singleSearchParam) { - return a + "%20and%20" + b; - } - else { - return [a,b]; - } - } - } - } - - if($scope.singleSearchParam) { - if (termParts.length === 1) { - params = _.merge(params, searchWithoutKey(term), combineSameSearches); - } - else { - let root = termParts[0].split(".")[0].replace(/^-/, ''); - if(_.has($scope.models[$scope.list.name].base, root) || root === "ansible_facts") { - if(_.has($scope.models[$scope.list.name].base[root], "type") && $scope.models[$scope.list.name].base[root].type === 'field'){ - // Intent is to land here for searching on the base model. - params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches); - } - else { - // Intent is to land here when performing ansible_facts searches - params = _.merge(params, qs.encodeParam({term: term, searchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches); - } - } - else if(_.contains($scope.models[$scope.list.name].related, root)) { - // Intent is to land here for related searches - params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true, singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false}), combineSameSearches); - } - // Its not a search term or a related search term - treat it as a string - else { - params = _.merge(params, searchWithoutKey(term), combineSameSearches); - } - } - } - - else { - // if only a value is provided, search using default keys - if (termParts.length === 1) { - params = _.merge(params, searchWithoutKey(term), combineSameSearches); - } else { - // Figure out if this is a search term - let root = termParts[0].split(".")[0].replace(/^-/, ''); - if(_.has($scope.models[$scope.list.name].base, root)) { - if($scope.models[$scope.list.name].base[root].type && $scope.models[$scope.list.name].base[root].type === 'field') { - params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true}), combineSameSearches); - } - else { - params = _.merge(params, qs.encodeParam({term: term, searchTerm: true}), combineSameSearches); - } - } - // The related fields need to also be checked for related searches. - // The related fields for the search are retrieved from the API - // options endpoint, and are stored in the $scope.model. FYI, the - // Django search model is what sets the related fields on the model. - else if(_.contains($scope.models[$scope.list.name].related, root)) { - params = _.merge(params, qs.encodeParam({term: term, relatedSearchTerm: true}), combineSameSearches); - } - // Its not a search term or a related search term - treat it as a string - else { - params = _.merge(params, searchWithoutKey(term), combineSameSearches); - } - - } - } - }); - - queryset = _.merge({}, queryset, params, (objectValue, sourceValue, key, object) => { - if (object[key] && object[key] !== sourceValue){ - if(_.isArray(object[key])) { - // Add the new value to the array and return - object[key].push(sourceValue); - return object[key]; - } - else { - if($scope.singleSearchParam) { - if(!object[key]) { - return sourceValue; - } - else { - let singleSearchParamKeys = object[key].split("%20and%20"); - - if(_.includes(singleSearchParamKeys, sourceValue)) { - return object[key]; - } - else { - return object[key] + "%20and%20" + sourceValue; - } - } - } - // Start the array of keys - return [object[key], sourceValue]; - } - } - else { - // // https://lodash.com/docs/3.10.1#merge - // If customizer fn returns undefined merging is handled by default _.merge algorithm - return undefined; - } - }); - - // Go back to the first page after a new search - delete queryset.page; - - // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic - // This transition will not reload controllers/resolves/views - // but will register new $stateParams[$scope.iterator + '_search'] terms - if(!$scope.querySet) { - $state.go('.', {[$scope.iterator + '_search']:queryset }).then(function(){ - // ISSUE: same as above in $scope.remove. For some reason deleting the page - // from the queryset works for all lists except lists in modals. - delete $stateParams[$scope.iterator + '_search'].page; - }); - } - qs.search(path, queryset).then((res) => { - if($scope.querySet) { - $scope.querySet = queryset; - } - $scope.dataset = res.data; - $scope.collection = res.data.results; - }) - .catch(function() { - revertSearch(origQueryset); - }); - - $scope.searchTerm = null; - - generateSearchTags(); - } - }; - - // remove tag, merge new queryset, $state.go - $scope.removeTerm = function(index) { - let tagToRemove = $scope.searchTags.splice(index, 1)[0], - termParts = SmartSearchService.splitTermIntoParts(tagToRemove), - removed; - - let removeFromQuerySet = function(set) { - _.each(removed, (value, key) => { - if (Array.isArray(set[key])){ - _.remove(set[key], (item) => item === value); - // If the array is now empty, remove that key - if (set[key].length === 0) { - delete set[key]; - } - } else { - if ($scope.singleSearchParam && set[$scope.singleSearchParam] && set[$scope.singleSearchParam].includes("%20and%20")) { - let searchParamParts = set[$scope.singleSearchParam].split("%20and%20"); - // The value side of each paramPart might have been encoded in SmartSearch.splitFilterIntoTerms - _.each(searchParamParts, (paramPart, paramPartIndex) => { - searchParamParts[paramPartIndex] = decodeURIComponent(paramPart); - }); - var index = searchParamParts.indexOf(value); - if (index !== -1) { - searchParamParts.splice(index, 1); - } - set[$scope.singleSearchParam] = searchParamParts.join("%20and%20"); - } else { - delete set[key]; - } - } - }); - }; + _.each(splitTerms, term => { + const termParts = SmartSearchService.splitTermIntoParts(term); + let termParams; if (termParts.length === 1) { - removed = searchWithoutKey(tagToRemove); + termParams = searchWithoutKey(term, singleSearchParam); + } else if (isAnsibleFactSearchTerm(termParts)) { + termParams = qs.encodeParam({ term, singleSearchParam }); + } else if (isRelatedField(termParts)) { + termParams = qs.encodeParam({ term, singleSearchParam, related: true }); } else { - let root = termParts[0].split(".")[0].replace(/^-/, ''); - let encodeParams = { - term: tagToRemove, - singleSearchParam: $scope.singleSearchParam ? $scope.singleSearchParam : false - }; - if($scope.models[$scope.list.name]) { - if($scope.singleSearchParam) { - removed = qs.encodeParam(encodeParams); - } - else if(_.has($scope.models[$scope.list.name].base, root)) { - if($scope.models[$scope.list.name].base[root].type && $scope.models[$scope.list.name].base[root].type === 'field') { - encodeParams.relatedSearchTerm = true; - } - else { - encodeParams.searchTerm = true; - } - removed = qs.encodeParam(encodeParams); - } - else if(_.contains($scope.models[$scope.list.name].related, root)) { - encodeParams.relatedSearchTerm = true; - removed = qs.encodeParam(encodeParams); - } - else { - removed = searchWithoutKey(termParts[termParts.length-1]); - } - } - else { - removed = searchWithoutKey(termParts[termParts.length-1]); - } + termParams = qs.encodeParam({ term, singleSearchParam }); } - removeFromQuerySet(queryset); - if (!$scope.querySet) { - $state.go('.', { - [$scope.iterator + '_search']: queryset }).then(function(){ - // ISSUE: for some reason deleting a tag from a list in a modal does not - // remove the param from $stateParams. Here we'll manually check to make sure - // that that happened and remove it if it didn't. - removeFromQuerySet($stateParams[`${$scope.iterator}_search`]); - }); - } - qs.search(path, queryset).then((res) => { - if($scope.querySet) { - $scope.querySet = queryset; - } + params = _.merge(params, termParams, combineSameSearches); + }); - $scope.dataset = res.data; - $scope.collection = res.data.results; - - generateSearchTags(); - }); - }; - - $scope.clearAllTerms = function(){ - let cleared = _.cloneDeep(defaults); - delete cleared.page; - queryset = cleared; - if(!$scope.querySet) { - $state.go('.', {[$scope.iterator + '_search']: queryset}); - } - qs.search(path, queryset).then((res) => { - if($scope.querySet) { - $scope.querySet = queryset; - } - $scope.dataset = res.data; - $scope.collection = res.data.results; - }); - $scope.searchTags = qs.stripDefaultParams(queryset, defaults); - }; + return params; } + + function mergeQueryset (qset, additional, singleSearchParam) { + const merged = _.merge({}, qset, additional, (objectValue, sourceValue, key, object) => { + if (!(object[key] && object[key] !== sourceValue)) { + // // https://lodash.com/docs/3.10.1#each + // If this returns undefined merging is handled by default _.merge algorithm + return undefined; + } + + if (_.isArray(object[key])) { + object[key].push(sourceValue); + return object[key]; + } + + if (singleSearchParam) { + if (!object[key]) { + return sourceValue; + } + + const singleSearchParamKeys = object[key].split('%20and%20'); + + if (_.includes(singleSearchParamKeys, sourceValue)) { + return object[key]; + } + + return `${object[key]}%20and%20${sourceValue}`; + } + + // Start the array of keys + return [object[key], sourceValue]; + }); + + return merged; + } + + $scope.addTerms = terms => { + const { singleSearchParam } = $scope; + const origQueryset = _.clone(queryset); + + // Remove leading/trailing whitespace if there is any + terms = (terms) ? terms.trim() : ''; + + if (!(terms && terms !== '')) { + return; + } + + const searchInputQueryset = getSearchInputQueryset({ terms, singleSearchParam }); + queryset = mergeQueryset(queryset, searchInputQueryset, singleSearchParam); + + // Go back to the first page after a new search + delete queryset.page; + + // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic + // This transition will not reload controllers/resolves/views but will register new + // $stateParams[searchKey] terms. + if (!$scope.querySet) { + $state.go('.', { [searchKey]: queryset }) + .then(() => { + // same as above in $scope.remove. For some reason deleting the page + // from the queryset works for all lists except lists in modals. + delete $stateParams[searchKey].page; + }); + } + + qs.search(path, queryset) + .then(({ data }) => { + if ($scope.querySet) { + $scope.querySet = queryset; + } + $scope.dataset = data; + $scope.collection = data.results; + }) + .catch(() => revertSearch(origQueryset)); + + $scope.searchTerm = null; + + generateSearchTags(); + }; + + function removeTermsFromQueryset(qset, term, singleSearchParam = null) { + const returnedQueryset = _.cloneDeep(qset); + + const removeSingleTermFromQueryset = (value, key) => { + const space = '%20and%20'; + + if (Array.isArray(returnedQueryset[key])) { + returnedQueryset[key] = returnedQueryset[key].filter(item => item !== value); + if (returnedQueryset[key].length < 1) { + delete returnedQueryset[key]; + } + } else if (singleSearchParam && _.get(returnedQueryset, singleSearchParam, []).includes(space)) { + const searchParamParts = returnedQueryset[singleSearchParam].split(space); + // The value side of each paramPart might have been encoded in + // SmartSearch.splitFilterIntoTerms + _.each(searchParamParts, (paramPart, paramPartIndex) => { + searchParamParts[paramPartIndex] = decodeURIComponent(paramPart); + }); + + const paramPartIndex = searchParamParts.indexOf(value); + + if (paramPartIndex !== -1) { + searchParamParts.splice(paramPartIndex, 1); + } + + returnedQueryset[singleSearchParam] = searchParamParts.join(space); + + } else { + delete returnedQueryset[key]; + } + }; + + const termParts = SmartSearchService.splitTermIntoParts(term); + + let removed; + + if (termParts.length === 1) { + removed = searchWithoutKey(term, singleSearchParam); + } else if (isRelatedField(termParts)) { + removed = qs.encodeParam({ term, singleSearchParam, related: true }); + } else { + removed = qs.encodeParam({ term, singleSearchParam }); + } + + if (!removed) { + removed = searchWithoutKey(termParts[termParts.length - 1], singleSearchParam); + } + + _.each(removed, removeSingleTermFromQueryset); + + return returnedQueryset; + } + + // remove tag, merge new queryset, $state.go + $scope.removeTerm = index => { + const { singleSearchParam } = $scope; + const [term] = $scope.searchTags.splice(index, 1); + + const modifiedQueryset = removeTermsFromQueryset(queryset, term, singleSearchParam); + + if (!$scope.querySet) { + $state.go('.', { [searchKey]: modifiedQueryset }) + .then(() => { + // for some reason deleting a tag from a list in a modal does not + // remove the param from $stateParams. Here we'll manually check to make sure + // that that happened and remove it if it didn't. + const clearedParams = removeTermsFromQueryset( + $stateParams[searchKey], term, singleSearchParam); + $stateParams[searchKey] = clearedParams; + }); + } + + qs.search(path, queryset) + .then(({ data }) => { + if ($scope.querySet) { + $scope.querySet = queryset; + } + $scope.dataset = data; + $scope.collection = data.results; + }); + + generateSearchTags(); + }; + + $scope.clearAllTerms = () => { + const cleared = _.cloneDeep(defaults); + + delete cleared.page; + + queryset = cleared; + + if (!$scope.querySet) { + $state.go('.', { [searchKey]: queryset }); + } + + qs.search(path, queryset) + .then(({ data }) => { + if ($scope.querySet) { + $scope.querySet = queryset; + } + $scope.dataset = data; + $scope.collection = data.results; + }); + + $scope.searchTags = qs.stripDefaultParams(queryset, defaults); + }; +} + +SmartSearchController.$inject = [ + '$scope', + '$state', + '$stateParams', + '$transitions', + 'ConfigService', + 'GetBasePath', + 'i18n', + 'QuerySet', + 'SmartSearchService', ]; + +export default SmartSearchController; diff --git a/awx/ui/client/src/shared/smart-search/smart-search.partial.html b/awx/ui/client/src/shared/smart-search/smart-search.partial.html index 1f31adcf9e..ce52fea759 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.partial.html +++ b/awx/ui/client/src/shared/smart-search/smart-search.partial.html @@ -3,11 +3,11 @@
-
+
-
+
From 13162ca33ab877d7b52e5fa08b03bef46b864521 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 4 Mar 2018 16:29:41 -0500 Subject: [PATCH 142/379] move data transformation logic into a service so it can be reused --- .../shared/smart-search/queryset.service.js | 782 +++++++++++------- .../smart-search/smart-search.controller.js | 196 +---- 2 files changed, 492 insertions(+), 486 deletions(-) diff --git a/awx/ui/client/src/shared/smart-search/queryset.service.js b/awx/ui/client/src/shared/smart-search/queryset.service.js index 7d07139c4c..780900d32f 100644 --- a/awx/ui/client/src/shared/smart-search/queryset.service.js +++ b/awx/ui/client/src/shared/smart-search/queryset.service.js @@ -1,333 +1,507 @@ -export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSearchModel', 'SmartSearchService', - function($q, Rest, ProcessErrors, $rootScope, Wait, DjangoSearchModel, SmartSearchService) { - return { - // kick off building a model for a specific endpoint - // this is usually a list's basePath - // unified_jobs is the exception, where we need to fetch many subclass OPTIONS and summary_fields - initFieldset(path, name) { - let defer = $q.defer(); - defer.resolve(this.getCommonModelOptions(path, name)); - return defer.promise; - }, +function searchWithoutKey (term, singleSearchParam = null) { + if (singleSearchParam) { + return { [singleSearchParam]: `search=${encodeURIComponent(term)}` }; + } + return { search: encodeURIComponent(term) }; +} - getCommonModelOptions(path, name) { - let resolve, base, - defer = $q.defer(); +function QuerysetService ($q, Rest, ProcessErrors, $rootScope, Wait, DjangoSearchModel, SmartSearchService) { + return { + // kick off building a model for a specific endpoint + // this is usually a list's basePath + // unified_jobs is the exception, where we need to fetch many subclass OPTIONS and summary_fields + initFieldset(path, name) { + let defer = $q.defer(); + defer.resolve(this.getCommonModelOptions(path, name)); + return defer.promise; + }, + getCommonModelOptions(path, name) { + let resolve, base, + defer = $q.defer(); - this.url = path; - resolve = this.options(path) - .then((res) => { - base = res.data.actions.GET; - let relatedSearchFields = res.data.related_search_fields; - defer.resolve({ - models: { - [name]: new DjangoSearchModel(name, base, relatedSearchFields) - }, - options: res - }); + this.url = path; + resolve = this.options(path) + .then((res) => { + base = res.data.actions.GET; + let relatedSearchFields = res.data.related_search_fields; + defer.resolve({ + models: { + [name]: new DjangoSearchModel(name, base, relatedSearchFields) + }, + options: res }); - return defer.promise; - }, + }); + return defer.promise; + }, + replaceDefaultFlags (value) { + value = value.toString().replace(/__icontains_DEFAULT/g, "__icontains"); + value = value.toString().replace(/__search_DEFAULT/g, "__search"); - replaceDefaultFlags (value) { - value = value.toString().replace(/__icontains_DEFAULT/g, "__icontains"); - value = value.toString().replace(/__search_DEFAULT/g, "__search"); + return value; + }, + replaceEncodedTokens(value) { + return decodeURIComponent(value).replace(/"|'/g, ""); + }, + encodeTerms (values, key) { + key = this.replaceDefaultFlags(key); - return value; - }, + if (!Array.isArray(values)) { + values = [values]; + } - replaceEncodedTokens(value) { - return decodeURIComponent(value).replace(/"|'/g, ""); - }, + return values + .map(value => { + value = this.replaceDefaultFlags(value); + value = this.replaceEncodedTokens(value); + return [key, value] + }) - encodeTerms (values, key) { - key = this.replaceDefaultFlags(key); + }, + // encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL + encodeQueryset(params) { + if (typeof params !== 'object') { + return ''; + } - if (!Array.isArray(values)) { - values = [values]; + return _.reduce(params, (result, value, key) => { + if (result !== '?') { + result += '&'; } - return values - .map(value => { - value = this.replaceDefaultFlags(value); - value = this.replaceEncodedTokens(value); - return [key, value] - }) + const encodedTermString = this.encodeTerms(value, key) + .map(([key, value]) => `${key}=${value}`) + .join('&'); - }, - // encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL - encodeQueryset(params) { - if (typeof params !== 'object') { - return ''; + return result += encodedTermString; + }, '?'); + }, + // like encodeQueryset, but return an actual unstringified API-consumable http param object + encodeQuerysetObject(params) { + return _.reduce(params, (obj, value, key) => { + const encodedTerms = this.encodeTerms(value, key); + + for (let encodedIndex in encodedTerms) { + const [encodedKey, encodedValue] = encodedTerms[encodedIndex]; + obj[encodedKey] = obj[encodedKey] || []; + obj[encodedKey].push(encodedValue) } - return _.reduce(params, (result, value, key) => { - if (result !== '?') { - result += '&'; - } - - const encodedTermString = this.encodeTerms(value, key) - .map(([key, value]) => `${key}=${value}`) - .join('&'); - - return result += encodedTermString; - }, '?'); - }, - // like encodeQueryset, but return an actual unstringified API-consumable http param object - encodeQuerysetObject(params) { - return _.reduce(params, (obj, value, key) => { - const encodedTerms = this.encodeTerms(value, key); - - for (let encodedIndex in encodedTerms) { - const [encodedKey, encodedValue] = encodedTerms[encodedIndex]; - obj[encodedKey] = obj[encodedKey] || []; - obj[encodedKey].push(encodedValue) - } - - return obj; - }, {}); - }, - // encodes a ui smart-search param to a django-friendly param - // operand:key:comparator:value => {operand__key__comparator: value} - encodeParam({ term, relatedSearchTerm, searchTerm, singleSearchParam }){ - // Assumption here is that we have a key and a value so the length - // of the paramParts array will be 2. [0] is the key and [1] the value - let paramParts = SmartSearchService.splitTermIntoParts(term); - let keySplit = paramParts[0].split('.'); - let exclude = false; - let lessThanGreaterThan = paramParts[1].match(/^(>|<).*$/) ? true : false; - if(keySplit[0].match(/^-/g)) { - exclude = true; - keySplit[0] = keySplit[0].replace(/^-/, ''); - } - let paramString = exclude ? "not__" : ""; - let valueString = paramParts[1]; - if(keySplit.length === 1) { - if(searchTerm && !lessThanGreaterThan) { - if(singleSearchParam) { - paramString += keySplit[0] + '__icontains'; - } - else { - paramString += keySplit[0] + '__icontains_DEFAULT'; - } - } - else if(relatedSearchTerm) { - if(singleSearchParam) { - paramString += keySplit[0]; - } - else { - paramString += keySplit[0] + '__search_DEFAULT'; - } + return obj; + }, {}); + }, + // encodes a ui smart-search param to a django-friendly param + // operand:key:comparator:value => {operand__key__comparator: value} + encodeParam({ term, relatedSearchTerm, searchTerm, singleSearchParam }){ + // Assumption here is that we have a key and a value so the length + // of the paramParts array will be 2. [0] is the key and [1] the value + let paramParts = SmartSearchService.splitTermIntoParts(term); + let keySplit = paramParts[0].split('.'); + let exclude = false; + let lessThanGreaterThan = paramParts[1].match(/^(>|<).*$/) ? true : false; + if(keySplit[0].match(/^-/g)) { + exclude = true; + keySplit[0] = keySplit[0].replace(/^-/, ''); + } + let paramString = exclude ? "not__" : ""; + let valueString = paramParts[1]; + if(keySplit.length === 1) { + if(searchTerm && !lessThanGreaterThan) { + if(singleSearchParam) { + paramString += keySplit[0] + '__icontains'; } else { + paramString += keySplit[0] + '__icontains_DEFAULT'; + } + } + else if(relatedSearchTerm) { + if(singleSearchParam) { paramString += keySplit[0]; } - } - else { - paramString += keySplit.join('__'); - } - - if(lessThanGreaterThan) { - if(paramParts[1].match(/^>=.*$/)) { - paramString += '__gte'; - valueString = valueString.replace(/^(>=)/,""); - } - else if(paramParts[1].match(/^<=.*$/)) { - paramString += '__lte'; - valueString = valueString.replace(/^(<=)/,""); - } - else if(paramParts[1].match(/^<.*$/)) { - paramString += '__lt'; - valueString = valueString.replace(/^(<)/,""); - } - else if(paramParts[1].match(/^>.*$/)) { - paramString += '__gt'; - valueString = valueString.replace(/^(>)/,""); - } - } - - if(singleSearchParam) { - return {[singleSearchParam]: paramString + "=" + valueString}; - } - else { - return {[paramString] : encodeURIComponent(valueString)}; - } - }, - // decodes a django queryset param into a ui smart-search tag or set of tags - decodeParam(value, key){ - - let decodeParamString = function(searchString) { - if(key === 'search') { - // Don't include 'search:' in the search tag - return decodeURIComponent(`${searchString}`); - } else { - key = key.toString().replace(/__icontains_DEFAULT/g, ""); - key = key.toString().replace(/__search_DEFAULT/g, ""); - let split = key.split('__'); - let decodedParam = searchString; - let exclude = false; - if(key.startsWith('not__')) { - exclude = true; - split = split.splice(1, split.length); - } - if(key.endsWith('__gt')) { - decodedParam = '>' + decodedParam; - split = split.splice(0, split.length-1); - } - else if(key.endsWith('__lt')) { - decodedParam = '<' + decodedParam; - split = split.splice(0, split.length-1); - } - else if(key.endsWith('__gte')) { - decodedParam = '>=' + decodedParam; - split = split.splice(0, split.length-1); - } - else if(key.endsWith('__lte')) { - decodedParam = '<=' + decodedParam; - split = split.splice(0, split.length-1); - } - - let uriDecodedParam = decodeURIComponent(decodedParam); - - return exclude ? `-${split.join('.')}:${uriDecodedParam}` : `${split.join('.')}:${uriDecodedParam}`; + paramString += keySplit[0] + '__search_DEFAULT'; } - }; + } + else { + paramString += keySplit[0]; + } + } + else { + paramString += keySplit.join('__'); + } + if(lessThanGreaterThan) { + if(paramParts[1].match(/^>=.*$/)) { + paramString += '__gte'; + valueString = valueString.replace(/^(>=)/,""); + } + else if(paramParts[1].match(/^<=.*$/)) { + paramString += '__lte'; + valueString = valueString.replace(/^(<=)/,""); + } + else if(paramParts[1].match(/^<.*$/)) { + paramString += '__lt'; + valueString = valueString.replace(/^(<)/,""); + } + else if(paramParts[1].match(/^>.*$/)) { + paramString += '__gt'; + valueString = valueString.replace(/^(>)/,""); + } + } + + if(singleSearchParam) { + return {[singleSearchParam]: paramString + "=" + valueString}; + } + else { + return {[paramString] : encodeURIComponent(valueString)}; + } + }, + // decodes a django queryset param into a ui smart-search tag or set of tags + decodeParam(value, key){ + + let decodeParamString = function(searchString) { + if(key === 'search') { + // Don't include 'search:' in the search tag + return decodeURIComponent(`${searchString}`); + } + else { + key = key.toString().replace(/__icontains_DEFAULT/g, ""); + key = key.toString().replace(/__search_DEFAULT/g, ""); + let split = key.split('__'); + let decodedParam = searchString; + let exclude = false; + if(key.startsWith('not__')) { + exclude = true; + split = split.splice(1, split.length); + } + if(key.endsWith('__gt')) { + decodedParam = '>' + decodedParam; + split = split.splice(0, split.length-1); + } + else if(key.endsWith('__lt')) { + decodedParam = '<' + decodedParam; + split = split.splice(0, split.length-1); + } + else if(key.endsWith('__gte')) { + decodedParam = '>=' + decodedParam; + split = split.splice(0, split.length-1); + } + else if(key.endsWith('__lte')) { + decodedParam = '<=' + decodedParam; + split = split.splice(0, split.length-1); + } + + let uriDecodedParam = decodeURIComponent(decodedParam); + + return exclude ? `-${split.join('.')}:${uriDecodedParam}` : `${split.join('.')}:${uriDecodedParam}`; + } + }; + + if (Array.isArray(value)){ + value = _.uniq(_.flattenDeep(value)); + return _.map(value, (item) => { + return decodeParamString(item); + }); + } + else { + return decodeParamString(value); + } + }, + // encodes a django queryset for ui-router's URLMatcherFactory + // {operand__key__comparator: value, } => 'operand:key:comparator:value;...' + // value.isArray expands to: + // {operand__key__comparator: [value1, value2], } => 'operand:key:comparator:value1;operand:key:comparator:value1...' + encodeArr(params) { + let url; + url = _.reduce(params, (result, value, key) => { + return result.concat(encodeUrlString(value, key)); + }, []); + + return url.join(';'); + + // {key:'value'} => 'key:value' + // {key: [value1, value2, ...]} => ['key:value1', 'key:value2'] + function encodeUrlString(value, key){ if (Array.isArray(value)){ value = _.uniq(_.flattenDeep(value)); return _.map(value, (item) => { - return decodeParamString(item); + return `${key}:${item}`; }); } else { - return decodeParamString(value); - } - }, - convertToSearchTags(obj) { - const tags = []; - for (let key in obj) { - const value = obj[key]; - tags.push(this.decodeParam(value, key)); - } - return tags; - }, - // encodes a django queryset for ui-router's URLMatcherFactory - // {operand__key__comparator: value, } => 'operand:key:comparator:value;...' - // value.isArray expands to: - // {operand__key__comparator: [value1, value2], } => 'operand:key:comparator:value1;operand:key:comparator:value1...' - encodeArr(params) { - let url; - url = _.reduce(params, (result, value, key) => { - return result.concat(encodeUrlString(value, key)); - }, []); - - return url.join(';'); - - // {key:'value'} => 'key:value' - // {key: [value1, value2, ...]} => ['key:value1', 'key:value2'] - function encodeUrlString(value, key){ - if (Array.isArray(value)){ - value = _.uniq(_.flattenDeep(value)); - return _.map(value, (item) => { - return `${key}:${item}`; - }); - } - else { - return `${key}:${value}`; - } - } - }, - - // decodes a django queryset for ui-router's URLMatcherFactory - // 'operand:key:comparator:value,...' => {operand__key__comparator: value, } - decodeArr(arr) { - let params = {}; - _.forEach(arr.split(';'), (item) => { - let key = item.split(':')[0], - value = item.split(':')[1]; - if(!params[key]){ - params[key] = value; - } - else if (Array.isArray(params[key])){ - params[key] = _.uniq(_.flattenDeep(params[key])); - params[key].push(value); - } - else { - params[key] = [params[key], value]; - } - }); - return params; - }, - // REST utilities - options(endpoint) { - Rest.setUrl(endpoint); - return Rest.options(endpoint); - }, - search(endpoint, params) { - Wait('start'); - this.url = `${endpoint}${this.encodeQueryset(params)}`; - Rest.setUrl(this.url); - - return Rest.get() - .then(function(response) { - Wait('stop'); - - if (response - .headers('X-UI-Max-Events') !== null) { - response.data.maxEvents = response. - headers('X-UI-Max-Events'); - } - - return response; - }) - .catch(function(response) { - Wait('stop'); - - this.error(response.data, response.status); - - throw response; - }.bind(this)); - }, - error(data, status) { - if(data && data.detail){ - let error = typeof data.detail === "string" ? data.detail : JSON.parse(data.detail); - - if(_.isArray(error)){ - data.detail = error[0]; - } - } - ProcessErrors($rootScope, data, status, null, { - hdr: 'Error!', - msg: `Invalid search term entered. GET returned: ${status}` - }); - }, - // Removes state definition defaults and pagination terms - stripDefaultParams(params, defaults) { - if(defaults) { - let stripped =_.pick(params, (value, key) => { - // setting the default value of a term to null in a state definition is a very explicit way to ensure it will NEVER generate a search tag, even with a non-default value - return defaults[key] !== value && key !== 'order_by' && key !== 'page' && key !== 'page_size' && defaults[key] !== null; - }); - let strippedCopy = _.cloneDeep(stripped); - if(_.keys(_.pick(defaults, _.keys(strippedCopy))).length > 0){ - for (var key in strippedCopy) { - if (strippedCopy.hasOwnProperty(key)) { - let value = strippedCopy[key]; - if(_.isArray(value)){ - let index = _.indexOf(value, defaults[key]); - value = value.splice(index, 1)[0]; - } - } - } - stripped = strippedCopy; - } - return _(strippedCopy).map(this.decodeParam).flatten().value(); - } - else { - return _(params).map(this.decodeParam).flatten().value(); + return `${key}:${value}`; } } - }; - } + }, + // decodes a django queryset for ui-router's URLMatcherFactory + // 'operand:key:comparator:value,...' => {operand__key__comparator: value, } + decodeArr(arr) { + let params = {}; + + if (!arr) { + return params; + } + + _.forEach(arr.split(';'), (item) => { + let key = item.split(':')[0], + value = item.split(':')[1]; + if(!params[key]){ + params[key] = value; + } + else if (Array.isArray(params[key])){ + params[key] = _.uniq(_.flattenDeep(params[key])); + params[key].push(value); + } + else { + params[key] = [params[key], value]; + } + }); + return params; + }, + // REST utilities + options(endpoint) { + Rest.setUrl(endpoint); + return Rest.options(endpoint); + }, + search(endpoint, params) { + Wait('start'); + this.url = `${endpoint}${this.encodeQueryset(params)}`; + Rest.setUrl(this.url); + + return Rest.get() + .then(function(response) { + Wait('stop'); + + if (response + .headers('X-UI-Max-Events') !== null) { + response.data.maxEvents = response. + headers('X-UI-Max-Events'); + } + + return response; + }) + .catch(function(response) { + Wait('stop'); + + this.error(response.data, response.status); + + throw response; + }.bind(this)); + }, + error(data, status) { + if(data && data.detail){ + let error = typeof data.detail === "string" ? data.detail : JSON.parse(data.detail); + + if(_.isArray(error)){ + data.detail = error[0]; + } + } + ProcessErrors($rootScope, data, status, null, { + hdr: 'Error!', + msg: `Invalid search term entered. GET returned: ${status}` + }); + }, + // Removes state definition defaults and pagination terms + stripDefaultParams(params, defaultParams) { + if (!params) { + return []; + } + if(defaultParams) { + let stripped =_.pick(params, (value, key) => { + // setting the default value of a term to null in a state definition is a very explicit way to ensure it will NEVER generate a search tag, even with a non-default value + return defaultParams[key] !== value && key !== 'order_by' && key !== 'page' && key !== 'page_size' && defaultParams[key] !== null; + }); + let strippedCopy = _.cloneDeep(stripped); + if(_.keys(_.pick(defaultParams, _.keys(strippedCopy))).length > 0){ + for (var key in strippedCopy) { + if (strippedCopy.hasOwnProperty(key)) { + let value = strippedCopy[key]; + if(_.isArray(value)){ + let index = _.indexOf(value, defaultParams[key]); + value = value.splice(index, 1)[0]; + } + } + } + stripped = strippedCopy; + } + return _(strippedCopy).map(this.decodeParam).flatten().value(); + } + else { + return _(params).map(this.decodeParam).flatten().value(); + } + }, + mergeQueryset (queryset, additional, singleSearchParam) { + const space = '%20and%20'; + + const merged = _.merge({}, queryset, additional, (objectValue, sourceValue, key, object) => { + if (!(object[key] && object[key] !== sourceValue)) { + // // https://lodash.com/docs/3.10.1#each + // If this returns undefined merging is handled by default _.merge algorithm + return undefined; + } + + if (_.isArray(object[key])) { + object[key].push(sourceValue); + return object[key]; + } + + if (singleSearchParam) { + if (!object[key]) { + return sourceValue; + } + + const singleSearchParamKeys = object[key].split(space); + + if (_.includes(singleSearchParamKeys, sourceValue)) { + return object[key]; + } + + return `${object[key]}${space}${sourceValue}`; + } + + // Start the array of keys + return [object[key], sourceValue]; + }); + + return merged; + }, + getSearchInputQueryset (searchInput, isRelatedField = null, isAnsibleFactField = null, singleSearchParam = null) { + // XXX Should find a better approach than passing in the two 'is...Field' callbacks XXX + const space = '%20and%20'; + let params = {}; + + // Remove leading/trailing whitespace if there is any + const terms = (searchInput) ? searchInput.trim() : ''; + + if (!(terms && terms !== '')) { + return; + } + + let splitTerms; + + if (singleSearchParam === 'host_filter') { + splitTerms = SmartSearchService.splitFilterIntoTerms(terms); + } else { + splitTerms = SmartSearchService.splitSearchIntoTerms(terms); + } + + const combineSameSearches = (a, b) => { + if (!a) { + return undefined; + } + + if (_.isArray(a)) { + return a.concat(b); + } + + if (singleSearchParam) { + return `${a}${space}${b}`; + } + + return [a, b]; + }; + + _.each(splitTerms, term => { + const termParts = SmartSearchService.splitTermIntoParts(term); + let termParams; + + if (termParts.length === 1) { + termParams = searchWithoutKey(term, singleSearchParam); + } else if (isAnsibleFactField && isAnsibleFactField(termParts)) { + termParams = this.encodeParam({ term, singleSearchParam }); + } else if (isRelatedField && isRelatedField(termParts)) { + termParams = this.encodeParam({ term, singleSearchParam, related: true }); + } else { + termParams = this.encodeParam({ term, singleSearchParam }); + } + + params = _.merge(params, termParams, combineSameSearches); + }); + + return params; + }, + removeTermsFromQueryset(queryset, term, isRelatedField = null, singleSearchParam = null) { + const modifiedQueryset = _.cloneDeep(queryset); + + const removeSingleTermFromQueryset = (value, key) => { + const space = '%20and%20'; + + if (Array.isArray(modifiedQueryset[key])) { + modifiedQueryset[key] = modifiedQueryset[key].filter(item => item !== value); + if (modifiedQueryset[key].length < 1) { + delete modifiedQueryset[key]; + } + } else if (singleSearchParam && _.get(modifiedQueryset, singleSearchParam, []).includes(space)) { + const searchParamParts = modifiedQueryset[singleSearchParam].split(space); + // The value side of each paramPart might have been encoded in + // SmartSearch.splitFilterIntoTerms + _.each(searchParamParts, (paramPart, paramPartIndex) => { + searchParamParts[paramPartIndex] = decodeURIComponent(paramPart); + }); + + const paramPartIndex = searchParamParts.indexOf(value); + + if (paramPartIndex !== -1) { + searchParamParts.splice(paramPartIndex, 1); + } + + modifiedQueryset[singleSearchParam] = searchParamParts.join(space); + + } else { + delete modifiedQueryset[key]; + } + }; + + const termParts = SmartSearchService.splitTermIntoParts(term); + + let removed; + + if (termParts.length === 1) { + removed = searchWithoutKey(term, singleSearchParam); + } else if (isRelatedField && isRelatedField(termParts)) { + removed = this.encodeParam({ term, singleSearchParam, related: true }); + } else { + removed = this.encodeParam({ term, singleSearchParam }); + } + + if (!removed) { + removed = searchWithoutKey(termParts[termParts.length - 1], singleSearchParam); + } + + _.each(removed, removeSingleTermFromQueryset); + + return modifiedQueryset; + }, + createSearchTagsFromQueryset(queryset, defaultParams = null, singleSearchParam = null) { + const space = '%20and%20'; + const modifiedQueryset = angular.copy(queryset); + + let searchTags = []; + + if (singleSearchParam && modifiedQueryset[singleSearchParam]) { + const searchParam = modifiedQueryset[singleSearchParam].split(space); + delete modifiedQueryset[singleSearchParam]; + + $.each(searchParam, (index, param) => { + const paramParts = decodeURIComponent(param).split(/=(.+)/); + const reconstructedSearchString = this.decodeParam(paramParts[1], paramParts[0]); + + searchTags.push(reconstructedSearchString); + }); + } + + return searchTags.concat(this.stripDefaultParams(modifiedQueryset, defaultParams)); + } + }; +} + +QuerysetService.$inject = [ + '$q', + 'Rest', + 'ProcessErrors', + '$rootScope', + 'Wait', + 'DjangoSearchModel', + 'SmartSearchService', ]; + +export default QuerysetService; diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index 5a59bbde81..414a0763db 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -7,7 +7,6 @@ function SmartSearchController ( GetBasePath, i18n, qs, - SmartSearchService ) { const searchKey = `${$scope.iterator}_search`; const optionsKey = `${$scope.list.iterator}_options`; @@ -58,7 +57,7 @@ function SmartSearchController ( } }); - function compareParams(a, b) { + function compareParams (a, b) { for (let key in a) { if (!(key in b) || a[key].toString() !== b[key].toString()) { return false; @@ -106,22 +105,8 @@ function SmartSearchController ( } function generateSearchTags () { - $scope.searchTags = []; - - const querysetCopy = angular.copy(queryset); - - if ($scope.singleSearchParam && querysetCopy[$scope.singleSearchParam]) { - const searchParam = querysetCopy[$scope.singleSearchParam].split('%20and%20'); - delete querysetCopy[$scope.singleSearchParam]; - - $.each(searchParam, (index, param) => { - const paramParts = decodeURIComponent(param).split(/=(.+)/); - const reconstructedSearchString = qs.decodeParam(paramParts[1], paramParts[0]); - $scope.searchTags.push(reconstructedSearchString); - }); - } - - $scope.searchTags = $scope.searchTags.concat(qs.stripDefaultParams(querysetCopy, defaults)); + const { singleSearchParam } = $scope; + $scope.searchTags = qs.createSearchTagsFromQueryset(queryset, defaults, singleSearchParam); } function revertSearch (queryToBeRestored) { @@ -149,14 +134,7 @@ function SmartSearchController ( $scope.showKeyPane = !$scope.showKeyPane; }; - function searchWithoutKey (term, singleSearchParam = null) { - if (singleSearchParam) { - return { [singleSearchParam]: `search=${encodeURIComponent(term)}` }; - } - return { search: encodeURIComponent(term) }; - } - - function isAnsibleFactSearchTerm (termParts) { + function isAnsibleFactField (termParts) { const rootField = termParts[0].split('.')[0].replace(/^-/, ''); return rootField === 'ansible_facts'; } @@ -172,111 +150,21 @@ function SmartSearchController ( return (isRelatedSearchTermField || isBaseModelRelatedSearchTermField); } - function getSearchInputQueryset ({ terms, singleSearchParam }) { - let params = {}; - - // remove leading/trailing whitespace - terms = (terms) ? terms.trim() : ''; - let splitTerms; - - if (singleSearchParam === 'host_filter') { - splitTerms = SmartSearchService.splitFilterIntoTerms(terms); - } else { - splitTerms = SmartSearchService.splitSearchIntoTerms(terms); - } - - const combineSameSearches = (a, b) => { - if (!a) { - return undefined; - } - - if (_.isArray(a)) { - return a.concat(b); - } - - if (singleSearchParam) { - return `${a}%20and%20${b}`; - } - - return [a, b]; - }; - - _.each(splitTerms, term => { - const termParts = SmartSearchService.splitTermIntoParts(term); - let termParams; - - if (termParts.length === 1) { - termParams = searchWithoutKey(term, singleSearchParam); - } else if (isAnsibleFactSearchTerm(termParts)) { - termParams = qs.encodeParam({ term, singleSearchParam }); - } else if (isRelatedField(termParts)) { - termParams = qs.encodeParam({ term, singleSearchParam, related: true }); - } else { - termParams = qs.encodeParam({ term, singleSearchParam }); - } - - params = _.merge(params, termParams, combineSameSearches); - }); - - return params; - } - - function mergeQueryset (qset, additional, singleSearchParam) { - const merged = _.merge({}, qset, additional, (objectValue, sourceValue, key, object) => { - if (!(object[key] && object[key] !== sourceValue)) { - // // https://lodash.com/docs/3.10.1#each - // If this returns undefined merging is handled by default _.merge algorithm - return undefined; - } - - if (_.isArray(object[key])) { - object[key].push(sourceValue); - return object[key]; - } - - if (singleSearchParam) { - if (!object[key]) { - return sourceValue; - } - - const singleSearchParamKeys = object[key].split('%20and%20'); - - if (_.includes(singleSearchParamKeys, sourceValue)) { - return object[key]; - } - - return `${object[key]}%20and%20${sourceValue}`; - } - - // Start the array of keys - return [object[key], sourceValue]; - }); - - return merged; - } - $scope.addTerms = terms => { const { singleSearchParam } = $scope; - const origQueryset = _.clone(queryset); + const unmodifiedQueryset = _.clone(queryset); - // Remove leading/trailing whitespace if there is any - terms = (terms) ? terms.trim() : ''; - - if (!(terms && terms !== '')) { - return; - } - - const searchInputQueryset = getSearchInputQueryset({ terms, singleSearchParam }); - queryset = mergeQueryset(queryset, searchInputQueryset, singleSearchParam); + const searchInputQueryset = qs.getSearchInputQueryset(terms, isRelatedField, isAnsibleFactField, singleSearchParam); + const modifiedQueryset = qs.mergeQueryset(queryset, searchInputQueryset, singleSearchParam); // Go back to the first page after a new search - delete queryset.page; + delete modifiedQueryset.page; // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic // This transition will not reload controllers/resolves/views but will register new // $stateParams[searchKey] terms. if (!$scope.querySet) { - $state.go('.', { [searchKey]: queryset }) + $state.go('.', { [searchKey]: modifiedQueryset }) .then(() => { // same as above in $scope.remove. For some reason deleting the page // from the queryset works for all lists except lists in modals. @@ -284,80 +172,26 @@ function SmartSearchController ( }); } - qs.search(path, queryset) + qs.search(path, modifiedQueryset) .then(({ data }) => { if ($scope.querySet) { - $scope.querySet = queryset; + $scope.querySet = modifiedQueryset; } $scope.dataset = data; $scope.collection = data.results; }) - .catch(() => revertSearch(origQueryset)); + .catch(() => revertSearch(unmodifiedQueryset)); $scope.searchTerm = null; generateSearchTags(); }; - - function removeTermsFromQueryset(qset, term, singleSearchParam = null) { - const returnedQueryset = _.cloneDeep(qset); - - const removeSingleTermFromQueryset = (value, key) => { - const space = '%20and%20'; - - if (Array.isArray(returnedQueryset[key])) { - returnedQueryset[key] = returnedQueryset[key].filter(item => item !== value); - if (returnedQueryset[key].length < 1) { - delete returnedQueryset[key]; - } - } else if (singleSearchParam && _.get(returnedQueryset, singleSearchParam, []).includes(space)) { - const searchParamParts = returnedQueryset[singleSearchParam].split(space); - // The value side of each paramPart might have been encoded in - // SmartSearch.splitFilterIntoTerms - _.each(searchParamParts, (paramPart, paramPartIndex) => { - searchParamParts[paramPartIndex] = decodeURIComponent(paramPart); - }); - - const paramPartIndex = searchParamParts.indexOf(value); - - if (paramPartIndex !== -1) { - searchParamParts.splice(paramPartIndex, 1); - } - - returnedQueryset[singleSearchParam] = searchParamParts.join(space); - - } else { - delete returnedQueryset[key]; - } - }; - - const termParts = SmartSearchService.splitTermIntoParts(term); - - let removed; - - if (termParts.length === 1) { - removed = searchWithoutKey(term, singleSearchParam); - } else if (isRelatedField(termParts)) { - removed = qs.encodeParam({ term, singleSearchParam, related: true }); - } else { - removed = qs.encodeParam({ term, singleSearchParam }); - } - - if (!removed) { - removed = searchWithoutKey(termParts[termParts.length - 1], singleSearchParam); - } - - _.each(removed, removeSingleTermFromQueryset); - - return returnedQueryset; - } - // remove tag, merge new queryset, $state.go $scope.removeTerm = index => { const { singleSearchParam } = $scope; const [term] = $scope.searchTags.splice(index, 1); - const modifiedQueryset = removeTermsFromQueryset(queryset, term, singleSearchParam); + const modifiedQueryset = qs.removeTermsFromQueryset(queryset, term, isRelatedField, singleSearchParam); if (!$scope.querySet) { $state.go('.', { [searchKey]: modifiedQueryset }) @@ -365,8 +199,7 @@ function SmartSearchController ( // for some reason deleting a tag from a list in a modal does not // remove the param from $stateParams. Here we'll manually check to make sure // that that happened and remove it if it didn't. - const clearedParams = removeTermsFromQueryset( - $stateParams[searchKey], term, singleSearchParam); + const clearedParams = qs.removeTermsFromQueryset($stateParams[searchKey], term, isRelatedField, singleSearchParam); $stateParams[searchKey] = clearedParams; }); } @@ -416,7 +249,6 @@ SmartSearchController.$inject = [ 'GetBasePath', 'i18n', 'QuerySet', - 'SmartSearchService', ]; export default SmartSearchController; From 7acc99cf15041e7122193107ebbe78b2b0b02650 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 4 Mar 2018 16:51:03 -0500 Subject: [PATCH 143/379] initial search integration --- awx/ui/client/features/output/_index.less | 49 +++++++++++ .../features/output/index.controller.js | 83 ++++++++++++++++++- awx/ui/client/features/output/index.js | 30 +++++-- awx/ui/client/features/output/index.view.html | 45 ++++++++++ .../features/output/search-key.directive.js | 38 +++++++++ .../features/output/search-key.partial.html | 20 +++++ 6 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 awx/ui/client/features/output/search-key.directive.js create mode 100644 awx/ui/client/features/output/search-key.partial.html diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index d661afae97..989b532ab6 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -150,3 +150,52 @@ white-space: pre-wrap; } + +// Search --------------------------------------------------------------------------------- +@at-jobz-top-search-key: @at-space-2x; +@at-jobz-bottom-search-key: @at-space-3x; + +.jobz-searchKeyPaneContainer { + margin-top: @at-jobz-top-search-key; + margin-bottom: @at-jobz-bottom-search-key; +} + +.jobz-searchKeyPane { + // background-color: @at-gray-f6; + background-color: @login-notice-bg; + color: @login-notice-text; + border-radius: @at-border-radius; + border: 1px solid @at-gray-b7; + // color: @at-gray-848992; + padding: 6px @at-padding-input 6px @at-padding-input; +} + +.jobz-searchClearAll { + font-size: 10px; + padding-bottom: @at-space; +} + +.jobz-Button-searchKey { + .at-mixin-Button(); + + background-color: @at-blue; + border-color: at-color-button-border-default; + color: @at-white; + + &:hover, &:active { + color: @at-white; + background-color: @at-blue-hover; + box-shadow: none; + } + + &:focus { + color: @at-white; + } +} + +.jobz-tagz { + margin-top: @at-space; + display: flex; + width: 100%; + flex-wrap: wrap; +} diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index e2aeae2d90..7e524a26f4 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -9,6 +9,8 @@ let page; let render; let scroll; let resource; +let $state; +let qs; let chain; @@ -19,7 +21,9 @@ function JobsIndexController ( _render_, _$scope_, _$compile_, - _$q_ + _$q_, + _$state_, + _qs_, ) { vm = this || {}; @@ -59,6 +63,23 @@ function JobsIndexController ( const stream = false; // TODO: Set in route chain = $q.resolve(); + + // search + $state = _$state_; + qs = _qs_; + + vm.searchValue = ''; + vm.searchRejected = null; + vm.searchKey = false; + vm.searchKeyExamples = searchKeyExamples; + vm.searchKeyFields = searchKeyFields; + + vm.clearSearch = clearSearch; + vm.search = search; + vm.toggleSearchKey = toggleSearchKey; + vm.removeSearchTag = removeSearchTag; + vm.searchTags = getSearchTags(getCurrentQueryset()); + render.requestAnimationFrame(() => init()); } @@ -318,6 +339,62 @@ function toggle (uuid, menu) { lines.removeClass('hidden'); } + +// +// Search +// + +const searchReloadOptions = { reload: true, inherit: false }; +const searchKeyExamples = ['id:>1', 'task:set', 'created:>=2000-01-01']; +const searchKeyFields = ['changed', 'failed', 'host_name', 'stdout', 'task', 'role', 'playbook', 'play']; + +function toggleSearchKey () { + vm.searchKey = !vm.searchKey; +} + +function getCurrentQueryset() { + const { job_event_search } = $state.params; + + return qs.decodeArr(job_event_search); +} + +function getSearchTags (queryset) { + return qs.createSearchTagsFromQueryset(queryset) + .filter(tag => !tag.startsWith('event')) + .filter(tag => !tag.startsWith('-event')) + .filter(tag => !tag.startsWith('page_size')) + .filter(tag => !tag.startsWith('order_by')); +} + +function removeSearchTag (index) { + const searchTerm = vm.searchTags[index]; + + const currentQueryset = getCurrentQueryset(); + const modifiedQueryset = qs.removeTermsFromQueryset(currentQueryset, searchTerm); + + vm.searchTags = getSearchTags(modifiedQueryset); + + $state.params.job_event_search = qs.encodeArr(modifiedQueryset); + $state.transitionTo($state.current, $state.params, searchReloadOptions); +} + +function search () { + const searchInputQueryset = qs.getSearchInputQueryset(vm.searchValue); + + const currentQueryset = getCurrentQueryset(); + const modifiedQueryset = qs.mergeQueryset(currentQueryset, searchInputQueryset); + + vm.searchTags = getSearchTags(modifiedQueryset); + + $state.params.job_event_search = qs.encodeArr(modifiedQueryset); + $state.transitionTo($state.current, $state.params, searchReloadOptions); +} + +function clearSearch () { + vm.searchTags = []; + + $state.params.job_event_search = ''; + $state.transitionTo($state.current, $state.params, searchReloadOptions); } JobsIndexController.$inject = [ @@ -327,7 +404,9 @@ JobsIndexController.$inject = [ 'JobRenderService', '$scope', '$compile', - '$q' + '$q', + '$state', + 'QuerySet', ]; module.exports = JobsIndexController; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index bc73ca83c7..593b7b1097 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -6,6 +6,7 @@ import Controller from '~features/output/index.controller'; import PageService from '~features/output/page.service'; import RenderService from '~features/output/render.service'; import ScrollService from '~features/output/scroll.service'; +import SearchKeyDirective from '~features/output/search-key.directive'; const Template = require('~features/output/index.view.html'); @@ -15,8 +16,8 @@ const PAGE_LIMIT = 3; const PAGE_SIZE = 100; const WS_PREFIX = 'ws'; -function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams) { - const { id, type } = $stateParams; +function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams, qs, Wait) { + const { id, type, job_event_search } = $stateParams; let Resource; let related = 'events'; @@ -43,14 +44,20 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ return null; } + const params = { page_size: PAGE_SIZE, order_by: 'start_line' }; + + if (job_event_search) { + const searchParams = qs.encodeQuerysetObject(qs.decodeArr(job_event_search)); + + Object.assign(params, searchParams); + } + + Wait('start'); return new Resource('get', id) .then(model => model.extend(related, { pageCache: PAGE_CACHE, pageLimit: PAGE_LIMIT, - params: { - page_size: PAGE_SIZE, - order_by: 'start_line' - } + params, })) .then(model => { return { @@ -67,7 +74,9 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ pageLimit: PAGE_LIMIT } }; - }); + }) + .catch(({ data, status }) => qs.error(data, status)) + .finally(() => Wait('stop')); } function resolveWebSocketConnection (SocketService, $stateParams) { @@ -131,8 +140,8 @@ function getWebSocketResource (type) { function JobsRun ($stateRegistry) { const state = { name: 'jobz', - url: '/jobz/:type/:id', - route: '/jobz/:type/:id', + url: '/jobz/:type/:id?job_event_search', + route: '/jobz/:type/:id?job_event_search', data: { activityStream: true, activityStreamTarget: 'jobs' @@ -152,6 +161,8 @@ function JobsRun ($stateRegistry) { 'SystemJobModel', 'WorkflowJobModel', '$stateParams', + 'QuerySet', + 'Wait', resolveResource ], ncyBreadcrumb: [ @@ -179,6 +190,7 @@ angular .service('JobStrings', Strings) .service('JobPageService', PageService) .service('JobScrollService', ScrollService) + .directive('atSearchKey', SearchKeyDirective) .run(JobsRun); export default MODULE_NAME; diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 3c1a276826..2516ede6c7 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -7,6 +7,51 @@
+ +
+
+ + + + + + +
+
+ +
+
+
{{ tag }}
+
+ +
+
+ +
+ + + +
{ + vm.examples = scope.examples || []; + vm.fields = scope.fields || []; + vm.relatedFields = scope.relatedFields || []; + } +} + +AtSearchKeyController.$inject = ['$scope']; + + +function atSearchKey () { + return { + templateUrl, + restrict: 'E', + require: ['atSearchKey'], + controllerAs: 'vm', + link: atSearchKeyLink, + controller: AtSearchKeyController, + scope: { + examples: '=', + fields: '=', + relatedFields: '=', + }, + }; +} + +export default atSearchKey; diff --git a/awx/ui/client/features/output/search-key.partial.html b/awx/ui/client/features/output/search-key.partial.html new file mode 100644 index 0000000000..d2790d285f --- /dev/null +++ b/awx/ui/client/features/output/search-key.partial.html @@ -0,0 +1,20 @@ + +
+
+
+
+
EXAMPLES:
+ +
+
+
+ FIELDS: + {{ field }}, +
+
+ ADDITIONAL INFORMATION: + For additional information on advanced search search syntax please see the Ansible Tower + documentation. +
+
+
From 189963ae83b9152c5f322091e7b9934355e9ec18 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 8 Mar 2018 16:59:30 -0500 Subject: [PATCH 144/379] Add independent stream service --- .../features/output/index.controller.js | 118 +++--------- awx/ui/client/features/output/index.js | 2 + awx/ui/client/features/output/index.view.html | 3 +- awx/ui/client/features/output/page.service.js | 24 ++- .../client/features/output/stream.service.js | 182 ++++++++++++++++++ 5 files changed, 230 insertions(+), 99 deletions(-) create mode 100644 awx/ui/client/features/output/stream.service.js diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 7e524a26f4..aeb615c22f 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -1,6 +1,3 @@ -const JOB_START = 'playbook_on_start'; -const JOB_END = 'playbook_on_stats'; - let vm; let $compile; let $scope; @@ -8,17 +5,20 @@ let $q; let page; let render; let scroll; +let stream; let resource; let $state; let qs; let chain; +let chainLength; function JobsIndexController ( _resource_, _page_, _scroll_, _render_, + _stream_, _$scope_, _$compile_, _$q_, @@ -35,6 +35,7 @@ function JobsIndexController ( page = _page_; scroll = _scroll_; render = _render_; + stream = _stream_; // Development helper(s) vm.clear = devClear; @@ -53,17 +54,6 @@ function JobsIndexController ( vm.expand = expand; vm.isExpanded = true; - // Real-time (active between JOB_START and JOB_END events only) - vm.stream = { - active: false, - rendering: false, - paused: false - }; - - const stream = false; // TODO: Set in route - - chain = $q.resolve(); - // search $state = _$state_; qs = _qs_; @@ -83,8 +73,10 @@ function JobsIndexController ( render.requestAnimationFrame(() => init()); } -function init (stream) { - page.init(resource); +function init (pageMode) { + page.init({ + resource + }); render.init({ get: () => resource.model.get(`related.${resource.related}.results`), @@ -97,76 +89,24 @@ function init (stream) { next }); - if (stream) { - $scope.$on(resource.ws.namespace, process); - } else { + stream.init({ + page, + scroll, + resource, + render: events => shift().then(() => append(events, true)), + listen: (namespace, listener) => { + $scope.$on(namespace, (scope, data) => listener(data)); + } + }); + + if (pageMode) { next(); } } -function process (scope, data) { - chain = chain.then(() => { - if (data.event === JOB_START) { - vm.stream.active = true; - scroll.lock(); - } else if (data.event === JOB_END) { - vm.stream.active = false; - } - - const pageAdded = page.addToBuffer(data); - - if (pageAdded && !scroll.isLocked()) { - vm.stream.paused = true; - } - - if (vm.stream.paused && scroll.isLocked()) { - vm.stream.paused = false; - } - - if (vm.stream.rendering || vm.stream.paused) { - return; - } - - const events = page.emptyBuffer(); - - return renderStream(events); - }) -} - -function renderStream (events) { - vm.stream.rendering = true; - - return shift() - .then(() => append(events, true)) - .then(() => { - if (scroll.isLocked()) { - scroll.setScrollPosition(scroll.getScrollHeight()); - } - - if (!vm.stream.active) { - const buffer = page.emptyBuffer(); - - if (buffer.length) { - return renderStream(buffer); - } else { - vm.stream.rendering = false; - scroll.unlock(); - } - } else { - vm.stream.rendering = false; - } - }); -} - -function devClear () { - init(true); +function devClear (pageMode) { + init(pageMode); render.clear(); - - vm.stream = { - active: false, - rendering: false, - paused: false - }; } function next () { @@ -257,14 +197,12 @@ function scrollHome () { } function scrollEnd () { - if (scroll.isLocked()) { - page.setBookmark(); - scroll.unlock(); - - return; - } else if (!scroll.isLocked() && vm.stream.active) { - page.removeBookmark(); - scroll.lock(); + if (stream.isActive()) { + if (stream.isPaused()) { + stream.resume(); + } else { + stream.pause(); + } return; } @@ -339,6 +277,7 @@ function toggle (uuid, menu) { lines.removeClass('hidden'); } +} // // Search @@ -402,6 +341,7 @@ JobsIndexController.$inject = [ 'JobPageService', 'JobScrollService', 'JobRenderService', + 'JobStreamService', '$scope', '$compile', '$q', diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 593b7b1097..64819e9ca3 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -7,6 +7,7 @@ import PageService from '~features/output/page.service'; import RenderService from '~features/output/render.service'; import ScrollService from '~features/output/scroll.service'; import SearchKeyDirective from '~features/output/search-key.directive'; +import StreamService from '~features/output/stream.service'; const Template = require('~features/output/index.view.html'); @@ -190,6 +191,7 @@ angular .service('JobStrings', Strings) .service('JobPageService', PageService) .service('JobScrollService', ScrollService) + .service('JobStreamService', StreamService) .directive('atSearchKey', SearchKeyDirective) .run(JobsRun); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 2516ede6c7..53b69a43b2 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,7 +1,8 @@
-

+

+

diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index a89d56d6b8..64f0542fe0 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -1,5 +1,5 @@ function JobPageService ($q) { - this.init = resource => { + this.init = ({ resource }) => { this.resource = resource; this.page = { @@ -54,12 +54,9 @@ function JobPageService ($q) { reference.state.count++; }; - this.addToPageCache = (index, event, reference) => { - reference.cache[index].events.push(event); - }; - this.addToBuffer = event => { const reference = this.getReference(); + const index = reference.cache.length - 1; let pageAdded = false; if (this.result.count % this.page.size === 0) { @@ -70,9 +67,10 @@ function JobPageService ($q) { } this.trimBuffer(); + pageAdded = true; } else { - this.addToPageCache(reference.cache.length - 1, event, reference); + reference.cache[index].events.push(event); } this.buffer.count++; @@ -97,6 +95,14 @@ function JobPageService ($q) { } }; + this.isBufferFull = () => { + if (this.buffer.count === 2) { + return true; + } + + return false; + } + this.emptyBuffer = () => { const reference = this.getReference(); let data = []; @@ -183,9 +189,9 @@ function JobPageService ($q) { return; } - this.bookmark.state.first = this.page.state.first; - this.bookmark.state.last = this.page.state.last; - this.bookmark.state.current = this.page.state.current; + this.bookmark.state.first = this.page.state.first - 1; + this.bookmark.state.last = this.page.state.last - 1; + this.bookmark.state.current = this.page.state.current - 1; this.bookmark.cache = JSON.parse(JSON.stringify(this.page.cache)); this.bookmark.set = true; this.bookmark.pending = false; diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js new file mode 100644 index 0000000000..557337adc3 --- /dev/null +++ b/awx/ui/client/features/output/stream.service.js @@ -0,0 +1,182 @@ +const JOB_START = 'playbook_on_start'; +const JOB_END = 'playbook_on_stats'; +const MAX_LAG = 120; + +function JobStreamService ($q) { + this.init = ({ resource, scroll, page, render, listen }) => { + this.resource = resource; + this.scroll = scroll; + this.page = page; + + this.lag = 0; + this.count = 0; + this.pageCount = 0; + this.chain = $q.resolve(); + this.factors = this.getBatchFactors(this.resource.page.size); + this.state = { + started: false, + paused: false, + pausing: false, + resuming: false, + ending: false, + ended: false + }; + + this.hooks = { + render, + listen + }; + + this.hooks.listen(resource.ws.namespace, this.listen); + }; + + this.getBatchFactors = size => { + const factors = [1]; + + for (let i = 2; i <= size / 2; i++) { + if (size % i === 0) { + factors.push(i); + } + } + + factors.push(size); + + return factors; + }; + + this.getBatchFactorIndex = () => { + const index = Math.floor((this.lag / MAX_LAG) * this.factors.length); + + return index > this.factors.length - 1 ? this.factors.length - 1 : index; + }; + + this.setBatchFrameCount = () => { + const index = this.getBatchFactorIndex(); + + this.framesPerRender = this.factors[index]; + }; + + this.buffer = data => { + const pageAdded = this.page.addToBuffer(data); + + this.pageCount++; + + if (pageAdded) { + this.setBatchFrameCount(); + + if (this.isPausing()) { + this.pause(true); + } else if (this.isResuming()) { + this.resume(true); + } + } + }; + + this.listen = data => { + this.lag++; + + this.chain = this.chain + .then(() => { + if (data.event === JOB_START) { + this.start(); + } else if (data.event === JOB_END) { + if (this.isPaused()) { + this.end(true); + } else { + this.end(); + } + } + + this.buffer(data); + this.count++; + + if (this.isPaused() || !this.isBatchFull()) { + return $q.resolve(); + } + + const events = this.page.emptyBuffer(); + this.count -= events.length; + + return this.renderFrame(events); + }) + .then(() => --this.lag); + }; + + this.renderFrame = events => { + return this.hooks.render(events) + .then(() => { + if (this.scroll.isLocked()) { + this.scroll.setScrollPosition(this.scroll.getScrollHeight()); + } + + if (this.isEnding()) { + const lastEvents = this.page.emptyBuffer(); + + if (lastEvents.length) { + return this.renderFrame(lastEvents); + } + + this.end(true); + } + + return $q.resolve(); + }); + }; + + this.resume = done => { + if (done) { + this.state.resuming = false; + this.state.paused = false; + + return; + } + + this.scroll.lock(); + this.state.resuming = true; + this.page.removeBookmark(); + }; + + this.pause = done => { + if (done) { + this.state.pausing = false; + this.state.paused = true; + this.scroll.resume(); + + return; + } + + this.scroll.unlock(); + this.scroll.pause(); + this.state.pausing = true; + this.page.setBookmark(); + }; + + this.start = () => { + this.state.started = true; + this.scroll.lock(); + }; + + this.end = done => { + if (done) { + this.state.ending = false; + this.state.ended = true; + this.scroll.unlock(); + + return; + } + + this.state.ending = true; + }; + + this.isBatchFull = () => this.count % this.framesPerRender === 0; + this.isPaused = () => this.state.paused; + this.isPausing = () => this.state.pausing; + this.isResuming = () => this.state.resuming; + this.isActive = () => this.state.started && !this.state.ended; + this.isEnding = () => this.state.ending; + this.isDone = () => this.state.ended; +} + +JobStreamService.$inject = ['$q']; + +export default JobStreamService; From 57ea582898da3e65ce55b700b1b9ac251b23de89 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 9 Mar 2018 12:35:19 -0500 Subject: [PATCH 145/379] Fix stream pause/resume transitions --- .../features/output/index.controller.js | 21 ++++++++++++-- awx/ui/client/features/output/index.view.html | 1 - awx/ui/client/features/output/page.service.js | 3 +- .../client/features/output/render.service.js | 11 ++----- .../client/features/output/scroll.service.js | 6 ++-- .../client/features/output/stream.service.js | 29 ++++++++++--------- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index aeb615c22f..4f84cfbf8d 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -10,9 +10,6 @@ let resource; let $state; let qs; -let chain; -let chainLength; - function JobsIndexController ( _resource_, _page_, @@ -179,6 +176,10 @@ function shift () { } function scrollHome () { + if (scroll.isPaused()) { + return; + } + scroll.pause(); return page.first() @@ -198,12 +199,18 @@ function scrollHome () { function scrollEnd () { if (stream.isActive()) { + if (stream.isTransitioning()) { + return; + } + if (stream.isPaused()) { stream.resume(); } else { stream.pause(); } + return; + } else if (scroll.isPaused()) { return; } @@ -225,10 +232,18 @@ function scrollEnd () { } function scrollPageUp () { + if (scroll.isPaused()) { + return; + } + scroll.pageUp(); } function scrollPageDown () { + if (scroll.isPaused()) { + return; + } + scroll.pageDown(); } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 53b69a43b2..dfb6790d78 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,7 +1,6 @@
-

diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index 64f0542fe0..3c5eca95e8 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -189,12 +189,13 @@ function JobPageService ($q) { return; } - this.bookmark.state.first = this.page.state.first - 1; + this.bookmark.state.first = this.page.state.first; this.bookmark.state.last = this.page.state.last - 1; this.bookmark.state.current = this.page.state.current - 1; this.bookmark.cache = JSON.parse(JSON.stringify(this.page.cache)); this.bookmark.set = true; this.bookmark.pending = false; + console.log('applied', JSON.stringify(this.bookmark.state, 0, 2)); }; this.removeBookmark = () => { diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 235de94198..9f883a126c 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -87,7 +87,7 @@ function JobRenderService ($q, $sce, $window) { return { html, count }; }; - this.createRecord = event => { + this.createRecord = (ln, lines, event) => { if (!event.uuid) { return null; } @@ -104,7 +104,7 @@ function JobRenderService ($q, $sce, $window) { }; if (event.parent_uuid) { - info.parents = getParentEvents(event.parent_uuid); + info.parents = this.getParentEvents(event.parent_uuid); } if (info.isTruncated) { @@ -209,7 +209,7 @@ function JobRenderService ($q, $sce, $window) { list.push(uuid); if (this.record[uuid].parents) { - list = list.concat(record[uuid].parents); + list = list.concat(this.record[uuid].parents); } } @@ -231,7 +231,6 @@ function JobRenderService ($q, $sce, $window) { this.remove = elements => { return this.requestAnimationFrame(() => { - elements.empty(); elements.remove(); }); }; @@ -254,10 +253,6 @@ function JobRenderService ($q, $sce, $window) { return this.requestAnimationFrame(); }; - this.build = () => { - - }; - this.clear = () => { const elements = this.el.children(); diff --git a/awx/ui/client/features/output/scroll.service.js b/awx/ui/client/features/output/scroll.service.js index ef80655bf5..791d495796 100644 --- a/awx/ui/client/features/output/scroll.service.js +++ b/awx/ui/client/features/output/scroll.service.js @@ -137,6 +137,10 @@ function JobScrollService ($q, $timeout) { this.isAtRest(); }; + this.scrollToBottom = () => { + this.setScrollPosition(this.getScrollHeight()); + }; + this.isAtRest = () => { if (this.position.current === 0 && !this.state.top) { this.state.top = true; @@ -161,12 +165,10 @@ function JobScrollService ($q, $timeout) { this.lock = () => { this.state.locked = true; - this.state.paused = true; }; this.unlock = () => { this.state.locked = false; - this.state.paused = false; }; this.isLocked = () => { diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index 557337adc3..05603d103a 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -106,7 +106,7 @@ function JobStreamService ($q) { return this.hooks.render(events) .then(() => { if (this.scroll.isLocked()) { - this.scroll.setScrollPosition(this.scroll.getScrollHeight()); + this.scroll.scrollToBottom(); } if (this.isEnding()) { @@ -127,13 +127,13 @@ function JobStreamService ($q) { if (done) { this.state.resuming = false; this.state.paused = false; - - return; + } else if (!this.isTransitioning()) { + this.scroll.pause(); + this.scroll.lock(); + this.scroll.scrollToBottom(); + this.state.resuming = true; + this.page.removeBookmark(); } - - this.scroll.lock(); - this.state.resuming = true; - this.page.removeBookmark(); }; this.pause = done => { @@ -141,18 +141,17 @@ function JobStreamService ($q) { this.state.pausing = false; this.state.paused = true; this.scroll.resume(); - - return; + } else if (!this.isTransitioning()) { + this.scroll.pause(); + this.scroll.unlock(); + this.state.pausing = true; + this.page.setBookmark(); } - - this.scroll.unlock(); - this.scroll.pause(); - this.state.pausing = true; - this.page.setBookmark(); }; this.start = () => { this.state.started = true; + this.scroll.pause(); this.scroll.lock(); }; @@ -161,6 +160,7 @@ function JobStreamService ($q) { this.state.ending = false; this.state.ended = true; this.scroll.unlock(); + this.scroll.resume(); return; } @@ -172,6 +172,7 @@ function JobStreamService ($q) { this.isPaused = () => this.state.paused; this.isPausing = () => this.state.pausing; this.isResuming = () => this.state.resuming; + this.isTransitioning = () => this.isActive() && (this.state.pausing || this.state.resuming); this.isActive = () => this.state.started && !this.state.ended; this.isEnding = () => this.state.ending; this.isDone = () => this.state.ended; From c9612b8c75e376a89a887951b6d28007743d4783 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 9 Mar 2018 13:09:46 -0500 Subject: [PATCH 146/379] Fix (most) lint errors --- .../features/output/index.controller.js | 22 ++++----- awx/ui/client/features/output/index.js | 47 ++++++++++-------- awx/ui/client/features/output/page.service.js | 47 +++++++----------- .../client/features/output/render.service.js | 49 +++++++------------ .../client/features/output/scroll.service.js | 34 ++++--------- .../features/output/search-key.directive.js | 3 +- .../client/features/output/stream.service.js | 32 ++++++------ awx/ui/client/lib/models/Base.js | 4 +- 8 files changed, 101 insertions(+), 137 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 4f84cfbf8d..b65cb52a68 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -110,7 +110,7 @@ function next () { return page.next() .then(events => { if (!events) { - return; + return $q.resolve(); } return shift() @@ -119,13 +119,13 @@ function next () { } function previous () { - let initialPosition = scroll.getScrollPosition(); + const initialPosition = scroll.getScrollPosition(); let postPopHeight; return page.previous() .then(events => { if (!events) { - return; + return $q.resolve(); } return pop() @@ -134,7 +134,7 @@ function previous () { return prepend(events); }) - .then(() => { + .then(() => { const currentHeight = scroll.getScrollHeight(); scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition); }); @@ -177,7 +177,7 @@ function shift () { function scrollHome () { if (scroll.isPaused()) { - return; + return $q.resolve(); } scroll.pause(); @@ -185,7 +185,7 @@ function scrollHome () { return page.first() .then(events => { if (!events) { - return; + return $q.resolve(); } return render.clear() @@ -200,7 +200,7 @@ function scrollHome () { function scrollEnd () { if (stream.isActive()) { if (stream.isTransitioning()) { - return; + return $q.resolve(); } if (stream.isPaused()) { @@ -209,9 +209,9 @@ function scrollEnd () { stream.pause(); } - return; + return $q.resolve(); } else if (scroll.isPaused()) { - return; + return $q.resolve(); } scroll.pause(); @@ -219,7 +219,7 @@ function scrollEnd () { return page.last() .then(events => { if (!events) { - return; + return $q.resolve(); } return render.clear() @@ -306,7 +306,7 @@ function toggleSearchKey () { vm.searchKey = !vm.searchKey; } -function getCurrentQueryset() { +function getCurrentQueryset () { const { job_event_search } = $state.params; return qs.decodeArr(job_event_search); diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 64819e9ca3..ebf69f5243 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -17,7 +17,16 @@ const PAGE_LIMIT = 3; const PAGE_SIZE = 100; const WS_PREFIX = 'ws'; -function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJob, $stateParams, qs, Wait) { +function resolveResource ( + Job, + ProjectUpdate, + AdHocCommand, + SystemJob, + WorkflowJob, + $stateParams, + qs, + Wait +) { const { id, type, job_event_search } = $stateParams; let Resource; @@ -60,22 +69,20 @@ function resolveResource (Job, ProjectUpdate, AdHocCommand, SystemJob, WorkflowJ pageLimit: PAGE_LIMIT, params, })) - .then(model => { - return { - id, - type, - model, - related, - ws: { - namespace: `${WS_PREFIX}-${getWebSocketResource(type).key}-${id}` - }, - page: { - cache: PAGE_CACHE, - size: PAGE_SIZE, - pageLimit: PAGE_LIMIT - } - }; - }) + .then(model => ({ + id, + type, + model, + related, + ws: { + namespace: `${WS_PREFIX}-${getWebSocketResource(type).key}-${id}` + }, + page: { + cache: PAGE_CACHE, + size: PAGE_SIZE, + pageLimit: PAGE_LIMIT + } + })) .catch(({ data, status }) => qs.error(data, status)) .finally(() => Wait('stop')); } @@ -84,9 +91,6 @@ function resolveWebSocketConnection (SocketService, $stateParams) { const { type, id } = $stateParams; const resource = getWebSocketResource(type); - let name; - let events; - const state = { data: { socket: { @@ -132,12 +136,13 @@ function getWebSocketResource (type) { name = 'jobs'; key = 'job_events'; break; + default: + throw new Error('Unsupported WebSocket type'); } return { name, key }; } - function JobsRun ($stateRegistry) { const state = { name: 'jobz', diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index 3c5eca95e8..3f6461345e 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -47,7 +47,7 @@ function JobPageService ($q) { } else { reference.cache.unshift(page); reference.state.first = page.number; - reference.state.last = reference.cache[reference.cache.length -1].number; + reference.state.last = reference.cache[reference.cache.length - 1].number; } reference.state.current = page.number; @@ -101,7 +101,7 @@ function JobPageService ($q) { } return false; - } + }; this.emptyBuffer = () => { const reference = this.getReference(); @@ -137,8 +137,9 @@ function JobPageService ($q) { }; this.trim = left => { - let reference = this.getActiveReference(); - let excess = reference.cache.length - this.page.limit; + const reference = this.getActiveReference(); + const excess = reference.cache.length - this.page.limit; + let ejected; if (left) { @@ -152,9 +153,8 @@ function JobPageService ($q) { return ejected.reduce((total, page) => total + page.lines, 0); }; - this.isPageBookmarked = number => { - return number >= this.page.bookmark.first && number <= this.page.bookmark.last; - }; + this.isPageBookmarked = number => number >= this.page.bookmark.first && + number <= this.page.bookmark.last; this.updateLineCount = (lines, stream) => { let reference; @@ -168,15 +168,10 @@ function JobPageService ($q) { const index = reference.cache.findIndex(item => item.number === reference.state.current); reference.cache[index].lines += lines; - } - - this.isBookmarkPending = () => { - return this.bookmark.pending; }; - this.isBookmarkSet = () => { - return this.bookmark.set; - }; + this.isBookmarkPending = () => this.bookmark.pending; + this.isBookmarkSet = () => this.bookmark.set; this.setBookmark = () => { if (this.isBookmarkSet()) { @@ -190,12 +185,11 @@ function JobPageService ($q) { } this.bookmark.state.first = this.page.state.first; - this.bookmark.state.last = this.page.state.last - 1; + this.bookmark.state.last = this.page.state.last - 1; this.bookmark.state.current = this.page.state.current - 1; this.bookmark.cache = JSON.parse(JSON.stringify(this.page.cache)); this.bookmark.set = true; this.bookmark.pending = false; - console.log('applied', JSON.stringify(this.bookmark.state, 0, 2)); }; this.removeBookmark = () => { @@ -271,19 +265,16 @@ function JobPageService ($q) { }); }; - this.buildRequestConfig = number => { - return { - page: number, - related: this.resource.related, - params: { - order_by: 'start_line' - } - }; - }; + this.buildRequestConfig = number => ({ + page: number, + related: this.resource.related, + params: { + order_by: 'start_line' + } + }); - this.getActiveReference = () => { - return this.isBookmarkSet() ? this.getReference(true) : this.getReference(); - }; + this.getActiveReference = () => (this.isBookmarkSet() ? + this.getReference(true) : this.getReference()); this.getReference = (bookmark) => { if (bookmark) { diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 9f883a126c..e8d1cd0dd0 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -5,8 +5,6 @@ const ELEMENT_TBODY = '#atStdoutResultTable'; const EVENT_START_TASK = 'playbook_on_task_start'; const EVENT_START_PLAY = 'playbook_on_play_start'; const EVENT_STATS_PLAY = 'playbook_on_stats'; -const JOB_START = 'playbook_on_start'; -const JOB_END = 'playbook_on_stats'; const EVENT_GROUPS = [ EVENT_START_TASK, @@ -70,7 +68,7 @@ function JobRenderService ($q, $sce, $window) { const current = this.createRecord(ln, lines, event); - const html = lines.reduce((html, line, i) => { + const html = lines.reduce((concat, line, i) => { ln++; const isLastLine = i === lines.length - 1; @@ -81,7 +79,7 @@ function JobRenderService ($q, $sce, $window) { count++; } - return `${html}${row}`; + return `${concat}${row}`; }, ''); return { html, count }; @@ -191,16 +189,16 @@ function JobRenderService ($q, $sce, $window) { ${tdEvent} ${timestamp} `; - } + }; - this.getTimestamp = (created) => { + this.getTimestamp = created => { const date = new Date(created); const hour = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours(); const minute = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes(); const second = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds(); return `${hour}:${minute}:${second}`; - } + }; this.getParentEvents = (uuid, list) => { list = list || []; @@ -216,9 +214,7 @@ function JobRenderService ($q, $sce, $window) { return list; }; - this.getEvents = () => { - return this.hooks.get(); - }; + this.getEvents = () => this.hooks.get(); this.insert = (events, insert) => { const result = this.transformEventGroup(events); @@ -229,23 +225,19 @@ function JobRenderService ($q, $sce, $window) { .then(() => result.lines); }; - this.remove = elements => { - return this.requestAnimationFrame(() => { - elements.remove(); - }); - }; + this.remove = elements => this.requestAnimationFrame(() => { + elements.remove(); + }); - this.requestAnimationFrame = fn => { - return $q(resolve => { - $window.requestAnimationFrame(() => { - if (fn) { - fn(); - } + this.requestAnimationFrame = fn => $q(resolve => { + $window.requestAnimationFrame(() => { + if (fn) { + fn(); + } - return resolve(); - }); + return resolve(); }); - }; + }); this.compile = html => { this.hooks.compile(html); @@ -271,13 +263,8 @@ function JobRenderService ($q, $sce, $window) { return this.remove(elements); }; - this.prepend = events => { - return this.insert(events, html => this.el.prepend(html)) - }; - - this.append = events => { - return this.insert(events, html => this.el.append(html)) - }; + this.prepend = events => this.insert(events, html => this.el.prepend(html)); + this.append = events => this.insert(events, html => this.el.append(html)); // TODO: stdout from the API should not be trusted. this.sanitize = html => { diff --git a/awx/ui/client/features/output/scroll.service.js b/awx/ui/client/features/output/scroll.service.js index 791d495796..ae186798e4 100644 --- a/awx/ui/client/features/output/scroll.service.js +++ b/awx/ui/client/features/output/scroll.service.js @@ -42,7 +42,6 @@ function JobScrollService ($q, $timeout) { this.register = () => { this.pause(); - const height = this.getScrollHeight(); const current = this.getScrollPosition(); const downward = current > this.position.previous; @@ -71,19 +70,16 @@ function JobScrollService ($q, $timeout) { }; this.isBeyondThreshold = (downward, current) => { - const previous = this.position.previous; - const height = this.getScrollHeight(); + const height = this.getScrollHeight(); - if (downward) { + if (downward) { current += this.getViewableHeight(); if (current >= height || ((height - current) / height) < THRESHOLD) { return true; } - } else { - if (current <= 0 || (current / height) < THRESHOLD) { - return true; - } + } else if (current <= 0 || (current / height) < THRESHOLD) { + return true; } return false; @@ -111,17 +107,9 @@ function JobScrollService ($q, $timeout) { this.setScrollPosition(top + height); }; - this.getScrollHeight = () => { - return this.el[0].scrollHeight; - }; - - this.getViewableHeight = () => { - return this.el[0].offsetHeight; - }; - - this.getScrollPosition = () => { - return this.el[0].scrollTop; - }; + this.getScrollHeight = () => this.el[0].scrollHeight; + this.getViewableHeight = () => this.el[0].offsetHeight; + this.getScrollPosition = () => this.el[0].scrollTop; this.setScrollPosition = position => { this.position.previous = this.position.current; @@ -159,9 +147,7 @@ function JobScrollService ($q, $timeout) { this.state.paused = true; }; - this.isPaused = () => { - return this.state.paused; - }; + this.isPaused = () => this.state.paused; this.lock = () => { this.state.locked = true; @@ -171,9 +157,7 @@ function JobScrollService ($q, $timeout) { this.state.locked = false; }; - this.isLocked = () => { - return this.state.locked; - }; + this.isLocked = () => this.state.locked; } JobScrollService.$inject = ['$q', '$timeout']; diff --git a/awx/ui/client/features/output/search-key.directive.js b/awx/ui/client/features/output/search-key.directive.js index f9a1ad1928..91455e3637 100644 --- a/awx/ui/client/features/output/search-key.directive.js +++ b/awx/ui/client/features/output/search-key.directive.js @@ -13,12 +13,11 @@ function AtSearchKeyController () { vm.examples = scope.examples || []; vm.fields = scope.fields || []; vm.relatedFields = scope.relatedFields || []; - } + }; } AtSearchKeyController.$inject = ['$scope']; - function atSearchKey () { return { templateUrl, diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index 05603d103a..a7e73cbdfa 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -102,26 +102,24 @@ function JobStreamService ($q) { .then(() => --this.lag); }; - this.renderFrame = events => { - return this.hooks.render(events) - .then(() => { - if (this.scroll.isLocked()) { - this.scroll.scrollToBottom(); + this.renderFrame = events => this.hooks.render(events) + .then(() => { + if (this.scroll.isLocked()) { + this.scroll.scrollToBottom(); + } + + if (this.isEnding()) { + const lastEvents = this.page.emptyBuffer(); + + if (lastEvents.length) { + return this.renderFrame(lastEvents); } - if (this.isEnding()) { - const lastEvents = this.page.emptyBuffer(); + this.end(true); + } - if (lastEvents.length) { - return this.renderFrame(lastEvents); - } - - this.end(true); - } - - return $q.resolve(); - }); - }; + return $q.resolve(); + }); this.resume = done => { if (done) { diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 98f2b25007..f3b0d4dfba 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -400,7 +400,7 @@ function extend (related, config) { function goToPage (config) { const params = config.params || {}; - const page = config.page; + const { page } = config; let url; let key; @@ -473,7 +473,7 @@ function goToPage (config) { return { results: data.results, page: pageNumber - } + }; }); } From 81c85913ac933db1f042b3c99e1ff74ca344875b Mon Sep 17 00:00:00 2001 From: gconsidine Date: Fri, 9 Mar 2018 15:30:44 -0500 Subject: [PATCH 147/379] Add encoding of html entities in stdout from the API --- awx/ui/client/features/output/render.service.js | 15 ++++++++------- awx/ui/package.json | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index e8d1cd0dd0..d7b32ae408 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -1,5 +1,6 @@ import Ansi from 'ansi-to-html'; import hasAnsi from 'has-ansi'; +import Entities from 'html-entities'; const ELEMENT_TBODY = '#atStdoutResultTable'; const EVENT_START_TASK = 'playbook_on_task_start'; @@ -18,6 +19,7 @@ const TIME_EVENTS = [ ]; const ansi = new Ansi(); +const entities = new Entities.AllHtmlEntities(); function JobRenderService ($q, $sce, $window) { this.init = ({ compile, apply, get }) => { @@ -60,7 +62,7 @@ function JobRenderService ($q, $sce, $window) { return { html: '', count: 0 }; } - const { stdout } = event; + const stdout = this.sanitize(event.stdout); const lines = stdout.split('\r\n'); let count = lines.length; @@ -72,6 +74,7 @@ function JobRenderService ($q, $sce, $window) { ln++; const isLastLine = i === lines.length - 1; + let row = this.createRow(current, ln, line); if (current && current.isTruncated && isLastLine) { @@ -218,7 +221,7 @@ function JobRenderService ($q, $sce, $window) { this.insert = (events, insert) => { const result = this.transformEventGroup(events); - const html = this.sanitize(result.html); + const html = this.trustHtml(result.html); return this.requestAnimationFrame(() => insert(html)) .then(() => this.compile(html)) @@ -264,14 +267,12 @@ function JobRenderService ($q, $sce, $window) { }; this.prepend = events => this.insert(events, html => this.el.prepend(html)); + this.append = events => this.insert(events, html => this.el.append(html)); - // TODO: stdout from the API should not be trusted. - this.sanitize = html => { - html = $sce.trustAsHtml(html); + this.trustHtml = html => $sce.getTrustedHtml($sce.trustAsHtml(html)); - return $sce.getTrustedHtml(html); - }; + this.sanitize = html => entities.encode(html); } JobRenderService.$inject = ['$q', '$sce', '$window']; diff --git a/awx/ui/package.json b/awx/ui/package.json index 5f8ff0ad5c..9c381f466f 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -115,6 +115,7 @@ "components-font-awesome": "^4.6.1", "d3": "~3.3.13", "has-ansi": "^3.0.0", + "html-entities": "^1.2.1", "javascript-detect-element-resize": "^0.5.3", "jquery": "~2.2.4", "jquery-ui": "^1.12.1", From e3d42d8e1b0d31339d7db09da10d4538cbfddfd5 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Mon, 12 Mar 2018 15:19:01 -0400 Subject: [PATCH 148/379] Fix webpack-dev-server proxy to accommodate auth changes --- awx/ui/build/webpack.watch.js | 47 +++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index a68c2eee25..c764ec4b85 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -55,26 +55,37 @@ const watch = { host: '127.0.0.1', https: true, port: 3000, - https: true, clientLogLevel: 'none', - proxy: { - '/': { - target: TARGET, - secure: false, - ws: false, - bypass: req => req.originalUrl.includes('hot-update.json') - }, - '/websocket': { - target: TARGET, - secure: false, - ws: true - }, - '/network_ui': { - target: TARGET, - secure: false, - ws: true + proxy: [{ + context: (pathname, req) => !(pathname === '/api/login/' && req.method === 'POST'), + target: TARGET, + secure: false, + ws: false, + bypass: req => req.originalUrl.includes('hot-update.json') + }, + { + context: '/api/login/', + target: TARGET, + secure: false, + ws: false, + headers: { + Host: `localhost:${TARGET_PORT}`, + Origin: TARGET, + Referer: `${TARGET}/` } - } + }, + { + context: '/websocket', + target: TARGET, + secure: false, + ws: true + }, + { + context: '/network_ui', + target: TARGET, + secure: false, + ws: true + }] } }; From 033314e4f648312a367dcc331692cbd13cdb37fb Mon Sep 17 00:00:00 2001 From: gconsidine Date: Tue, 13 Mar 2018 15:24:12 -0400 Subject: [PATCH 149/379] Add fixes to results - Handle out of order events by batching lines until all lines are present - In static mode, fetch pages of results until container is full and scroll bar appears (for scroll events related to pagination) --- .../features/output/index.controller.js | 12 +++++- awx/ui/client/features/output/index.js | 4 +- .../client/features/output/scroll.service.js | 2 + .../client/features/output/stream.service.js | 39 ++++++++++++++++++- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index b65cb52a68..f2b6235476 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -114,7 +114,12 @@ function next () { } return shift() - .then(() => append(events)); + .then(() => append(events)) + .then(() => { + if(scroll.isMissing()) { + return next(); + } + }); }); } @@ -193,6 +198,11 @@ function scrollHome () { .then(() => { scroll.resetScrollPosition(); scroll.resume(); + }) + .then(() => { + if(scroll.isMissing()) { + return next(); + } }); }); } diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index ebf69f5243..b56e09a1ff 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -13,8 +13,8 @@ const Template = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; const PAGE_CACHE = true; -const PAGE_LIMIT = 3; -const PAGE_SIZE = 100; +const PAGE_LIMIT = 5; +const PAGE_SIZE = 50; const WS_PREFIX = 'ws'; function resolveResource ( diff --git a/awx/ui/client/features/output/scroll.service.js b/awx/ui/client/features/output/scroll.service.js index ae186798e4..a568813ddc 100644 --- a/awx/ui/client/features/output/scroll.service.js +++ b/awx/ui/client/features/output/scroll.service.js @@ -1,4 +1,5 @@ const ELEMENT_CONTAINER = '.at-Stdout-container'; +const ELEMENT_TBODY = '#atStdoutResultTable'; const DELAY = 100; const THRESHOLD = 0.1; @@ -158,6 +159,7 @@ function JobScrollService ($q, $timeout) { }; this.isLocked = () => this.state.locked; + this.isMissing = () => $(ELEMENT_TBODY)[0].clientHeight < this.getViewableHeight(); } JobScrollService.$inject = ['$q', '$timeout']; diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index a7e73cbdfa..65aa44ea43 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -27,6 +27,14 @@ function JobStreamService ($q) { listen }; + this.lines = { + used: [], + missing: [], + ready: false, + min: 0, + max: 0 + }; + this.hooks.listen(resource.ws.namespace, this.listen); }; @@ -72,6 +80,31 @@ function JobStreamService ($q) { } }; + this.checkLines = data => { + for (let i = data.start_line; i < data.end_line; i++) { + if (i > this.lines.max) { + this.lines.max = i; + } + + this.lines.used.push(i); + } + + let missing = []; + for (let i = this.lines.min; i < this.lines.max; i++) { + if (this.lines.used.indexOf(i) === -1) { + missing.push(i); + } + } + + if (missing.length === 0) { + this.lines.ready = true; + this.lines.min = this.lines.max + 1; + this.lines.used = []; + } else { + this.lines.ready = false; + } + }; + this.listen = data => { this.lag++; @@ -87,10 +120,11 @@ function JobStreamService ($q) { } } + this.checkLines(data); this.buffer(data); this.count++; - if (this.isPaused() || !this.isBatchFull()) { + if (!this.isReadyToRender()) { return $q.resolve(); } @@ -166,6 +200,9 @@ function JobStreamService ($q) { this.state.ending = true; }; + this.isReadyToRender = () => this.isEnding() || + (!this.isPaused() && this.hasAllLines() && this.isBatchFull()); + this.hasAllLines = () => this.lines.ready; this.isBatchFull = () => this.count % this.framesPerRender === 0; this.isPaused = () => this.state.paused; this.isPausing = () => this.state.pausing; From a23e5e920f100ae0e9c7887b9295b9f6be0540bc Mon Sep 17 00:00:00 2001 From: gconsidine Date: Wed, 14 Mar 2018 15:21:33 -0400 Subject: [PATCH 150/379] Add support for in progress jobs and omit expand * Any event received by the stream service will start rendering (instead of JOB_START events only) * Expand/collapse only shown for static results --- awx/ui/client/features/output/index.controller.js | 5 +++-- awx/ui/client/features/output/render.service.js | 6 +++--- awx/ui/client/features/output/stream.service.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index f2b6235476..5167eab0b5 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -72,12 +72,13 @@ function JobsIndexController ( function init (pageMode) { page.init({ - resource + resource, }); render.init({ get: () => resource.model.get(`related.${resource.related}.results`), - compile: html => $compile(html)($scope) + compile: html => $compile(html)($scope), + isStreamActive: stream.isActive }); scroll.init({ diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index d7b32ae408..e55f3901f1 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -22,11 +22,11 @@ const ansi = new Ansi(); const entities = new Entities.AllHtmlEntities(); function JobRenderService ($q, $sce, $window) { - this.init = ({ compile, apply, get }) => { + this.init = ({ compile, apply, isStreamActive }) => { this.parent = null; this.record = {}; this.el = $(ELEMENT_TBODY); - this.hooks = { get, compile, apply }; + this.hooks = { isStreamActive, compile, apply }; }; this.sortByLineNumber = (a, b) => { @@ -155,7 +155,7 @@ function JobRenderService ($q, $sce, $window) { } if (current) { - if (current.isParent && current.line === ln) { + if (!this.hooks.isStreamActive() && current.isParent && current.line === ln) { id = current.uuid; tdToggle = ``; } diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index 65aa44ea43..d7f8a72555 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -110,7 +110,7 @@ function JobStreamService ($q) { this.chain = this.chain .then(() => { - if (data.event === JOB_START) { + if (!this.isActive()) { this.start(); } else if (data.event === JOB_END) { if (this.isPaused()) { From f65d170caba88b8d5ab832571b26825927e8f175 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 12 Mar 2018 17:17:00 -0400 Subject: [PATCH 151/379] initial details panel integration --- .../features/output/details.directive.js | 502 ++++++++++++++++++ .../features/output/details.partial.html | 236 ++++++++ .../features/output/index.controller.js | 7 + awx/ui/client/features/output/index.js | 20 +- awx/ui/client/features/output/index.view.html | 1 + awx/ui/client/features/output/jobs.strings.js | 5 + awx/ui/client/lib/models/Base.js | 5 +- 7 files changed, 765 insertions(+), 11 deletions(-) create mode 100644 awx/ui/client/features/output/details.directive.js create mode 100644 awx/ui/client/features/output/details.partial.html diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js new file mode 100644 index 0000000000..2393284913 --- /dev/null +++ b/awx/ui/client/features/output/details.directive.js @@ -0,0 +1,502 @@ +const templateUrl = require('~features/output/details.partial.html'); + +let $http; +let $filter; +let $state; + +let error; +let parse; +let prompt; +let resource; +let strings; +let wait; + +function mapChoices (choices) { + return Object.assign(...choices.map(([k, v]) => ({[k]: v}))); +} + +function getStatusDetails (status) { + const value = status || resource.model.get('status'); + const label = 'Status'; + const choices = mapChoices(resource.model.options('actions.GET.status.choices')); + + const displayValue = choices[value]; + + return { displayValue, label, value }; +} + +function getStartTimeDetails (started) { + const value = started || resource.model.get('started'); + const label = 'Started'; + + let displayValue; + + if (value) { + displayValue = $filter('longDate')(value); + } else { + displayValue = 'Not Started'; + } + + return { displayValue, label, value }; +} + +function getFinishTimeDetails (finished) { + const value = finished || resource.model.get('finished'); + const label = 'Finished'; + + let displayValue; + + if (value) { + displayValue = $filter('longDate')(value); + } else { + displayValue = 'Not Finished'; + } + + return { displayValue, label, value }; +} + +function getJobTypeDetails () { + const value = resource.model.get('job_type'); + const label = 'Job Type'; + const choices = mapChoices(resource.model.options('actions.GET.job_type.choices')); + + const displayValue = choices[value]; + + return { displayValue, label, value }; +} + + +function getVerbosityDetails () { + const value = resource.model.get('verbosity'); + const choices = mapChoices(resource.model.options('actions.GET.verbosity.choices')); + + const displayValue = choices[value]; + const label = 'Verbosity'; + + return { displayValue, label, value }; +} + +function getSourceWorkflowJobDetails () { + const sourceWorkflowJob = resource.model.get('summary_fields.source_workflow_job'); + + if (!sourceWorkflowJob) { + return null; + } + + const link = `/#/workflows/${sourceWorkflowJob.id}`; + + return { link }; +} + +function getJobTemplateDetails () { + const jobTemplate = resource.model.get('summary_fields.job_template'); + + if (!jobTemplate) { + return null; + } + + const label = 'Job Template'; + const link = `/#/templates/job_template/${jobTemplate.id}`; + const value = $filter('sanitize')(jobTemplate.name); + + return { label, link, value }; +} + +function getLaunchedByDetails () { + const createdBy = resource.model.get('summary_fields.created_by'); + const jobTemplate = resource.model.get('summary_fields.job_template'); + + const relatedSchedule = resource.model.get('related.schedule'); + const schedule = resource.model.get('summary_fields.schedule'); + + if (!createdBy && !schedule) { + return null; + } + + const label = 'Launched By'; + + let link; + let tooltip; + let value; + + if (createdBy) { + tooltip = 'Edit the User'; + link = `/#/users/${createdBy.id}`; + value = $filter('sanitize')(createdBy.username); + } else if (relatedSchedule && jobTemplate) { + tooltip = 'Edit the Schedule'; + link = `/#/templates/job_template/${jobTemplate.id}/schedules/${schedule.id}`; + value = $filter('sanitize')(schedule.name); + } else { + tooltip = null; + link = null; + value = $filter('sanitize')(schedule.name); + } + + return { label, link, tooltip, value }; +} + +function getInventoryDetails () { + const inventory = resource.model.get('summary_fields.inventory'); + + if (!inventory) { + return null; + } + + const label = 'Inventory'; + const tooltip = 'Edit the inventory'; + const value = $filter('sanitize')(inventory.name); + + let link; + + if (inventory.kind === 'smart') { + link = `/#/inventories/smart/${inventory.id}`; + } else { + link = `/#/inventories/inventory/${inventory.id}`; + } + + return { label, link, tooltip, value }; +} + +function getProjectDetails () { + const project = resource.model.get('summary_fields.project'); + const projectUpdate = resource.model.get('summary_fields.project_update'); + + if (!project) { + return null; + } + + const label = 'Project'; + const link = `/#/projects/${project.id}`; + const value = $filter('sanitize')(project.name); + + if (projectUpdate) { + const update = { + link: `/#/jobz/project/${projectUpdate.id}`, + tooltip: 'View project checkout results', + status: projectUpdate.status, + }; + + return { label, link, value, update }; + } + + return { label, link, value }; +} + +function getSCMRevisionDetails () { + const label = 'Revision'; + const value = resource.model.get('scm_revision'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getPlaybookDetails () { + const label = 'Playbook'; + const value = resource.model.get('playbook'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getJobExplanationDetails () { + const jobExplanation = resource.model.get('job_explanation'); + + if (!jobExplanation) { + return null; + } + + const value = null; + + return { value }; +} + +function getResultTracebackDetails () { + const previousTaskFailed = false; + const resultTraceback = resource.model.get('result_traceback'); + + if (!resultTraceback) { + return null; + } + + if (!previousTaskFailed) { + return null; + } + + const label = 'Results Traceback'; + const value = null; + + return { label, value }; +} + +function getMachineCredentialDetails () { + const machineCredential = resource.model.get('summary_fields.credential'); + + if (!machineCredential) { + return null; + } + + const label = 'Machine Credential'; + const link = `/#/credentials/${machineCredential.id}`; + const tooltip = 'Edit the Credential'; + const value = $filter('sanitize')(machineCredential.name); + + return { label, link, tooltip, value }; +} + +function getForkDetails () { + const label = 'Forks'; + const value = resource.model.get('forks'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getLimitDetails () { + const label = 'Limit'; + const value = resource.model.get('limit'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getInstanceGroupDetails () { + + const instanceGroup = resource.model.get('summary_fields.instance_group'); + + if (!instanceGroup) { + return null; + } + + const label = 'Instance Group'; + const value = $filter('sanitize')(instanceGroup.name); + + let isolated = null; + + if (instanceGroup.controller_id) { + isolated = 'Isolated'; + } + + return { label, value, isolated }; +} + +function getJobTagDetails () { + const label = 'Job Tags'; + const value = resource.model.get('job_tags'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getSkipTagDetails () { + const label = 'Skip Tags'; + const value = resource.model.get('skip_tags'); + + if (!value) { + return null; + } + + return { label, value }; +} + +function getExtraVarsDetails () { + const extraVars = resource.model.get('extra_vars'); + + if (!extraVars) { + return null; + } + + const label = 'Extra Variables'; + const tooltip = 'Read-only view of extra variables added to the job template.'; + const value = parse(extraVars); + + return { label, tooltip, value }; +} + +function getLabelDetails () { + const jobLabels = _.get(resource.model.get('related.labels'), 'results', []); + + if (jobLabels.length < 1) { + return null; + } + + const label = 'Labels'; + const value = jobLabels.map(({ name }) => name).map($filter('sanitize')); + + let more = false; + + return { label, more, value }; +} + +function createErrorHandler (path, action) { + return ({ data, status }) => { + const hdr = strings.get('error.HEADER'); + const msg = strings.get('error.CALL', { path, action, status }); + + error($scope, data, status, null, { hdr, msg }); + }; +} + +const ELEMENT_LABELS = '#job-results-labels'; +const ELEMENT_PROMPT_MODAL = '#prompt-modal'; +const LABELS_SLIDE_DISTANCE = 200; + +function toggleLabels () { + if (!this.labels.more) { + $(ELEMENT_LABELS).slideUp(LABELS_SLIDE_DISTANCE); + this.labels.more = true; + } else { + $(ELEMENT_LABELS).slideDown(LABELS_SLIDE_DISTANCE); + this.labels.more = false; + } +} + +function cancelJob () { + const actionText = strings.get('CANCEL'); + const hdr = strings.get('warnings.CANCEL_HEADER'); + const warning = strings.get('warnings.CANCEL_BODY'); + + const id = resource.model.get('id'); + const name = $filter('sanitize')(resource.model.get('name')); + + const body = `
${warning}
`; + const resourceName = `#${id} ${name}`; + + const method = 'POST'; + const url = `${resource.model.path}/${id}/cancel/`; + + const errorHandler = createErrorHandler('cancel job', method); + + const action = () => { + wait('start'); + $http({ method, url }) + .then(() => $state.go('jobs')) + .catch(errorHandler) + .finally(() => { + $(ELEMENT_PROMPT_MODAL).modal('hide'); + wait('stop'); + }); + }; + + prompt({ hdr, resourceName, body, actionText, action }); +} + +function deleteJob () { + return; +} + +function AtDetailsController ( + _$http_, + _$filter_, + _$state_, + _error_, + _prompt_, + _strings_, + _wait_, + ParseTypeChange, + ParseVariableString, +) { + const vm = this || {}; + + $http = _$http_; + $filter = _$filter_; + $state = _$state_; + + error = _error_; + // resource = _resource_; + parse = ParseVariableString; + prompt = _prompt_; + strings = _strings_; + wait = _wait_; + + // statusChoices = mapChoices(resource.options('status.choices')); + + vm.init = scope => { + vm.job = scope.job || {}; + resource = scope.resource; + + vm.status = getStatusDetails(scope.status); + vm.startTime = getStartTimeDetails(); + vm.finishTime = getFinishTimeDetails(); + vm.jobType = getJobTypeDetails(); + vm.jobTemplate = getJobTemplateDetails(); + vm.sourceWorkflowJob = getSourceWorkflowJobDetails(); + vm.inventory = getInventoryDetails(); + vm.project = getProjectDetails(); + vm.scmRevision = getSCMRevisionDetails(); + vm.playbook = getPlaybookDetails(); + vm.resultTraceback = getResultTracebackDetails(); + vm.launchedBy = getLaunchedByDetails(); + vm.jobExplanation = getJobExplanationDetails(); + vm.verbosity = getVerbosityDetails(); + vm.machineCredential = getMachineCredentialDetails(); + vm.forks = getForkDetails(); + vm.limit = getLimitDetails(); + vm.instanceGroup = getInstanceGroupDetails(); + vm.jobTags = getJobTagDetails(); + vm.skipTags = getSkipTagDetails(); + vm.extraVars = getExtraVarsDetails(); + vm.labels = getLabelDetails(); + + vm.cancelJob = cancelJob; + vm.deleteJob = deleteJob; + vm.toggleLabels = toggleLabels; + + // codemirror + const cm = { parseType: 'yaml', variables: vm.extraVars.value, $apply: scope.$apply }; + ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true }); + + scope.$watch('status', value => { vm.status = getStatusDetails(value); }); + } +} + +AtDetailsController.$inject = [ + '$http', + '$filter', + '$state', + 'ProcessErrors', + 'Prompt', + 'JobStrings', + 'Wait', + 'ParseTypeChange', + 'ParseVariableString', +]; + +function atDetailsLink (scope, el, attrs, controllers) { + const [atDetailsController] = controllers; + + atDetailsController.init(scope); +} + +function atDetails () { + return { + templateUrl, + restrict: 'E', + require: ['atDetails'], + controllerAs: 'vm', + link: atDetailsLink, + controller: AtDetailsController, + scope: { + job: '=', + status: '=', + resource: '=', + }, + }; +} + +export default atDetails; diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html new file mode 100644 index 0000000000..2741d43d0a --- /dev/null +++ b/awx/ui/client/features/output/details.partial.html @@ -0,0 +1,236 @@ + + +
+
DETAILS
+ +
+ + + + + + + + +
+
+ + +
+ +
+ +
+ + {{ vm.status.displayValue | translate }} +
+
+ + + +
+ +
+ {{ vm.startTime.displayValue }} +
+
+ + +
+ +
+ {{ vm.finishTime.displayValue }} +
+
+ + +
+ +
+
+ + +
+ + +
+ + +
+ +
{{ vm.jobType.displayValue }}
+
+ + +
+ + +
+ {{ vm.launchedBy.value }} +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
{{ vm.playbook.value }}
+
+ + +
+ + +
+ + +
+ +
{{ vm.forks.value }}
+
+ + +
+ +
{{ vm.limit.value }}
+
+ + +
+ +
{{ vm.verbosity.displayValue }}
+
+ + +
+ +
+ {{ vm.instanceGroup.value }} + + {{ vm.instanceGroup.isolated }} + +
+
+ + +
+ +
{{ vm.jobTags.value }}
+
+ + +
+ +
{{ vm.skipTags.value }}
+
+ + +
+ + +
+ + +
+ +
+
+
{{ label }}
+
+
+
+
diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 5167eab0b5..055e9c9a95 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -67,6 +67,13 @@ function JobsIndexController ( vm.removeSearchTag = removeSearchTag; vm.searchTags = getSearchTags(getCurrentQueryset()); + // details + vm.details = { + job: resource.model.model.GET, + status: resource.model.model.GET.status, + resource, + }; + render.requestAnimationFrame(() => init()); } diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index b56e09a1ff..367f5c1f89 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -8,6 +8,7 @@ import RenderService from '~features/output/render.service'; import ScrollService from '~features/output/scroll.service'; import SearchKeyDirective from '~features/output/search-key.directive'; import StreamService from '~features/output/stream.service'; +import DetailsDirective from '~features/output/details.directive.js'; const Template = require('~features/output/index.view.html'); @@ -55,21 +56,21 @@ function resolveResource ( } const params = { page_size: PAGE_SIZE, order_by: 'start_line' }; + const config = { pageCache: PAGE_CACHE, pageLimit: PAGE_LIMIT, params }; if (job_event_search) { - const searchParams = qs.encodeQuerysetObject(qs.decodeArr(job_event_search)); + const queryParams = qs.encodeQuerysetObject(qs.decodeArr(job_event_search)); - Object.assign(params, searchParams); + Object.assign(config.params, queryParams); } Wait('start'); - return new Resource('get', id) - .then(model => model.extend(related, { - pageCache: PAGE_CACHE, - pageLimit: PAGE_LIMIT, - params, - })) - .then(model => ({ + return new Resource(['get', 'options'], [id, id]) + .then(model => Promise.all([ + model.extend('labels'), + model.extend(related, config) + ])) + .then(([ model ]) => ({ id, type, model, @@ -197,6 +198,7 @@ angular .service('JobPageService', PageService) .service('JobScrollService', ScrollService) .service('JobStreamService', StreamService) + .directive('atDetails', DetailsDirective) .directive('atSearchKey', SearchKeyDirective) .run(JobsRun); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index dfb6790d78..778a44d7d8 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,6 +1,7 @@
+

diff --git a/awx/ui/client/features/output/jobs.strings.js b/awx/ui/client/features/output/jobs.strings.js index aa1afcdfaf..a77da49f62 100644 --- a/awx/ui/client/features/output/jobs.strings.js +++ b/awx/ui/client/features/output/jobs.strings.js @@ -7,6 +7,11 @@ function JobsStrings (BaseString) { ns.state = { TITLE: t.s('JOBZ') }; + + ns.warnings = { + CANCEL_BODY: t.s('Are you sure you want to cancel this job?'), + CANCEL_HEADER: t.s('Cancel Job'), + }; } JobsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index f3b0d4dfba..5111f25f6d 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -353,10 +353,11 @@ function has (method, keys) { return value !== undefined && value !== null; } -function extend (related, config) { +function extend (related, config = {}) { + const req = this.parseRequestConfig('GET', config); - if (config.params.page_size) { + if (_.get(config, 'params.page_size')) { this.page.size = config.params.page_size; this.page.current = 1; From b577f5093030c85809adabefca338ff05477fe77 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 19 Mar 2018 12:01:17 -0400 Subject: [PATCH 152/379] event processing for details panel and initial stats bar integration --- awx/ui/client/features/output/_index.less | 82 ++++++++++++ .../features/output/details.directive.js | 120 +++++++++++------- .../features/output/details.partial.html | 20 +-- .../features/output/index.controller.js | 44 ++++++- awx/ui/client/features/output/index.js | 26 +++- awx/ui/client/features/output/index.view.html | 14 +- awx/ui/client/features/output/jobs.strings.js | 5 + .../features/output/status.directive.js | 92 ++++++++++++++ .../features/output/status.partial.html | 37 ++++++ .../client/features/output/stream.service.js | 16 ++- awx/ui/client/lib/models/Job.js | 34 ++++- awx/ui/client/lib/models/ProjectUpdate.js | 39 +++++- 12 files changed, 447 insertions(+), 82 deletions(-) create mode 100644 awx/ui/client/features/output/status.directive.js create mode 100644 awx/ui/client/features/output/status.partial.html diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index 989b532ab6..ab4e589213 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -199,3 +199,85 @@ width: 100%; flex-wrap: wrap; } + + +// Status Bar ----------------------------------------------------------------------------- +.HostStatusBar { + display: flex; + flex: 0 0 auto; + width: 100%; + margin-top: 10px; +} + +.HostStatusBar-ok, +.HostStatusBar-changed, +.HostStatusBar-unreachable, +.HostStatusBar-failed, +.HostStatusBar-skipped, +.HostStatusBar-noData { + height: 15px; + border-top: 5px solid @default-bg; + border-bottom: 5px solid @default-bg; +} + +.HostStatusBar-ok { + background-color: @default-succ; + display: flex; + flex: 0 0 auto; +} + +.HostStatusBar-changed { + background-color: @default-warning; + flex: 0 0 auto; +} + +.HostStatusBar-unreachable { + background-color: @default-unreachable; + flex: 0 0 auto; +} + +.HostStatusBar-failed { + background-color: @default-err; + flex: 0 0 auto; +} + +.HostStatusBar-skipped { + background-color: @default-link; + flex: 0 0 auto; +} + +.HostStatusBar-noData { + background-color: @default-icon-hov; + flex: 1 0 auto; +} + +.HostStatusBar-tooltipLabel { + text-transform: uppercase; + margin-right: 15px; +} + +.HostStatusBar-tooltipBadge { + border-radius: 5px; + border: 1px solid @default-bg; +} + +.HostStatusBar-tooltipBadge--ok { + background-color: @default-succ; +} + +.HostStatusBar-tooltipBadge--unreachable { + background-color: @default-unreachable; +} + +.HostStatusBar-tooltipBadge--skipped { + background-color: @default-link; +} + +.HostStatusBar-tooltipBadge--changed { + background-color: @default-warning; +} + +.HostStatusBar-tooltipBadge--failed { + background-color: @default-err; + +} diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 2393284913..309c50612d 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -2,6 +2,7 @@ const templateUrl = require('~features/output/details.partial.html'); let $http; let $filter; +let $scope; let $state; let error; @@ -12,68 +13,86 @@ let strings; let wait; function mapChoices (choices) { - return Object.assign(...choices.map(([k, v]) => ({[k]: v}))); + if (!choices) return {}; + return Object.assign(...choices.map(([k, v]) => ({ [k]: v }))); } function getStatusDetails (status) { - const value = status || resource.model.get('status'); - const label = 'Status'; + const unmapped = status || resource.model.get('status'); + + if (!unmapped) { + return null; + } + const choices = mapChoices(resource.model.options('actions.GET.status.choices')); - const displayValue = choices[value]; + const label = 'Status'; + const icon = `fa icon-job-${unmapped}`; + const value = choices[unmapped]; - return { displayValue, label, value }; + return { label, icon, value }; } function getStartTimeDetails (started) { - const value = started || resource.model.get('started'); + const unfiltered = started || resource.model.get('started'); + const label = 'Started'; - let displayValue; + let value; - if (value) { - displayValue = $filter('longDate')(value); + if (unfiltered) { + value = $filter('longDate')(unfiltered); } else { - displayValue = 'Not Started'; + value = 'Not Started'; } - return { displayValue, label, value }; + return { label, value }; } function getFinishTimeDetails (finished) { - const value = finished || resource.model.get('finished'); + const unfiltered = finished || resource.model.get('finished'); + const label = 'Finished'; - let displayValue; + let value; - if (value) { - displayValue = $filter('longDate')(value); + if (unfiltered) { + value = $filter('longDate')(unfiltered); } else { - displayValue = 'Not Finished'; + value = 'Not Finished'; } - return { displayValue, label, value }; + return { label, value }; } function getJobTypeDetails () { - const value = resource.model.get('job_type'); - const label = 'Job Type'; + const unmapped = resource.model.get('job_type'); + + if (!unmapped) { + return null; + } + const choices = mapChoices(resource.model.options('actions.GET.job_type.choices')); - const displayValue = choices[value]; + const label = 'Job Type'; + const value = choices[unmapped]; - return { displayValue, label, value }; + return { label, value }; } - function getVerbosityDetails () { - const value = resource.model.get('verbosity'); + const verbosity = resource.model.get('verbosity'); + + if (!verbosity) { + return null; + } + const choices = mapChoices(resource.model.options('actions.GET.verbosity.choices')); - const displayValue = choices[value]; const label = 'Verbosity'; + const value = choices[value]; - return { displayValue, label, value }; + return { label, value }; } function getSourceWorkflowJobDetails () { @@ -273,7 +292,6 @@ function getLimitDetails () { } function getInstanceGroupDetails () { - const instanceGroup = resource.model.get('summary_fields.instance_group'); if (!instanceGroup) { @@ -336,9 +354,9 @@ function getLabelDetails () { } const label = 'Labels'; - const value = jobLabels.map(({ name }) => name).map($filter('sanitize')); + const more = false; - let more = false; + const value = jobLabels.map(({ name }) => name).map($filter('sanitize')); return { label, more, value }; } @@ -396,9 +414,7 @@ function cancelJob () { prompt({ hdr, resourceName, body, actionText, action }); } -function deleteJob () { - return; -} +function deleteJob () {} function AtDetailsController ( _$http_, @@ -418,21 +434,18 @@ function AtDetailsController ( $state = _$state_; error = _error_; - // resource = _resource_; parse = ParseVariableString; prompt = _prompt_; strings = _strings_; wait = _wait_; - // statusChoices = mapChoices(resource.options('status.choices')); + vm.init = _$scope_ => { + $scope = _$scope_; + resource = $scope.resource; - vm.init = scope => { - vm.job = scope.job || {}; - resource = scope.resource; - - vm.status = getStatusDetails(scope.status); - vm.startTime = getStartTimeDetails(); - vm.finishTime = getFinishTimeDetails(); + vm.status = getStatusDetails(); + vm.started = getStartTimeDetails(); + vm.finished = getFinishTimeDetails(); vm.jobType = getJobTypeDetails(); vm.jobTemplate = getJobTemplateDetails(); vm.sourceWorkflowJob = getSourceWorkflowJobDetails(); @@ -457,12 +470,24 @@ function AtDetailsController ( vm.deleteJob = deleteJob; vm.toggleLabels = toggleLabels; - // codemirror - const cm = { parseType: 'yaml', variables: vm.extraVars.value, $apply: scope.$apply }; - ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true }); + const observe = (key, transform) => { + $scope.$watch(key, value => { this[key] = transform(value); }); + }; - scope.$watch('status', value => { vm.status = getStatusDetails(value); }); - } + observe('status', getStatusDetails); + observe('started', getStartTimeDetails); + observe('finished', getFinishTimeDetails); + + // relaunch component + $scope.job = _.get(resource.model, 'model.GET', {}); + this.job = $scope.job; + + // codemirror + if (this.extraVars) { + const cm = { parseType: 'yaml', variables: this.extraVars.value, $apply: $scope.$apply }; + ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true }); + } + }; } AtDetailsController.$inject = [ @@ -492,9 +517,10 @@ function atDetails () { link: atDetailsLink, controller: AtDetailsController, scope: { - job: '=', - status: '=', resource: '=', + status: '=', + started: '=', + finished: '=', }, }; } diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index 2741d43d0a..bb80346a7b 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -36,25 +36,25 @@
- - {{ vm.status.displayValue | translate }} + + {{ vm.status.value }}
-
- +
+
- {{ vm.startTime.displayValue }} + {{ vm.started.value }}
-
- +
+
- {{ vm.finishTime.displayValue }} + {{ vm.finished.value }}
@@ -81,7 +81,7 @@
-
{{ vm.jobType.displayValue }}
+
{{ vm.jobType.value }}
@@ -164,7 +164,7 @@
-
{{ vm.verbosity.displayValue }}
+
{{ vm.verbosity.value }}
diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 055e9c9a95..a2fc44a8a8 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -10,6 +10,8 @@ let resource; let $state; let qs; +let hack; + function JobsIndexController ( _resource_, _page_, @@ -51,7 +53,7 @@ function JobsIndexController ( vm.expand = expand; vm.isExpanded = true; - // search + // Search $state = _$state_; qs = _qs_; @@ -67,17 +69,47 @@ function JobsIndexController ( vm.removeSearchTag = removeSearchTag; vm.searchTags = getSearchTags(getCurrentQueryset()); - // details + // Host Status Bar + vm.status = { + running: Boolean(resource.model.get('started')) && !resource.model.get('finished'), + stats: resource.stats, + } + + // Details vm.details = { - job: resource.model.model.GET, - status: resource.model.model.GET.status, resource, + started: resource.model.get('started'), + finished: resource.model.get('finished'), + status: resource.model.get('status'), }; render.requestAnimationFrame(() => init()); } +function onStreamStart (data) { + const status = _.get(data, 'summary_fields.job.status'); + + if (!hack) { + hack = true; + vm.details.status = status; + vm.details.started = data.created; + + vm.status.running = true; + } +} + +function onStreamFinish (data) { + const failed = _.get(data, 'summary_fields.job.failed'); + + vm.details.status = failed ? 'failed' : 'successful'; + vm.details.finished = data.created; + + vm.status = { stats: data, running: false }; +}; + function init (pageMode) { + hack = false; + page.init({ resource, }); @@ -98,10 +130,12 @@ function init (pageMode) { page, scroll, resource, + onStreamStart, + onStreamFinish, render: events => shift().then(() => append(events, true)), listen: (namespace, listener) => { $scope.$on(namespace, (scope, data) => listener(data)); - } + }, }); if (pageMode) { diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 367f5c1f89..707b333adb 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -6,13 +6,16 @@ import Controller from '~features/output/index.controller'; import PageService from '~features/output/page.service'; import RenderService from '~features/output/render.service'; import ScrollService from '~features/output/scroll.service'; -import SearchKeyDirective from '~features/output/search-key.directive'; import StreamService from '~features/output/stream.service'; -import DetailsDirective from '~features/output/details.directive.js'; + +import DetailsDirective from '~features/output/details.directive'; +import SearchKeyDirective from '~features/output/search-key.directive'; +import StatusDirective from '~features/output/status.directive'; const Template = require('~features/output/index.view.html'); const MODULE_NAME = 'at.features.output'; + const PAGE_CACHE = true; const PAGE_LIMIT = 5; const PAGE_SIZE = 50; @@ -66,13 +69,21 @@ function resolveResource ( Wait('start'); return new Resource(['get', 'options'], [id, id]) - .then(model => Promise.all([ - model.extend('labels'), - model.extend(related, config) - ])) - .then(([ model ]) => ({ + .then(model => { + const promises = [model.getStats()]; + + if (model.has('related.labels')) { + promises.push(model.extend('labels')); + } + + promises.push(model.extend(related, config)); + + return Promise.all(promises); + }) + .then(([stats, model]) => ({ id, type, + stats, model, related, ws: { @@ -200,6 +211,7 @@ angular .service('JobStreamService', StreamService) .directive('atDetails', DetailsDirective) .directive('atSearchKey', SearchKeyDirective) + .directive('atStatus', StatusDirective) .run(JobsRun); export default MODULE_NAME; diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 778a44d7d8..8034b13182 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,14 +1,20 @@
- -

+ + +

- + +
- +
({ [key]: 0 }))); + + HOST_STATUS_KEYS.forEach(key => { + const hostData = _.get(statsEvent, ['event_data', key], {}); + + Object.keys(hostData).forEach(hostName => { + const isAlreadyCounted = (countedHostNames.indexOf(hostName) > -1); + const shouldBeCounted = ((!isAlreadyCounted) && hostData[hostName] > 0); + + if (shouldBeCounted) { + countedHostNames.push(hostName); + counts[key]++; + } + }); + }); + + return counts; +} + +function createStatusBarTooltip (key, count) { + const label = `${key}`; + const badge = `${count}`; + + return `${label}${badge}`; +} + +function atStatusLink (scope, el, attrs, controllers) { + const [atStatusController] = controllers; + + atStatusController.init(scope); +} + +function AtStatusController (strings) { + const vm = this || {}; + + vm.tooltips = { + running: strings.get('status.RUNNING'), + unavailable: strings.get('status.UNAVAILABLE'), + }; + + vm.init = scope => { + const { running, stats } = scope; + + vm.running = running || false; + vm.setStats(stats); + + scope.$watch('running', value => { vm.running = value; }); + scope.$watch('stats', vm.setStats); + }; + + vm.setStats = stats => { + const counts = getHostStatusCounts(stats); + + HOST_STATUS_KEYS.forEach(key => { + const count = counts[key]; + const statusBarElement = $(`.HostStatusBar-${key}`); + + statusBarElement.css('flex', `${count} 0 auto`); + + vm.tooltips[key] = createStatusBarTooltip(key, count); + }); + + vm.statsAreAvailable = Boolean(stats); + }; +} + +function atStatus () { + return { + templateUrl, + restrict: 'E', + require: ['atStatus'], + controllerAs: 'vm', + link: atStatusLink, + controller: [ + 'JobStrings', + AtStatusController + ], + scope: { + running: '=', + stats: '=', + }, + }; +} + +export default atStatus; diff --git a/awx/ui/client/features/output/status.partial.html b/awx/ui/client/features/output/status.partial.html new file mode 100644 index 0000000000..6608a23262 --- /dev/null +++ b/awx/ui/client/features/output/status.partial.html @@ -0,0 +1,37 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index d7f8a72555..0ce0199ac4 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -3,7 +3,7 @@ const JOB_END = 'playbook_on_stats'; const MAX_LAG = 120; function JobStreamService ($q) { - this.init = ({ resource, scroll, page, render, listen }) => { + this.init = ({ resource, scroll, page, onStreamStart, onStreamFinish, render, listen }) => { this.resource = resource; this.scroll = scroll; this.page = page; @@ -23,8 +23,10 @@ function JobStreamService ($q) { }; this.hooks = { + onStreamStart, + onStreamFinish, render, - listen + listen, }; this.lines = { @@ -35,7 +37,7 @@ function JobStreamService ($q) { max: 0 }; - this.hooks.listen(resource.ws.namespace, this.listen); + this.hooks.listen(resource.ws.namespace, this.listener); }; this.getBatchFactors = size => { @@ -105,19 +107,25 @@ function JobStreamService ($q) { } }; - this.listen = data => { + this.listener = data => { this.lag++; this.chain = this.chain .then(() => { + // console.log(data); if (!this.isActive()) { this.start(); + if (!this.isEnding()) { + this.hooks.onStreamStart(data); + } } else if (data.event === JOB_END) { if (this.isPaused()) { this.end(true); } else { this.end(); } + + this.hooks.onStreamFinish(data); } this.checkLines(data); diff --git a/awx/ui/client/lib/models/Job.js b/awx/ui/client/lib/models/Job.js index ef80c5dafb..07b5da3174 100644 --- a/awx/ui/client/lib/models/Job.js +++ b/awx/ui/client/lib/models/Job.js @@ -23,26 +23,54 @@ function postRelaunch (params) { return $http(req); } +function getStats () { + if (!this.has('GET', 'id')) { + return Promise.reject(new Error('No property, id, exists')); + } + + if (!this.has('GET', 'related.job_events')) { + return Promise.reject(new Error('No related property, job_events, exists')); + } + + const req = { + method: 'GET', + url: `${this.path}${this.get('id')}/job_events/`, + params: { event: 'playbook_on_stats' }, + }; + + return $http(req) + .then(({ data }) => { + if (data.results.length > 0) { + return data.results[0]; + } + + return null; + }); +} + + function JobModel (method, resource, config) { BaseModel.call(this, 'jobs'); this.Constructor = JobModel; + this.postRelaunch = postRelaunch.bind(this); this.getRelaunch = getRelaunch.bind(this); + this.getStats = getStats.bind(this); return this.create(method, resource, config); } -function JobModelLoader (_BaseModel_, _$http_) { - BaseModel = _BaseModel_; +function JobModelLoader (_$http_, _BaseModel_) { $http = _$http_; + BaseModel = _BaseModel_; return JobModel; } JobModelLoader.$inject = [ + '$http', 'BaseModel', - '$http' ]; export default JobModelLoader; diff --git a/awx/ui/client/lib/models/ProjectUpdate.js b/awx/ui/client/lib/models/ProjectUpdate.js index 84fae23f50..f600eb7e7c 100644 --- a/awx/ui/client/lib/models/ProjectUpdate.js +++ b/awx/ui/client/lib/models/ProjectUpdate.js @@ -1,19 +1,54 @@ +let $http; let BaseModel; +function getStats () { + if (!this.has('GET', 'id')) { + return Promise.reject(new Error('No property, id, exists')); + } + + if (!this.has('GET', 'related.events')) { + return Promise.reject(new Error('No related property, events, exists')); + } + + + + const req = { + method: 'GET', + url: `${this.path}${this.get('id')}/events/`, + params: { event: 'playbook_on_stats' }, + }; + + return $http(req) + .then(({ data }) => { + console.log(data); + if (data.results.length > 0) { + return data.results[0]; + } + + return null; + }) +} + function ProjectUpdateModel (method, resource, config) { BaseModel.call(this, 'project_updates'); + this.getStats = getStats; + this.Constructor = ProjectUpdateModel; return this.create(method, resource, config); } -function ProjectUpdateModelLoader (_BaseModel_) { +function ProjectUpdateModelLoader (_$http_, _BaseModel_) { + $http = _$http_; BaseModel = _BaseModel_; return ProjectUpdateModel; } -ProjectUpdateModelLoader.$inject = ['BaseModel']; +ProjectUpdateModelLoader.$inject = [ + '$http', + 'BaseModel' +]; export default ProjectUpdateModelLoader; From faa33e0becbf0e91fca31b02a1747207596841c2 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 19 Mar 2018 12:01:48 -0400 Subject: [PATCH 153/379] navigate to new job results view on relaunch --- .../launchjob.factory.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js index 37fd3292b3..2e80cdc369 100644 --- a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js +++ b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js @@ -149,8 +149,8 @@ export default if(base !== 'portal' && Empty(data.system_job) || (base === 'home')){ // use $state.go with reload: true option to re-instantiate sockets in - var goTojobResults = function(state) { - $state.go(state, {id: job}, {reload:true}); + var goTojobResults = function(type) { + $state.go('jobz', {id: job, type}, {reload:true}); }; if($state.includes('jobs')) { @@ -159,23 +159,23 @@ export default else { if(_.has(data, 'job')) { - goTojobResults('jobResult'); + goTojobResults('playbook'); } else if(data.type && data.type === 'workflow_job') { job = data.id; - goTojobResults('workflowResults'); + goTojobResults('workflow_job'); } else if(_.has(data, 'ad_hoc_command')) { - goTojobResults('adHocJobStdout'); + goTojobResults('ad_hoc_command'); } else if(_.has(data, 'system_job')) { - goTojobResults('managementJobStdout'); + goTojobResults('system_job'); } else if(_.has(data, 'project_update')) { // If we are on the projects list or any child state of that list // then we want to stay on that page. Otherwise go to the stdout // view. if(!$state.includes('projects')) { - goTojobResults('scmUpdateStdout'); + goTojobResults('project_update'); } } else if(_.has(data, 'inventory_update')) { @@ -183,7 +183,7 @@ export default // page then we want to stay on that page. Otherwise go to the stdout // view. if(!$state.includes('inventories.edit')) { - goTojobResults('inventorySyncStdout'); + goTojobResults('playbook'); } } } From 450eaeca96cfc57f04a63c833c8bf9fa76924118 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Fri, 23 Mar 2018 11:38:20 -0400 Subject: [PATCH 154/379] add event processing for stats and host status components --- awx/ui/client/features/output/_index.less | 7 +- .../features/output/details.directive.js | 34 +++-- .../features/output/details.partial.html | 5 +- .../features/output/index.controller.js | 127 ++++++++++++------ awx/ui/client/features/output/index.js | 8 +- awx/ui/client/features/output/index.view.html | 9 +- .../client/features/output/render.service.js | 1 - ...status.directive.js => stats.directive.js} | 43 ++++-- ...status.partial.html => stats.partial.html} | 0 .../client/features/output/stream.service.js | 44 +++--- .../client/lib/components/panel/_index.less | 12 ++ .../relaunchButton.component.js | 3 +- awx/ui/client/lib/models/Base.js | 7 +- awx/ui/client/lib/models/Job.js | 1 - awx/ui/client/lib/models/ProjectUpdate.js | 9 +- awx/ui/client/lib/theme/_global.less | 11 ++ awx/ui/client/lib/theme/_utility.less | 8 ++ .../host-status-bar.block.less | 10 +- .../src/standard-out/standard-out.block.less | 6 +- 19 files changed, 227 insertions(+), 118 deletions(-) rename awx/ui/client/features/output/{status.directive.js => stats.directive.js} (65%) rename awx/ui/client/features/output/{status.partial.html => stats.partial.html} (100%) diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index ab4e589213..e44be3b39f 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -206,12 +206,11 @@ display: flex; flex: 0 0 auto; width: 100%; - margin-top: 10px; } .HostStatusBar-ok, .HostStatusBar-changed, -.HostStatusBar-unreachable, +.HostStatusBar-dark, .HostStatusBar-failed, .HostStatusBar-skipped, .HostStatusBar-noData { @@ -231,7 +230,7 @@ flex: 0 0 auto; } -.HostStatusBar-unreachable { +.HostStatusBar-dark { background-color: @default-unreachable; flex: 0 0 auto; } @@ -265,7 +264,7 @@ background-color: @default-succ; } -.HostStatusBar-tooltipBadge--unreachable { +.HostStatusBar-tooltipBadge--dark { background-color: @default-unreachable; } diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 309c50612d..22c66d12cc 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -441,7 +441,7 @@ function AtDetailsController ( vm.init = _$scope_ => { $scope = _$scope_; - resource = $scope.resource; + resource = $scope.resource; // eslint-disable-line prefer-destructuring vm.status = getStatusDetails(); vm.started = getStartTimeDetails(); @@ -466,27 +466,35 @@ function AtDetailsController ( vm.extraVars = getExtraVarsDetails(); vm.labels = getLabelDetails(); + // Relaunch Component + vm.job = _.get(resource.model, 'model.GET', {}); + + // XX - Codemirror + if (vm.extraVars) { + const cm = { + parseType: 'yaml', + $apply: $scope.$apply, + variables: vm.extraVars.value, + }; + + ParseTypeChange({ + field_id: 'cm-extra-vars', + readOnly: true, + scope: cm, + }); + } + vm.cancelJob = cancelJob; vm.deleteJob = deleteJob; vm.toggleLabels = toggleLabels; const observe = (key, transform) => { - $scope.$watch(key, value => { this[key] = transform(value); }); + $scope.$watch(key, value => { vm[key] = transform(value); }); }; + observe('finished', getFinishTimeDetails); observe('status', getStatusDetails); observe('started', getStartTimeDetails); - observe('finished', getFinishTimeDetails); - - // relaunch component - $scope.job = _.get(resource.model, 'model.GET', {}); - this.job = $scope.job; - - // codemirror - if (this.extraVars) { - const cm = { parseType: 'yaml', variables: this.extraVars.value, $apply: $scope.$apply }; - ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true }); - } }; } diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index bb80346a7b..e72b682e6e 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -1,11 +1,10 @@ - - +
DETAILS
- + + + + + +
+
-
+ aw-tool-tip="{{ vm.tooltips.dark }}" + data-tip-watch="vm.tooltips.dark">
{ jobObj.getRelaunch({ id: vm.job.id @@ -182,7 +184,7 @@ function atRelaunchCtrl ( project.postUpdate(vm.job.project) .then((postUpdateRes) => { if (!$state.includes('jobs')) { - $state.go('scmUpdateStdout', { id: postUpdateRes.data.id }, { reload: true }); + $state.go('jobz', { id: postUpdateRes.data.id, type: 'project' }, { reload: true }); } }); } else { @@ -218,7 +220,7 @@ function atRelaunchCtrl ( id: vm.job.id }).then((launchRes) => { if (!$state.includes('jobs')) { - $state.go('adHocJobStdout', { id: launchRes.data.id }, { reload: true }); + $state.go('jobz', { id: launchRes.data.id, type: 'command' }, { reload: true }); } }); } diff --git a/awx/ui/client/lib/models/AdHocCommand.js b/awx/ui/client/lib/models/AdHocCommand.js index 9f259a929a..7bea2677ac 100644 --- a/awx/ui/client/lib/models/AdHocCommand.js +++ b/awx/ui/client/lib/models/AdHocCommand.js @@ -19,12 +19,17 @@ function postRelaunch (params) { return $http(req); } +function getStats () { + return Promise.resolve(null); +} + function AdHocCommandModel (method, resource, config) { BaseModel.call(this, 'ad_hoc_commands'); this.Constructor = AdHocCommandModel; this.postRelaunch = postRelaunch.bind(this); this.getRelaunch = getRelaunch.bind(this); + this.getStats = getStats.bind(this); return this.create(method, resource, config); } diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js index b26dae58d7..0e50b0e671 100644 --- a/awx/ui/client/src/shared/socket/socket.service.js +++ b/awx/ui/client/src/shared/socket/socket.service.js @@ -216,7 +216,7 @@ export default // socket-enabled AND socket-disabled, and whether the $state // requires a subscribe or an unsubscribe var self = this; - socketPromise.promise.then(function(){ + return socketPromise.promise.then(function(){ if(!state.data || !state.data.socket){ _.merge(state.data, {socket: {groups: {}}}); self.unsubscribe(state); diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js index 70bab1727b..7cfc754d86 100644 --- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js +++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js @@ -898,13 +898,13 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge let goToJobResults = function(job_type) { if(job_type === 'job') { - $state.go('jobResult', {id: d.job.id}); + $state.go('jobz', {id: d.job.id, type: 'playbook'}); } else if(job_type === 'inventory_update') { - $state.go('inventorySyncStdout', {id: d.job.id}); + $state.go('jobz', {id: d.job.id, type: 'inventory'}); } else if(job_type === 'project_update') { - $state.go('scmUpdateStdout', {id: d.job.id}); + $state.go('jobz', {id: d.job.id, type: 'project'}); } }; From 8da2c3cad2686f04f41e60465a120f71fbd4be28 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 28 Mar 2018 20:58:11 -0400 Subject: [PATCH 157/379] fix regression with opening credential edit/add --- awx/ui/client/features/credentials/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js index e3a1ea7734..2fa094ef9f 100644 --- a/awx/ui/client/features/credentials/index.js +++ b/awx/ui/client/features/credentials/index.js @@ -11,7 +11,7 @@ function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, O const id = $stateParams.credential_id; const promises = { - me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations')) + me: new Me('get').then((me) => me.extend('admin_of_organizations')) }; if (!id) { From a53f70f0afe45e138a215f8ab3daea5344d7ce15 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 28 Mar 2018 21:58:35 -0400 Subject: [PATCH 158/379] add inventory updates --- .../features/output/details.directive.js | 27 ++++++++++++------- .../features/output/details.partial.html | 12 ++++----- .../features/output/index.controller.js | 2 +- awx/ui/client/features/output/index.js | 6 ++++- .../relaunchButton.component.js | 4 +-- awx/ui/client/lib/models/InventoryUpdate.js | 27 +++++++++++++++++++ awx/ui/client/lib/models/SystemJob.js | 6 +++++ awx/ui/client/lib/models/index.js | 2 ++ 8 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 awx/ui/client/lib/models/InventoryUpdate.js diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 7f36ee84e6..221fc2266a 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -92,7 +92,7 @@ function getVerbosityDetails () { const choices = mapChoices(resource.model.options('actions.GET.verbosity.choices')); const label = 'Verbosity'; - const value = choices[value]; + const value = choices[verbosity]; return { label, value }; } @@ -256,17 +256,26 @@ function getResultTracebackDetails () { return { label, value }; } -function getMachineCredentialDetails () { - const machineCredential = resource.model.get('summary_fields.credential'); +function getCredentialDetails () { + const credential = resource.model.get('summary_fields.credential'); - if (!machineCredential) { + if (!credential) { return null; } - const label = 'Machine Credential'; - const link = `/#/credentials/${machineCredential.id}`; + let label = 'Credential'; + + if (resource.type === 'playbook') { + label = 'Machine Credential'; + } + + if (resource.type === 'inventory') { + label = 'Source Credential'; + } + + const link = `/#/credentials/${credential.id}`; const tooltip = 'Edit the Credential'; - const value = $filter('sanitize')(machineCredential.name); + const value = $filter('sanitize')(credential.name); return { label, link, tooltip, value }; } @@ -427,7 +436,7 @@ function handleSocketEvent (data) { vm.project.update = vm.project.update || {}; vm.project.update.status = data.status; } -}; +} function AtDetailsController ( _$http_, @@ -470,7 +479,7 @@ function AtDetailsController ( vm.launchedBy = getLaunchedByDetails(); vm.jobExplanation = getJobExplanationDetails(); vm.verbosity = getVerbosityDetails(); - vm.machineCredential = getMachineCredentialDetails(); + vm.credential = getCredentialDetails(); vm.forks = getForkDetails(); vm.limit = getLimitDetails(); vm.instanceGroup = getInstanceGroupDetails(); diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index e72b682e6e..4d26151b9e 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -136,14 +136,14 @@
{{ vm.playbook.value }}
- -
- + + diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index df2a06034c..e938e45cd6 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -84,7 +84,7 @@ function JobsIndexController ( eventCounter = null; statsEvent = resource.stats; - // Panel Title + // Panel vm.title = resource.model.get('name'); // Status Bar diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 62155aed1e..33c67d6f11 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -27,6 +27,7 @@ function resolveResource ( AdHocCommand, SystemJob, WorkflowJob, + InventoryUpdate, $stateParams, qs, Wait @@ -51,6 +52,9 @@ function resolveResource ( case 'system': Resource = SystemJob; break; + case 'inventory': + Resource = InventoryUpdate; + break; // case 'workflow': // todo: integrate workflow chart components into this view // break; @@ -117,7 +121,6 @@ function resolveWebSocketConnection ($stateParams, SocketService) { }; return SocketService.addStateResolve(state, id); - } function resolveBreadcrumb (strings) { @@ -181,6 +184,7 @@ function JobsRun ($stateRegistry) { 'AdHocCommandModel', 'SystemJobModel', 'WorkflowJobModel', + 'InventoryUpdateModel', '$stateParams', 'QuerySet', 'Wait', diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js index e6ec40733b..a76e62eb52 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js @@ -23,8 +23,6 @@ function atRelaunchCtrl ( const jobObj = new Job(); const jobTemplate = new JobTemplate(); - - const checkRelaunchPlaybook = (option) => { jobObj.getRelaunch({ id: vm.job.id @@ -165,7 +163,7 @@ function atRelaunchCtrl ( inventorySource.postUpdate(vm.job.inventory_source) .then((postUpdateRes) => { if (!$state.includes('jobs')) { - $state.go('inventorySyncStdout', { id: postUpdateRes.data.id }, { reload: true }); + $state.go('jobz', { id: postUpdateRes.data.id, type: 'inventory' }, { reload: true }); } }); } else { diff --git a/awx/ui/client/lib/models/InventoryUpdate.js b/awx/ui/client/lib/models/InventoryUpdate.js new file mode 100644 index 0000000000..668a05459d --- /dev/null +++ b/awx/ui/client/lib/models/InventoryUpdate.js @@ -0,0 +1,27 @@ +let BaseModel; + +function getStats () { + return Promise.resolve(null); +} + +function InventoryUpdateModel (method, resource, config) { + BaseModel.call(this, 'inventory_updates'); + + this.getStats = getStats.bind(this); + + this.Constructor = InventoryUpdateModel; + + return this.create(method, resource, config); +} + +function InventoryUpdateModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return InventoryUpdateModel; +} + +InventoryUpdateModelLoader.$inject = [ + 'BaseModel' +]; + +export default InventoryUpdateModelLoader; diff --git a/awx/ui/client/lib/models/SystemJob.js b/awx/ui/client/lib/models/SystemJob.js index cc092ff8f4..1f1f1c5ee3 100644 --- a/awx/ui/client/lib/models/SystemJob.js +++ b/awx/ui/client/lib/models/SystemJob.js @@ -1,8 +1,14 @@ let BaseModel; +function getStats () { + return Promise.resolve(null); +} + function SystemJobModel (method, resource, config) { BaseModel.call(this, 'system_jobs'); + this.getStats = getStats.bind(this); + this.Constructor = SystemJobModel; return this.create(method, resource, config); diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 3875975d4d..d50c825e22 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -11,6 +11,7 @@ import InstanceGroup from '~models/InstanceGroup'; import Inventory from '~models/Inventory'; import InventoryScript from '~models/InventoryScript'; import InventorySource from '~models/InventorySource'; +import InventoryUpdate from '~models/InventoryUpdate'; import Job from '~models/Job'; import JobEvent from '~models/JobEvent'; import JobTemplate from '~models/JobTemplate'; @@ -46,6 +47,7 @@ angular .service('InventoryModel', Inventory) .service('InventoryScriptModel', InventoryScript) .service('InventorySourceModel', InventorySource) + .service('InventoryUpdateModel', InventoryUpdate) .service('JobEventModel', JobEvent) .service('JobModel', Job) .service('JobTemplateModel', JobTemplate) From 6b302ef1674c8eb62e95de99361139b184ae7bdd Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 28 Mar 2018 22:46:40 -0400 Subject: [PATCH 159/379] job results link-in --- awx/ui/client/features/jobs/jobsList.controller.js | 10 +++++----- .../launchTemplateButton.component.js | 4 ++-- .../relaunchButton/relaunchButton.component.js | 2 +- .../home/dashboard/lists/jobs/jobs-list.directive.js | 11 ++++++++++- .../inventories/adhoc/adhoc.controller.js | 2 +- .../host-summary-popover.controller.js | 2 +- .../source-summary-popover.controller.js | 2 +- .../sources/factories/view-update-status.factory.js | 2 +- .../job-submission-factories/adhoc-run.factory.js | 2 +- .../src/management-jobs/card/card.controller.js | 4 ++-- .../organizations-inventories.controller.js | 2 +- .../controllers/organizations-projects.controller.js | 2 +- .../src/projects/list/projects-list.controller.js | 2 +- .../src/smart-status/smart-status.controller.js | 2 +- 14 files changed, 29 insertions(+), 20 deletions(-) diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js index b78a1b77b6..ea57c4e9f5 100644 --- a/awx/ui/client/features/jobs/jobsList.controller.js +++ b/awx/ui/client/features/jobs/jobsList.controller.js @@ -52,19 +52,19 @@ function ListJobsController ( switch (type) { case 'job': - link = `/#/jobs/${id}`; + link = `/#/jobz/playbook/${id}`; break; case 'ad_hoc_command': - link = `/#/ad_hoc_commands/${id}`; + link = `/#/jobz/command/${id}`; break; case 'system_job': - link = `/#/management_jobs/${id}`; + link = `/#/jobz/system/${id}`; break; case 'project_update': - link = `/#/scm_update/${id}`; + link = `/#/jobz/project/${id}`; break; case 'inventory_update': - link = `/#/inventory_sync/${id}`; + link = `/#/jobz/inventory/${id}`; break; case 'workflow_job': link = `/#/workflows/${id}`; diff --git a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js index d4c9403823..40dcf2907c 100644 --- a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js +++ b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.component.js @@ -41,7 +41,7 @@ function atLaunchTemplateCtrl ( selectedJobTemplate .postLaunch({ id: vm.template.id }) .then(({ data }) => { - $state.go('jobResult', { id: data.job }, { reload: true }); + $state.go('jobz', { id: data.job, type: 'playbook' }, { reload: true }); }); } else { const promptData = { @@ -138,7 +138,7 @@ function atLaunchTemplateCtrl ( id: vm.promptData.template, launchData: jobLaunchData }).then((launchRes) => { - $state.go('jobResult', { id: launchRes.data.job }, { reload: true }); + $state.go('jobz', { id: launchRes.data.job, type: 'playbook' }, { reload: true }); }).catch(createErrorHandler('launch job template', 'POST')); } else if (vm.promptData.templateType === 'workflow_job_template') { workflowTemplate.create().postLaunch({ diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js index a76e62eb52..3d7d6b09ff 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js @@ -238,7 +238,7 @@ function atRelaunchCtrl ( relaunchData: PromptService.bundlePromptDataForRelaunch(vm.promptData) }).then((launchRes) => { if (!$state.includes('jobs')) { - $state.go('jobResult', { id: launchRes.data.job }, { reload: true }); + $state.go('jobz', { id: launchRes.data.job, type: 'playbook' }, { reload: true }); } }).catch(({ data, status }) => { ProcessErrors($scope, data, status, null, { diff --git a/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js b/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js index 4d8be63ccd..93a2147834 100644 --- a/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js +++ b/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js @@ -28,8 +28,17 @@ export default function createList(list) { // detailsUrl, status, name, time scope.jobs = _.map(list, function(job){ + + let detailsUrl; + + if (job.type === 'workflow_job') { + detailsUrl = `/#/workflows/${job.id}`; + } else { + detailsUrl = `/#/jobz/playbook/${job.id}`; + } + return { - detailsUrl: job.type && job.type === 'workflow_job' ? job.url.replace(/api\/v\d+\/workflow_jobs/, "#/workflows") : job.url.replace(/api\/v\d+/, "#"), + detailsUrl, status: job.status, name: job.name, id: job.id, diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js index 5aa5938714..e5e020fc6d 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js @@ -241,7 +241,7 @@ function adhocController($q, $scope, $stateParams, Rest.post(data) .then(({data}) => { Wait('stop'); - $state.go('adHocJobStdout', {id: data.id}); + $state.go('jobz', {id: data.id, type: 'command'}); }) .catch(({data, status}) => { ProcessErrors($scope, data, status, adhocForm, { diff --git a/awx/ui/client/src/inventories-hosts/inventories/list/host-summary-popover/host-summary-popover.controller.js b/awx/ui/client/src/inventories-hosts/inventories/list/host-summary-popover/host-summary-popover.controller.js index 495c201646..7b2e28e8aa 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/list/host-summary-popover/host-summary-popover.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/list/host-summary-popover/host-summary-popover.controller.js @@ -23,7 +23,7 @@ export default [ '$scope', 'Empty', 'Wait', 'GetBasePath', 'Rest', 'ProcessError }; $scope.viewJob = function(jobId) { - $state.go('jobResult', {id: jobId}); + $state.go('jobz', { id: jobId, type: 'playbook' }); }; } diff --git a/awx/ui/client/src/inventories-hosts/inventories/list/source-summary-popover/source-summary-popover.controller.js b/awx/ui/client/src/inventories-hosts/inventories/list/source-summary-popover/source-summary-popover.controller.js index 661d122c88..725069044e 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/list/source-summary-popover/source-summary-popover.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/list/source-summary-popover/source-summary-popover.controller.js @@ -20,7 +20,7 @@ export default [ '$scope', 'Wait', 'Empty', 'Rest', 'ProcessErrors', '$state', $scope.viewJob = function(url) { // Pull the id out of the URL var id = url.replace(/^\//, '').split('/')[3]; - $state.go('inventorySyncStdout', {id: id}); + $state.go('jobz', { id, type: 'inventory' } ); }; } diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/factories/view-update-status.factory.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/factories/view-update-status.factory.js index e7820f79c8..c889517506 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/factories/view-update-status.factory.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/factories/view-update-status.factory.js @@ -17,7 +17,7 @@ export default // Get the ID from the correct summary field var update_id = (data.summary_fields.current_update) ? data.summary_fields.current_update.id : data.summary_fields.last_update.id; - $state.go('inventorySyncStdout', {id: update_id}); + $state.go('jobz', { id: update_id, type: 'inventory' }); }) .catch(({data, status}) => { ProcessErrors(scope, data, status, null, { hdr: 'Error!', diff --git a/awx/ui/client/src/job-submission/job-submission-factories/adhoc-run.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/adhoc-run.factory.js index 240c41df5b..65a7b1c578 100644 --- a/awx/ui/client/src/job-submission/job-submission-factories/adhoc-run.factory.js +++ b/awx/ui/client/src/job-submission/job-submission-factories/adhoc-run.factory.js @@ -89,7 +89,7 @@ .then(({data}) => { Wait('stop'); if($location.path().replace(/^\//, '').split('/')[0] !== 'jobs') { - $state.go('adHocJobStdout', {id: data.id}); + $state.go('jobz', { id: data.id, type: 'command' }); } }) .catch(({data, status}) => { diff --git a/awx/ui/client/src/management-jobs/card/card.controller.js b/awx/ui/client/src/management-jobs/card/card.controller.js index a66f5d7ec5..12dceb22b9 100644 --- a/awx/ui/client/src/management-jobs/card/card.controller.js +++ b/awx/ui/client/src/management-jobs/card/card.controller.js @@ -132,7 +132,7 @@ export default Wait('stop'); $("#prompt-for-days-facts").dialog("close"); $("#configure-dialog").dialog('close'); - $state.go('managementJobStdout', {id: data.system_job}, {reload:true}); + $state.go('jobz', { id: data.system_job, type: 'system' }, { reload: true }); }) .catch(({data, status}) => { let template_id = scope.job_template_id; @@ -222,7 +222,7 @@ export default Wait('stop'); $("#prompt-for-days").dialog("close"); // $("#configure-dialog").dialog('close'); - $state.go('managementJobStdout', {id: data.system_job}, {reload:true}); + $state.go('jobz', { id: data.system_job, type: 'system' }, { reload: true }); }) .catch(({data, status}) => { let template_id = scope.job_template_id; diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-inventories.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-inventories.controller.js index 0e74eb5132..b26fffb264 100644 --- a/awx/ui/client/src/organizations/linkout/controllers/organizations-inventories.controller.js +++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-inventories.controller.js @@ -234,7 +234,7 @@ export default ['$scope', '$rootScope', '$location', $scope.viewJob = function(url) { // Pull the id out of the URL var id = url.replace(/^\//, '').split('/')[3]; - $state.go('inventorySyncStdout', { id: id }); + $state.go('jobz', { id: id, type: 'inventory' }); }; diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-projects.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-projects.controller.js index 39c322e183..853534f142 100644 --- a/awx/ui/client/src/organizations/linkout/controllers/organizations-projects.controller.js +++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-projects.controller.js @@ -187,7 +187,7 @@ export default ['$scope', '$rootScope', '$log', '$stateParams', 'Rest', 'Alert', // 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('jobz', { id: id, type: 'project' }); } else { Alert('No Updates Available', 'There is no SCM update information available for this project. An update has not yet been ' + diff --git a/awx/ui/client/src/projects/list/projects-list.controller.js b/awx/ui/client/src/projects/list/projects-list.controller.js index ec21e0009c..1bcd7db2d6 100644 --- a/awx/ui/client/src/projects/list/projects-list.controller.js +++ b/awx/ui/client/src/projects/list/projects-list.controller.js @@ -146,7 +146,7 @@ export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert', // 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('jobz', { id: id, type: 'project'}, { reload: true }); } else { Alert(i18n._('No Updates Available'), i18n._('There is no SCM update information available for this project. An update has not yet been ' + diff --git a/awx/ui/client/src/smart-status/smart-status.controller.js b/awx/ui/client/src/smart-status/smart-status.controller.js index 8026a4d10c..1966e0b68c 100644 --- a/awx/ui/client/src/smart-status/smart-status.controller.js +++ b/awx/ui/client/src/smart-status/smart-status.controller.js @@ -27,7 +27,7 @@ export default ['$scope', '$filter', if (typeof $scope.templateType !== 'undefined' && $scope.templateType === 'workflow_job_template') { detailsBaseUrl = '/#/workflows/'; } else { - detailsBaseUrl = '/#/jobs/'; + detailsBaseUrl = '/#/jobz/playbook/'; } var sparkData = From 07186e1606335d040f4d87effe7e625bfa2db1e0 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 29 Mar 2018 00:32:46 -0400 Subject: [PATCH 160/379] disable search when running --- .../features/output/index.controller.js | 70 ++++++++++--------- awx/ui/client/features/output/index.view.html | 38 +++++----- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index e938e45cd6..6e160f02a5 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -50,36 +50,11 @@ function JobsIndexController ( // Development helper(s) vm.clear = devClear; - // Stdout Navigation - vm.scroll = { - showBackToTop: false, - home: scrollHome, - end: scrollEnd, - down: scrollPageDown, - up: scrollPageUp - }; - // Expand/collapse vm.toggle = toggle; vm.expand = expand; vm.isExpanded = true; - // Search - $state = _$state_; - qs = _qs_; - - vm.searchValue = ''; - vm.searchRejected = null; - vm.searchKey = false; - vm.searchKeyExamples = searchKeyExamples; - vm.searchKeyFields = searchKeyFields; - - vm.clearSearch = clearSearch; - vm.search = search; - vm.toggleSearchKey = toggleSearchKey; - vm.removeSearchTag = removeSearchTag; - vm.searchTags = getSearchTags(getCurrentQueryset()); - // Events eventCounter = null; statsEvent = resource.stats; @@ -103,6 +78,33 @@ function JobsIndexController ( finished: resource.model.get('finished'), }; + // Search + $state = _$state_; + qs = _qs_; + + vm.search = { + clearSearch, + searchKeyExamples, + searchKeyFields, + toggleSearchKey, + removeSearchTag, + submitSearch, + value: '', + key: false, + rejected: false, + disabled: !resource.model.get('finished'), + tags: getSearchTags(getCurrentQueryset()), + }; + + // Stdout Navigation + vm.scroll = { + showBackToTop: false, + home: scrollHome, + end: scrollEnd, + down: scrollPageDown, + up: scrollPageUp + }; + render.requestAnimationFrame(() => init(!vm.status.running)); } @@ -134,11 +136,15 @@ function init (pageMode) { vm.status.plays = 0; vm.status.tasks = 0; vm.status.running = true; + + vm.search.disabled = true; }, onStop () { vm.status.stats = statsEvent; vm.status.running = false; + vm.search.disabled = false; + vm.details.status = statsEvent.failed ? 'failed' : 'successful'; vm.details.finished = statsEvent.created; } @@ -398,7 +404,7 @@ const searchKeyExamples = ['id:>1', 'task:set', 'created:>=2000-01-01']; const searchKeyFields = ['changed', 'failed', 'host_name', 'stdout', 'task', 'role', 'playbook', 'play']; function toggleSearchKey () { - vm.searchKey = !vm.searchKey; + vm.search.key = !vm.search.key; } function getCurrentQueryset () { @@ -416,31 +422,31 @@ function getSearchTags (queryset) { } function removeSearchTag (index) { - const searchTerm = vm.searchTags[index]; + const searchTerm = vm.search.tags[index]; const currentQueryset = getCurrentQueryset(); const modifiedQueryset = qs.removeTermsFromQueryset(currentQueryset, searchTerm); - vm.searchTags = getSearchTags(modifiedQueryset); + vm.search.tags = getSearchTags(modifiedQueryset); $state.params.job_event_search = qs.encodeArr(modifiedQueryset); $state.transitionTo($state.current, $state.params, searchReloadOptions); } -function search () { - const searchInputQueryset = qs.getSearchInputQueryset(vm.searchValue); +function submitSearch () { + const searchInputQueryset = qs.getSearchInputQueryset(vm.search.value); const currentQueryset = getCurrentQueryset(); const modifiedQueryset = qs.mergeQueryset(currentQueryset, searchInputQueryset); - vm.searchTags = getSearchTags(modifiedQueryset); + vm.search.tags = getSearchTags(modifiedQueryset); $state.params.job_event_search = qs.encodeArr(modifiedQueryset); $state.transitionTo($state.current, $state.params, searchReloadOptions); } function clearSearch () { - vm.searchTags = []; + vm.search.tags = []; $state.params.job_event_search = ''; $state.transitionTo($state.current, $state.params, searchReloadOptions); diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 4dde59725a..4ba0d1c91b 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -22,31 +22,31 @@ tasks="vm.status.tasks"> - +
+ ng-class="{ 'at-Input--rejected': vm.search.rejected }" + ng-model="vm.search.value" + ng-attr-placeholder="{{ vm.search.disabled ? 'CANNOT SEARCH RUNNING JOB' : 'SEARCH' }}" + ng-disabled="vm.search.disabled"> @@ -54,16 +54,20 @@
-
+
{{ tag }}
-
+
- +
- + +
From fc01af22988fd716b59cb6dd7f9d8f8a48905630 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 29 Mar 2018 00:39:08 -0400 Subject: [PATCH 161/379] hide dev utility --- awx/ui/client/features/output/index.view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 4ba0d1c91b..78a4ecd395 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -6,7 +6,7 @@ started="vm.details.started" finished="vm.details.finished"> -

+
From 181d7e0e011a120127fb6efd81b352a55a20b815 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 29 Mar 2018 01:24:57 -0400 Subject: [PATCH 162/379] add delete and cancel --- .../features/output/details.directive.js | 36 ++++++++++++++++--- .../features/output/details.partial.html | 19 ++++++---- awx/ui/client/features/output/jobs.strings.js | 3 ++ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 221fc2266a..8a466371f6 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -396,7 +396,7 @@ function toggleLabels () { } function cancelJob () { - const actionText = strings.get('CANCEL'); + const actionText = strings.get('warnings.CANCEL_ACTION'); const hdr = strings.get('warnings.CANCEL_HEADER'); const warning = strings.get('warnings.CANCEL_BODY'); @@ -414,7 +414,6 @@ function cancelJob () { const action = () => { wait('start'); $http({ method, url }) - .then(() => $state.go('jobs')) .catch(errorHandler) .finally(() => { $(ELEMENT_PROMPT_MODAL).modal('hide'); @@ -425,7 +424,35 @@ function cancelJob () { prompt({ hdr, resourceName, body, actionText, action }); } -function deleteJob () {} +function deleteJob () { + const actionText = strings.get('DELETE'); + const hdr = strings.get('warnings.DELETE_HEADER'); + const warning = strings.get('warnings.DELETE_BODY'); + + const id = resource.model.get('id'); + const name = $filter('sanitize')(resource.model.get('name')); + + const body = `
${warning}
`; + const resourceName = `#${id} ${name}`; + + const method = 'DELETE'; + const url = `${resource.model.path}/${id}/`; + + const errorHandler = createErrorHandler('delete job', method); + + const action = () => { + wait('start'); + $http({ method, url }) + .then(() => $state.go('jobs')) + .catch(errorHandler) + .finally(() => { + $(ELEMENT_PROMPT_MODAL).modal('hide'); + wait('stop'); + }); + }; + + prompt({ hdr, resourceName, body, actionText, action }); +} function handleSocketEvent (data) { const project = resource.model.get('project'); @@ -488,8 +515,9 @@ function AtDetailsController ( vm.extraVars = getExtraVarsDetails(); vm.labels = getLabelDetails(); - // Relaunch Component + // Relaunch and Delete Components vm.job = _.get(resource.model, 'model.GET', {}); + vm.canDelete = resource.model.get('summary_fields.user_capabilities.delete'); // XX - Codemirror if (vm.extraVars) { diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index 4d26151b9e..edd92f6fe0 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -7,21 +7,26 @@ - - - + + - - - -
- + - - - - - -
{ - vm.examples = scope.examples || []; - vm.fields = scope.fields || []; - vm.relatedFields = scope.relatedFields || []; - }; -} - -AtSearchKeyController.$inject = ['$scope']; - -function atSearchKey () { - return { - templateUrl, - restrict: 'E', - require: ['atSearchKey'], - controllerAs: 'vm', - link: atSearchKeyLink, - controller: AtSearchKeyController, - scope: { - examples: '=', - fields: '=', - relatedFields: '=', - }, - }; -} - -export default atSearchKey; diff --git a/awx/ui/client/features/output/search-key.partial.html b/awx/ui/client/features/output/search-key.partial.html deleted file mode 100644 index d2790d285f..0000000000 --- a/awx/ui/client/features/output/search-key.partial.html +++ /dev/null @@ -1,20 +0,0 @@ - -
-
-
-
-
EXAMPLES:
- -
-
-
- FIELDS: - {{ field }}, -
-
- ADDITIONAL INFORMATION: - For additional information on advanced search search syntax please see the Ansible Tower - documentation. -
-
-
diff --git a/awx/ui/client/features/output/search.directive.js b/awx/ui/client/features/output/search.directive.js new file mode 100644 index 0000000000..5945594ab1 --- /dev/null +++ b/awx/ui/client/features/output/search.directive.js @@ -0,0 +1,119 @@ +const templateUrl = require('~features/output/search.partial.html'); + +const searchReloadOptions = { reload: true, inherit: false }; +const searchKeyExamples = ['id:>1', 'task:set', 'created:>=2000-01-01']; +const searchKeyFields = ['changed', 'failed', 'host_name', 'stdout', 'task', 'role', 'playbook', 'play']; + +let $state; +let status; +let qs; + +let vm; + +function toggleSearchKey () { + vm.key = !vm.key; +} + +function getCurrentQueryset () { + const { job_event_search } = $state.params; // eslint-disable-line camelcase + + return qs.decodeArr(job_event_search); +} + +function getSearchTags (queryset) { + return qs.createSearchTagsFromQueryset(queryset) + .filter(tag => !tag.startsWith('event')) + .filter(tag => !tag.startsWith('-event')) + .filter(tag => !tag.startsWith('page_size')) + .filter(tag => !tag.startsWith('order_by')); +} + +function removeSearchTag (index) { + const searchTerm = vm.tags[index]; + + const currentQueryset = getCurrentQueryset(); + const modifiedQueryset = qs.removeTermsFromQueryset(currentQueryset, searchTerm); + + vm.tags = getSearchTags(modifiedQueryset); + + $state.params.job_event_search = qs.encodeArr(modifiedQueryset); + $state.transitionTo($state.current, $state.params, searchReloadOptions); +} + +function submitSearch () { + const searchInputQueryset = qs.getSearchInputQueryset(vm.value); + + const currentQueryset = getCurrentQueryset(); + const modifiedQueryset = qs.mergeQueryset(currentQueryset, searchInputQueryset); + + vm.tags = getSearchTags(modifiedQueryset); + + $state.params.job_event_search = qs.encodeArr(modifiedQueryset); + $state.transitionTo($state.current, $state.params, searchReloadOptions); +} + +function clearSearch () { + vm.tags = []; + + $state.params.job_event_search = ''; + $state.transitionTo($state.current, $state.params, searchReloadOptions); +} + +function atJobSearchLink (scope, el, attrs, controllers) { + const [atJobSearchController] = controllers; + + atJobSearchController.init(scope); +} + +function AtJobSearchController (_$state_, _status_, _qs_) { + $state = _$state_; + status = _status_; + qs = _qs_; + + vm = this || {}; + + vm.value = ''; + vm.key = false; + vm.rejected = false; + vm.disabled = true; + vm.tags = getSearchTags(getCurrentQueryset()); + + vm.clearSearch = clearSearch; + vm.searchKeyExamples = searchKeyExamples; + vm.searchKeyFields = searchKeyFields; + vm.toggleSearchKey = toggleSearchKey; + vm.removeSearchTag = removeSearchTag; + vm.submitSearch = submitSearch; + + vm.init = scope => { + vm.examples = scope.examples || searchKeyExamples; + vm.fields = scope.fields || searchKeyFields; + vm.relatedFields = scope.relatedFields || []; + + scope.$watch(status.isRunning, value => { vm.disabled = value; }); + }; +} + +AtJobSearchController.$inject = [ + '$state', + 'JobStatusService', + 'QuerySet', +]; + +function atJobSearch () { + return { + templateUrl, + restrict: 'E', + require: ['atJobSearch'], + controllerAs: 'vm', + link: atJobSearchLink, + controller: AtJobSearchController, + scope: { + examples: '=', + fields: '=', + relatedFields: '=', + }, + }; +} + +export default atJobSearch; diff --git a/awx/ui/client/features/output/search.partial.html b/awx/ui/client/features/output/search.partial.html new file mode 100644 index 0000000000..af34bac344 --- /dev/null +++ b/awx/ui/client/features/output/search.partial.html @@ -0,0 +1,61 @@ + +
+
+ + + + + + +
+
+ +
+
+
{{ tag }}
+
+ +
+
+ +
+ +
+
+
+
+
EXAMPLES:
+ +
+
+
+ FIELDS: + {{ field }}, +
+
+ ADDITIONAL INFORMATION: + For additional information on advanced search search syntax please see the Ansible Tower + documentation. +
+
+
From 95a37fab0573963ebd9ca4913e181cda153e224b Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 3 Apr 2018 18:06:20 -0400 Subject: [PATCH 168/379] fix lint errors --- .../features/output/index.controller.js | 75 ++++++++++--------- awx/ui/client/features/output/page.service.js | 4 +- .../shared/smart-search/queryset.service.js | 6 +- .../smart-search/smart-search.controller.js | 2 +- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index c06e744faa..0fee736561 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -38,8 +38,8 @@ function JobsIndexController ( vm.clear = devClear; // Expand/collapse - vm.toggle = toggle; - vm.expand = expand; + // vm.toggle = toggle; + // vm.expand = expand; vm.isExpanded = true; // Panel @@ -160,10 +160,10 @@ function previous () { }); } -function append (events, engine) { +function append (events, eng) { return render.append(events) .then(count => { - page.updateLineCount(count, engine); + page.updateLineCount(count, eng); }); } @@ -277,48 +277,49 @@ function scrollIsAtRest (isAtRest) { vm.scroll.showBackToTop = !isAtRest; } -function expand () { - vm.toggle(parent, true); -} +// function expand () { +// vm.toggle(parent, true); +// } -function showHostDetails (id) { - jobEvent.request('get', id) - .then(() => { - const title = jobEvent.get('host_name'); +// function showHostDetails (id) { +// jobEvent.request('get', id) +// .then(() => { +// const title = jobEvent.get('host_name'); - vm.host = { - menu: true, - stdout: jobEvent.get('stdout') - }; +// vm.host = { +// menu: true, +// stdout: jobEvent.get('stdout') +// }; - $scope.jobs.modal.show(title); - }); -} +// $scope.jobs.modal.show(title); +// }); +// } -function toggle (uuid, menu) { - const lines = $(`.child-of-${uuid}`); - let icon = $(`#${uuid} .at-Stdout-toggle > i`); +// function toggle (uuid, menu) { +// const lines = $(`.child-of-${uuid}`); +// let icon = $(`#${uuid} .at-Stdout-toggle > i`); - if (menu || record[uuid].level === 1) { - vm.isExpanded = !vm.isExpanded; - } +// if (menu || record[uuid].level === 1) { +// vm.isExpanded = !vm.isExpanded; +// } - if (record[uuid].children) { - icon = icon.add($(`#${record[uuid].children.join(', #')}`).find('.at-Stdout-toggle > i')); - } +// if (record[uuid].children) { +// icon = icon.add($(`#${record[uuid].children.join(', #')}`) +// .find('.at-Stdout-toggle > i')); +// } - if (icon.hasClass('fa-angle-down')) { - icon.addClass('fa-angle-right'); - icon.removeClass('fa-angle-down'); +// if (icon.hasClass('fa-angle-down')) { +// icon.addClass('fa-angle-right'); +// icon.removeClass('fa-angle-down'); - lines.addClass('hidden'); - } else { - icon.addClass('fa-angle-down'); - icon.removeClass('fa-angle-right'); +// lines.addClass('hidden'); +// } else { +// icon.addClass('fa-angle-down'); +// icon.removeClass('fa-angle-right'); - lines.removeClass('hidden'); - } -} +// lines.removeClass('hidden'); +// } +// } JobsIndexController.$inject = [ 'resource', diff --git a/awx/ui/client/features/output/page.service.js b/awx/ui/client/features/output/page.service.js index 3f6461345e..5f19fe921a 100644 --- a/awx/ui/client/features/output/page.service.js +++ b/awx/ui/client/features/output/page.service.js @@ -156,10 +156,10 @@ function JobPageService ($q) { this.isPageBookmarked = number => number >= this.page.bookmark.first && number <= this.page.bookmark.last; - this.updateLineCount = (lines, stream) => { + this.updateLineCount = (lines, engine) => { let reference; - if (stream) { + if (engine) { reference = this.getReference(); } else { reference = this.getActiveReference(); diff --git a/awx/ui/client/src/shared/smart-search/queryset.service.js b/awx/ui/client/src/shared/smart-search/queryset.service.js index 780900d32f..7d79ac1ec1 100644 --- a/awx/ui/client/src/shared/smart-search/queryset.service.js +++ b/awx/ui/client/src/shared/smart-search/queryset.service.js @@ -53,8 +53,8 @@ function QuerysetService ($q, Rest, ProcessErrors, $rootScope, Wait, DjangoSearc .map(value => { value = this.replaceDefaultFlags(value); value = this.replaceEncodedTokens(value); - return [key, value] - }) + return [key, value]; + }); }, // encodes ui-router params from {operand__key__comparator: value} pairs to API-consumable URL @@ -83,7 +83,7 @@ function QuerysetService ($q, Rest, ProcessErrors, $rootScope, Wait, DjangoSearc for (let encodedIndex in encodedTerms) { const [encodedKey, encodedValue] = encodedTerms[encodedIndex]; obj[encodedKey] = obj[encodedKey] || []; - obj[encodedKey].push(encodedValue) + obj[encodedKey].push(encodedValue); } return obj; diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index 414a0763db..7c1e7eaca8 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -6,7 +6,7 @@ function SmartSearchController ( configService, GetBasePath, i18n, - qs, + qs ) { const searchKey = `${$scope.iterator}_search`; const optionsKey = `${$scope.list.iterator}_options`; From cf4b29c6d557936b597449a7b7b766ae4ade07c6 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 3 Apr 2018 21:32:45 -0400 Subject: [PATCH 169/379] remove has-ansi dependency --- awx/ui/client/features/output/render.service.js | 10 +++++++++- awx/ui/package.json | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 06a6bc84a2..351d038387 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -1,5 +1,4 @@ import Ansi from 'ansi-to-html'; -import hasAnsi from 'has-ansi'; import Entities from 'html-entities'; const ELEMENT_TBODY = '#atStdoutResultTable'; @@ -21,6 +20,15 @@ const TIME_EVENTS = [ const ansi = new Ansi(); const entities = new Entities.AllHtmlEntities(); +// https://github.com/chalk/ansi-regex +const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))' +].join('|'); + +const re = new RegExp(pattern); +const hasAnsi = input => re.test(input); + function JobRenderService ($q, $sce, $window) { this.init = ({ compile, apply, isStreamActive }) => { this.parent = null; diff --git a/awx/ui/package.json b/awx/ui/package.json index 9c381f466f..24a42aa207 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -114,7 +114,6 @@ "codemirror": "^5.17.0", "components-font-awesome": "^4.6.1", "d3": "~3.3.13", - "has-ansi": "^3.0.0", "html-entities": "^1.2.1", "javascript-detect-element-resize": "^0.5.3", "jquery": "~2.2.4", From e4ad34fa14d8aeca2f1f1a44165dda4677bce86c Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 3 Apr 2018 22:45:03 -0400 Subject: [PATCH 170/379] update smoke test for new job results view --- awx/ui/test/e2e/tests/smoke.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/test/e2e/tests/smoke.js b/awx/ui/test/e2e/tests/smoke.js index 5ef4a4ebfd..931ef9f6d5 100644 --- a/awx/ui/test/e2e/tests/smoke.js +++ b/awx/ui/test/e2e/tests/smoke.js @@ -296,7 +296,7 @@ module.exports = { client.waitForElementVisible('div.spinny'); client.waitForElementNotVisible('div.spinny'); - client.waitForElementVisible('.JobResults-detailsPanel'); + client.waitForElementVisible('at-job-details'); client.waitForElementNotPresent(running, 60000); client.waitForElementVisible(success, 60000); From 379e2226fa1c3b7d95e5649cfeeafeedb868facc Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 3 Apr 2018 22:46:24 -0400 Subject: [PATCH 171/379] rename search tag test --- .../e2e/tests/test-searchez.js => test-search-tag-add-remove.js | 2 -- 1 file changed, 2 deletions(-) rename awx/ui/test/e2e/tests/test-searchez.js => test-search-tag-add-remove.js (97%) diff --git a/awx/ui/test/e2e/tests/test-searchez.js b/test-search-tag-add-remove.js similarity index 97% rename from awx/ui/test/e2e/tests/test-searchez.js rename to test-search-tag-add-remove.js index 9442ac66c0..db491712e9 100644 --- a/awx/ui/test/e2e/tests/test-searchez.js +++ b/test-search-tag-add-remove.js @@ -2,8 +2,6 @@ import { range } from 'lodash'; import { getAdminMachineCredential } from '../fixtures'; -// AWX_E2E_URL='https://localhost:3000' npm --prefix awx/ui run e2e -- --filter="*jobz*" - const spinny = 'div.spinny'; const searchInput = 'smart-search input'; const searchSubmit = 'smart-search i[class*="search"]'; From 356defff09d61fb1241dafcd4b496141dddc5d84 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 3 Apr 2018 22:53:30 -0400 Subject: [PATCH 172/379] remove unused lib code --- .../lib/components/code/events.directive.js | 104 --------------- .../lib/components/code/events.partial.html | 0 .../components/code/menu-bottom.directive.js | 41 ------ .../components/code/menu-bottom.partial.html | 7 -- .../lib/components/code/menu-top.directive.js | 50 -------- .../lib/components/code/menu-top.partial.html | 12 -- .../lib/components/code/stdout.directive.js | 105 ---------------- .../lib/components/code/stdout.partial.html | 19 --- awx/ui/client/lib/components/index.js | 8 -- awx/ui/test/e2e/tests/test-jobz.js | 118 ------------------ 10 files changed, 464 deletions(-) delete mode 100644 awx/ui/client/lib/components/code/events.directive.js delete mode 100644 awx/ui/client/lib/components/code/events.partial.html delete mode 100644 awx/ui/client/lib/components/code/menu-bottom.directive.js delete mode 100644 awx/ui/client/lib/components/code/menu-bottom.partial.html delete mode 100644 awx/ui/client/lib/components/code/menu-top.directive.js delete mode 100644 awx/ui/client/lib/components/code/menu-top.partial.html delete mode 100644 awx/ui/client/lib/components/code/stdout.directive.js delete mode 100644 awx/ui/client/lib/components/code/stdout.partial.html delete mode 100644 awx/ui/test/e2e/tests/test-jobz.js diff --git a/awx/ui/client/lib/components/code/events.directive.js b/awx/ui/client/lib/components/code/events.directive.js deleted file mode 100644 index 8426bf3ae6..0000000000 --- a/awx/ui/client/lib/components/code/events.directive.js +++ /dev/null @@ -1,104 +0,0 @@ -import Ansi from 'ansi-to-html'; -import hasAnsi from 'has-ansi'; - -const templateUrl = require('~components/code/events.partial.html'); - -let $sce; -let $timeout; -let ansi; - -function atOutputEventLink (scope, element, attrs, controller) { - controller.init(scope, element); -} - -function AtOutputEventController (_$sce_, _$timeout_) { - const vm = this || {}; - - $timeout = _$timeout_; - $sce = _$sce_; - ansi = new Ansi(); - - let scope; - let element; - - vm.init = (_scope_, _element_) => { - scope = _scope_; - element = _element_; - - scope.$watch('state.stdout', curr => { - if (!curr) { - return; - } - - render(scope.state.stdout); - }); - }; - - vm.scroll = position => { - const container = element.find('.at-Stdout-container')[0]; - - if (position === 'bottom') { - container.scrollTop = container.scrollHeight; - } else { - container.scrollTop = 0; - } - }; -} - -AtOutputEventController.$inject = [ - '$sce', - '$timeout', -]; - -function render (stdout) { - const html = $sce.trustAsHtml(parseStdout(stdout)); - - $timeout(() => { - const table = $('#atStdoutTBody'); - - table.html($sce.getTrustedHtml(html)); - }); -} - -function parseStdout (stdout) { - const lines = stdout.split('\r\n'); - - let ln = 0; - - return lines.reduce((html, line) => { - ln++; - - return `${html}${createRow(ln, line)}`; - }, ''); -} - -function createRow (ln, content) { - content = content || ''; - - if (hasAnsi(content)) { - content = ansi.toHtml(content); - } - - return ` - - ${ln} - ${content} - `; -} -function atOutputEvent () { - return { - restrict: 'E', - transclude: true, - replace: true, - require: 'atOutputEvent', - templateUrl, - controller: AtOutputEventController, - controllerAs: 'vm', - link: atOutputEventLink, - scope: { - state: '=', - } - }; -} - -export default atOutputEvent; diff --git a/awx/ui/client/lib/components/code/events.partial.html b/awx/ui/client/lib/components/code/events.partial.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/awx/ui/client/lib/components/code/menu-bottom.directive.js b/awx/ui/client/lib/components/code/menu-bottom.directive.js deleted file mode 100644 index 5b15a7bc10..0000000000 --- a/awx/ui/client/lib/components/code/menu-bottom.directive.js +++ /dev/null @@ -1,41 +0,0 @@ -const templateUrl = require('~components/code/menu-bottom.partial.html'); - -function atCodeMenuBottomLink (scope, element, attrs, controller) { - controller.init(scope, element); -} - -function AtCodeMenuBottomController () { - const vm = this || {}; - - let element; - - vm.init = (_scope_, _element_) => { - element = _element_; - }; - - vm.scroll = () => { - const container = element.find('.at-Stdout-container')[0]; - - container.scrollTop = container.scrollHeight; - }; -} - -AtCodeMenuBottomController.$inject = []; - -function atCodeMenuBottom () { - return { - restrict: 'E', - transclude: true, - replace: true, - require: 'atCodeMenuBottom', - templateUrl, - controller: AtCodeMenuBottomController, - controllerAs: 'vm', - link: atCodeMenuBottomLink, - scope: { - state: '=', - } - }; -} - -export default atCodeMenuBottom; diff --git a/awx/ui/client/lib/components/code/menu-bottom.partial.html b/awx/ui/client/lib/components/code/menu-bottom.partial.html deleted file mode 100644 index 1a0717a360..0000000000 --- a/awx/ui/client/lib/components/code/menu-bottom.partial.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- -
- -
-
diff --git a/awx/ui/client/lib/components/code/menu-top.directive.js b/awx/ui/client/lib/components/code/menu-top.directive.js deleted file mode 100644 index e78ead1bae..0000000000 --- a/awx/ui/client/lib/components/code/menu-top.directive.js +++ /dev/null @@ -1,50 +0,0 @@ -const templateUrl = require('~components/code/menu-top.partial.html'); - -function atCodeMenuTopLink (scope, element, attrs, controller) { - controller.init(scope, element); -} - -function AtCodeMenuTopController () { - const vm = this || {}; - - let element; - let scope; - - vm.init = (_scope_, _element_) => { - scope = _scope_; - element = _element_; - - scope.state.isExpanded = scope.state.isExpanded || false; - }; - - vm.scroll = () => { - const container = element.parent().find('.at-Stdout-container')[0]; - - container.scrollTop = 0; - }; - - vm.expand = () => { - scope.state.isExpanded = !scope.state.isExpanded; - scope.state.expand(); - }; -} - -AtCodeMenuTopController.$inject = []; - -function atCodeMenuTop () { - return { - restrict: 'E', - transclude: true, - replace: true, - require: 'atCodeMenuTop', - templateUrl, - controller: AtCodeMenuTopController, - controllerAs: 'vm', - link: atCodeMenuTopLink, - scope: { - state: '=', - } - }; -} - -export default atCodeMenuTop; diff --git a/awx/ui/client/lib/components/code/menu-top.partial.html b/awx/ui/client/lib/components/code/menu-top.partial.html deleted file mode 100644 index 2e21dc1b8b..0000000000 --- a/awx/ui/client/lib/components/code/menu-top.partial.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
- -
- -
- -
- -
-
diff --git a/awx/ui/client/lib/components/code/stdout.directive.js b/awx/ui/client/lib/components/code/stdout.directive.js deleted file mode 100644 index c73e90b7e8..0000000000 --- a/awx/ui/client/lib/components/code/stdout.directive.js +++ /dev/null @@ -1,105 +0,0 @@ -import Ansi from 'ansi-to-html'; -import hasAnsi from 'has-ansi'; - -const templateUrl = require('~components/code/stdout.partial.html'); - -let $sce; -let $timeout; -let ansi; - -function atCodeStdoutLink (scope, element, attrs, controller) { - controller.init(scope, element); -} - -function AtCodeStdoutController (_$sce_, _$timeout_) { - const vm = this || {}; - - $timeout = _$timeout_; - $sce = _$sce_; - ansi = new Ansi(); - - let scope; - let element; - - vm.init = (_scope_, _element_) => { - scope = _scope_; - element = _element_; - - scope.$watch('state.stdout', curr => { - if (!curr) { - return; - } - - render(scope.state.stdout); - }); - }; - - vm.scroll = position => { - const container = element.find('.at-Stdout-container')[0]; - - if (position === 'bottom') { - container.scrollTop = container.scrollHeight; - } else { - container.scrollTop = 0; - } - }; -} - -AtCodeStdoutController.$inject = [ - '$sce', - '$timeout', -]; - -function render (stdout) { - const html = $sce.trustAsHtml(parseStdout(stdout)); - - $timeout(() => { - const table = $('#atStdoutTBody'); - - table.html($sce.getTrustedHtml(html)); - }); -} - -function parseStdout (stdout) { - const lines = stdout.split('\r\n'); - - let ln = 0; - - return lines.reduce((html, line) => { - ln++; - - return `${html}${createRow(ln, line)}`; - }, ''); -} - -function createRow (ln, content) { - content = content || ''; - - if (hasAnsi(content)) { - content = ansi.toHtml(content); - } - - return ` - - ${ln} - ${content} - `; -} - -function atCodeStdout () { - return { - restrict: 'E', - transclude: true, - replace: true, - require: 'atCodeStdout', - templateUrl, - controller: AtCodeStdoutController, - controllerAs: 'vm', - link: atCodeStdoutLink, - scope: { - state: '=', - } - }; -} - -export default atCodeStdout; diff --git a/awx/ui/client/lib/components/code/stdout.partial.html b/awx/ui/client/lib/components/code/stdout.partial.html deleted file mode 100644 index ca38d736f4..0000000000 --- a/awx/ui/client/lib/components/code/stdout.partial.html +++ /dev/null @@ -1,19 +0,0 @@ -
-
-
- -
- -
-
- -
- -
-
- -
- -
-
-
diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index bf45c63a6d..9ac933628c 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -1,10 +1,6 @@ import atLibServices from '~services'; import actionGroup from '~components/action/action-group.directive'; -import codeMenuBottom from '~components/code/menu-bottom.directive'; -import codeMenuTop from '~components/code/menu-top.directive'; -import codeEvents from '~components/code/events.directive'; -import codeStdout from '~components/code/stdout.directive'; import divider from '~components/utility/divider.directive'; import form from '~components/form/form.directive'; import formAction from '~components/form/action.directive'; @@ -49,10 +45,6 @@ angular atLibServices ]) .directive('atActionGroup', actionGroup) - .directive('atCodeEvents', codeEvents) - .directive('atCodeMenuBottom', codeMenuBottom) - .directive('atCodeMenuTop', codeMenuTop) - .directive('atCodeStdout', codeStdout) .directive('atDivider', divider) .directive('atForm', form) .directive('atFormAction', formAction) diff --git a/awx/ui/test/e2e/tests/test-jobz.js b/awx/ui/test/e2e/tests/test-jobz.js deleted file mode 100644 index 9f76fc7bec..0000000000 --- a/awx/ui/test/e2e/tests/test-jobz.js +++ /dev/null @@ -1,118 +0,0 @@ -import uuid from 'uuid'; - -import { - get, - post, -} from '../api'; -import { - getAdminMachineCredential, - getInventory, - getOrCreate, - getOrganization, - waitForJob, -} from '../fixtures'; - -// AWX_E2E_URL='https://localhost:3000' npm --prefix awx/ui run e2e -- --filter="*jobz*" - -const session = `e2e-${uuid().substr(0, 8)}`; - -const SCM_URL = 'https://github.com/jakemcdermott/ansible-playbooks'; -const PLAYBOOK = 'setfact_50.yml'; -const PARAMS = '?job_event_search=page_size:200;order_by:start_line;not__event__in:playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats;task:set'; - -let data; - -const waitForJobz = endpoint => { - const interval = 2000; - const statuses = ['successful', 'failed', 'error', 'canceled']; - - let attempts = 20; - - return new Promise((resolve, reject) => { - (function pollStatus () { - get(endpoint).then(update => { - const completed = statuses.indexOf(update.data.status) > -1; - - if (completed) { - return resolve(update.data); - } - - if (--attempts <= 0) { - return reject(new Error('Retry limit exceeded.')); - } - - return setTimeout(pollStatus, interval); - }); - }()); - }); -}; - -const getProject = (namespace = session) => getOrganization(namespace) - .then(organization => getOrCreate('/projects/', { - name: `${namespace}-project`, - description: namespace, - organization: organization.id, - scm_url: SCM_URL, - scm_type: 'git' - }) - .then(project => { - if (project.related.current_update) { - return waitForJobz(project.related.current_update) - .then(() => project); - } - return project; - })); - -const getJobTemplate = (namespace = session) => { - const promises = [ - getInventory(namespace), - getAdminMachineCredential(namespace), - getProject(namespace) - ]; - - return Promise.all(promises) - .then(([inventory, credential, project]) => getOrCreate('/job_templates/', { - name: `${namespace}-job-template`, - description: namespace, - inventory: inventory.id, - credential: credential.id, - project: project.id, - playbook: PLAYBOOK, - })); -}; - -const getJob = (namespace = session) => getJobTemplate(namespace) - .then(template => { - if (template.related.last_job) { - return waitForJobz(template.related.last_job); - } - - return post(template.related.launch, {}) - .then(res => waitForJobz(res.data.url)); - }); - -module.exports = { - before: (client, done) => { - getJob() - .then(job => { - data = { job }; - done(); - }) - }, - 'test jobz': client => { - const location = `${client.globals.launch_url}/#/jobz/playbook/${data.job.id}`; - const templates = client.page.templates(); - - client.useCss(); - client.resizeWindow(1200, 800); - client.login(); - client.waitForAngular(); - - // client.url(location); - client.url(`${location}${PARAMS}`); - - client.pause(); - - client.end(); - }, -}; From a7bcb491d761d86638ef29442b60d55d8911cf7a Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 4 Apr 2018 15:52:07 -0400 Subject: [PATCH 173/379] disable search while searching --- awx/ui/client/features/output/search.directive.js | 12 +++++++++++- awx/ui/client/features/output/search.partial.html | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/output/search.directive.js b/awx/ui/client/features/output/search.directive.js index 5945594ab1..0a688f92bb 100644 --- a/awx/ui/client/features/output/search.directive.js +++ b/awx/ui/client/features/output/search.directive.js @@ -4,6 +4,9 @@ const searchReloadOptions = { reload: true, inherit: false }; const searchKeyExamples = ['id:>1', 'task:set', 'created:>=2000-01-01']; const searchKeyFields = ['changed', 'failed', 'host_name', 'stdout', 'task', 'role', 'playbook', 'play']; +const PLACEHOLDER_RUNNING = 'CANNOT SEARCH RUNNING JOB'; +const PLACEHOLDER_DEFAULT = 'SEARCH'; + let $state; let status; let qs; @@ -35,6 +38,7 @@ function removeSearchTag (index) { const modifiedQueryset = qs.removeTermsFromQueryset(currentQueryset, searchTerm); vm.tags = getSearchTags(modifiedQueryset); + vm.disabled = true; $state.params.job_event_search = qs.encodeArr(modifiedQueryset); $state.transitionTo($state.current, $state.params, searchReloadOptions); @@ -47,6 +51,7 @@ function submitSearch () { const modifiedQueryset = qs.mergeQueryset(currentQueryset, searchInputQueryset); vm.tags = getSearchTags(modifiedQueryset); + vm.disabled = true; $state.params.job_event_search = qs.encodeArr(modifiedQueryset); $state.transitionTo($state.current, $state.params, searchReloadOptions); @@ -54,6 +59,7 @@ function submitSearch () { function clearSearch () { vm.tags = []; + vm.disabled = true; $state.params.job_event_search = ''; $state.transitionTo($state.current, $state.params, searchReloadOptions); @@ -88,9 +94,13 @@ function AtJobSearchController (_$state_, _status_, _qs_) { vm.init = scope => { vm.examples = scope.examples || searchKeyExamples; vm.fields = scope.fields || searchKeyFields; + vm.placeholder = PLACEHOLDER_DEFAULT; vm.relatedFields = scope.relatedFields || []; - scope.$watch(status.isRunning, value => { vm.disabled = value; }); + scope.$watch(status.isRunning, value => { + vm.disabled = value; + vm.placeholder = value ? PLACEHOLDER_RUNNING : PLACEHOLDER_DEFAULT; + }); }; } diff --git a/awx/ui/client/features/output/search.partial.html b/awx/ui/client/features/output/search.partial.html index af34bac344..d7acedc3d4 100644 --- a/awx/ui/client/features/output/search.partial.html +++ b/awx/ui/client/features/output/search.partial.html @@ -5,7 +5,7 @@ class="form-control at-Input" ng-class="{ 'at-Input--rejected': vm.rejected }" ng-model="vm.value" - ng-attr-placeholder="{{ vm.disabled ? 'CANNOT SEARCH RUNNING JOB' : 'SEARCH' }}" + ng-attr-placeholder="{{ vm.placeholder }}" ng-disabled="vm.disabled"> + + - - - - -
From fe58b74d1eb1a84c54d35d1daa7c3b2ab9f9dc5b Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 2 Apr 2018 11:21:11 -0700 Subject: [PATCH 175/379] Adds the host event modal to the standard out feature Removes old host modal code --- awx/ui/client/features/output/_index.less | 1 + .../features/output/details.partial.html | 1 + .../output/host-event/_index.less} | 1 + .../host-event-codemirror.partial.html | 0 .../host-event/host-event-modal.partial.html | 12 +- .../host-event/host-event-stderr.partial.html | 0 .../host-event/host-event-stdout.partial.html | 0 .../host-event/host-event.controller.js | 170 ++++++++++++++++++ .../output/host-event/host-event.route.js | 72 ++++++++ .../output/host-event/host-event.service.js | 69 +++++++ .../features/output/host-event/index.js | 26 +++ awx/ui/client/features/output/index.js | 4 +- .../client/features/output/render.service.js | 3 +- awx/ui/client/lib/theme/index.less | 1 - .../host-event/host-event.controller.js | 143 --------------- .../host-event/host-event.route.js | 66 ------- .../client/src/job-results/host-event/main.js | 20 --- awx/ui/client/src/job-results/main.js | 3 +- 18 files changed, 352 insertions(+), 240 deletions(-) rename awx/ui/client/{src/job-results/host-event/host-event.block.less => features/output/host-event/_index.less} (99%) rename awx/ui/client/{src/job-results => features/output}/host-event/host-event-codemirror.partial.html (100%) rename awx/ui/client/{src/job-results => features/output}/host-event/host-event-modal.partial.html (83%) rename awx/ui/client/{src/job-results => features/output}/host-event/host-event-stderr.partial.html (100%) rename awx/ui/client/{src/job-results => features/output}/host-event/host-event-stdout.partial.html (100%) create mode 100644 awx/ui/client/features/output/host-event/host-event.controller.js create mode 100644 awx/ui/client/features/output/host-event/host-event.route.js create mode 100644 awx/ui/client/features/output/host-event/host-event.service.js create mode 100644 awx/ui/client/features/output/host-event/index.js delete mode 100644 awx/ui/client/src/job-results/host-event/host-event.controller.js delete mode 100644 awx/ui/client/src/job-results/host-event/host-event.route.js delete mode 100644 awx/ui/client/src/job-results/host-event/main.js diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index e44be3b39f..5fa8bec237 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -1,3 +1,4 @@ +@import 'host-event/_index'; .at-Stdout { &-menuTop { color: @at-gray-848992; diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index edd92f6fe0..9417d30670 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -1,4 +1,5 @@ +
DETAILS
diff --git a/awx/ui/client/src/job-results/host-event/host-event.block.less b/awx/ui/client/features/output/host-event/_index.less similarity index 99% rename from awx/ui/client/src/job-results/host-event/host-event.block.less rename to awx/ui/client/features/output/host-event/_index.less index 6153466934..bec8548cd2 100644 --- a/awx/ui/client/src/job-results/host-event/host-event.block.less +++ b/awx/ui/client/features/output/host-event/_index.less @@ -15,6 +15,7 @@ } .HostEvent .CodeMirror{ overflow-x: hidden; + max-height: none!important; } .HostEvent-close:hover{ diff --git a/awx/ui/client/src/job-results/host-event/host-event-codemirror.partial.html b/awx/ui/client/features/output/host-event/host-event-codemirror.partial.html similarity index 100% rename from awx/ui/client/src/job-results/host-event/host-event-codemirror.partial.html rename to awx/ui/client/features/output/host-event/host-event-codemirror.partial.html diff --git a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html b/awx/ui/client/features/output/host-event/host-event-modal.partial.html similarity index 83% rename from awx/ui/client/src/job-results/host-event/host-event-modal.partial.html rename to awx/ui/client/features/output/host-event/host-event-modal.partial.html index 7da83dfb43..a79b3cde68 100644 --- a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html +++ b/awx/ui/client/features/output/host-event/host-event-modal.partial.html @@ -40,19 +40,19 @@
- - - diff --git a/awx/ui/client/src/job-results/host-event/host-event-stderr.partial.html b/awx/ui/client/features/output/host-event/host-event-stderr.partial.html similarity index 100% rename from awx/ui/client/src/job-results/host-event/host-event-stderr.partial.html rename to awx/ui/client/features/output/host-event/host-event-stderr.partial.html diff --git a/awx/ui/client/src/job-results/host-event/host-event-stdout.partial.html b/awx/ui/client/features/output/host-event/host-event-stdout.partial.html similarity index 100% rename from awx/ui/client/src/job-results/host-event/host-event-stdout.partial.html rename to awx/ui/client/features/output/host-event/host-event-stdout.partial.html diff --git a/awx/ui/client/features/output/host-event/host-event.controller.js b/awx/ui/client/features/output/host-event/host-event.controller.js new file mode 100644 index 0000000000..67105ba7a0 --- /dev/null +++ b/awx/ui/client/features/output/host-event/host-event.controller.js @@ -0,0 +1,170 @@ +function HostEventsController ( + $scope, + $state, + HostEventService, + hostEvent +) { + $scope.processEventStatus = HostEventService.processEventStatus; + $scope.processResults = processResults; + $scope.isActiveState = isActiveState; + $scope.getActiveHostIndex = getActiveHostIndex; + $scope.closeHostEvent = closeHostEvent; + + function init () { + hostEvent.event_name = hostEvent.event; + $scope.event = _.cloneDeep(hostEvent); + + // grab standard out & standard error if present from the host + // event's 'res' object, for things like Ansible modules. Small + // wrinkle in this implementation is that the stdout/stderr tabs + // should be shown if the `res` object has stdout/stderr keys, even + // if they're a blank string. The presence of these keys is + // potentially significant to a user. + if (_.has(hostEvent.event_data, 'task_action')) { + $scope.module_name = hostEvent.event_data.task_action; + } else if (!_.has(hostEvent.event_data, 'task_action')) { + $scope.module_name = 'No result found'; + } + + if (_.has(hostEvent.event_data, 'res.result.stdout')) { + if (hostEvent.event_data.res.stdout === '') { + $scope.stdout = ' '; + } else { + $scope.stdout = hostEvent.event_data.res.stdout; + } + } + + if (_.has(hostEvent.event_data, 'res.result.stderr')) { + if (hostEvent.event_data.res.stderr === '') { + $scope.stderr = ' '; + } else { + $scope.stderr = hostEvent.event_data.res.stderr; + } + } + + if (_.has(hostEvent.event_data, 'res')) { + $scope.json = hostEvent.event_data.res; + } + + if ($scope.module_name === 'debug' && + _.has(hostEvent.event_data, 'res.result.stdout')) { + $scope.stdout = hostEvent.event_data.res.result.stdout; + } + if ($scope.module_name === 'yum' && + _.has(hostEvent.event_data, 'res.results') && + _.isArray(hostEvent.event_data.res.results)) { + const event = hostEvent.event_data.res.results; + $scope.stdout = event[0];// eslint-disable-line prefer-destructuring + } + // instantiate Codemirror + if ($state.current.name === 'jobz.host-event.json') { + try { + if (_.has(hostEvent.event_data, 'res')) { + initCodeMirror( + 'HostEvent-codemirror', + JSON.stringify($scope.json, null, 4), + { name: 'javascript', json: true } + ); + resize(); + } else { + $scope.no_json = true; + } + } catch (err) { + // element with id HostEvent-codemirror is not the view + // controlled by this instance of HostEventController + } + } else if ($state.current.name === 'jobz.host-event.stdout') { + try { + resize(); + } catch (err) { + // element with id HostEvent-codemirror is not the view + // controlled by this instance of HostEventController + } + } else if ($state.current.name === 'jobz.host-event.stderr') { + try { + resize(); + } catch (err) { + // element with id HostEvent-codemirror is not the view + // controlled by this instance of HostEventController + } + } + $('#HostEvent').modal('show'); + $('.modal-content').resizable({ + minHeight: 523, + minWidth: 600 + }); + $('.modal-dialog').draggable({ + cancel: '.HostEvent-view--container' + }); + + function resize () { + if ($state.current.name === 'jobz.host-event.json') { + const editor = $('.CodeMirror')[0].CodeMirror; + const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120; + editor.setSize('100%', height); + } else if ($state.current.name === 'jobz.host-event.stdout' || $state.current.name === 'jobz.host-event.stderr') { + const height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120; + $('.HostEvent-stdout').width('100%'); + $('.HostEvent-stdout').height(height); + $('.HostEvent-stdoutContainer').height(height); + $('.HostEvent-numberColumnPreload').height(height); + } + } + + $('.modal-dialog').on('resize', resize); + + $('#HostEvent').on('hidden.bs.modal', $scope.closeHostEvent); + } + + function processResults (value) { + if (typeof value === 'object') { + return false; + } + return true; + } + + function initCodeMirror (el, data, mode) { + const container = document.getElementById(el); + const options = {}; + options.lineNumbers = true; + options.mode = mode; + options.readOnly = true; + options.scrollbarStyle = null; + const editor = CodeMirror.fromTextArea(// eslint-disable-line no-undef + container, + options + ); + editor.setSize('100%', 200); + editor.getDoc().setValue(data); + } + + function isActiveState (name) { + return $state.current.name === name; + } + + function getActiveHostIndex () { + function hostResultfilter (obj) { + return obj.id === $scope.event.id; + } + const result = $scope.hostResults.filter(hostResultfilter); + return $scope.hostResults.indexOf(result[0]); + } + + function closeHostEvent () { + // Unbind the listener so it doesn't fire when we close the modal via navigation + $('#HostEvent').off('hidden.bs.modal'); + $('#HostEvent').modal('hide'); + $state.go('jobz'); + } + $scope.init = init; + $scope.init(); +} + +HostEventsController.$inject = [ + '$scope', + '$state', + 'HostEventService', + 'hostEvent', +]; + +module.exports = HostEventsController; diff --git a/awx/ui/client/features/output/host-event/host-event.route.js b/awx/ui/client/features/output/host-event/host-event.route.js new file mode 100644 index 0000000000..06f3eeac51 --- /dev/null +++ b/awx/ui/client/features/output/host-event/host-event.route.js @@ -0,0 +1,72 @@ +const HostEventModalTemplate = require('~features/output/host-event/host-event-modal.partial.html'); +const HostEventCodeMirrorTemplate = require('~features/output/host-event/host-event-codemirror.partial.html'); +const HostEventStdoutTemplate = require('~features/output/host-event/host-event-stdout.partial.html'); +const HostEventStderrTemplate = require('~features/output/host-event/host-event-stderr.partial.html'); + +function exit () { + // close the modal + // using an onExit event to handle cases where the user navs away + // using the url bar / back and not modal "X" + $('#HostEvent').modal('hide'); + // hacky way to handle user browsing away via URL bar + $('.modal-backdrop').remove(); + $('body').removeClass('modal-open'); +} + +function HostEventResolve (HostEventService, $stateParams) { + return HostEventService.getRelatedJobEvents($stateParams.id, { + id: $stateParams.eventId + }).then((response) => response.data.results[0]); +} + +HostEventResolve.$inject = [ + 'HostEventService', + '$stateParams', +]; + +const hostEventModal = { + name: 'jobz.host-event', + url: '/host-event/:eventId', + controller: 'HostEventsController', + templateUrl: HostEventModalTemplate, + abstract: false, + ncyBreadcrumb: { + skip: true + }, + resolve: { + hostEvent: HostEventResolve + }, + onExit: exit +}; + +const hostEventJson = { + name: 'jobz.host-event.json', + url: '/json', + controller: 'HostEventsController', + templateUrl: HostEventCodeMirrorTemplate, + ncyBreadcrumb: { + skip: true + }, +}; + +const hostEventStdout = { + name: 'jobz.host-event.stdout', + url: '/stdout', + controller: 'HostEventsController', + templateUrl: HostEventStdoutTemplate, + ncyBreadcrumb: { + skip: true + }, +}; + +const hostEventStderr = { + name: 'jobz.host-event.stderr', + url: '/stderr', + controller: 'HostEventsController', + templateUrl: HostEventStderrTemplate, + ncyBreadcrumb: { + skip: true + }, +}; + +export { hostEventJson, hostEventModal, hostEventStdout, hostEventStderr }; diff --git a/awx/ui/client/features/output/host-event/host-event.service.js b/awx/ui/client/features/output/host-event/host-event.service.js new file mode 100644 index 0000000000..a1e6952725 --- /dev/null +++ b/awx/ui/client/features/output/host-event/host-event.service.js @@ -0,0 +1,69 @@ +function HostEventService ( + Rest, + ProcessErrors, + GetBasePath, + $rootScope +) { + // GET events related to a job run + // e.g. + // ?event=playbook_on_stats + // ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter + this.getRelatedJobEvents = (id, params) => { + let url = GetBasePath('jobs'); + url = `${url}${id}/job_events/?${this.stringifyParams(params)}`; + Rest.setUrl(url); + return Rest.get() + .then(response => response) + .catch(({ data, status }) => { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: `Call to ${url}. GET returned: ${status}` }); + }); + }; + + this.stringifyParams = params => { + function reduceFunction (result, value, key) { + return `${result}${key}=${value}&`; + } + return _.reduce(params, reduceFunction, ''); + }; + + // Generate a helper class for job_event statuses + // the stack for which status to display is + // unreachable > failed > changed > ok + // uses the API's runner events and convenience properties .failed .changed to determine status. + // see: job_event_callback.py for more filters to support + this.processEventStatus = event => { + const obj = {}; + if (event.event === 'runner_on_unreachable') { + obj.class = 'HostEvent-status--unreachable'; + obj.status = 'unreachable'; + } + // equiv to 'runner_on_error' && 'runner on failed' + if (event.failed) { + obj.class = 'HostEvent-status--failed'; + obj.status = 'failed'; + } + // catch the changed case before ok, because both can be true + if (event.changed) { + obj.class = 'HostEvent-status--changed'; + obj.status = 'changed'; + } + if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok') { + obj.class = 'HostEvent-status--ok'; + obj.status = 'ok'; + } + if (event.event === 'runner_on_skipped') { + obj.class = 'HostEvent-status--skipped'; + obj.status = 'skipped'; + } + return obj; + }; +} + +HostEventService.$inject = [ + 'Rest', + 'ProcessErrors', + 'GetBasePath', + '$rootScope' +]; +export default HostEventService; diff --git a/awx/ui/client/features/output/host-event/index.js b/awx/ui/client/features/output/host-event/index.js new file mode 100644 index 0000000000..f00b0b36b4 --- /dev/null +++ b/awx/ui/client/features/output/host-event/index.js @@ -0,0 +1,26 @@ +import { + hostEventModal, + hostEventJson, + hostEventStdout, + hostEventStderr +} from './host-event.route'; +import controller from './host-event.controller'; +import service from './host-event.service'; + +const MODULE_NAME = 'hostEvents'; + +function hostEventRun ($stateExtender) { + $stateExtender.addState(hostEventModal); + $stateExtender.addState(hostEventJson); + $stateExtender.addState(hostEventStdout); + $stateExtender.addState(hostEventStderr); +} +hostEventRun.$inject = [ + '$stateExtender' +]; + +angular.module(MODULE_NAME, []) + .controller('HostEventsController', controller) + .service('HostEventService', service) + .run(hostEventRun); +export default MODULE_NAME; diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index e1727c3dc5..9542b072ad 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -12,6 +12,7 @@ import StatusService from '~features/output/status.service'; import DetailsDirective from '~features/output/details.directive'; import SearchDirective from '~features/output/search.directive'; import StatsDirective from '~features/output/stats.directive'; +import HostEvent from './host-event/index'; const Template = require('~features/output/index.view.html'); @@ -211,7 +212,8 @@ JobsRun.$inject = ['$stateRegistry']; angular .module(MODULE_NAME, [ atLibModels, - atLibComponents + atLibComponents, + HostEvent ]) .service('JobStrings', Strings) .service('JobPageService', PageService) diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 351d038387..12ca797b32 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -169,7 +169,7 @@ function JobRenderService ($q, $sce, $window) { } if (current.isHost) { - tdEvent = `${content}`; + tdEvent = `${content}`; } if (current.time && current.line === ln) { @@ -251,6 +251,7 @@ function JobRenderService ($q, $sce, $window) { }); this.compile = html => { + html = $(this.el); this.hooks.compile(html); return this.requestAnimationFrame(); diff --git a/awx/ui/client/lib/theme/index.less b/awx/ui/client/lib/theme/index.less index 9a9f564840..ec42e88a45 100644 --- a/awx/ui/client/lib/theme/index.less +++ b/awx/ui/client/lib/theme/index.less @@ -81,7 +81,6 @@ @import '../../src/inventories-hosts/inventories/inventories.block.less'; @import '../../src/inventories-hosts/shared/associate-groups/associate-groups.block.less'; @import '../../src/inventories-hosts/shared/associate-hosts/associate-hosts.block.less'; -@import '../../src/job-results/host-event/host-event.block.less'; @import '../../src/job-results/host-status-bar/host-status-bar.block.less'; @import '../../src/job-results/job-results-stdout/job-results-stdout.block.less'; @import '../../src/job-results/job-results.block.less'; diff --git a/awx/ui/client/src/job-results/host-event/host-event.controller.js b/awx/ui/client/src/job-results/host-event/host-event.controller.js deleted file mode 100644 index 330e581189..0000000000 --- a/awx/ui/client/src/job-results/host-event/host-event.controller.js +++ /dev/null @@ -1,143 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - - export default - ['$scope', '$state', 'jobResultsService', 'hostEvent', - function($scope, $state, jobResultsService, hostEvent){ - - $scope.processEventStatus = jobResultsService.processEventStatus; - $scope.processResults = function(value){ - if (typeof value === 'object'){return false;} - else {return true;} - }; - - var initCodeMirror = function(el, data, mode){ - var container = document.getElementById(el); - var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line - lineNumbers: true, - mode: mode, - readOnly: true, - scrollbarStyle: null - }); - editor.setSize("100%", 200); - editor.getDoc().setValue(data); - }; - /*ignore jslint end*/ - $scope.isActiveState = function(name){ - return $state.current.name === name; - }; - - $scope.getActiveHostIndex = function(){ - var result = $scope.hostResults.filter(function( obj ) { - return obj.id === $scope.event.id; - }); - return $scope.hostResults.indexOf(result[0]); - }; - - $scope.closeHostEvent = function() { - // Unbind the listener so it doesn't fire when we close the modal via navigation - $('#HostEvent').off('hidden.bs.modal'); - $('#HostEvent').modal('hide'); - $state.go('jobResult'); - }; - - var init = function(){ - hostEvent.event_name = hostEvent.event; - $scope.event = _.cloneDeep(hostEvent); - - // grab standard out & standard error if present from the host - // event's "res" object, for things like Ansible modules. Small - // wrinkle in this implementation is that the stdout/stderr tabs - // should be shown if the `res` object has stdout/stderr keys, even - // if they're a blank string. The presence of these keys is - // potentially significant to a user. - try{ - $scope.module_name = hostEvent.event_data.task_action || "No result found"; - $scope.stdout = hostEvent.event_data.res.stdout ? hostEvent.event_data.res.stdout : hostEvent.event_data.res.stdout === "" ? " " : undefined; - $scope.stderr = hostEvent.event_data.res.stderr ? hostEvent.event_data.res.stderr : hostEvent.event_data.res.stderr === "" ? " " : undefined; - $scope.json = hostEvent.event_data.res; - } - catch(err){ - // do nothing, no stdout/stderr for this module - } - if($scope.module_name === "debug" && - _.has(hostEvent.event_data, "res.result.stdout")){ - $scope.stdout = hostEvent.event_data.res.result.stdout; - } - if($scope.module_name === "yum" && - _.has(hostEvent.event_data, "res.results") && - _.isArray(hostEvent.event_data.res.results)){ - $scope.stdout = hostEvent.event_data.res.results[0]; - } - // instantiate Codemirror - // try/catch pattern prevents the abstract-state controller from complaining about element being null - if ($state.current.name === 'jobResult.host-event.json'){ - try{ - if(_.has(hostEvent.event_data, "res")){ - initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true}); - resize(); - } - else{ - $scope.no_json = true; - } - - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - else if ($state.current.name === 'jobResult.host-event.stdout'){ - try{ - resize(); - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - else if ($state.current.name === 'jobResult.host-event.stderr'){ - try{ - resize(); - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - $('#HostEvent').modal('show'); - $('.modal-content').resizable({ - minHeight: 523, - minWidth: 600 - }); - $('.modal-dialog').draggable({ - cancel: '.HostEvent-view--container' - }); - - function resize(){ - if ($state.current.name === 'jobResult.host-event.json'){ - let editor = $('.CodeMirror')[0].CodeMirror; - let height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120; - editor.setSize("100%", height); - } - else if($state.current.name === 'jobResult.host-event.stdout' || $state.current.name === 'jobResult.host-event.stderr'){ - let height = $('.modal-dialog').height() - $('.HostEvent-header').height() - $('.HostEvent-details').height() - $('.HostEvent-nav').height() - $('.HostEvent-controls').height() - 120; - $(".HostEvent-stdout").width("100%"); - $(".HostEvent-stdout").height(height); - $(".HostEvent-stdoutContainer").height(height); - $(".HostEvent-numberColumnPreload").height(height); - } - - } - - $('.modal-dialog').on('resize', function(){ - resize(); - }); - - $('#HostEvent').on('hidden.bs.modal', function () { - $scope.closeHostEvent(); - }); - }; - init(); - }]; diff --git a/awx/ui/client/src/job-results/host-event/host-event.route.js b/awx/ui/client/src/job-results/host-event/host-event.route.js deleted file mode 100644 index e0a32ad7e4..0000000000 --- a/awx/ui/client/src/job-results/host-event/host-event.route.js +++ /dev/null @@ -1,66 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import { templateUrl } from '../../shared/template-url/template-url.factory'; - -var hostEventModal = { - name: 'jobResult.host-event', - url: '/host-event/:eventId', - controller: 'HostEventController', - templateUrl: templateUrl('job-results/host-event/host-event-modal'), - 'abstract': false, - ncyBreadcrumb: { - skip: true - }, - resolve: { - hostEvent: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) { - return jobResultsService.getRelatedJobEvents($stateParams.id, { - id: $stateParams.eventId - }).then((response) => response.data.results[0]); - }] - }, - onExit: function() { - // close the modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - $('#HostEvent').modal('hide'); - // hacky way to handle user browsing away via URL bar - $('.modal-backdrop').remove(); - $('body').removeClass('modal-open'); - } -}; - -var hostEventJson = { - name: 'jobResult.host-event.json', - url: '/json', - controller: 'HostEventController', - templateUrl: templateUrl('job-results/host-event/host-event-codemirror'), - ncyBreadcrumb: { - skip: true - }, -}; - -var hostEventStdout = { - name: 'jobResult.host-event.stdout', - url: '/stdout', - controller: 'HostEventController', - templateUrl: templateUrl('job-results/host-event/host-event-stdout'), - ncyBreadcrumb: { - skip: true - }, -}; - -var hostEventStderr = { - name: 'jobResult.host-event.stderr', - url: '/stderr', - controller: 'HostEventController', - templateUrl: templateUrl('job-results/host-event/host-event-stderr'), - ncyBreadcrumb: { - skip: true - }, -}; - - -export { hostEventJson, hostEventModal, hostEventStdout, hostEventStderr }; diff --git a/awx/ui/client/src/job-results/host-event/main.js b/awx/ui/client/src/job-results/host-event/main.js deleted file mode 100644 index 76832b45e5..0000000000 --- a/awx/ui/client/src/job-results/host-event/main.js +++ /dev/null @@ -1,20 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - import {hostEventModal, - hostEventJson, hostEventStdout, hostEventStderr} from './host-event.route'; - import controller from './host-event.controller'; - - export default - angular.module('jobResults.hostEvent', []) - .controller('HostEventController', controller) - - .run(['$stateExtender', function($stateExtender){ - $stateExtender.addState(hostEventModal); - $stateExtender.addState(hostEventJson); - $stateExtender.addState(hostEventStdout); - $stateExtender.addState(hostEventStderr); - }]); diff --git a/awx/ui/client/src/job-results/main.js b/awx/ui/client/src/job-results/main.js index f0aedc7c43..f48ac081d8 100644 --- a/awx/ui/client/src/job-results/main.js +++ b/awx/ui/client/src/job-results/main.js @@ -6,7 +6,6 @@ import hostStatusBar from './host-status-bar/main'; import jobResultsStdOut from './job-results-stdout/main'; -import hostEvent from './host-event/main'; import route from './job-results.route.js'; @@ -17,7 +16,7 @@ import eventQueueService from './event-queue.service'; import parseStdoutService from './parse-stdout.service'; export default - angular.module('jobResults', [hostStatusBar.name, jobResultsStdOut.name, hostEvent.name, 'angularMoment']) + angular.module('jobResults', [hostStatusBar.name, jobResultsStdOut.name, 'angularMoment']) .run(['$stateExtender', function($stateExtender) { $stateExtender.addState(route); }]) From b44c7127f71a0a2cde514f8375e52176c16f8ae1 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 4 Apr 2018 20:11:53 -0400 Subject: [PATCH 176/379] reactivate linter for dev server --- awx/ui/build/webpack.watch.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index c764ec4b85..5bf1aad89c 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -20,18 +20,16 @@ const watch = { output: { filename: OUTPUT }, - /* - *module: { - * rules: [ - * { - * test: /\.js$/, - * enforce: 'pre', - * exclude: /node_modules/, - * loader: 'eslint-loader' - * } - * ] - *}, - */ + module: { + rules: [ + { + test: /\.js$/, + enforce: 'pre', + exclude: /node_modules/, + loader: 'eslint-loader' + } + ] + }, plugins: [ new HtmlWebpackHarddiskPlugin(), new HardSourceWebpackPlugin({ From 939666f17288ac729ab2628b5c3b1dde4ef662fa Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 4 Apr 2018 20:13:04 -0400 Subject: [PATCH 177/379] add polyfills for phantomjs --- awx/ui/test/spec/karma.spec.js | 1 + awx/ui/test/spec/polyfills.js | 31 +++++++++++++++++++++++++++++++ awx/ui/test/unit/karma.unit.js | 1 + awx/ui/test/unit/polyfills.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 awx/ui/test/spec/polyfills.js create mode 100644 awx/ui/test/unit/polyfills.js diff --git a/awx/ui/test/spec/karma.spec.js b/awx/ui/test/spec/karma.spec.js index abff3172f5..ae2dc8899d 100644 --- a/awx/ui/test/spec/karma.spec.js +++ b/awx/ui/test/spec/karma.spec.js @@ -13,6 +13,7 @@ module.exports = config => { frameworks: ['jasmine'], reporters: ['progress', 'junit'], files:[ + './polyfills.js', path.join(SRC_PATH, '**/*.html'), path.join(SRC_PATH, 'vendor.js'), path.join(NODE_MODULES, 'angular-mocks/angular-mocks.js'), diff --git a/awx/ui/test/spec/polyfills.js b/awx/ui/test/spec/polyfills.js new file mode 100644 index 0000000000..8b7342e5fd --- /dev/null +++ b/awx/ui/test/spec/polyfills.js @@ -0,0 +1,31 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill + +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} diff --git a/awx/ui/test/unit/karma.unit.js b/awx/ui/test/unit/karma.unit.js index 5ccd1fe151..d839aff5f2 100644 --- a/awx/ui/test/unit/karma.unit.js +++ b/awx/ui/test/unit/karma.unit.js @@ -14,6 +14,7 @@ module.exports = config => { browsers: ['PhantomJS'], reporters: ['progress', 'junit'], files: [ + './polyfills.js', path.join(SRC_PATH, 'vendor.js'), path.join(SRC_PATH, 'app.js'), path.join(SRC_PATH, '**/*.html'), diff --git a/awx/ui/test/unit/polyfills.js b/awx/ui/test/unit/polyfills.js new file mode 100644 index 0000000000..25b18055bd --- /dev/null +++ b/awx/ui/test/unit/polyfills.js @@ -0,0 +1,33 @@ +/* eslint-disable */ + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill + +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} From 01d9c8546e31f12d5de5ba7769ea87e8053586ca Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 4 Apr 2018 23:55:53 -0400 Subject: [PATCH 178/379] fix team, credential, and workflow copy regressions --- awx/ui/client/features/credentials/index.js | 2 +- awx/ui/client/features/output/index.js | 4 ++-- awx/ui/client/lib/models/Base.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js index 2fa094ef9f..e3a1ea7734 100644 --- a/awx/ui/client/features/credentials/index.js +++ b/awx/ui/client/features/credentials/index.js @@ -11,7 +11,7 @@ function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, O const id = $stateParams.credential_id; const promises = { - me: new Me('get').then((me) => me.extend('admin_of_organizations')) + me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations')) }; if (!id) { diff --git a/awx/ui/client/features/output/index.js b/awx/ui/client/features/output/index.js index 9542b072ad..3aaadc2553 100644 --- a/awx/ui/client/features/output/index.js +++ b/awx/ui/client/features/output/index.js @@ -80,10 +80,10 @@ function resolveResource ( const promises = [model.getStats()]; if (model.has('related.labels')) { - promises.push(model.extend('labels')); + promises.push(model.extend('get', 'labels')); } - promises.push(model.extend(related, config)); + promises.push(model.extend('get', related, config)); return Promise.all(promises); }) diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 3aa507ce10..912d9a984c 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -353,8 +353,8 @@ function has (method, keys) { return value !== undefined && value !== null; } -function extend (related, config = {}) { - const req = this.parseRequestConfig('GET', config); +function extend (method, related, config = {}) { + const req = this.parseRequestConfig(method.toUpperCase(), config); if (_.get(config, 'params.page_size')) { this.page.size = config.params.page_size; From cf68df41d57f78f30248a60a496ec6af32f059ca Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 5 Apr 2018 00:51:45 -0400 Subject: [PATCH 179/379] remove unused code --- awx/ui/client/features/output/_index.less | 251 ++++++ awx/ui/client/lib/theme/index.less | 5 +- awx/ui/client/src/app.js | 22 - .../src/job-results/event-queue.service.js | 77 -- .../host-status-bar.block.less | 87 -- .../host-status-bar.directive.js | 47 -- .../host-status-bar.partial.html | 30 - .../src/job-results/host-status-bar/main.js | 11 - .../job-results-stdout.block.less | 271 ------ .../job-results-stdout.directive.js | 415 --------- .../job-results-stdout.partial.html | 65 -- .../job-results/job-results-stdout/main.js | 11 - .../src/job-results/job-results.block.less | 248 ------ .../src/job-results/job-results.controller.js | 784 ------------------ .../src/job-results/job-results.partial.html | 566 ------------- .../src/job-results/job-results.route.js | 187 ----- .../src/job-results/job-results.service.js | 269 ------ awx/ui/client/src/job-results/main.js | 26 - .../src/job-results/parse-stdout.service.js | 293 ------- .../adhoc/standard-out-adhoc.partial.html | 147 ---- .../adhoc/standard-out-adhoc.route.js | 36 - .../standard-out-inventory-sync.partial.html | 152 ---- .../standard-out-inventory-sync.route.js | 38 - awx/ui/client/src/standard-out/log/main.js | 10 - .../log/standard-out-log.controller.js | 202 ----- .../log/standard-out-log.directive.js | 47 -- .../log/standard-out-log.partial.html | 8 - awx/ui/client/src/standard-out/main.js | 22 - .../standard-out-management-jobs.partial.html | 84 -- .../standard-out-management-jobs.route.js | 36 - .../standard-out-scm-update.partial.html | 113 --- .../standard-out-scm-update.route.js | 38 - .../delete-job.factory.js | 145 ---- .../lookup-name.factory.js | 36 - .../standard-out-factories/main.js | 13 - .../standard-out/standard-out.controller.js | 266 ------ .../standard-out.block.less | 2 +- .../job-results.controller-test.js | 701 ---------------- .../job-results/job-results.service-test.js | 50 -- .../job-results/parse-stdout.service-test.js | 212 ----- 40 files changed, 253 insertions(+), 5770 deletions(-) delete mode 100644 awx/ui/client/src/job-results/event-queue.service.js delete mode 100644 awx/ui/client/src/job-results/host-status-bar/host-status-bar.block.less delete mode 100644 awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js delete mode 100644 awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html delete mode 100644 awx/ui/client/src/job-results/host-status-bar/main.js delete mode 100644 awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less delete mode 100644 awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js delete mode 100644 awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html delete mode 100644 awx/ui/client/src/job-results/job-results-stdout/main.js delete mode 100644 awx/ui/client/src/job-results/job-results.block.less delete mode 100644 awx/ui/client/src/job-results/job-results.controller.js delete mode 100644 awx/ui/client/src/job-results/job-results.partial.html delete mode 100644 awx/ui/client/src/job-results/job-results.route.js delete mode 100644 awx/ui/client/src/job-results/job-results.service.js delete mode 100644 awx/ui/client/src/job-results/main.js delete mode 100644 awx/ui/client/src/job-results/parse-stdout.service.js delete mode 100644 awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.partial.html delete mode 100644 awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js delete mode 100644 awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html delete mode 100644 awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js delete mode 100644 awx/ui/client/src/standard-out/log/main.js delete mode 100644 awx/ui/client/src/standard-out/log/standard-out-log.controller.js delete mode 100644 awx/ui/client/src/standard-out/log/standard-out-log.directive.js delete mode 100644 awx/ui/client/src/standard-out/log/standard-out-log.partial.html delete mode 100644 awx/ui/client/src/standard-out/main.js delete mode 100644 awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html delete mode 100644 awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js delete mode 100644 awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html delete mode 100644 awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js delete mode 100644 awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js delete mode 100644 awx/ui/client/src/standard-out/standard-out-factories/lookup-name.factory.js delete mode 100644 awx/ui/client/src/standard-out/standard-out-factories/main.js delete mode 100644 awx/ui/client/src/standard-out/standard-out.controller.js rename awx/ui/client/src/{standard-out => workflow-results}/standard-out.block.less (99%) delete mode 100644 awx/ui/test/spec/job-results/job-results.controller-test.js delete mode 100644 awx/ui/test/spec/job-results/job-results.service-test.js delete mode 100644 awx/ui/test/spec/job-results/parse-stdout.service-test.js diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index 5fa8bec237..71228d20e7 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -281,3 +281,254 @@ background-color: @default-err; } + +// Job Details --------------------------------------------------------------------------------- + +@breakpoint-md: 1200px; + +.JobResults { + .OnePlusTwo-container(100%, @breakpoint-md); + + &.fullscreen { + .JobResults-rightSide { + max-width: 100%; + } + } +} + +.JobResults-leftSide { + .OnePlusTwo-left--panel(100%, @breakpoint-md); + max-width: 30%; + height: ~"calc(100vh - 177px)"; + + @media screen and (max-width: @breakpoint-md) { + max-width: 100%; + } +} + +.JobResults-rightSide { + .OnePlusTwo-right--panel(100%, @breakpoint-md); + height: ~"calc(100vh - 177px)"; + + @media (max-width: @breakpoint-md - 1px) { + padding-right: 15px; + } +} + +.JobResults-detailsPanel{ + overflow-y: scroll; +} + +.JobResults-stdoutActionButton--active { + display: none; + visibility: hidden; + flex:none; + width:0px; + padding-right: 0px; +} + +.JobResults-panelHeader { + display: flex; + height: 30px; +} + +.JobResults-panelHeaderText { + color: @default-interface-txt; + flex: 1 0 auto; + font-size: 14px; + font-weight: bold; + margin-right: 10px; + text-transform: uppercase; +} + +.JobResults-panelHeaderButtonActions { + display: flex; +} + +.JobResults-resultRow { + width: 100%; + display: flex; + padding-bottom: 10px; + flex-wrap: wrap; +} + +.JobResults-resultRow--variables { + flex-direction: column; + + #cm-variables-container { + width: 100%; + } +} + +.JobResults-resultRowLabel { + text-transform: uppercase; + color: @default-interface-txt; + font-size: 12px; + font-weight: normal!important; + width: 30%; + margin-right: 20px; + + @media screen and (max-width: @breakpoint-md) { + flex: 2.5 0 auto; + } +} + +.JobResults-resultRowLabel--fullWidth { + width: 100%; + margin-right: 0px; +} + +.JobResults-resultRowText { + width: ~"calc(70% - 20px)"; + flex: 1 0 auto; + text-transform: none; + word-wrap: break-word; +} + +.JobResults-resultRowText--fullWidth { + width: 100%; +} + +.JobResults-expandArrow { + color: #D7D7D7; + font-size: 14px; + font-weight: bold; + margin-right: 10px; + text-transform: uppercase; + margin-left: 10px; +} + +.JobResults-resultRowText--instanceGroup { + display: flex; +} + +.JobResults-isolatedBadge { + align-items: center; + background-color: @default-list-header-bg; + border-radius: 5px; + color: @default-stdout-txt; + display: flex; + font-size: 10px; + height: 16px; + margin: 3px 0 0 10px; + padding: 0 10px; + text-transform: uppercase; +} + +.JobResults-statusResultIcon { + padding-left: 0px; + padding-right: 10px; +} + +.JobResults-badgeRow { + display: flex; + align-items: center; + margin-right: 5px; +} + +.JobResults-badgeTitle{ + color: @default-interface-txt; + font-size: 14px; + margin-right: 10px; + font-weight: normal; + text-transform: uppercase; + margin-left: 20px; +} + +@media (max-width: @breakpoint-md) { + .JobResults-detailsPanel { + overflow-y: auto; + } + + .JobResults-rightSide { + height: inherit; + } +} + +.JobResults-timeBadge { + float:right; + font-size: 11px; + font-weight: normal; + padding: 1px 10px; + height: 14px; + margin: 3px 15px; + width: 80px; + background-color: @default-bg; + border-radius: 5px; + color: @default-interface-txt; + margin-right: -5px; +} + +.JobResults-panelRight { + display: flex; + flex-direction: column; +} + +.JobResults-panelRight .SmartSearch-bar { + width: 100%; +} + +.JobResults-panelRightTitle{ + flex-wrap: wrap; +} + +.JobResults-panelRightTitleText{ + word-wrap: break-word; + word-break: break-all; + max-width: 100%; +} + +.JobResults-badgeAndActionRow{ + display:flex; + flex: 1 0 auto; + justify-content: flex-end; + flex-wrap: wrap; + max-width: 100%; +} + +.StandardOut-panelHeader { + flex: initial; +} + +.StandardOut-panelHeader--jobIsRunning { + margin-bottom: 20px; +} + +host-status-bar { + flex: initial; + margin-bottom: 20px; +} + +smart-search { + flex: initial; +} + +job-results-standard-out { + flex: 1; + flex-basis: auto; + height: ~"calc(100% - 800px)"; + display: flex; + border: 1px solid @d7grey; + border-radius: 5px; + margin-top: 20px; +} +@media screen and (max-width: @breakpoint-md) { + job-results-standard-out { + height: auto; + } +} + +.JobResults-extraVarsHelp { + margin-left: 10px; + color: @default-icon; +} + +.JobResults-seeMoreLess { + color: #337AB7; + margin: 4px 0px; + text-transform: uppercase; + padding: 2px 0px; + cursor: pointer; + border-radius: 5px; + font-size: 11px; +} \ No newline at end of file diff --git a/awx/ui/client/lib/theme/index.less b/awx/ui/client/lib/theme/index.less index ec42e88a45..a0f7738272 100644 --- a/awx/ui/client/lib/theme/index.less +++ b/awx/ui/client/lib/theme/index.less @@ -81,9 +81,6 @@ @import '../../src/inventories-hosts/inventories/inventories.block.less'; @import '../../src/inventories-hosts/shared/associate-groups/associate-groups.block.less'; @import '../../src/inventories-hosts/shared/associate-hosts/associate-hosts.block.less'; -@import '../../src/job-results/host-status-bar/host-status-bar.block.less'; -@import '../../src/job-results/job-results-stdout/job-results-stdout.block.less'; -@import '../../src/job-results/job-results.block.less'; @import '../../src/job-submission/job-submission.block.less'; @import '../../src/license/license.block.less'; @import '../../src/login/loginModal/thirdPartySignOn/thirdPartySignOn.block.less'; @@ -116,7 +113,7 @@ @import '../../src/shared/text-label'; @import '../../src/shared/upgrade/upgrade.block.less'; @import '../../src/smart-status/smart-status.block.less'; -@import '../../src/standard-out/standard-out.block.less'; +@import '../../src/workflow-results/standard-out.block.less'; @import '../../src/system-tracking/date-picker/date-picker.block.less'; @import '../../src/system-tracking/fact-data-table/fact-data-table.block.less'; @import '../../src/system-tracking/fact-module-filter.block.less'; diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 7399b6d130..0eec0fa356 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -19,7 +19,6 @@ import credentialTypes from './credential-types/main'; import organizations from './organizations/main'; import managementJobs from './management-jobs/main'; import workflowResults from './workflow-results/main'; -import jobResults from './job-results/main'; import jobSubmission from './job-submission/main'; import notifications from './notifications/main'; import about from './about/main'; @@ -30,7 +29,6 @@ import configuration from './configuration/main'; import home from './home/main'; import login from './login/main'; import activityStream from './activity-stream/main'; -import standardOut from './standard-out/main'; import Templates from './templates/main'; import teams from './teams/main'; import users from './users/main'; @@ -67,7 +65,6 @@ angular 'gettext', 'Timezones', 'lrInfiniteScroll', - about.name, access.name, license.name, @@ -86,10 +83,8 @@ angular login.name, activityStream.name, workflowResults.name, - jobResults.name, jobSubmission.name, notifications.name, - standardOut.name, Templates.name, portalMode.name, teams.name, @@ -242,23 +237,6 @@ angular $rootScope.crumbCache = []; $transitions.onStart({}, function(trans) { - // Remove any lingering intervals - // except on jobResults.* states - var jobResultStates = [ - 'jobResult', - 'jobResult.host-summary', - 'jobResult.host-event.details', - 'jobResult.host-event.json', - 'jobResult.host-events', - 'jobResult.host-event.stdout' - ]; - if ($rootScope.jobResultInterval && !_.includes(jobResultStates, trans.to().name) ) { - window.clearInterval($rootScope.jobResultInterval); - } - if ($rootScope.jobStdOutInterval && !_.includes(jobResultStates, trans.to().name) ) { - window.clearInterval($rootScope.jobStdOutInterval); - } - $rootScope.flashMessage = null; $('#form-modal2 .modal-body').empty(); diff --git a/awx/ui/client/src/job-results/event-queue.service.js b/awx/ui/client/src/job-results/event-queue.service.js deleted file mode 100644 index c97861ac6b..0000000000 --- a/awx/ui/client/src/job-results/event-queue.service.js +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************* -* Copyright (c) 2016 Ansible, Inc. -* -* All Rights Reserved -*************************************************/ - -export default ['jobResultsService', 'parseStdoutService', function(jobResultsService, parseStdoutService){ - var val = {}; - - val = { - populateDefers: {}, - queue: {}, - // munge the raw event from the backend into the event_queue's format - munge: function(event) { - // basic data needed in the munged event - var mungedEvent = { - counter: event.counter, - id: event.id, - processed: false, - name: event.event_name, - changes: [] - }; - - // the interface for grabbing standard out is generalized and - // present across many types of events, so go ahead and check for - // updates to it - if (event.stdout) { - mungedEvent.stdout = parseStdoutService.parseStdout(event); - mungedEvent.start_line = event.start_line + 1; - mungedEvent.end_line = event.end_line + 1; - mungedEvent.actual_end_line = parseStdoutService.actualEndLine(event) + 1; - mungedEvent.changes.push('stdout'); - } - - // for different types of events, you need different types of data - if (event.event_name === 'playbook_on_start') { - mungedEvent.startTime = event.modified; - mungedEvent.changes.push('startTime'); - } if (event.event_name === 'playbook_on_stats') { - // get the data for populating the host status bar - mungedEvent.count = jobResultsService - .getCountsFromStatsEvent(event.event_data); - mungedEvent.finishedTime = event.modified; - mungedEvent.changes.push('count'); - mungedEvent.changes.push('countFinished'); - mungedEvent.changes.push('finishedTime'); - } - return mungedEvent; - }, - // reinitializes the event queue value for the job results page - initialize: function() { - val.queue = {}; - val.populateDefers = {}; - }, - // populates the event queue - populate: function(event) { - if (event) { - val.queue[event.counter] = val.munge(event); - - if (!val.queue[event.counter].processed) { - return val.munge(event); - } else { - return {}; - } - } else { - return {}; - } - }, - // the event has been processed in the view and should be marked as - // completed in the queue - markProcessed: function(event) { - val.queue[event.counter].processed = true; - } - }; - - return val; -}]; diff --git a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.block.less b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.block.less deleted file mode 100644 index ff27489751..0000000000 --- a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.block.less +++ /dev/null @@ -1,87 +0,0 @@ -.HostStatusBar { - display: flex; - flex: 0 0 auto; - width: 100%; -} - -.HostStatusBar-ok, -.HostStatusBar-changed, -.HostStatusBar-unreachable, -.HostStatusBar-failures, -.HostStatusBar-skipped, -.HostStatusBar-noData { - height: 15px; - border-top: 5px solid @default-bg; - border-bottom: 5px solid @default-bg; -} - -.HostStatusBar-ok { - background-color: @default-succ; - display: flex; - flex: 0 0 auto; -} - -.HostStatusBar-changed { - background-color: @default-warning; - flex: 0 0 auto; -} - -.HostStatusBar-unreachable { - background-color: @default-unreachable; - flex: 0 0 auto; -} - -.HostStatusBar-dark { - background-color: @default-unreachable; - flex: 0 0 auto; -} - -.HostStatusBar-failures { - background-color: @default-err; - flex: 0 0 auto; -} - -.HostStatusBar-skipped { - background-color: @default-link; - flex: 0 0 auto; -} - -.HostStatusBar-noData { - background-color: @default-icon-hov; - flex: 1 0 auto; -} - -.HostStatusBar-tooltipLabel { - text-transform: uppercase; - margin-right: 15px; -} - -.HostStatusBar-tooltipBadge { - border-radius: 5px; - border: 1px solid @default-bg; -} - -.HostStatusBar-tooltipBadge--ok { - background-color: @default-succ; -} - -.HostStatusBar-tooltipBadge--unreachable { - background-color: @default-unreachable; -} - -.HostStatusBar-tooltipBadge--dark { - background-color: @default-unreachable; -} - -.HostStatusBar-tooltipBadge--skipped { - background-color: @default-link; -} - -.HostStatusBar-tooltipBadge--changed { - background-color: @default-warning; -} - -.HostStatusBar-tooltipBadge--failures { - background-color: @default-err; - -} diff --git a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js deleted file mode 100644 index 5a8b5b3206..0000000000 --- a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -// import hostStatusBarController from './host-status-bar.controller'; -export default [ 'templateUrl', - function(templateUrl) { - return { - scope: true, - templateUrl: templateUrl('job-results/host-status-bar/host-status-bar'), - restrict: 'E', - // controller: standardOutLogController, - link: function(scope) { - // as count is changed by event data coming in, - // update the host status bar - var toDestroy = scope.$watch('count', function(val) { - if (val) { - Object.keys(val).forEach(key => { - // reposition the hosts status bar by setting - // the various flex values to the count of - // those hosts - $(`.HostStatusBar-${key}`) - .css('flex', `${val[key]} 0 auto`); - - // set the tooltip to give how many hosts of - // each type - if (val[key] > 0) { - scope[`${key}CountTip`] = `${key}${val[key]}`; - } - }); - - // if there are any hosts that have finished, don't - // show default grey bar - scope.hasCount = (Object - .keys(val) - .filter(key => (val[key] > 0)).length > 0); - } - }); - - scope.$on('$destroy', function(){ - toDestroy(); - }); - } - }; -}]; diff --git a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html deleted file mode 100644 index a8854b6d09..0000000000 --- a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.partial.html +++ /dev/null @@ -1,30 +0,0 @@ -
-
-
-
-
-
-
-
-
diff --git a/awx/ui/client/src/job-results/host-status-bar/main.js b/awx/ui/client/src/job-results/host-status-bar/main.js deleted file mode 100644 index 2b17a2e414..0000000000 --- a/awx/ui/client/src/job-results/host-status-bar/main.js +++ /dev/null @@ -1,11 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import hostStatusBar from './host-status-bar.directive'; - -export default - angular.module('hostStatusBarDirective', []) - .directive('hostStatusBar', hostStatusBar); diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less deleted file mode 100644 index d186677cd1..0000000000 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.block.less +++ /dev/null @@ -1,271 +0,0 @@ -@breakpoint-md: 1200px; - -.JobResultsStdOut { - height: auto; - width: 100%; - display: flex; - flex-direction: column; - align-items: stretch; -} - -.JobResultsStdOut-toolbar { - flex: initial; - display: flex; - border-bottom: 0px; - border-radius: 5px; - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; - user-select: none; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; -} - -.JobResultsStdOut-toolbarNumberColumn { - background-color: @default-list-header-bg; - color: @b7grey; - flex: initial; - display: flex; - justify-content: space-between; - width: 70px; - padding-bottom: 10px; - padding-left: 8px; - padding-right: 8px; - padding-top: 10px; - border-top-left-radius: 5px; - z-index: 1; - border-right: 1px solid @d7grey; -} - -.JobResultsStdOut-expandAllButton { - height: 18px; - width: 18px; - padding-left: 4px; - padding-top: 1px; - border-radius: 50%; - background-color: @default-bg; - font-size: 12px; - cursor: pointer; - color: #848992; -} - -.JobResultsStdOut-expandAllButton:hover .JobResultsStdOut-expandAllIcon, -.JobResultsStdOut-expandAllIcon:hover { - color: @default-data-txt; -} - -.JobResultsStdOut-toolbarStdoutColumn { - white-space: normal; - flex: 1; - display: flex; - justify-content: flex-end; - padding-right: 10px; - background-color: @default-secondary-bg; - border-top-right-radius: 5px; -} - -.JobResultsStdOut-followButton { - cursor: pointer; - width: 18px; - height: 18px; - width: 18px; - padding: 1px 0 0 4px; - border-radius: 50%; - margin-top: 10px; - font-size: 11px; - background-color: @default-icon; - color: @default-bg; -} - -.JobResultsStdOut-followIcon { - color: @default-bg; -} - -.JobResultsStdOut-followButton:hover { - background-color: @default-data-txt; -} - -.JobResultsStdOut-followButton.is-engaged { - background-color: @default-link; - color: @default-bg; -} - -.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon { - color: @default-bg; -} - -.JobResultsStdOut-followButton.is-engaged:hover { - background-color: @default-icon; -} - -.JobResultsStdOut-followButton.is-engaged:hover .JobResultsStdOut-followIcon, -.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon:hover { - color: @default-border; -} - -.JobResultsStdOut-stdoutContainer { - flex: 1; - position: relative; - background-color: @default-secondary-bg; - overflow-y: scroll; - overflow-x: hidden; -} - -.JobResultsStdOut-numberColumnPreload { - background-color: @default-list-header-bg; - border-right: 1px solid @d7grey; - position: absolute; - height: 100%; - width: 70px; -} - -.JobResultsStdOut-aLineOfStdOut { - display: flex; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; -} - -.JobResultsStdOut-lineExpander { - text-align: left; - padding-left: 11px; - margin-right: auto; -} - -.JobResultsStdOut-lineExpanderIcon { - font-size: 19px; - cursor: pointer; -} - -.JobResultsStdOut-lineExpanderIcon:hover { - color: @default-data-txt; -} - -.JobResultsStdOut-lineNumberColumn { - display: flex; - background-color: @default-list-header-bg; - text-align: right; - padding-right: 10px; - padding-top: 2px; - padding-bottom: 2px; - width: 75px; - flex: 1 0 70px; - user-select: none; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - z-index: 1; - border-right: 1px solid @d7grey; - color: @default-icon; -} - -.JobResultsStdOut-stdoutColumn { - padding-left: 20px; - padding-right: 20px; - padding-top: 2px; - padding-bottom: 2px; - color: @default-interface-txt; - display: inline-block; - white-space: pre-wrap; - word-break: break-all; - width:100%; - background-color: @default-secondary-bg; -} - -.JobResultsStdOut-stdoutColumn--tooMany { - font-weight: bold; - text-transform: uppercase; - color: @default-err; -} - -.JobResultsStdOut-stdoutColumn--clickable { - cursor: pointer; -} - -.JobResultsStdOut-aLineOfStdOut:hover, -.JobResultsStdOut-aLineOfStdOut:hover .JobResultsStdOut-lineNumberColumn, -.JobResultsStdOut-aLineOfStdOut:hover .JobResultsStdOut-stdoutColumn { - background-color: @default-bg; -} - -.JobResultsStdOut-aLineOfStdOut:hover .JobResultsStdOut-lineNumberColumn { - border-right: 1px solid @default-bg; -} - -.JobResultsStdOut-footer { - height: 20px; - border-bottom-right-radius: 5px; - border-bottom-left-radius: 5px; - background-color: @default-secondary-bg; - border-top: 0px; - border-radius: 5px; - border-top-left-radius: 0px; - border-top-right-radius: 0px; - overflow: hidden; - margin-top: -1px; -} - -.JobResultsStdOut-footerNumberColumn { - background-color: @default-list-header-bg; - width: 70px; - height: 100%; - border-right: 1px solid @d7grey; -} - -.JobResultsStdOut-followAnchor { - height: 0px; -} - -.JobResultsStdOut-toTop { - color: @default-icon; - cursor: pointer; - font-family: monaco; - font-size: 10px; - margin-right: 20px; - text-align: right; - display: flex; - - span { - margin-left: auto; - } -} - -.JobResultsStdOut-toTop--numberColumn { - background: @default-list-header-bg; - height: 40px; - width: 70px; - border-right: 1px solid #D7D7D7; -} - -.JobResultsStdOut-toTop:hover { - color: @default-data-txt; -} - -.JobResultsStdOut-cappedLine { - color: @b7grey; - font-style: italic; -} - -@media (max-width: @breakpoint-md) { - .JobResultsStdOut-numberColumnPreload { - display: none; - } - - .JobResultsStdOut-topAnchor { - position: static; - width: 100%; - top: -20px; - margin-top: -250px; - margin-bottom: 250px; - } - - .JobResultsStdOut-followAnchor { - height: 0px; - } - - .JobResultsStdOut-stdoutContainer { - overflow-y: auto; - } - - .JobResultsStdOut-lineAnchor { - display: none !important; - } -} diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js deleted file mode 100644 index 14a34a607a..0000000000 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js +++ /dev/null @@ -1,415 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -// import hostStatusBarController from './host-status-bar.controller'; -export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', - function(templateUrl, $timeout, $location, $anchorScroll) { - return { - scope: false, - templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'), - restrict: 'E', - link: function(scope, element) { - var toDestroy = [], - resizer, - scrollWatcher; - - scope.$on('$destroy', function(){ - $(window).off("resize", resizer); - $(window).off("scroll", scrollWatcher); - $(".JobResultsStdOut-stdoutContainer").off('scroll', - scrollWatcher); - toDestroy.forEach(closureFunc => closureFunc()); - }); - - scope.stdoutContainerAvailable.resolve("container available"); - // utility function used to find the top visible line and - // parent header in the pane - // - // note that while this function is called when in mobile width - // the line anchor is not displayed in the css so calls - // to lineAnchor do nothing - var findTopLines = function() { - var $container = $('.JobResultsStdOut-stdoutContainer'); - - // get the first visible line's head element - var getHeadElement = function (line) { - var lineHasHeaderClass = !!(line - .hasClass("header_play") || - line.hasClass("header_task")); - var lineClassList; - var lineUUIDClass; - - if (lineHasHeaderClass) { - // find head element when the first visible - // line is a header - - lineClassList = line.attr("class") - .split(" "); - - // get the header class w task uuid... - lineUUIDClass = lineClassList - .filter(n => n - .indexOf("header_task_") > -1)[0]; - - // ...if that doesn't exist get the one - // w play uuid - if (!lineUUIDClass) { - lineUUIDClass = lineClassList - .filter(n => n. - indexOf("header_play_") > -1)[0]; - } - - // get the header line (not their might - // be more than one, so get the one with - // the actual header class) - // - // TODO it might be better in this case to just - // return `line` (less jumping with a cowsay - // case) - return $(".actual_header." + - lineUUIDClass); - } else { - // find head element when the first visible - // line is not a header - - lineClassList = line.attr("class") - .split(" "); - - // get the class w task uuid... - lineUUIDClass = lineClassList - .filter(n => n - .indexOf("task_") > -1)[0]; - - // ...if that doesn't exist get the one - // w play uuid - if (!lineUUIDClass) { - lineUUIDClass = lineClassList - .filter(n => n - .indexOf("play_") > -1)[0]; - } - - // get the header line (not their might - // be more than one, so get the one with - // the actual header class) - return $(".actual_header.header_" + - lineUUIDClass); - } - }; - - var visItem, - parentItem; - - // iterate through each line of standard out - $container.find('.JobResultsStdOut-aLineOfStdOut:visible') - .each( function () { - var $this = $(this); - - // check to see if the line is the first visible - // line in the viewport... - if ($this.position().top >= 0) { - - // ...if it is, return the line number - // for this line - visItem = parseInt($($this - .children()[0]) - .text()); - - // as well as the line number for it's - // closest parent header line - var $head = getHeadElement($this); - parentItem = parseInt($($head - .children()[0]) - .text()); - - // stop iterating over the standard out - // lines once the first one has been - // found - - $this = null; - return false; - } - - $this = null; - }); - - $container = null; - - return { - visLine: visItem, - parentVisLine: parentItem - }; - }; - - // find if window is initially mobile or desktop width - if (window.innerWidth <= 1200) { - scope.isMobile = true; - } else { - scope.isMobile = false; - } - - resizer = function() { - // and update the isMobile var accordingly - if (window.innerWidth <= 1200 && !scope.isMobile) { - scope.isMobile = true; - } else if (window.innerWidth > 1200 & scope.isMobile) { - scope.isMobile = false; - } - }; - // watch changes to the window size - $(window).resize(resizer); - - var lastScrollTop; - - var initScrollTop = function() { - lastScrollTop = 0; - }; - scrollWatcher = function() { - var st = $(this).scrollTop(); - var netScroll = st + $(this).innerHeight(); - var fullHeight; - - if (st < lastScrollTop){ - // user up scrolled, so disengage follow - scope.followEngaged = false; - } - - if (scope.isMobile) { - // for mobile the height is the body of the entire - // page - fullHeight = $("body").height(); - } else { - // for desktop the height is the body of the - // stdoutContainer, minus the "^ TOP" indicator - fullHeight = $(this)[0].scrollHeight - 25; - } - - if(netScroll >= fullHeight) { - // user scrolled all the way to bottom, so engage - // follow - scope.followEngaged = true; - } - - // pane is now overflowed, show top indicator. - if (st > 0) { - scope.stdoutOverflowed = true; - } - - lastScrollTop = st; - - st = null; - netScroll = null; - fullHeight = null; - }; - - // update scroll watchers when isMobile changes based on - // window resize - toDestroy.push(scope.$watch('isMobile', function(val) { - if (val === true) { - // make sure ^ TOP always shown for mobile - scope.stdoutOverflowed = true; - - // unbind scroll watcher on standard out container - $(".JobResultsStdOut-stdoutContainer") - .unbind('scroll'); - - // init scroll watcher on window - initScrollTop(); - $(window).on('scroll', scrollWatcher); - - } else if (val === false) { - // unbind scroll watcher on window - $(window).unbind('scroll'); - - // init scroll watcher on standard out container - initScrollTop(); - $(".JobResultsStdOut-stdoutContainer").on('scroll', - scrollWatcher); - } - })); - - // called to scroll to follow anchor - scope.followScroll = function() { - // a double-check to make sure the follow anchor is at - // the bottom of the standard out container - $(".JobResultsStdOut-followAnchor") - .appendTo(".JobResultsStdOut-stdoutContainer"); - - $location.hash('followAnchor'); - $anchorScroll(); - }; - - // called to scroll to top of standard out (when "^ TOP" is - // clicked) - scope.toTop = function() { - $location.hash('topAnchor'); - $anchorScroll(); - }; - - // called to scroll to the current line anchor - // when expand all/collapse all/filtering is engaged - // - // note that while this function can be called when in mobile - // width the line anchor is not displayed in the css so those - // calls do nothing - scope.lineAnchor = function() { - $location.hash('lineAnchor'); - $anchorScroll(); - }; - - // if following becomes active, go ahead and get to the bottom - // of the standard out pane - toDestroy.push(scope.$watch('followEngaged', function(val) { - // scroll to follow point if followEngaged is true - if (val) { - scope.followScroll(); - } - - // set up tooltip changes for not finsihed job - if (!scope.jobFinished) { - if (val) { - scope.followTooltip = "Currently following standard out as it comes in. Click to unfollow."; - } else { - scope.followTooltip = "Click to follow standard out as it comes in."; - } - } - })); - - // follow button ng-click function - scope.followToggleClicked = function() { - if (scope.jobFinished) { - // when the job is finished engage followScroll - scope.followScroll(); - } else { - // when the job is not finished toggle followEngaged - // which is watched above - scope.followEngaged = !scope.followEngaged; - } - }; - - // expand all/collapse all ng-click function - scope.toggleAllStdout = function(type) { - // find the top visible line in the container currently, - // as well as the header parent of that line - var topLines = findTopLines(); - - if (type === 'expand') { - // for expand prepend the lineAnchor to the visible - // line - $(".line_num_" + topLines.visLine) - .prepend($("#lineAnchor")); - } else { - // for collapse prepent the lineAnchor to the - // visible line's parent header - $(".line_num_" + topLines.parentVisLine) - .prepend($("#lineAnchor")); - } - - var expandClass; - if (type === 'expand') { - // for expand all, you'll need to find all the - // collapsed headers to expand them - expandClass = "fa-caret-right"; - } else { - // for collapse all, you'll need to find all the - // expanded headers to collapse them - expandClass = "fa-caret-down"; - } - - // find all applicable task headers that need to be - // toggled - element.find(".expanderizer--task."+expandClass) - .each((i, val) => { - // and trigger their expansion/collapsing - $timeout(function(){ - // TODO change to a direct call of the - // toggleLine function - angular.element(val).trigger('click'); - // TODO only call lineAnchor for those - // that are above the first visible line - scope.lineAnchor(); - }); - }); - - // find all applicable play headers that need to be - // toggled - element.find(".expanderizer--play."+expandClass) - .each((i, val) => { - // for collapse all, only collapse play - // headers that do not have children task - // headers - if(angular.element("." + - angular.element(val).attr("data-uuid")) - .find(".expanderizer--task") - .length === 0 || - type !== 'collapse') { - - // trigger their expansion/ - // collapsing - $timeout(function(){ - // TODO change to a direct - // call of the - // toggleLine function - angular.element(val) - .trigger('click'); - // TODO only call lineAnchor - // for those that are above - // the first visible line - scope.lineAnchor(); - }); - } - }); - }; - - // expand/collapse triangle ng-click function - scope.toggleLine = function($event, id) { - // if the section is currently expanded - if ($($event.currentTarget).hasClass("fa-caret-down")) { - // hide all the children lines - $(id).hide(); - - // and change the triangle for the header to collapse - $($event.currentTarget) - .removeClass("fa-caret-down"); - $($event.currentTarget) - .addClass("fa-caret-right"); - } else { - // if the section is currently collapsed - - // show all the children lines - $(id).show(); - - // and change the triangle for the header to expanded - $($event.currentTarget) - .removeClass("fa-caret-right"); - $($event.currentTarget) - .addClass("fa-caret-down"); - - // if the section you expanded is a play - if ($($event.currentTarget) - .hasClass("expanderizer--play")) { - // find all children task headers and - // expand them too - $("." + $($event.currentTarget) - .attr("data-uuid")) - .find(".expanderizer--task") - .each((i, val) => { - if ($(val) - .hasClass("fa-caret-right")) { - $timeout(function(){ - // TODO change to a - // direct call of the - // toggleLine function - angular.element(val) - .trigger('click'); - }); - } - }); - } - } - }; - } - }; -}]; diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html deleted file mode 100644 index 63ae96d90c..0000000000 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html +++ /dev/null @@ -1,65 +0,0 @@ -
-
-
-
- - -
-
- - -
-
-
-
- - -
-
-
-
-
-
-
-
-
- -
-
The standard output is too large to display. Please specify additional filters to narrow the standard out.
-
Too much previous output to display. Showing running standard output.
-
Job details are not available for this job. Please download to view standard out.
-
- -
-
-
-
- -
diff --git a/awx/ui/client/src/job-results/job-results-stdout/main.js b/awx/ui/client/src/job-results/job-results-stdout/main.js deleted file mode 100644 index 5fc583b9b1..0000000000 --- a/awx/ui/client/src/job-results/job-results-stdout/main.js +++ /dev/null @@ -1,11 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import jobResultsStdOut from './job-results-stdout.directive'; - -export default - angular.module('jobResultStdOutDirective', []) - .directive('jobResultsStandardOut', jobResultsStdOut); diff --git a/awx/ui/client/src/job-results/job-results.block.less b/awx/ui/client/src/job-results/job-results.block.less deleted file mode 100644 index 356aeb5a6a..0000000000 --- a/awx/ui/client/src/job-results/job-results.block.less +++ /dev/null @@ -1,248 +0,0 @@ -@breakpoint-md: 1200px; - -.JobResults { - .OnePlusTwo-container(100%, @breakpoint-md); - - &.fullscreen { - .JobResults-rightSide { - max-width: 100%; - } - } -} - -.JobResults-leftSide { - .OnePlusTwo-left--panel(100%, @breakpoint-md); - max-width: 30%; - height: ~"calc(100vh - 177px)"; - - @media screen and (max-width: @breakpoint-md) { - max-width: 100%; - } -} - -.JobResults-rightSide { - .OnePlusTwo-right--panel(100%, @breakpoint-md); - height: ~"calc(100vh - 177px)"; - - @media (max-width: @breakpoint-md - 1px) { - padding-right: 15px; - } -} - -.JobResults-detailsPanel{ - overflow-y: scroll; -} - -.JobResults-stdoutActionButton--active { - display: none; - visibility: hidden; - flex:none; - width:0px; - padding-right: 0px; -} - -.JobResults-panelHeader { - display: flex; - height: 30px; -} - -.JobResults-panelHeaderText { - color: @default-interface-txt; - flex: 1 0 auto; - font-size: 14px; - font-weight: bold; - margin-right: 10px; - text-transform: uppercase; -} - -.JobResults-panelHeaderButtonActions { - display: flex; -} - -.JobResults-resultRow { - width: 100%; - display: flex; - padding-bottom: 10px; - flex-wrap: wrap; -} - -.JobResults-resultRow--variables { - flex-direction: column; - - #cm-variables-container { - width: 100%; - } -} - -.JobResults-resultRowLabel { - text-transform: uppercase; - color: @default-interface-txt; - font-size: 12px; - font-weight: normal!important; - width: 30%; - margin-right: 20px; - - @media screen and (max-width: @breakpoint-md) { - flex: 2.5 0 auto; - } -} - -.JobResults-resultRowLabel--fullWidth { - width: 100%; - margin-right: 0px; -} - -.JobResults-resultRowText { - width: ~"calc(70% - 20px)"; - flex: 1 0 auto; - text-transform: none; - word-wrap: break-word; -} - -.JobResults-resultRowText--fullWidth { - width: 100%; -} - -.JobResults-expandArrow { - color: #D7D7D7; - font-size: 14px; - font-weight: bold; - margin-right: 10px; - text-transform: uppercase; - margin-left: 10px; -} - -.JobResults-resultRowText--instanceGroup { - display: flex; -} - -.JobResults-isolatedBadge { - align-items: center; - background-color: @default-list-header-bg; - border-radius: 5px; - color: @default-stdout-txt; - display: flex; - font-size: 10px; - height: 16px; - margin: 3px 0 0 10px; - padding: 0 10px; - text-transform: uppercase; -} - -.JobResults-statusResultIcon { - padding-left: 0px; - padding-right: 10px; -} - -.JobResults-badgeRow { - display: flex; - align-items: center; - margin-right: 5px; -} - -.JobResults-badgeTitle{ - color: @default-interface-txt; - font-size: 14px; - margin-right: 10px; - font-weight: normal; - text-transform: uppercase; - margin-left: 20px; -} - -@media (max-width: @breakpoint-md) { - .JobResults-detailsPanel { - overflow-y: auto; - } - - .JobResults-rightSide { - height: inherit; - } -} - -.JobResults-timeBadge { - float:right; - font-size: 11px; - font-weight: normal; - padding: 1px 10px; - height: 14px; - margin: 3px 15px; - width: 80px; - background-color: @default-bg; - border-radius: 5px; - color: @default-interface-txt; - margin-right: -5px; -} - -.JobResults-panelRight { - display: flex; - flex-direction: column; -} - -.JobResults-panelRight .SmartSearch-bar { - width: 100%; -} - -.JobResults-panelRightTitle{ - flex-wrap: wrap; -} - -.JobResults-panelRightTitleText{ - word-wrap: break-word; - word-break: break-all; - max-width: 100%; -} - -.JobResults-badgeAndActionRow{ - display:flex; - flex: 1 0 auto; - justify-content: flex-end; - flex-wrap: wrap; - max-width: 100%; -} - -.StandardOut-panelHeader { - flex: initial; -} - -.StandardOut-panelHeader--jobIsRunning { - margin-bottom: 20px; -} - -host-status-bar { - flex: initial; - margin-bottom: 20px; -} - -smart-search { - flex: initial; -} - -job-results-standard-out { - flex: 1; - flex-basis: auto; - height: ~"calc(100% - 800px)"; - display: flex; - border: 1px solid @d7grey; - border-radius: 5px; - margin-top: 20px; -} -@media screen and (max-width: @breakpoint-md) { - job-results-standard-out { - height: auto; - } -} - -.JobResults-extraVarsHelp { - margin-left: 10px; - color: @default-icon; -} - -.JobResults-seeMoreLess { - color: #337AB7; - margin: 4px 0px; - text-transform: uppercase; - padding: 2px 0px; - cursor: pointer; - border-radius: 5px; - font-size: 11px; -} diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js deleted file mode 100644 index e7e8f2c716..0000000000 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ /dev/null @@ -1,784 +0,0 @@ -export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', - 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', - 'QuerySet', '$rootScope', 'moment', '$stateParams', 'i18n', 'fieldChoices', 'fieldLabels', - 'workflowResultsService', 'statusSocket', 'GetBasePath', '$state', 'jobExtraCredentials', -function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, - ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, - QuerySet, $rootScope, moment, $stateParams, i18n, fieldChoices, fieldLabels, - workflowResultsService, statusSocket, GetBasePath, $state, jobExtraCredentials) { - - var toDestroy = []; - var cancelRequests = false; - var runTimeElapsedTimer = null; - - // download stdout tooltip text - $scope.standardOutTooltip = i18n._('Download Output'); - - // stdout full screen toggle tooltip text - $scope.toggleStdoutFullscreenTooltip = i18n._("Expand Output"); - - // this allows you to manage the timing of rest-call based events as - // filters are updated. see processPage for more info - var currentContext = 1; - $scope.firstCounterFromSocket = -1; - - $scope.explanationLimit = 150; - - // if the user enters the page mid-run, reset the search to include a param - // to only grab events less than the first counter from the websocket events - toDestroy.push($scope.$watch('firstCounterFromSocket', function(counter) { - if (counter > -1) { - // make it so that the search include a counter less than the - // first counter from the socket - let params = _.cloneDeep($stateParams.job_event_search); - params.counter__lte = "" + counter; - - Dataset = QuerySet.search(jobData.related.job_events, - params); - - Dataset.then(function(actualDataset) { - $scope.job_event_dataset = actualDataset.data; - }); - } - })); - - // used for tag search - $scope.job_event_dataset = Dataset.data; - - // used for tag search - $scope.list = { - basePath: jobData.related.job_events, - name: 'job_events' - }; - - // used for tag search - $scope.job_events = $scope.job_event_dataset.results; - - $scope.jobExtraCredentials = jobExtraCredentials; - - var getLinks = function() { - var getLink = function(key) { - if(key === 'schedule') { - if($scope.job.related.schedule) { - return '/#/templates/job_template/' + $scope.job.job_template + '/schedules' + $scope.job.related.schedule.split(/api\/v\d+\/schedules/)[1]; - } - else { - return null; - } - } - else if(key === 'inventory') { - if($scope.job.summary_fields.inventory && $scope.job.summary_fields.inventory.id) { - if($scope.job.summary_fields.inventory.kind && $scope.job.summary_fields.inventory.kind === 'smart') { - return '/#/inventories/smart/' + $scope.job.summary_fields.inventory.id; - } - else { - return '/#/inventories/inventory/' + $scope.job.summary_fields.inventory.id; - } - } - else { - return null; - } - } - else { - if ($scope.job.related[key]) { - return '/#/' + $scope.job.related[key] - .split(/api\/v\d+\//)[1]; - } else { - return null; - } - } - }; - - $scope.created_by_link = getLink('created_by'); - $scope.scheduled_by_link = getLink('schedule'); - $scope.inventory_link = getLink('inventory'); - $scope.project_link = getLink('project'); - $scope.machine_credential_link = getLink('credential'); - $scope.cloud_credential_link = getLink('cloud_credential'); - $scope.network_credential_link = getLink('network_credential'); - $scope.vault_credential_link = getLink('vault_credential'); - $scope.schedule_link = getLink('schedule'); - }; - - // uses options to set scope variables to their readable string - // value - var getLabels = function() { - var getLabel = function(key) { - if ($scope.jobOptions && $scope.jobOptions[key]) { - return $scope.jobOptions[key].choices - .filter(val => val[0] === $scope.job[key]) - .map(val => val[1])[0]; - } else { - return null; - } - }; - - $scope.type_label = getLabel('job_type'); - $scope.verbosity_label = getLabel('verbosity'); - }; - - var getTotalHostCount = function(count) { - return Object - .keys(count).reduce((acc, i) => acc += count[i], 0); - }; - - // put initially resolved request data on scope - $scope.job = jobData; - $scope.jobOptions = jobDataOptions.actions.GET; - $scope.labels = jobLabels; - $scope.jobFinished = jobFinished; - - // update label in left pane and tooltip in right pane when the job_status - // changes - toDestroy.push($scope.$watch('job_status', function(status) { - if (status) { - $scope.status_label = $scope.jobOptions.status.choices - .filter(val => val[0] === status) - .map(val => val[1])[0]; - $scope.status_tooltip = "Job " + $scope.status_label; - } - })); - - $scope.previousTaskFailed = false; - - toDestroy.push($scope.$watch('job.job_explanation', function(explanation) { - if (explanation && explanation.split(":")[0] === "Previous Task Failed") { - $scope.previousTaskFailed = true; - - var taskObj = JSON.parse(explanation.substring(explanation.split(":")[0].length + 1)); - // return a promise from the options request with the permission type choices (including adhoc) as a param - var fieldChoice = fieldChoices({ - $scope: $scope, - url: GetBasePath('unified_jobs'), - field: 'type' - }); - - // manipulate the choices from the options request to be set on - // scope and be usable by the list form - fieldChoice.then(function (choices) { - choices = - fieldLabels({ - choices: choices - }); - $scope.explanation_fail_type = choices[taskObj.job_type]; - $scope.explanation_fail_name = taskObj.job_name; - $scope.explanation_fail_id = taskObj.job_id; - $scope.task_detail = $scope.explanation_fail_type + " failed for " + $scope.explanation_fail_name + " with ID " + $scope.explanation_fail_id + "."; - }); - } else { - $scope.previousTaskFailed = false; - } - })); - - - // update the job_status value. Use the cached rootScope value if there - // is one. This is a workaround when the rest call for the jobData is - // made before some socket events come in for the job status - if ($rootScope['lastSocketStatus' + jobData.id]) { - $scope.job_status = $rootScope['lastSocketStatus' + jobData.id]; - delete $rootScope['lastSocketStatus' + jobData.id]; - } else { - $scope.job_status = jobData.status; - } - - // turn related api browser routes into front end routes - getLinks(); - - // the links below can't be set in getLinks because the - // links on the UI don't directly match the corresponding URL - // on the API browser - if(jobData.summary_fields && jobData.summary_fields.job_template && - jobData.summary_fields.job_template.id){ - $scope.job_template_link = `/#/templates/job_template/${$scope.job.summary_fields.job_template.id}`; - } - if(jobData.summary_fields && jobData.summary_fields.project_update && - jobData.summary_fields.project_update.status){ - $scope.project_status = jobData.summary_fields.project_update.status; - } - if(jobData.summary_fields && jobData.summary_fields.project_update && - jobData.summary_fields.project_update.id){ - $scope.project_update_link = `/#/scm_update/${jobData.summary_fields.project_update.id}`; - } - if(jobData.summary_fields && jobData.summary_fields.source_workflow_job && - jobData.summary_fields.source_workflow_job.id){ - $scope.workflow_result_link = `/#/workflows/${jobData.summary_fields.source_workflow_job.id}`; - } - if(jobData.result_traceback) { - $scope.job.result_traceback = jobData.result_traceback.trim().split('\n').join('
'); - } - - // use options labels to manipulate display of details - getLabels(); - - // set up a read only code mirror for extra vars - $scope.variables = ParseVariableString($scope.job.extra_vars); - $scope.parseType = 'yaml'; - ParseTypeChange({ scope: $scope, - field_id: 'pre-formatted-variables', - readOnly: true }); - - // Click binding for the expand/collapse button on the standard out log - $scope.stdoutFullScreen = false; - $scope.toggleStdoutFullscreen = function() { - $scope.stdoutFullScreen = !$scope.stdoutFullScreen; - - if ($scope.stdoutFullScreen === true) { - $scope.toggleStdoutFullscreenTooltip = i18n._("Collapse Output"); - } else if ($scope.stdoutFullScreen === false) { - $scope.toggleStdoutFullscreenTooltip = i18n._("Expand Output"); - } - }; - - $scope.deleteJob = function() { - jobResultsService.deleteJob($scope.job); - }; - - $scope.cancelJob = function() { - jobResultsService.cancelJob($scope.job); - }; - - $scope.lessLabels = false; - $scope.toggleLessLabels = function() { - if (!$scope.lessLabels) { - $('#job-results-labels').slideUp(200); - $scope.lessLabels = true; - } - else { - $('#job-results-labels').slideDown(200); - $scope.lessLabels = false; - } - }; - - // get initial count from resolve - $scope.count = count.val; - $scope.hostCount = getTotalHostCount(count.val); - $scope.countFinished = count.countFinished; - - // if the job is still running engage following of the last line in the - // standard out pane - $scope.followEngaged = !$scope.jobFinished; - - // follow button for completed job should specify that the - // button will jump to the bottom of the standard out pane, - // not follow lines as they come in - if ($scope.jobFinished) { - $scope.followTooltip = i18n._("Jump to last line of standard out."); - } else { - $scope.followTooltip = i18n._("Currently following standard out as it comes in. Click to unfollow."); - } - - $scope.events = {}; - - function updateJobElapsedTimer(time) { - $scope.job.elapsed = time; - } - - // For elapsed time while a job is running, compute the differnce in seconds, - // from the time the job started until now. Moment() returns the current - // time as a moment object. - if ($scope.job.started !== null && $scope.job.status === 'running') { - runTimeElapsedTimer = workflowResultsService.createOneSecondTimer($scope.job.started, updateJobElapsedTimer); - } - - // EVENT STUFF BELOW - var linesInPane = []; - - function addToLinesInPane(event) { - var arr = _.range(event.start_line, event.actual_end_line); - linesInPane = linesInPane.concat(arr); - linesInPane = linesInPane.sort(function(a, b) { - return a - b; - }); - } - - function appendToBottom (event){ - // if we get here then the event type was either a - // header line, recap line, or one of the additional - // event types, so we append it to the bottom. - // These are the 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), - angular - .element(".JobResultsStdOut-stdoutContainer") - .append($compile(event - .stdout)($scope.events[event - .counter])); - } - - function putInCorrectPlace(event) { - if (linesInPane.length) { - for (var i = linesInPane.length - 1; i >= 0; i--) { - if (event.start_line > linesInPane[i]) { - $(`.line_num_${linesInPane[i]}`) - .after($compile(event - .stdout)($scope.events[event - .counter])); - i = -1; - } - } - } else { - appendToBottom(event); - } - } - - // This is where the async updates to the UI actually happen. - // Flow is event queue munging in the service -> $scope setting in here - var processEvent = function(event, context) { - // only care about filter context checking when the event comes - // as a rest call - if (context && context !== currentContext) { - return; - } - // put the event in the queue - var mungedEvent = eventQueue.populate(event); - - // make changes to ui based on the event returned from the queue - if (mungedEvent.changes) { - mungedEvent.changes.forEach(change => { - // we've got a change we need to make to the UI! - // update the necessary scope and make the change - if (change === 'startTime' && !$scope.job.start) { - $scope.job.start = mungedEvent.startTime; - } - - if (change === 'count' && !$scope.countFinished) { - // for all events that affect the host count, - // update the status bar as well as the host - // count badge - $scope.count = mungedEvent.count; - $scope.hostCount = getTotalHostCount(mungedEvent - .count); - } - - if (change === 'finishedTime' && !$scope.job.finished) { - $scope.job.finished = mungedEvent.finishedTime; - $scope.jobFinished = true; - $scope.followTooltip = i18n._("Jump to last line of standard out."); - if ($scope.followEngaged) { - if (!$scope.followScroll) { - $scope.followScroll = function() { - $log.error("follow scroll undefined, standard out directive not loaded yet?"); - }; - } - $scope.followScroll(); - } - } - - if (change === 'countFinished') { - // the playbook_on_stats event actually lets - // us know that we don't need to iteratively - // look at event to update the host counts - // any more. - $scope.countFinished = true; - } - - if(change === 'stdout'){ - if (!$scope.events[mungedEvent.counter]) { - // line hasn't been put in the pane yet - - // create new child scope - $scope.events[mungedEvent.counter] = $scope.$new(); - $scope.events[mungedEvent.counter] - .event = mungedEvent; - - // let's see if we have a specific place to put it in - // the pane - let $prevElem = $(`.next_is_${mungedEvent.start_line}`); - if ($prevElem && $prevElem.length) { - // if so, put it there - $(`.next_is_${mungedEvent.start_line}`) - .after($compile(mungedEvent - .stdout)($scope.events[mungedEvent - .counter])); - addToLinesInPane(mungedEvent); - } else { - var putIn; - var classList = $("div", - "
"+mungedEvent.stdout+"
") - .attr("class").split(" "); - if (classList - .filter(v => v.indexOf("task_") > -1) - .length) { - putIn = classList - .filter(v => v.indexOf("task_") > -1)[0]; - } else if(classList - .filter(v => v.indexOf("play_") > -1) - .length) { - putIn = classList - .filter(v => v.indexOf("play_") > -1)[0]; - } - - var putAfter; - var isDup = false; - - if ($(".header_" + putIn + ",." + putIn).length === 0) { - putInCorrectPlace(mungedEvent); - addToLinesInPane(mungedEvent); - } else { - $(".header_" + putIn + ",." + putIn) - .each((i, v) => { - if (angular.element(v).scope() - .event.start_line < mungedEvent - .start_line) { - putAfter = v; - } else if (angular.element(v).scope() - .event.start_line === mungedEvent - .start_line) { - isDup = true; - return false; - } else if (angular.element(v).scope() - .event.start_line > mungedEvent - .start_line) { - return false; - } else { - appendToBottom(mungedEvent); - addToLinesInPane(mungedEvent); - } - }); - } - - if (!isDup && putAfter) { - addToLinesInPane(mungedEvent); - $(putAfter).after($compile(mungedEvent - .stdout)($scope.events[mungedEvent - .counter])); - } - - - classList = null; - putIn = null; - } - - // delete ref to the elem because it might leak scope - // if you don't - $prevElem = null; - } - - // move the followAnchor to the bottom of the - // container - $(".JobResultsStdOut-followAnchor") - .appendTo(".JobResultsStdOut-stdoutContainer"); - } - }); - - // the changes have been processed in the ui, mark it in the - // queue - eventQueue.markProcessed(event); - } - }; - - $scope.stdoutContainerAvailable = $q.defer(); - $scope.hasSkeleton = $q.defer(); - - eventQueue.initialize(); - - $scope.playCount = 0; - $scope.taskCount = 0; - - - // used to show a message to just download for old jobs - // remove in 3.2.0 - $scope.isOld = 0; - $scope.showLegacyJobErrorMessage = false; - - toDestroy.push($scope.$watch('isOld', function (val) { - if (val >= 2) { - $scope.showLegacyJobErrorMessage = true; - } - })); - - // get header and recap lines - var skeletonPlayCount = 0; - var skeletonTaskCount = 0; - var getSkeleton = function(url) { - jobResultsService.getEvents(url) - .then(events => { - events.results.forEach(event => { - if (event.start_line === 0 && event.end_line === 0) { - $scope.isOld++; - } - // get the name in the same format as the data - // coming over the websocket - event.event_name = event.event; - delete event.event; - - // increment play and task count - if (event.event_name === "playbook_on_play_start") { - skeletonPlayCount++; - } else if (event.event_name === "playbook_on_task_start") { - skeletonTaskCount++; - } - - processEvent(event); - }); - if (events.next) { - getSkeleton(events.next); - } else { - // after the skeleton requests have completed, - // put the play and task count into the dom - $scope.playCount = skeletonPlayCount; - $scope.taskCount = skeletonTaskCount; - $scope.hasSkeleton.resolve("skeleton resolved"); - } - }); - }; - - $scope.stdoutContainerAvailable.promise.then(() => { - getSkeleton(jobData.related.job_events + "?order_by=start_line&or__event__in=playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats"); - }); - - var getEvents; - - var processPage = function(events, context) { - // currentContext is the context of the filter when this request - // to processPage was made - // - // currentContext is the context of the filter currently - // - // if they are not the same, make sure to stop process events/ - // making rest calls for next pages/etc. (you can see context is - // also passed into getEvents and processEvent and similar checks - // exist in these functions) - // - // also, if the page doesn't contain results (i.e.: the response - // returns an error), don't process the page - if (context !== currentContext || events === undefined || - events.results === undefined) { - return; - } - - events.results.forEach(event => { - // get the name in the same format as the data - // coming over the websocket - event.event_name = event.event; - delete event.event; - - processEvent(event, context); - }); - if (events.next && !cancelRequests) { - getEvents(events.next, context); - } else { - // put those paused events into the pane - $scope.gotPreviouslyRanEvents.resolve(""); - } - }; - - // grab non-header recap lines - getEvents = function(url, context) { - if (context !== currentContext) { - return; - } - - jobResultsService.getEvents(url) - .then(events => { - processPage(events, context); - }); - }; - - // grab non-header recap lines - toDestroy.push($scope.$watch('job_event_dataset', function(val) { - if (val) { - eventQueue.initialize(); - - Object.keys($scope.events) - .forEach(v => { - // dont destroy scope events for skeleton lines - let name = $scope.events[v].event.name; - - if (!(name === "playbook_on_play_start" || - name === "playbook_on_task_start" || - name === "playbook_on_stats")) { - $scope.events[v].$destroy(); - $scope.events[v] = null; - delete $scope.events[v]; - } - }); - - // pause websocket events from coming in to the pane - $scope.gotPreviouslyRanEvents = $q.defer(); - currentContext += 1; - - let context = currentContext; - - $( ".JobResultsStdOut-aLineOfStdOut.not_skeleton" ).remove(); - $scope.hasSkeleton.promise.then(() => { - if (val.count > parseInt(val.maxEvents)) { - $(".header_task").hide(); - $(".header_play").hide(); - $scope.standardOutTooltip = '
' + - i18n._('The output is too large to display. Please download.') + - '
' + - '
' + - '' + - '' + - '' + - '' + - '
' + - '
'; - - if ($scope.job_status === "successful" || - $scope.job_status === "failed" || - $scope.job_status === "error" || - $scope.job_status === "canceled") { - $scope.tooManyEvents = true; - $scope.tooManyPastEvents = false; - } else { - $scope.tooManyPastEvents = true; - $scope.tooManyEvents = false; - $scope.gotPreviouslyRanEvents.resolve(""); - } - } else { - $(".header_task").show(); - $(".header_play").show(); - $scope.tooManyEvents = false; - $scope.tooManyPastEvents = false; - processPage(val, context); - } - }); - } - })); - - var buffer = []; - - var processBuffer = function() { - var follow = function() { - // if follow is engaged, - // scroll down to the followAnchor - if ($scope.followEngaged) { - if (!$scope.followScroll) { - $scope.followScroll = function() { - $log.error("follow scroll undefined, standard out directive not loaded yet?"); - }; - } - $scope.followScroll(); - } - }; - - for (let i = 0; i < 4; i++) { - processEvent(buffer[i]); - buffer.splice(i, 1); - } - - follow(); - }; - - var bufferInterval; - - // Processing of job_events messages from the websocket - toDestroy.push($scope.$on(`ws-job_events-${$scope.job.id}`, function(e, data) { - if (!bufferInterval) { - bufferInterval = setInterval(function(){ - processBuffer(); - }, 500); - } - - // use the lowest counter coming over the socket to retrigger pull data - // to only be for stuff lower than that id - // - // only do this for entering the jobs page mid-run (thus the - // data.counter is 1 conditional - if (data.counter === 1) { - $scope.firstCounterFromSocket = -2; - } - - if ($scope.firstCounterFromSocket !== -2 && - $scope.firstCounterFromSocket === -1 || - data.counter < $scope.firstCounterFromSocket) { - $scope.firstCounterFromSocket = data.counter; - } - - $q.all([$scope.gotPreviouslyRanEvents.promise, - $scope.hasSkeleton.promise]).then(() => { - // put the line in the - // standard out pane (and increment play and task - // count if applicable) - if (data.event_name === "playbook_on_play_start") { - $scope.playCount++; - } else if (data.event_name === "playbook_on_task_start") { - $scope.taskCount++; - } - buffer.push(data); - }); - })); - - // get previously set up socket messages from resolve - if (statusSocket && statusSocket[0] && statusSocket[0].job_status) { - $scope.job_status = statusSocket[0].job_status; - } - if ($scope.job_status === "running" && !$scope.job.elapsed) { - runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateJobElapsedTimer); - } - - // Processing of job-status messages from the websocket - toDestroy.push($scope.$on(`ws-jobs`, function(e, data) { - if (parseInt(data.unified_job_id, 10) === - parseInt($scope.job.id,10)) { - // controller is defined, so set the job_status - $scope.job_status = data.status; - if(_.has(data, 'instance_group_name')){ - $scope.job.instance_group = true; - $scope.job.summary_fields.instance_group = { - "name": data.instance_group_name - }; - } - if (data.status === "running") { - if (!runTimeElapsedTimer) { - runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateJobElapsedTimer); - } - } else if (data.status === "successful" || - data.status === "failed" || - data.status === "error" || - data.status === "canceled") { - workflowResultsService.destroyTimer(runTimeElapsedTimer); - - // When the fob is finished retrieve the job data to - // correct anything that was out of sync from the job run - jobResultsService.getJobData($scope.job.id).then(function(data){ - $scope.job = data; - $scope.jobFinished = true; - }); - } - } else if (parseInt(data.project_id, 10) === - parseInt($scope.job.project,10)) { - // this is a project status update message, so set the - // project status in the left pane - $scope.project_status = data.status; - $scope.project_update_link = `/#/scm_update/${data - .unified_job_id}`; - } else { - // controller was previously defined, but is not yet defined - // for this job. cache the socket status on root scope - $rootScope['lastSocketStatus' + data.unified_job_id] = data.status; - } - })); - - if (statusSocket && statusSocket[1]) { - statusSocket[1](); - } - - $scope.$on('$destroy', function(){ - if (statusSocket && statusSocket[1]) { - statusSocket[1](); - } - $( ".JobResultsStdOut-aLineOfStdOut" ).remove(); - cancelRequests = true; - eventQueue.initialize(); - Object.keys($scope.events) - .forEach(v => { - $scope.events[v].$destroy(); - $scope.events[v] = null; - }); - $scope.events = {}; - workflowResultsService.destroyTimer(runTimeElapsedTimer); - if (bufferInterval) { - clearInterval(bufferInterval); - } - toDestroy.forEach(closureFunc => closureFunc()); - }); -}]; diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html deleted file mode 100644 index 9d8e1a119b..0000000000 --- a/awx/ui/client/src/job-results/job-results.partial.html +++ /dev/null @@ -1,566 +0,0 @@ -
-
-
- - -
-
- - -
-
- DETAILS -
- - -
- - - - - - - - - -
-
- - -
- - -
- -
- - {{ status_label | translate }} -
-
- - -
- -
- {{ job.job_explanation }} -
-
- {{task_detail | limitTo:explanationLimit}} - - ... - Show More - - Show Less -
-
- - -
- -
- {{ job.started | longDate }} -
-
- - -
- -
- {{ (job.finished | - longDate) || "Not Finished" }} -
-
- - -
- -
-
-
- - - - - - -
- -
- {{ type_label }} -
-
- - - - - - - - - - - - - - -
- - - -
- - -
- -
- {{ job.playbook }} -
-
- - -
- - -
- - -
- -
- - - {{ extraCredential.name }} - - {{$last ? '' : ', '}} - -
-
- - - - - - - - - - - -
- -
- {{ job.forks }} -
-
- - -
- -
- {{ job.limit }} -
-
- - -
- -
- {{ verbosity_label }} -
-
- - -
- -
- {{ job.summary_fields.instance_group.name }} - - Isolated - -
-
- - -
- -
- {{ job.job_tags }} -
-
- - -
- -
- {{ job.skip_tags }} -
-
- - -
- - -
- - -
- -
-
-
-
- {{ label }} -
-
-
-
-
- -
- -
-
- - -
-
- - -
-
- - - {{ job.name }} -
-
- -
- -
- Plays -
- - {{ playCount || 0}} - - - -
- Tasks -
- - {{ taskCount || 0}} - - - -
- Hosts -
- - {{ hostCount || 0}} - - - - - - - -
- Elapsed -
- - {{ job.elapsed * 1000 | duration: "hh:mm:ss" }} - -
- - -
- - - - - - - - - -
-
-
- - - - -
- -
-
-
diff --git a/awx/ui/client/src/job-results/job-results.route.js b/awx/ui/client/src/job-results/job-results.route.js deleted file mode 100644 index 60c06de7cd..0000000000 --- a/awx/ui/client/src/job-results/job-results.route.js +++ /dev/null @@ -1,187 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import {templateUrl} from '../shared/template-url/template-url.factory'; - -const defaultParams = { - page_size: "200", - order_by: 'start_line', - not__event__in: 'playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats' -}; - -export default { - name: 'jobResult', - url: '/jobs/{id: int}', - searchPrefix: 'job_event', - ncyBreadcrumb: { - parent: 'jobs', - label: '{{ job.id }} - {{ job.name }}' - }, - data: { - socket: { - "groups":{ - "jobs": ["status_changed", "summary"], - "job_events": [] - } - } - }, - params: { - job_event_search: { - value: defaultParams, - dynamic: true, - squash: '' - } - }, - resolve: { - statusSocket: ['$rootScope', '$stateParams', function($rootScope, $stateParams) { - var preScope = {}; - var eventOn = $rootScope.$on(`ws-jobs`, function(e, data) { - if (parseInt(data.unified_job_id, 10) === - parseInt($stateParams.id,10)) { - preScope.job_status = data.status; - } - }); - return [preScope, eventOn]; - }], - // the GET for the particular job - jobData: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) { - return jobResultsService.getJobData($stateParams.id); - }], - Dataset: ['QuerySet', '$stateParams', 'jobData', - function(qs, $stateParams, jobData) { - let path = jobData.related.job_events; - return qs.search(path, $stateParams[`job_event_search`]); - } - ], - // used to signify if job is completed or still running - jobFinished: ['jobData', function(jobData) { - if (jobData.finished) { - return true; - } else { - return false; - } - }], - // after the GET for the job, this helps us keep the status bar from - // flashing as rest data comes in. If the job is finished and - // there's a playbook_on_stats event, go ahead and resolve the count - // so you don't get that flashing! - count: ['jobData', 'jobResultsService', 'Rest', '$q', '$stateParams', '$state', function(jobData, jobResultsService, Rest, $q, $stateParams, $state) { - var defer = $q.defer(); - if (jobData.finished) { - // if the job is finished, grab the playbook_on_stats - // role to get the final count - Rest.setUrl(jobData.related.job_events + - "?event=playbook_on_stats"); - Rest.get() - .then(({data}) => { - if(!data.results[0]){ - defer.resolve({val: { - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }, countFinished: false}); - } - else { - defer.resolve({ - val: jobResultsService - .getCountsFromStatsEvent(data - .results[0].event_data), - countFinished: true}); - } - }) - .catch(() => { - defer.resolve({val: { - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }, countFinished: false}); - }); - } else { - // make sure to not include any extra - // search params for a running job (because we can't filter - // incoming job events) - if (!_.isEqual($stateParams.job_event_search, defaultParams)) { - let params = _.cloneDeep($stateParams); - params.job_event_search = defaultParams; - $state.go('.', params, { reload: true }); - } - - // job isn't finished so just send an empty count and read - // from events - defer.resolve({val: { - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }, countFinished: false}); - } - return defer.promise; - }], - // GET for the particular jobs labels to be displayed in the - // left-hand pane - jobLabels: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) { - var getNext = function(data, arr, resolve) { - Rest.setUrl(data.next); - Rest.get() - .then(({data}) => { - if (data.next) { - getNext(data, arr.concat(data.results), resolve); - } else { - resolve.resolve(arr.concat(data.results) - .map(val => val.name)); - } - }); - }; - - var seeMoreResolve = $q.defer(); - - Rest.setUrl(GetBasePath('jobs') + $stateParams.id + '/labels/'); - Rest.get() - .then(({data}) => { - if (data.next) { - getNext(data, data.results, seeMoreResolve); - } else { - seeMoreResolve.resolve(data.results - .map(val => val.name)); - } - }); - - return seeMoreResolve.promise; - }], - // OPTIONS request for the job. Used to make things like the - // verbosity data in the left-hand pane prettier than just an - // integer - jobDataOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) { - Rest.setUrl(GetBasePath('jobs') + $stateParams.id); - var val = $q.defer(); - Rest.options() - .then(function(data) { - val.resolve(data.data); - }, function(data) { - val.reject(data); - }); - return val.promise; - }], - jobExtraCredentials: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) { - Rest.setUrl(GetBasePath('jobs') + $stateParams.id + '/extra_credentials'); - var val = $q.defer(); - Rest.get() - .then(function(res) { - val.resolve(res.data.results); - }, function(res) { - val.reject(res); - }); - return val.promise; - }] - }, - templateUrl: templateUrl('job-results/job-results'), - controller: 'jobResultsController' -}; diff --git a/awx/ui/client/src/job-results/job-results.service.js b/awx/ui/client/src/job-results/job-results.service.js deleted file mode 100644 index 6b77575fff..0000000000 --- a/awx/ui/client/src/job-results/job-results.service.js +++ /dev/null @@ -1,269 +0,0 @@ -/************************************************* -* Copyright (c) 2016 Ansible, Inc. -* -* All Rights Reserved -*************************************************/ - - -export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'GetBasePath', 'Alert', '$rootScope', 'i18n', -function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, GetBasePath, Alert, $rootScope, i18n) { - var val = { - // the playbook_on_stats event returns the count data in a weird format. - // format to what we need! - getCountsFromStatsEvent: function(event_data) { - var hosts = {}, - hostsArr; - - // iterate over the event_data and populate an object with hosts - // and their status data - Object.keys(event_data).forEach(key => { - // failed passes boolean not integer - if (key === "changed" || - key === "dark" || - key === "failures" || - key === "ok" || - key === "skipped") { - // array of hosts from each type ("changed", "dark", etc.) - hostsArr = Object.keys(event_data[key]); - hostsArr.forEach(host => { - if (!hosts[host]) { - // host has not been added to hosts object - // add now - hosts[host] = {}; - } - - if (!hosts[host][key]) { - // host doesn't have key - hosts[host][key] = 0; - } - hosts[host][key] += event_data[key][host]; - }); - } - }); - - var total_hosts_by_state = { - ok: 0, - skipped: 0, - unreachable: 0, - failures: 0, - changed: 0 - }; - - // each host belongs in at most *one* of these states depending on - // the state of its tasks - _.each(hosts, function(host) { - if (host.dark > 0){ - total_hosts_by_state.unreachable++; - } else if (host.failures > 0){ - total_hosts_by_state.failures++; - } else if (host.changed > 0){ - total_hosts_by_state.changed++; - } else if (host.ok > 0){ - total_hosts_by_state.ok++; - } else if (host.skipped > 0){ - total_hosts_by_state.skipped++; - } - }); - - return total_hosts_by_state; - }, - // rest call to grab previously complete job_events - getEvents: function(url) { - var val = $q.defer(); - - Rest.setUrl(url); - Rest.get() - .then(({data}) => { - val.resolve({results: data.results, - next: data.next}); - }) - .catch(({obj, status}) => { - ProcessErrors(null, obj, status, null, { - hdr: 'Error!', - msg: `Could not get job events. - Returned status: ${status}` - }); - val.reject(obj); - }); - - return val.promise; - }, - deleteJob: function(job) { - Prompt({ - hdr: i18n._("Delete Job"), - resourceName: `#${job.id} ` + $filter('sanitize')(job.name), - body: `
- ${i18n._("Are you sure you want to delete this job?")} -
`, - action: function() { - Wait('start'); - Rest.setUrl(job.url); - Rest.destroy() - .then(() => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - $state.go('jobs'); - }) - .catch(({obj, status}) => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - ProcessErrors(null, obj, status, null, { - hdr: 'Error!', - msg: `Could not delete job. - Returned status: ${status}` - }); - }); - }, - actionText: i18n._('DELETE') - }); - }, - cancelJob: function(job) { - var doCancel = function() { - Rest.setUrl(job.url + 'cancel'); - Rest.post({}) - .then(() => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - }) - .catch(({obj, status}) => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - ProcessErrors(null, obj, status, null, { - hdr: 'Error!', - msg: `Could not cancel job. - Returned status: ${status}` - }); - }); - }; - - Prompt({ - hdr: i18n._('Cancel Job'), - resourceName: `#${job.id} ` + $filter('sanitize')(job.name), - body: `
- ${i18n._("Are you sure you want to cancel this job?")} -
`, - action: function() { - Wait('start'); - Rest.setUrl(job.url + 'cancel'); - Rest.get() - .then(({data}) => { - if (data.can_cancel === true) { - doCancel(); - } else { - $('#prompt-modal').modal('hide'); - ProcessErrors(null, data, null, null, { - hdr: 'Error!', - msg: `Job has completed, - unabled to be canceled.` - }); - } - }); - }, - actionText: i18n._('PROCEED') - }); - }, - getJobData: function(id){ - var val = $q.defer(); - - Rest.setUrl(GetBasePath('jobs') + id ); - Rest.get() - .then(function(data) { - val.resolve(data.data); - }, function(data) { - val.reject(data); - - if (data.status === 404) { - Alert('Job Not Found', 'Cannot find job.', 'alert-info'); - } else if (data.status === 403) { - Alert('Insufficient Permissions', 'You do not have permission to view this job.', 'alert-info'); - } - - $state.go('jobs'); - }); - - return val.promise; - }, - // Generate a helper class for job_event statuses - // the stack for which status to display is - // unreachable > failed > changed > ok - // uses the API's runner events and convenience properties .failed .changed to determine status. - // see: job_event_callback.py for more filters to support - processEventStatus: function(event){ - if (event.event === 'runner_on_unreachable'){ - return { - class: 'HostEvent-status--unreachable', - status: 'unreachable' - }; - } - // equiv to 'runner_on_error' && 'runner on failed' - if (event.failed){ - return { - class: 'HostEvent-status--failed', - status: 'failed' - }; - } - // catch the changed case before ok, because both can be true - if (event.changed){ - return { - class: 'HostEvent-status--changed', - status: 'changed' - }; - } - if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok'){ - return { - class: 'HostEvent-status--ok', - status: 'ok' - }; - } - if (event.event === 'runner_on_skipped'){ - return { - class: 'HostEvent-status--skipped', - status: 'skipped' - }; - } - }, - // GET events related to a job run - // e.g. - // ?event=playbook_on_stats - // ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter - getRelatedJobEvents: function(id, params){ - var url = GetBasePath('jobs'); - url = url + id + '/job_events/?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .then((response) => { - return response; - }) - .catch(({data, status}) => { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - stringifyParams: function(params){ - return _.reduce(params, (result, value, key) => { - return result + key + '=' + value + '&'; - }, ''); - }, - // the the API passes through Ansible's event_data response - // we need to massage away the verbose & redundant stdout/stderr properties - processJson: function(data){ - // configure fields to ignore - var ignored = [ - 'type', - 'event_data', - 'related', - 'summary_fields', - 'url', - 'ansible_facts', - ]; - // remove ignored properties - var result = _.chain(data).cloneDeep().forEach(function(value, key, collection){ - if (ignored.indexOf(key) > -1){ - delete collection[key]; - } - }).value(); - return result; - } - }; - return val; -}]; diff --git a/awx/ui/client/src/job-results/main.js b/awx/ui/client/src/job-results/main.js deleted file mode 100644 index f48ac081d8..0000000000 --- a/awx/ui/client/src/job-results/main.js +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import hostStatusBar from './host-status-bar/main'; -import jobResultsStdOut from './job-results-stdout/main'; - -import route from './job-results.route.js'; - -import jobResultsController from './job-results.controller'; - -import jobResultsService from './job-results.service'; -import eventQueueService from './event-queue.service'; -import parseStdoutService from './parse-stdout.service'; - -export default - angular.module('jobResults', [hostStatusBar.name, jobResultsStdOut.name, 'angularMoment']) - .run(['$stateExtender', function($stateExtender) { - $stateExtender.addState(route); - }]) - .controller('jobResultsController', jobResultsController) - .service('jobResultsService', jobResultsService) - .service('eventQueue', eventQueueService) - .service('parseStdoutService', parseStdoutService); diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js deleted file mode 100644 index 66c969b9c4..0000000000 --- a/awx/ui/client/src/job-results/parse-stdout.service.js +++ /dev/null @@ -1,293 +0,0 @@ -/************************************************* -* Copyright (c) 2016 Ansible, Inc. -* -* All Rights Reserved -*************************************************/ - -export default ['$log', 'moment', 'i18n', function($log, moment, i18n){ - var val = { - // parses stdout string from api and formats various codes to the - // correct dom structure - prettify: function(line, unstyled){ - line = line - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - - // TODO: remove once Chris's fixes to the [K lines comes in - if (line.indexOf("[K") > -1) { - $log.error(line); - } - - if(!unstyled){ - // add span tags with color styling - line = line.replace(/u001b/g, ''); - - // ansi classes - /* jshint ignore:start */ - line = line.replace(/(|)\[1;im/g, ''); - line = line.replace(/(|)\[0;30m/g, ''); - line = line.replace(/(|)\[1;30m/g, ''); - line = line.replace(/(|)\[[0,1];31m/g, ''); - line = line.replace(/(|)\[0;32m(=|)/g, ''); - line = line.replace(/(|)\[0;32m1/g, ''); - line = line.replace(/(|)\[0;33m/g, ''); - line = line.replace(/(|)\[0;34m/g, ''); - line = line.replace(/(|)\[[0,1];35m/g, ''); - line = line.replace(/(|)\[0;36m/g, ''); - line = line.replace(/()\s/g, '$1'); - - //end span - line = line.replace(/(|)\[0m/g, ''); - /* jshint ignore:end */ - } else { - // For the host event modal in the standard out tab, - // the styling isn't necessary - line = line.replace(/u001b/g, ''); - - // ansi classes - /* jshint ignore:start */ - line = line.replace(/(|)\[[0,1];3[0-9]m(1|=|)/g, ''); - line = line.replace(/()\s/g, '$1'); - - //end span - line = line.replace(/(|)\[0m/g, ''); - /* jshint ignore:end */ - } - - return line; - }, - // adds anchor tags and tooltips to host status lines - getAnchorTags: function(event){ - if(event.event_name.indexOf("runner_") === -1){ - return `"`; - } - else{ - return ` JobResultsStdOut-stdoutColumn--clickable" ui-sref="jobResult.host-event.json({eventId: ${event.id}, taskUuid: '${event.event_data.task_uuid}' })" aw-tool-tip="${i18n._("Event ID")}: ${event.id}
${i18n._("Status")}: ${event.event_display}
${i18n._("Click for details")}" data-placement="top"`; - } - - }, - // this adds classes based on event data to the - // .JobResultsStdOut-aLineOfStdOut element - getLineClasses: function(event, line, lineNum) { - var string = ""; - - if (lineNum === event.end_line) { - // used to tell you where to put stuff in the pane - string += ` next_is_${event.end_line + 1}`; - } - - if (event.event_name === "playbook_on_play_start") { - // play header classes - string += " header_play"; - string += " header_play_" + event.event_data.play_uuid; - - // give the actual header class to the line with the - // actual header info (think cowsay) - if (line.indexOf("PLAY") > -1) { - string += " actual_header"; - } - } else if (event.event_name === "playbook_on_task_start") { - // task header classes - string += " header_task"; - string += " header_task_" + event.event_data.task_uuid; - - // give the actual header class to the line with the - // actual header info (think cowsay) - if (line.indexOf("TASK") > -1 || - line.indexOf("RUNNING HANDLER") > -1) { - string += " actual_header"; - } - - // task headers also get classed by their parent play - // if applicable - if (event.event_data.play_uuid) { - string += " play_" + event.event_data.play_uuid; - } - } else if (event.event_name !== "playbook_on_stats"){ - string += " not_skeleton"; - // host status or debug line - - // these get classed by their parent play if applicable - if (event.event_data.play_uuid) { - string += " play_" + event.event_data.play_uuid; - } - // as well as their parent task if applicable - if (event.event_data.task_uuid) { - string += " task_" + event.event_data.task_uuid; - } - } - - // TODO: adding this line_num_XX class is hacky because the - // line number is availabe in children of this dom element - string += " line_num_" + lineNum; - - return string; - }, - getStartTimeBadge: function(event, line){ - // This will return a div with the badge class - // for the start time to show at the right hand - // side of each stdout header line. - // returns an empty string if not a header line - var emptySpan = "", time; - if ((event.event_name === "playbook_on_play_start" || - event.event_name === "playbook_on_task_start") && - line !== "") { - time = moment(event.created).format('HH:mm:ss'); - return `
${time}
`; - } - else if(event.event_name === "playbook_on_stats" && line.indexOf("PLAY") > -1){ - time = moment(event.created).format('HH:mm:ss'); - return `
${time}
`; - } - else { - return emptySpan; - } - - }, - // used to add expand/collapse icon next to line numbers of headers - getCollapseIcon: function(event, line) { - var clickClass, - expanderizerSpecifier; - - var emptySpan = ` -`; - - if ((event.event_name === "playbook_on_play_start" || - event.event_name === "playbook_on_task_start") && - line !== "") { - if (event.event_name === "playbook_on_play_start" && - line.indexOf("PLAY") > -1) { - // play header specific attrs - expanderizerSpecifier = "play"; - clickClass = "play_" + - event.event_data.play_uuid; - } else if (line.indexOf("TASK") > -1 || - line.indexOf("RUNNING HANDLER") > -1) { - // task header specific attrs - expanderizerSpecifier = "task"; - clickClass = "task_" + - event.event_data.task_uuid; - } else { - // header lines that don't have PLAY, TASK, - // or RUNNING HANDLER in them don't get - // expand icon. - // This provides cowsay support. - return emptySpan; - } - - - var expandDom = ` - - - -`; - return expandDom; - } else { - // non-header lines don't get an expander - return emptySpan; - } - }, - distributeColors: function(lines) { - var colorCode; - return lines.map(line => { - - if (colorCode) { - line = colorCode + line; - } - - if (line.indexOf("[0m") === -1) { - if (line.indexOf("[1;31m") > -1) { - colorCode = "[1;31m"; - } else if (line.indexOf("[1;30m") > -1) { - colorCode = "[1;30m"; - } else if (line.indexOf("[0;31m") > -1) { - colorCode = "[0;31m"; - } else if (line.indexOf("[0;32m=") > -1) { - colorCode = "[0;32m="; - } else if (line.indexOf("[0;32m1") > -1) { - colorCode = "[0;32m1"; - } else if (line.indexOf("[0;32m") > -1) { - colorCode = "[0;32m"; - } else if (line.indexOf("[0;33m") > -1) { - colorCode = "[0;33m"; - } else if (line.indexOf("[0;34m") > -1) { - colorCode = "[0;34m"; - } else if (line.indexOf("[0;35m") > -1) { - colorCode = "[0;35m"; - } else if (line.indexOf("[1;35m") > -1) { - colorCode = "[1;35m"; - } else if (line.indexOf("[0;36m") > -1) { - colorCode = "[0;36m"; - } - } else { - colorCode = null; - } - - return line; - }); - }, - getLineArr: function(event) { - let lineNums = _.range(event.start_line + 1, - event.end_line + 1); - - // hack around no-carriage return issues - if (!lineNums.length) { - lineNums = [event.start_line + 1]; - } - - let lines = event.stdout - .replace("\t", " ") - .split("\r\n"); - - if (lineNums.length > lines.length) { - lineNums = lineNums.slice(0, lines.length); - } - - lines = this.distributeColors(lines); - - // hack around no-carriage return issues - if (lineNums.length === lines.length) { - return _.zip(lineNums, lines); - } - - return _.zip(lineNums, lines).slice(0, -1); - }, - actualEndLine: function(event) { - return event.start_line + this.getLineArr(event).length; - }, - // public function that provides the parsed stdout line, given a - // job_event - parseStdout: function(event){ - // this utilizes the start/end lines and stdout blob - // to create an array in the format: - // [ - // [lineNum, lineText], - // [lineNum, lineText], - // ] - var lineArr = this.getLineArr(event); - - // this takes each `[lineNum: lineText]` element and calls the - // relevant helper functions in this service to build the - // parsed line of standard out - lineArr = lineArr - .map(lineArr => { - return ` -
-
${this.getCollapseIcon(event, lineArr[1])}${lineArr[0]}
-
-
-
-
-
-
-
- RESULTS -
-
-
- -
- - -
-
-
- -
-
Name
-
{{ job.module_name }}
-
- -
-
STATUS
-
- - {{ job.status }} -
-
- -
-
STARTED
-
- {{ job.started | longDate }} -
-
- -
-
FINISHED
-
- {{ job.finished | longDate }} -
-
- -
-
ELAPSED
-
- {{ job.elapsed }} seconds -
-
- -
-
Module Args
-
{{ job.module_args }}
-
- -
-
Inventory
- -
- -
-
Credential
- -
- -
-
Launched By
- -
- - -
-
Forks
-
{{ forks }}
-
- -
-
Limit
-
{{ limit }}
-
- - -
-
Verbosity
-
{{ verbosity }}
-
- -
-
- {{ 'Extra Variables' | translate }} - - -
-
- -
-
- -
-
-
-
-
-
-
STANDARD OUT
-
- - - - -
-
- -
-
-
-
-
diff --git a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js b/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js deleted file mode 100644 index 899a98e9f7..0000000000 --- a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import { templateUrl } from '../../shared/template-url/template-url.factory'; - -export default { - name: 'adHocJobStdout', - route: '/ad_hoc_commands/:id', - templateUrl: templateUrl('standard-out/adhoc/standard-out-adhoc'), - controller: 'JobStdoutController', - ncyBreadcrumb: { - parent: "jobs", - label: "{{ job.module_name }}" - }, - data: { - jobType: 'ad_hoc_commands', - socket: { - "groups": { - "jobs": ["status_changed", "summary"], - "ad_hoc_command_events": [] - } - } - }, - resolve: { - jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) { - Rest.setUrl(GetBasePath('base') + 'ad_hoc_commands/' + $stateParams.id + '/'); - return Rest.get() - .then(({data}) => { - return data; - }); - }] - } -}; diff --git a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html deleted file mode 100644 index 48f2d65b7e..0000000000 --- a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html +++ /dev/null @@ -1,152 +0,0 @@ -
-
-
-
-
-
-
- RESULTS -
-
- - - -
-
-
- - - -
-
STATUS
-
- - {{ job.status }} -
-
- - -
-
EXPLANATION
-
- {{task_detail | limitTo:explanationLimit}} - - ... - Show More - - Show Less -
-
- -
-
LICENSE ERROR
-
- {{ job.license_error }} -
-
- -
-
STARTED
-
- {{ job.started | longDate }} -
-
- -
-
FINISHED
-
- {{ job.finished | longDate }} -
-
- -
-
ELAPSED
-
- {{ job.elapsed }} seconds -
-
- -
-
LAUNCH TYPE
-
- {{ job.launch_type }} -
-
- -
-
CREDENTIAL
- -
- -
-
SOURCE
-
- {{ source }} -
-
- -
-
REGIONS
-
- {{ source_regions }} -
-
- -
-
OVERWRITE
-
- {{ job.overwrite }} -
-
- -
-
OVERWRITE VARS
-
- {{ job.overwrite_vars }} -
-
- -
-
-
-
-
-
-
STANDARD OUT
-
- - - - -
-
- -
-
-
-
-
diff --git a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js deleted file mode 100644 index bdd1a9a2b1..0000000000 --- a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import {templateUrl} from '../../shared/template-url/template-url.factory'; - -// TODO: figure out what this route should be - should it be inventory_sync? - -export default { - name: 'inventorySyncStdout', - route: '/inventory_sync/:id', - templateUrl: templateUrl('standard-out/inventory-sync/standard-out-inventory-sync'), - controller: 'JobStdoutController', - ncyBreadcrumb: { - parent: "jobs", - label: "{{ inventory_source_name }}" - }, - data: { - socket: { - "groups":{ - "jobs": ["status_changed", "summary"], - "inventory_update_events": [], - } - }, - jobType: 'inventory_updates' - }, - resolve: { - jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) { - Rest.setUrl(GetBasePath('base') + 'inventory_updates/' + $stateParams.id + '/'); - return Rest.get() - .then(({data}) => { - return data; - }); - }] - } -}; diff --git a/awx/ui/client/src/standard-out/log/main.js b/awx/ui/client/src/standard-out/log/main.js deleted file mode 100644 index bb97a737be..0000000000 --- a/awx/ui/client/src/standard-out/log/main.js +++ /dev/null @@ -1,10 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import standardOutLog from './standard-out-log.directive'; -export default - angular.module('standardOutLogDirective', []) - .directive('standardOutLog', standardOutLog); diff --git a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js b/awx/ui/client/src/standard-out/log/standard-out-log.controller.js deleted file mode 100644 index 728f8faedf..0000000000 --- a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js +++ /dev/null @@ -1,202 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'ProcessErrors', 'Rest', 'Wait', - function ($log, $rootScope, $scope, $state, $stateParams, ProcessErrors, Rest, Wait) { - - var api_complete = false, - current_range, - loaded_sections = [], - event_queue = 0, - auto_scroll_down=true, // programmatic scroll to bottom - live_event_processing = true, - page_size = 500, - job_id = $stateParams.id; - - $scope.should_apply_live_events = true; - - // Open up a socket for events depending on the type of job - function openSockets() { - if ($state.current.name === 'jobResult') { - $log.debug("socket watching on job_events-" + job_id); - $scope.$on(`ws-job_events-${job_id}`, function() { - $log.debug("socket fired on job_events-" + job_id); - if (api_complete) { - event_queue++; - } - }); - } - if ($state.current.name === 'adHocJobStdout') { - $log.debug("socket watching on ad_hoc_command_events-" + job_id); - $scope.$on(`ws-ad_hoc_command_events-${job_id}`, function() { - $log.debug("socket fired on ad_hoc_command_events-" + job_id); - if (api_complete) { - event_queue++; - } - }); - } - } - - openSockets(); - - // This is a trigger for loading up the standard out - if ($scope.removeLoadStdout) { - $scope.removeLoadStdout(); - } - $scope.removeLoadStdout = $scope.$on('LoadStdout', function() { - if (loaded_sections.length === 0) { - loadStdout(); - } - else if (live_event_processing) { - getNextSection(); - } - }); - - // This interval checks to see whether or not we've gotten a new - // event via sockets. If so, go out and update the standard out - // log. - $rootScope.jobStdOutInterval = setInterval( function() { - if (event_queue > 0) { - // events happened since the last check - $log.debug('checking for stdout...'); - if (loaded_sections.length === 0) { ////this if statement for refresh - $log.debug('calling LoadStdout'); - loadStdout(); - } - else if (live_event_processing) { - $log.debug('calling getNextSection'); - getNextSection(); - } - event_queue = 0; - } - }, 2000); - - // stdoutEndpoint gets passed through in the directive declaration. - // This watcher fires off loadStdout() when the endpoint becomes - // available. - $scope.$watch('stdoutEndpoint', function(newVal, oldVal) { - if(newVal && newVal !== oldVal) { - // Fire off the server call - loadStdout(); - } - }); - - // stdoutText optionall gets passed through in the directive declaration. - $scope.$watch('stdoutText', function(newVal, oldVal) { - if(newVal && newVal !== oldVal) { - $('#pre-container-content').html(newVal); - } - }); - - function loadStdout() { - if (!$scope.stdoutEndpoint) { - return; - } - - Rest.setUrl($scope.stdoutEndpoint + '?format=json&start_line=0&end_line=' + page_size); - Rest.get() - .then(({data}) => { - Wait('stop'); - if (data.content) { - api_complete = true; - $('#pre-container-content').html(data.content); - current_range = data.range; - if (data.content !== "Waiting for results...") { - loaded_sections.push({ - start: (data.range.start < 0) ? 0 : data.range.start, - end: data.range.end - }); - } - - $('#pre-container').scrollTop($('#pre-container').prop("scrollHeight")); - } - else { - api_complete = true; - } - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve stdout for job: ' + job_id + '. GET returned: ' + status }); - }); - } - - function getNextSection() { - if (!$scope.stdoutEndpoint) { - return; - } - - // get the next range of data from the API - var start = loaded_sections[loaded_sections.length - 1].end, url; - url = $scope.stdoutEndpoint + '?format=json&start_line=' + start + '&end_line=' + (start + page_size); - $('#stdoutMoreRowsBottom').fadeIn(); - Rest.setUrl(url); - Rest.get() - .then(({data}) => { - if ($('#pre-container-content').html() === "Waiting for results...") { - $('#pre-container-content').html(data.content); - } else { - $('#pre-container-content').append(data.content); - } - loaded_sections.push({ - start: (data.range.start < 0) ? 0 : data.range.start, - end: data.range.end - }); - if ($scope.should_apply_live_events) { - // if user has not disabled live event view by scrolling upward, then scroll down to the new content - current_range = data.range; - auto_scroll_down = true; // prevent auto load from happening - $('#pre-container').scrollTop($('#pre-container').prop("scrollHeight")); - } - $('#stdoutMoreRowsBottom').fadeOut(400); - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve stdout for job: ' + job_id + '. GET returned: ' + status }); - }); - } - - // lrInfiniteScroll handler - // grabs the next stdout section - $scope.stdOutGetNextSection = function(){ - if (current_range.absolute_end > current_range.end){ - var url = $scope.stdoutEndpoint + '?format=json&start_line=' + current_range.end + - '&end_line=' + (current_range.end + page_size); - Rest.setUrl(url); - Rest.get() - .then(({data}) => { - $('#pre-container-content').append(data.content); - current_range = data.range; - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve stdout for job: ' + job_id + '. GET returned: ' + status }); - }); - } - }; - - // We watch for job status changes here. If the job completes we want to clear out the - // stdout interval and kill the live_event_processing flag. - $scope.$on(`ws-jobs`, function(e, data) { - if (parseInt(data.unified_job_id, 10) === parseInt(job_id,10)) { - if (data.status === 'failed' || data.status === 'canceled' || - data.status === 'error' || data.status === 'successful') { - if ($rootScope.jobStdOutInterval) { - window.clearInterval($rootScope.jobStdOutInterval); - } - if (live_event_processing) { - if (loaded_sections.length === 0) { - loadStdout(); - } - else { - getNextSection(); - } - } - live_event_processing = false; - } - } - }); - -}]; diff --git a/awx/ui/client/src/standard-out/log/standard-out-log.directive.js b/awx/ui/client/src/standard-out/log/standard-out-log.directive.js deleted file mode 100644 index d7d0656441..0000000000 --- a/awx/ui/client/src/standard-out/log/standard-out-log.directive.js +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import standardOutLogController from './standard-out-log.controller'; -export default [ 'templateUrl', - function(templateUrl) { - return { - scope: { - stdoutEndpoint: '=', - stdoutText: '=', - jobId: '=' - }, - templateUrl: templateUrl('standard-out/log/standard-out-log'), - restrict: 'E', - controller: standardOutLogController, - link: function(scope) { - // All of our DOM related stuff will go in here - - var lastScrollTop, - direction; - - function detectDirection() { - var st = $('#pre-container').scrollTop(); - if (st > lastScrollTop) { - direction = "down"; - } else { - direction = "up"; - } - lastScrollTop = st; - return direction; - } - - $('#pre-container').bind('scroll', function() { - if (detectDirection() === "up") { - scope.should_apply_live_events = false; - } - - if ($(this).scrollTop() + $(this).height() === $(this).prop("scrollHeight")) { - scope.should_apply_live_events = true; - } - }); - } - }; -}]; diff --git a/awx/ui/client/src/standard-out/log/standard-out-log.partial.html b/awx/ui/client/src/standard-out/log/standard-out-log.partial.html deleted file mode 100644 index 45d9f70cbb..0000000000 --- a/awx/ui/client/src/standard-out/log/standard-out-log.partial.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
-
-
- -
-
diff --git a/awx/ui/client/src/standard-out/main.js b/awx/ui/client/src/standard-out/main.js deleted file mode 100644 index 1e0f451014..0000000000 --- a/awx/ui/client/src/standard-out/main.js +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import stdoutAdhocRoute from './adhoc/standard-out-adhoc.route'; -import stdoutManagementJobsRoute from './management-jobs/standard-out-management-jobs.route'; -import stdoutInventorySyncRoute from './inventory-sync/standard-out-inventory-sync.route'; -import stdoutScmUpdateRoute from './scm-update/standard-out-scm-update.route'; -import {JobStdoutController} from './standard-out.controller'; -import StandardOutHelper from './standard-out-factories/main'; -import standardOutLogDirective from './log/main'; - -export default angular.module('standardOut', [StandardOutHelper.name, standardOutLogDirective.name]) - .controller('JobStdoutController', JobStdoutController) - .run(['$stateExtender', function($stateExtender) { - $stateExtender.addState(stdoutAdhocRoute); - $stateExtender.addState(stdoutManagementJobsRoute); - $stateExtender.addState(stdoutInventorySyncRoute); - $stateExtender.addState(stdoutScmUpdateRoute); - }]); diff --git a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html deleted file mode 100644 index 8c9740a5eb..0000000000 --- a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html +++ /dev/null @@ -1,84 +0,0 @@ -
-
-
-
-
-
-
- RESULTS -
-
- - -
-
-
- -
-
NAME
-
{{ job.name }}
-
- -
-
STATUS
-
- - {{ job.status }} -
-
- -
-
STARTED
-
- {{ job.started | longDate }} -
-
- -
-
FINISHED
-
- {{ job.finished | longDate }} -
-
- -
-
ELAPSED
-
- {{ job.elapsed }} seconds -
-
- -
-
LAUNCH TYPE
-
- {{ job.launch_type }} -
-
- -
-
EXTRA VARIABLES
-
- -
- -
- -
-
-
-
-
-
-
STANDARD OUT
-
- -
-
- -
-
-
-
-
diff --git a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js deleted file mode 100644 index e3a59e884d..0000000000 --- a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import { templateUrl } from '../../shared/template-url/template-url.factory'; - -export default { - name: 'managementJobStdout', - route: '/management_jobs/:id', - templateUrl: templateUrl('standard-out/management-jobs/standard-out-management-jobs'), - controller: 'JobStdoutController', - ncyBreadcrumb: { - parent: "jobs", - label: "{{ job.name }}" - }, - data: { - jobType: 'system_jobs', - socket: { - "groups": { - "jobs": ["status_changed", "summary"], - "system_job_events": [], - } - } - }, - resolve: { - jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) { - Rest.setUrl(GetBasePath('base') + 'system_jobs/' + $stateParams.id + '/'); - return Rest.get() - .then(({data}) => { - return data; - }); - }] - } -}; diff --git a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html deleted file mode 100644 index f9828d4c02..0000000000 --- a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html +++ /dev/null @@ -1,113 +0,0 @@ -
-
-
-
-
-
-
- RESULTS -
-
- - - -
-
-
- - - -
-
STATUS
-
- - {{ job.status }} -
-
- -
-
STARTED
-
- {{ job.started | longDate }} -
-
- -
-
FINISHED
-
- {{ job.finished | longDate }} -
-
- -
-
ELAPSED
-
- {{ job.elapsed }} seconds -
-
- -
-
LAUNCH TYPE
-
- {{ job.launch_type }} -
-
- -
-
PROJECT
- -
- -
-
CREDENTIAL
- -
- -
-
-
-
-
-
-
STANDARD OUT
-
- - - - -
-
- -
-
-
-
-
diff --git a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js deleted file mode 100644 index 818509ccc7..0000000000 --- a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import { templateUrl } from '../../shared/template-url/template-url.factory'; - -// TODO: figure out what this route should be - should it be scm_update? - -export default { - name: 'scmUpdateStdout', - route: '/scm_update/:id', - templateUrl: templateUrl('standard-out/scm-update/standard-out-scm-update'), - controller: 'JobStdoutController', - ncyBreadcrumb: { - parent: "jobs", - label: "{{ project_name }}" - }, - data: { - jobType: 'project_updates', - socket: { - "groups": { - "jobs": ["status_changed", "summary"], - "project_update_events": [], - } - }, - }, - resolve: { - jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) { - Rest.setUrl(GetBasePath('base') + 'project_updates/' + $stateParams.id + '/'); - return Rest.get() - .then(({data}) => { - return data; - }); - }] - } -}; diff --git a/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js b/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js deleted file mode 100644 index 6e28362f3a..0000000000 --- a/awx/ui/client/src/standard-out/standard-out-factories/delete-job.factory.js +++ /dev/null @@ -1,145 +0,0 @@ -export default -function DeleteJob($state, Find, Rest, Wait, ProcessErrors, Prompt, Alert, - $filter, i18n) { - return function(params) { - var scope = params.scope, - id = params.id, - job = params.job, - callback = params.callback, - action, jobs, url, action_label, hdr; - - if (!job) { - if (scope.completed_jobs) { - jobs = scope.completed_jobs; - } - else if (scope.running_jobs) { - jobs = scope.running_jobs; - } - else if (scope.queued_jobs) { - jobs = scope.queued_jobs; - } - else if (scope.all_jobs) { - jobs = scope.all_jobs; - } - else if (scope.jobs) { - jobs = scope.jobs; - } - job = Find({list: jobs, key: 'id', val: id }); - } - - if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') { - url = job.related.cancel; - action_label = 'cancel'; - hdr = i18n._('Cancel'); - } else { - url = job.url; - action_label = 'delete'; - hdr = i18n._('Delete'); - } - - action = function () { - Wait('start'); - Rest.setUrl(url); - if (action_label === 'cancel') { - Rest.post() - .then(() => { - $('#prompt-modal').modal('hide'); - if (callback) { - scope.$emit(callback, action_label); - } - else { - $state.reload(); - Wait('stop'); - } - }) - .catch(({obj, status}) => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - if (status === 403) { - Alert('Error', obj.detail); - } - // Ignore the error. The job most likely already finished. - // ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + - // ' failed. POST returned status: ' + status }); - }); - } else { - Rest.destroy() - .then(() => { - $('#prompt-modal').modal('hide'); - if (callback) { - scope.$emit(callback, action_label); - } - else { - let reloadListStateParams = null; - - if(scope.jobs.length === 1 && $state.params.job_search && !_.isEmpty($state.params.job_search.page) && $state.params.job_search.page !== '1') { - reloadListStateParams = _.cloneDeep($state.params); - reloadListStateParams.job_search.page = (parseInt(reloadListStateParams.job_search.page)-1).toString(); - } - - $state.go('.', reloadListStateParams, {reload: true}); - Wait('stop'); - } - }) - .catch(({obj, status}) => { - Wait('stop'); - $('#prompt-modal').modal('hide'); - if (status === 403) { - Alert('Error', obj.detail); - } - // Ignore the error. The job most likely already finished. - //ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + - // ' failed. DELETE returned status: ' + status }); - }); - } - }; - - if (scope.removeCancelNotAllowed) { - scope.removeCancelNotAllowed(); - } - scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() { - Wait('stop'); - Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info'); - }); - - if (scope.removeCancelJob) { - scope.removeCancelJob(); - } - scope.removeCancelJob = scope.$on('CancelJob', function() { - var cancelBody = "
" + i18n._("Are you sure you want to submit the request to cancel this job?") + "
"; - var deleteBody = "
" + i18n._("Are you sure you want to delete this job?") + "
"; - Prompt({ - hdr: hdr, - resourceName: `#${job.id} ` + $filter('sanitize')(job.name), - body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody, - action: action, - actionText: (action_label === 'cancel' || job.status === 'new') ? i18n._("OK") : i18n._("DELETE") - }); - }); - - if (action_label === 'cancel') { - Rest.setUrl(url); - Rest.get() - .then(({data}) => { - if (data.can_cancel) { - scope.$emit('CancelJob'); - } - else { - scope.$emit('CancelNotAllowed'); - } - }) - .catch(({data, status}) => { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + - ' failed. GET returned: ' + status }); - }); - } - else { - scope.$emit('CancelJob'); - } - }; -} - -DeleteJob.$inject = -[ '$state', 'Find', 'Rest', 'Wait', - 'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n' -]; diff --git a/awx/ui/client/src/standard-out/standard-out-factories/lookup-name.factory.js b/awx/ui/client/src/standard-out/standard-out-factories/lookup-name.factory.js deleted file mode 100644 index 43b39da58c..0000000000 --- a/awx/ui/client/src/standard-out/standard-out-factories/lookup-name.factory.js +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - export default - ['Rest', 'ProcessErrors', 'Empty', function(Rest, ProcessErrors, Empty) { - return function(params) { - var url = params.url, - scope_var = params.scope_var, - scope = params.scope, - callback = params.callback; - Rest.setUrl(url); - Rest.get() - .then(({data}) => { - if (scope_var === 'inventory_source') { - scope.inventory = data.inventory; - } - if (!Empty(data.name)) { - scope[scope_var + '_name'] = data.name; - } - - if (callback) { - scope.$emit(callback, data); - } - }) - .catch(({data, status}) => { - if (status === 403 && params.ignore_403) { - return; - } - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve ' + url + '. GET returned: ' + status }); - }); - }; - }]; diff --git a/awx/ui/client/src/standard-out/standard-out-factories/main.js b/awx/ui/client/src/standard-out/standard-out-factories/main.js deleted file mode 100644 index 935c8dca37..0000000000 --- a/awx/ui/client/src/standard-out/standard-out-factories/main.js +++ /dev/null @@ -1,13 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import lookUpName from './lookup-name.factory'; -import DeleteJob from './delete-job.factory'; - -export default - angular.module('StandardOutHelper', []) - .factory('LookUpName', lookUpName) - .factory('DeleteJob', DeleteJob); diff --git a/awx/ui/client/src/standard-out/standard-out.controller.js b/awx/ui/client/src/standard-out/standard-out.controller.js deleted file mode 100644 index 1a707aa706..0000000000 --- a/awx/ui/client/src/standard-out/standard-out.controller.js +++ /dev/null @@ -1,266 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:JobStdout - * @description This controller's for the standard out page that can be displayed when a job runs -*/ - -export function JobStdoutController ($rootScope, $scope, $state, $stateParams, - GetBasePath, Rest, ProcessErrors, Empty, GetChoices, LookUpName, - ParseTypeChange, ParseVariableString, DeleteJob, Wait, i18n, - fieldChoices, fieldLabels, Project, Alert, InventorySource, - jobData) { - - var job_id = $stateParams.id, - jobType = $state.current.data.jobType; - - // This scope variable controls whether or not the left panel is shown and the right panel - // is expanded to take up the full screen - $scope.stdoutFullScreen = false; - $scope.toggleStdoutFullscreenTooltip = i18n._("Expand Output"); - - $scope.explanationLimit = 150; - - // Listen for job status updates that may come across via sockets. We need to check the payload - // to see whethere the updated job is the one that we're currently looking at. - $scope.$on(`ws-jobs`, function(e, data) { - if (parseInt(data.unified_job_id, 10) === parseInt(job_id,10) && $scope.job) { - $scope.job.status = data.status; - } - - if (data.status === 'failed' || data.status === 'canceled' || data.status === 'error' || data.status === 'successful') { - // Go out and refresh the job details - - Rest.setUrl(GetBasePath('base') + jobType + '/' + job_id + '/'); - Rest.get() - .then(({data}) => { - updateJobObj(data); - }); - } - }); - - $scope.previousTaskFailed = false; - - $scope.$watch('job.job_explanation', function(explanation) { - if (explanation && explanation.split(":")[0] === "Previous Task Failed") { - $scope.previousTaskFailed = true; - - var taskObj = JSON.parse(explanation.substring(explanation.split(":")[0].length + 1)); - // return a promise from the options request with the permission type choices (including adhoc) as a param - var fieldChoice = fieldChoices({ - $scope: $scope, - url: GetBasePath('unified_jobs'), - field: 'type' - }); - - // manipulate the choices from the options request to be set on - // scope and be usable by the list form - fieldChoice.then(function (choices) { - choices = - fieldLabels({ - choices: choices - }); - $scope.explanation_fail_type = choices[taskObj.job_type]; - $scope.explanation_fail_name = taskObj.job_name; - $scope.explanation_fail_id = taskObj.job_id; - $scope.task_detail = $scope.explanation_fail_type + " failed for " + $scope.explanation_fail_name + " with ID " + $scope.explanation_fail_id + "."; - }); - } else { - $scope.previousTaskFailed = false; - } - }); - - // Set the parse type so that CodeMirror knows how to display extra params YAML/JSON - $scope.parseType = 'yaml'; - - function updateJobObj(updatedJobData) { - - // Go out and get the job details based on the job type. jobType gets defined - // in the data block of the route declaration for each of the different types - // of stdout jobs. - - $scope.job = updatedJobData; - $scope.job_template_name = updatedJobData.name; - $scope.created_by = updatedJobData.summary_fields.created_by; - $scope.project_name = (updatedJobData.summary_fields.project) ? updatedJobData.summary_fields.project.name : ''; - $scope.inventory_name = (updatedJobData.summary_fields.inventory) ? updatedJobData.summary_fields.inventory.name : ''; - $scope.job_template_url = '/#/templates/' + updatedJobData.unified_job_template; - if($scope.inventory_name && updatedJobData.inventory && updatedJobData.summary_fields.inventory && updatedJobData.summary_fields.inventory.kind) { - if(updatedJobData.summary_fields.inventory.kind === '') { - $scope.inventory_url = '/#/inventories/inventory' + updatedJobData.inventory; - } - else if(updatedJobData.summary_fields.inventory.kind === 'smart') { - $scope.inventory_url = '/#/inventories/smart_inventory' + updatedJobData.inventory; - } - } - else { - $scope.inventory_url = ''; - } - $scope.project_url = ($scope.project_name && updatedJobData.project) ? '/#/projects/' + updatedJobData.project : ''; - $scope.credential_name = (updatedJobData.summary_fields.credential) ? updatedJobData.summary_fields.credential.name : ''; - $scope.credential_url = (updatedJobData.credential) ? '/#/credentials/' + updatedJobData.credential : ''; - $scope.cloud_credential_url = (updatedJobData.cloud_credential) ? '/#/credentials/' + updatedJobData.cloud_credential : ''; - if(updatedJobData.summary_fields && updatedJobData.summary_fields.source_workflow_job && - updatedJobData.summary_fields.source_workflow_job.id){ - $scope.workflow_result_link = `/#/workflows/${updatedJobData.summary_fields.source_workflow_job.id}`; - } - $scope.playbook = updatedJobData.playbook; - $scope.credential = updatedJobData.credential; - $scope.cloud_credential = updatedJobData.cloud_credential; - $scope.forks = updatedJobData.forks; - $scope.limit = updatedJobData.limit; - $scope.verbosity = updatedJobData.verbosity; - $scope.job_tags = updatedJobData.job_tags; - $scope.job.module_name = updatedJobData.module_name; - if (updatedJobData.extra_vars) { - $scope.variables = ParseVariableString(updatedJobData.extra_vars); - } - - $scope.$on('getInventorySource', function(e, d) { - $scope.inv_manage_group_link = '/#/inventories/inventory/' + d.inventory + '/inventory_sources/edit/' + d.id; - }); - - // If we have a source then we have to go get the source choices from the server - if (!Empty(updatedJobData.source)) { - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('ChoicesReady', function() { - $scope.source_choices.every(function(e) { - if (e.value === updatedJobData.source) { - $scope.source = e.label; - return false; - } - return true; - }); - }); - // GetChoices can be found in the helper: Utilities.js - // It attaches the source choices to $scope.source_choices. - // Then, when the callback is fired, $scope.source is bound - // to the corresponding label. - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'source', - variable: 'source_choices', - choice_name: 'choices', - callback: 'ChoicesReady' - }); - } - - // LookUpName can be found in the lookup-name.factory - // It attaches the name that it gets (based on the url) - // to the $scope variable defined by the attribute scope_var. - if (!Empty(updatedJobData.credential)) { - LookUpName({ - scope: $scope, - scope_var: 'credential', - url: GetBasePath('credentials') + updatedJobData.credential + '/', - ignore_403: true - }); - } - - if (!Empty(updatedJobData.inventory)) { - LookUpName({ - scope: $scope, - scope_var: 'inventory', - url: GetBasePath('inventory') + updatedJobData.inventory + '/' - }); - } - - if (!Empty(updatedJobData.project)) { - LookUpName({ - scope: $scope, - scope_var: 'project', - url: GetBasePath('projects') + updatedJobData.project + '/' - }); - } - - if (!Empty(updatedJobData.cloud_credential)) { - LookUpName({ - scope: $scope, - scope_var: 'cloud_credential', - url: GetBasePath('credentials') + updatedJobData.cloud_credential + '/', - ignore_403: true - }); - } - - if (!Empty(updatedJobData.inventory_source)) { - LookUpName({ - scope: $scope, - scope_var: 'inventory_source', - url: GetBasePath('inventory_sources') + updatedJobData.inventory_source + '/', - callback: 'getInventorySource' - }); - } - - if (updatedJobData.extra_vars) { - ParseTypeChange({ - scope: $scope, - field_id: 'pre-formatted-variables', - readOnly: true - }); - } - - // If the job isn't running we want to clear out the interval that goes out and checks for stdout updates. - // This interval is defined in the standard out log directive controller. - if (updatedJobData.status === 'successful' || updatedJobData.status === 'failed' || updatedJobData.status === 'error' || updatedJobData.status === 'canceled') { - if ($rootScope.jobStdOutInterval) { - window.clearInterval($rootScope.jobStdOutInterval); - } - } - - } - - if ($scope.removeDeleteFinished) { - $scope.removeDeleteFinished(); - } - $scope.removeDeleteFinished = $scope.$on('DeleteFinished', function(e, action) { - Wait('stop'); - if (action !== 'cancel') { - Wait('stop'); - $state.go('jobs'); - } - }); - - // TODO: this is currently not used but is necessary for cases where sockets - // are not available and a manual refresh trigger is needed. - $scope.refresh = function(){ - $scope.$emit('LoadStdout'); - }; - - // Click binding for the expand/collapse button on the standard out log - $scope.toggleStdoutFullscreen = function() { - $scope.stdoutFullScreen = !$scope.stdoutFullScreen; - - if ($scope.stdoutFullScreen === true) { - $scope.toggleStdoutFullscreenTooltip = i18n._("Collapse Output"); - } else if ($scope.stdoutFullScreen === false) { - $scope.toggleStdoutFullscreenTooltip = i18n._("Expand Output"); - } - }; - - $scope.deleteJob = function() { - DeleteJob({ - scope: $scope, - id: $scope.job.id, - job: $scope.job, - callback: 'DeleteFinished' - }); - }; - - updateJobObj(jobData); - -} - -JobStdoutController.$inject = [ '$rootScope', '$scope', '$state', - '$stateParams', 'GetBasePath', 'Rest', 'ProcessErrors', - 'Empty', 'GetChoices', 'LookUpName', 'ParseTypeChange', - 'ParseVariableString', 'DeleteJob', 'Wait', 'i18n', - 'fieldChoices', 'fieldLabels', 'ProjectModel', 'Alert', 'InventorySourceModel', - 'jobData']; diff --git a/awx/ui/client/src/standard-out/standard-out.block.less b/awx/ui/client/src/workflow-results/standard-out.block.less similarity index 99% rename from awx/ui/client/src/standard-out/standard-out.block.less rename to awx/ui/client/src/workflow-results/standard-out.block.less index 5fd5742e82..980f401c75 100644 --- a/awx/ui/client/src/standard-out/standard-out.block.less +++ b/awx/ui/client/src/workflow-results/standard-out.block.less @@ -169,4 +169,4 @@ standard-out-log { cursor: pointer; border-radius: 5px; font-size: 11px; -} +} \ No newline at end of file diff --git a/awx/ui/test/spec/job-results/job-results.controller-test.js b/awx/ui/test/spec/job-results/job-results.controller-test.js deleted file mode 100644 index c05f2329f7..0000000000 --- a/awx/ui/test/spec/job-results/job-results.controller-test.js +++ /dev/null @@ -1,701 +0,0 @@ -'use strict'; -import moment from 'moment'; - -describe('Controller: jobResultsController', () => { - // Setup - let jobResultsController; - - let jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, eventResolve, populateResolve, $rScope, q, $log, Dataset, Rest, $state, QuerySet, i18n,fieldChoices, fieldLabels, $interval, workflowResultsService, statusSocket, jobExtraCredentials; - - statusSocket = function() { - var fn = function() {}; - return fn; - }; - jobData = { - related: {}, - summary_fields: { - inventory: { - id: null, - kind: '' - } - } - }; - jobDataOptions = { - actions: { - get: {} - } - }; - jobLabels = {}; - jobFinished = true; - count = { - val: {}, - countFinished: false - }; - eventResolve = { - results: [] - }; - populateResolve = {}; - - Dataset = { - data: {foo: "bar"} - }; - - let provideVals = () => { - angular.mock.module('jobResults', ($provide) => { - ParseTypeChange = jasmine.createSpy('ParseTypeChange'); - ParseVariableString = jasmine.createSpy('ParseVariableString'); - jobResultsService = jasmine.createSpyObj('jobResultsService', [ - 'deleteJob', - 'cancelJob', - 'relaunchJob', - 'getEvents', - 'getJobData', - ]); - eventQueue = jasmine.createSpyObj('eventQueue', [ - 'populate', - 'markProcessed', - 'initialize' - ]); - - Rest = jasmine.createSpyObj('Rest', [ - 'setUrl', - 'get' - ]); - - $state = jasmine.createSpyObj('$state', [ - 'reload' - ]); - - QuerySet = jasmine.createSpyObj('QuerySet', [ - 'encodeQueryset' - ]); - - i18n = { - _: function(txt) { - return txt; - } - }; - - $provide.service('workflowResultsService', () => { - return jasmine.createSpyObj('workflowResultsService', ['createOneSecondTimer', 'destroyTimer']); - }); - - $provide.value('statusSocket', statusSocket); - - $provide.value('jobData', jobData); - $provide.value('jobDataOptions', jobDataOptions); - $provide.value('jobLabels', jobLabels); - $provide.value('jobFinished', jobFinished); - $provide.value('count', count); - $provide.value('ParseTypeChange', ParseTypeChange); - $provide.value('ParseVariableString', ParseVariableString); - $provide.value('jobResultsService', jobResultsService); - $provide.value('eventQueue', eventQueue); - $provide.value('Dataset', Dataset); - $provide.value('Rest', Rest); - $provide.value('$state', $state); - $provide.value('QuerySet', QuerySet); - $provide.value('i18n', i18n); - $provide.value('fieldChoices', fieldChoices); - $provide.value('fieldLabels', fieldLabels); - $provide.value('jobExtraCredentials', jobExtraCredentials); - }); - }; - - let injectVals = () => { - angular.mock.inject((_jobData_, _jobDataOptions_, _jobLabels_, _jobFinished_, _count_, _ParseTypeChange_, _ParseVariableString_, _jobResultsService_, _eventQueue_, _$compile_, $rootScope, $controller, $q, $httpBackend, _$log_, _Dataset_, _Rest_, _$state_, _QuerySet_, _$interval_, _workflowResultsService_, _statusSocket_) => { - // when you call $scope.$apply() (which you need to do to - // to get inside of .then blocks to test), something is - // causing a request for all static files. - // - // this is a hack to just pass those requests through - // - // from googling this is probably due to angular-router - // weirdness - $httpBackend.when("GET", (url) => (url - .indexOf("/static/") !== -1)) - .respond(''); - - $httpBackend - .whenGET('/api') - .respond(200, ''); - - $scope = $rootScope.$new(); - $rScope = $rootScope; - q = $q; - jobData = _jobData_; - jobDataOptions = _jobDataOptions_; - jobLabels = _jobLabels_; - jobFinished = _jobFinished_; - count = _count_; - ParseTypeChange = _ParseTypeChange_; - ParseVariableString = _ParseVariableString_; - ParseVariableString.and.returnValue(jobData.extra_vars); - jobResultsService = _jobResultsService_; - eventQueue = _eventQueue_; - $log = _$log_; - Dataset = _Dataset_; - Rest = _Rest_; - $state = _$state_; - QuerySet = _QuerySet_; - $interval = _$interval_; - workflowResultsService = _workflowResultsService_; - statusSocket = _statusSocket_; - - jobResultsService.getEvents.and - .returnValue(eventResolve); - eventQueue.populate.and - .returnValue(populateResolve); - - jobResultsService.getJobData = function() { - var deferred = $q.defer(); - deferred.resolve({}); - return deferred.promise; - }; - - $compile = _$compile_; - - jobResultsController = $controller('jobResultsController', { - $scope: $scope, - jobData: jobData, - jobDataOptions: jobDataOptions, - jobLabels: jobLabels, - jobFinished: jobFinished, - count: count, - ParseTypeChange: ParseTypeChange, - jobResultsService: jobResultsService, - eventQueue: eventQueue, - $compile: $compile, - $log: $log, - $q: q, - Dataset: Dataset, - Rest: Rest, - $state: $state, - QuerySet: QuerySet, - statusSocket: statusSocket - }); - }); - }; - - beforeEach(angular.mock.module('shared')); - - let bootstrapTest = () => { - provideVals(); - injectVals(); - }; - - describe('bootstrap resolve values on scope', () => { - beforeEach(() => { - bootstrapTest(); - }); - - it('should set values to scope based on resolve', () => { - expect($scope.job).toBe(jobData); - expect($scope.jobOptions).toBe(jobDataOptions.actions.GET); - expect($scope.labels).toBe(jobLabels); - }); - }); - - describe('getLinks()', () => { - beforeEach(() => { - jobData.related = { - "created_by": "api/v2/users/12", - "inventory": "api/v2/inventories/12", - "project": "api/v2/projects/12", - "credential": "api/v2/credentials/12", - "cloud_credential": "api/v2/credentials/13", - "network_credential": "api/v2/credentials/14", - }; - - jobData.summary_fields.inventory = { - id: 12, - kind: '' - }; - - bootstrapTest(); - }); - - it('should transform related links and set to scope var', () => { - expect($scope.created_by_link).toBe('/#/users/12'); - expect($scope.inventory_link).toBe('/#/inventories/inventory/12'); - expect($scope.project_link).toBe('/#/projects/12'); - expect($scope.machine_credential_link).toBe('/#/credentials/12'); - expect($scope.cloud_credential_link).toBe('/#/credentials/13'); - expect($scope.network_credential_link).toBe('/#/credentials/14'); - }); - }); - - describe('getLabels()', () => { - beforeEach(() => { - jobDataOptions.actions.GET = { - status: { - choices: [ - ["new", - "New"] - ] - }, - job_type: { - choices: [ - ["job", - "Playbook Run"] - ] - }, - verbosity: { - choices: [ - [0, - "0 (Normal)"] - ] - } - }; - jobData.status = "new"; - jobData.job_type = "job"; - jobData.verbosity = 0; - - bootstrapTest(); - }); - - it('should set scope variables based on options', () => { - $scope.job_status = jobData.status; - - $scope.$apply(); - expect($scope.status_label).toBe("New"); - expect($scope.type_label).toBe("Playbook Run"); - expect($scope.verbosity_label).toBe("0 (Normal)"); - }); - }); - - describe('elapsed timer', () => { - describe('job running', () => { - beforeEach(() => { - jobData.started = moment(); - jobData.status = 'running'; - - bootstrapTest(); - }); - - it('should start timer', () => { - expect(workflowResultsService.createOneSecondTimer).toHaveBeenCalled(); - }); - }); - - describe('job waiting', () => { - beforeEach(() => { - jobData.started = null; - jobData.status = 'waiting'; - - bootstrapTest(); - }); - - it('should not start timer', () => { - expect(workflowResultsService.createOneSecondTimer).not.toHaveBeenCalled(); - }); - }); - - describe('job transitions to running', () => { - beforeEach(() => { - jobData.started = null; - jobData.status = 'waiting'; - jobData.id = 13; - - bootstrapTest(); - - $rScope.$broadcast('ws-jobs', { unified_job_id: jobData.id, status: 'running' }); - }); - - it('should start timer', () => { - expect(workflowResultsService.createOneSecondTimer).toHaveBeenCalled(); - }); - - describe('job transitions from running to finished', () => { - it('should cleanup timer', () => { - $rScope.$broadcast('ws-jobs', { unified_job_id: jobData.id, status: 'successful' }); - expect(workflowResultsService.destroyTimer).toHaveBeenCalled(); - }); - }); - }); - }); - - describe('extra vars stuff', () => { - let extraVars = "foo"; - - beforeEach(() => { - jobData.extra_vars = extraVars; - - bootstrapTest(); - }); - - it('should have extra vars on scope', () => { - expect($scope.job.extra_vars).toBe(extraVars); - }); - - it('should call ParseVariableString and set to scope', () => { - expect(ParseVariableString) - .toHaveBeenCalledWith(extraVars); - expect($scope.variables).toBe(extraVars); - }); - - it('should set the parse type to yaml', () => { - expect($scope.parseType).toBe('yaml'); - }); - - it('should call ParseTypeChange with proper params', () => { - let params = { - scope: $scope, - field_id: 'pre-formatted-variables', - readOnly: true - }; - - expect(ParseTypeChange) - .toHaveBeenCalledWith(params); - }); - }); - - describe('$scope.toggleStdoutFullscreen', () => { - beforeEach(() => { - bootstrapTest(); - }); - - it('should toggle $scope.stdoutFullScreen', () => { - // essentially set to false - expect($scope.stdoutFullScreen).toBe(false); - - // toggle once to true - $scope.toggleStdoutFullscreen(); - expect($scope.stdoutFullScreen).toBe(true); - - // toggle again to false - $scope.toggleStdoutFullscreen(); - expect($scope.stdoutFullScreen).toBe(false); - }); - }); - - describe('$scope.deleteJob', () => { - beforeEach(() => { - bootstrapTest(); - }); - - it('should delete the job', () => { - let job = $scope.job; - $scope.deleteJob(); - expect(jobResultsService.deleteJob).toHaveBeenCalledWith(job); - }); - }); - - describe('$scope.cancelJob', () => { - beforeEach(() => { - bootstrapTest(); - }); - - it('should cancel the job', () => { - let job = $scope.job; - $scope.cancelJob(); - expect(jobResultsService.cancelJob).toHaveBeenCalledWith(job); - }); - }); - - describe('count stuff', () => { - beforeEach(() => { - count = { - val: { - ok: 1, - skipped: 2, - unreachable: 3, - failures: 4, - changed: 5 - }, - countFinished: true - }; - - bootstrapTest(); - }); - - it('should set count values to scope', () => { - expect($scope.count).toBe(count.val); - expect($scope.countFinished).toBe(true); - }); - - it('should find the hostCount based on the count', () => { - expect($scope.hostCount).toBe(15); - }); - }); - - describe('follow stuff - incomplete', () => { - beforeEach(() => { - jobFinished = false; - - bootstrapTest(); - }); - - it('should set followEngaged based on jobFinished incomplete', () => { - expect($scope.followEngaged).toBe(true); - }); - - it('should set followTooltip based on jobFinished incomplete', () => { - expect($scope.followTooltip).toBe("Currently following standard out as it comes in. Click to unfollow."); - }); - }); - - describe('follow stuff - finished', () => { - beforeEach(() => { - jobFinished = true; - - bootstrapTest(); - }); - - it('should set followEngaged based on jobFinished', () => { - expect($scope.followEngaged).toBe(false); - }); - - it('should set followTooltip based on jobFinished', () => { - expect($scope.followTooltip).toBe("Jump to last line of standard out."); - }); - }); - - describe('event stuff', () => { - beforeEach(() => { - jobData.id = 1; - jobData.related.job_events = "url"; - - bootstrapTest(); - }); - - xit('should make a rest call to get already completed events', () => { - expect(jobResultsService.getEvents).toHaveBeenCalledWith("url"); - }); - - xit('should call processEvent when receiving message', () => { - let eventPayload = {"foo": "bar"}; - $rScope.$broadcast('ws-job_events-1', eventPayload); - expect(eventQueue.populate).toHaveBeenCalledWith(eventPayload); - }); - - it('should set the job status on scope when receiving message', () => { - let eventPayload = { - unified_job_id: 1, - status: 'finished' - }; - $rScope.$broadcast('ws-jobs', eventPayload); - expect($scope.job_status).toBe(eventPayload.status); - }); - }); - - describe('getEvents and populate stuff', () => { - describe('getEvents', () => { - let event1 = { - event: 'foo' - }; - - let event2 = { - event_name: 'bar' - }; - - let event1Processed = { - event_name: 'foo' - }; - - beforeEach(() => { - eventResolve = { - results: [ - event1, - event2 - ] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('should change the event name to event_name', () => { - expect(eventQueue.populate) - .toHaveBeenCalledWith(event1Processed); - }); - - xit('should pass through the event with event_name', () => { - expect(eventQueue.populate) - .toHaveBeenCalledWith(event2); - }); - - xit('should have called populate twice', () => { - expect(eventQueue.populate.calls.count()).toEqual(2); - }); - - // TODO: can't figure out how to a test of events.next... - // if you set events.next to true it causes the tests to - // stop running - }); - - describe('populate - start time', () => { - beforeEach(() => { - jobData.start = ""; - - populateResolve = { - startTime: 'foo', - changes: ['startTime'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('sets start time when passed as a change', () => { - expect($scope.job.start).toBe('foo'); - }); - }); - - describe('populate - start time already set', () => { - beforeEach(() => { - jobData.start = "bar"; - - populateResolve = { - startTime: 'foo', - changes: ['startTime'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('does not set start time because already set', () => { - expect($scope.job.start).toBe('bar'); - }); - }); - - describe('populate - count already received', () => { - let receiveCount = { - ok: 2, - skipped: 2, - unreachable: 2, - failures: 2, - changed: 2 - }; - - let alreadyCount = { - ok: 3, - skipped: 3, - unreachable: 3, - failures: 3, - changed: 3 - }; - - beforeEach(() => { - count.countFinished = true; - count.val = alreadyCount; - - populateResolve = { - count: receiveCount, - changes: ['count'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('count does not change', () => { - expect($scope.count).toBe(alreadyCount); - expect($scope.hostCount).toBe(15); - }); - }); - - describe('populate - playCount, taskCount and countFinished', () => { - beforeEach(() => { - - populateResolve = { - playCount: 12, - taskCount: 13, - changes: ['playCount', 'taskCount', 'countFinished'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('sets playCount', () => { - expect($scope.playCount).toBe(12); - }); - - xit('sets taskCount', () => { - expect($scope.taskCount).toBe(13); - }); - - xit('sets countFinished', () => { - expect($scope.countFinished).toBe(true); - }); - }); - - describe('populate - finishedTime', () => { - beforeEach(() => { - jobData.finished = ""; - - populateResolve = { - finishedTime: "finished_time", - changes: ['finishedTime'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('sets finished time and changes follow tooltip', () => { - expect($scope.job.finished).toBe('finished_time'); - expect($scope.jobFinished).toBe(true); - expect($scope.followTooltip) - .toBe("Jump to last line of standard out."); - }); - }); - - describe('populate - finishedTime when already finished', () => { - beforeEach(() => { - jobData.finished = "already_set"; - - populateResolve = { - finishedTime: "finished_time", - changes: ['finishedTime'] - }; - - bootstrapTest(); - - $scope.$apply(); - }); - - xit('does not set finished time because already set', () => { - expect($scope.job.finished).toBe('already_set'); - expect($scope.jobFinished).toBe(true); - expect($scope.followTooltip) - .toBe("Jump to last line of standard out."); - }); - }); - - describe('populate - stdout', () => { - beforeEach(() => { - - populateResolve = { - counter: 12, - stdout: "line", - changes: ['stdout'] - }; - - bootstrapTest(); - - spyOn($log, 'error'); - - $scope.followEngaged = true; - - $scope.$apply(); - }); - - xit('creates new child scope for the event', () => { - expect($scope.events[12].event).toBe(populateResolve); - - // in unit test, followScroll should not be defined as - // directive has not been instantiated - expect($log.error).toHaveBeenCalledWith("follow scroll undefined, standard out directive not loaded yet?"); - }); - }); - }); -}); diff --git a/awx/ui/test/spec/job-results/job-results.service-test.js b/awx/ui/test/spec/job-results/job-results.service-test.js deleted file mode 100644 index 1219f2e450..0000000000 --- a/awx/ui/test/spec/job-results/job-results.service-test.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -describe('jobResultsService', () => { - let jobResultsService; - - beforeEach(angular.mock.module('awApp')); - - beforeEach(angular.mock.inject(( _jobResultsService_) => { - jobResultsService = _jobResultsService_; - })); - - describe('getCountsFromStatsEvent()', () => { - it('properly counts hosts based on task state', () => { - let event_data = { - "skipped": { - "skipped-host": 5 // this host skipped all 5 tasks - }, - "ok": { - "ok-host": 5, // this host was ok on all 5 tasks - "changed-host": 4 // this host had 4 ok tasks, had 1 changed task - }, - "changed": { - "changed-host": 1 - }, - "failures": { - "failed-host": 1 // this host had a failed task - }, - "dark": { - "unreachable-host": 1 // this host was unreachable - }, - "processed": { - "ok-host": 1, - "changed-host": 1, - "skipped-host": 1, - "failed-host": 1, - "unreachable-host": 1 - }, - "playbook_uuid": "c23d8872-c92a-4e96-9f78-abe6fef38f33", - "playbook": "some_playbook.yml", - }; - expect(jobResultsService.getCountsFromStatsEvent(event_data)).toEqual({ - 'ok': 1, - 'skipped': 1, - 'unreachable': 1, - 'failures': 1, - 'changed': 1 - }); - }); - }); -}); diff --git a/awx/ui/test/spec/job-results/parse-stdout.service-test.js b/awx/ui/test/spec/job-results/parse-stdout.service-test.js deleted file mode 100644 index 86441c1ffa..0000000000 --- a/awx/ui/test/spec/job-results/parse-stdout.service-test.js +++ /dev/null @@ -1,212 +0,0 @@ -'use strict'; - -describe('parseStdoutService', () => { - let parseStdoutService, - log; - - beforeEach(angular.mock.module('awApp')); - - beforeEach(angular.mock.module('jobResults',($provide) => { - log = jasmine.createSpyObj('$log', [ - 'error' - ]); - - $provide.value('$log', log); - })); - - beforeEach(angular.mock.inject((_$log_, _parseStdoutService_) => { - parseStdoutService = _parseStdoutService_; - })); - - describe('prettify()', () => { - it('returns lines of stdout with styling classes', () => { - let line = "[0;32mok: [host-00]", - styledLine = 'ok: [host-00]'; - expect(parseStdoutService.prettify(line)).toBe(styledLine); - }); - - it('can return lines of stdout without styling classes', () => { - let line = "[0;32mok: [host-00][0m", - unstyled = "unstyled", - unstyledLine = 'ok: [host-00]'; - expect(parseStdoutService.prettify(line, unstyled)).toBe(unstyledLine); - }); - - it('can return empty strings', () => { - expect(parseStdoutService.prettify("")).toBe(""); - }); - }); - - describe('getLineClasses()', () => { - it('creates a string that is used as a class', () => { - let headerEvent = { - event_name: 'playbook_on_task_start', - event_data: { - play_uuid:"0f667a23-d9ab-4128-a735-80566bcdbca0", - task_uuid: "80dd087c-268b-45e8-9aab-1083bcfd9364" - } - }; - let lineNum = 3; - let line = "TASK [setup] *******************************************************************"; - let styledLine = " header_task header_task_80dd087c-268b-45e8-9aab-1083bcfd9364 actual_header play_0f667a23-d9ab-4128-a735-80566bcdbca0 line_num_3"; - expect(parseStdoutService.getLineClasses(headerEvent, line, lineNum)).toBe(styledLine); - }); - }); - - describe('getStartTime()', () => { - // TODO: the problem is that the date here calls moment, and thus - // the date will be timezone'd in the string (this could be - // different based on where you are) - xit('creates returns a badge with the start time of the event', () => { - let headerEvent = { - event_name: 'playbook_on_play_start', - created: "2016-11-22T21:15:54.736Z" - }; - - let line = "PLAY [add hosts to inventory] **************************************************"; - let badgeDiv = '
13:15:54
'; - expect(parseStdoutService.getStartTimeBadge(headerEvent, line)).toBe(badgeDiv); - }); - }); - - describe('getCollapseIcon()', () => { - let emptySpan = ` -`; - - it('returns empty expander for non-header event', () => { - let nonHeaderEvent = { - event_name: 'not_header', - start_line: 0, - end_line: 1, - stdout:"line1" - }; - expect(parseStdoutService.getCollapseIcon(nonHeaderEvent)) - .toBe(emptySpan); - }); - - it('returns collapse/decollapse icons for header events', () => { - let headerEvent = { - event_name: 'playbook_on_task_start', - start_line: 0, - end_line: 1, - stdout:"line1", - event_data: { - task_uuid: '1da9012d-18e6-4562-85cd-83cf10a97f86' - } - }; - let line = "TASK [setup] *******************************************************************"; - let expandSpan = ` - - - -`; - - expect(parseStdoutService.getCollapseIcon(headerEvent, line)) - .toBe(expandSpan); - }); - }); - - describe('getLineArr()', () => { - it('returns stdout in array format', () => { - let mockEvent = { - start_line: 12, - end_line: 14, - stdout: "line1\r\nline2\r\n" - }; - let expectedReturn = [[13, "line1"],[14, "line2"]]; - - let returnedEvent = parseStdoutService.getLineArr(mockEvent); - - expect(returnedEvent).toEqual(expectedReturn); - }); - - it('deals correctly with capped lines', () => { - let mockEvent = { - start_line: 7, - end_line: 11, - stdout: "a\r\nb\r\nc..." - }; - let expectedReturn = [[8, "a"],[9, "b"], [10,"c..."]]; - - let returnedEvent = parseStdoutService.getLineArr(mockEvent); - - expect(returnedEvent).toEqual(expectedReturn); - }); - }); - - describe('parseStdout()', () => { - let mockEvent = {"foo": "bar"}; - - it('calls functions', function() { - spyOn(parseStdoutService, 'getLineArr').and - .returnValue([[13, 'line1'], [14, 'line2']]); - spyOn(parseStdoutService, 'getLineClasses').and - .returnValue(""); - spyOn(parseStdoutService, 'getCollapseIcon').and - .returnValue(""); - spyOn(parseStdoutService, 'getAnchorTags').and - .returnValue(""); - spyOn(parseStdoutService, 'prettify').and - .returnValue("prettified_line"); - spyOn(parseStdoutService, 'getStartTimeBadge').and - .returnValue(""); - - parseStdoutService.parseStdout(mockEvent); - - expect(parseStdoutService.getLineArr) - .toHaveBeenCalledWith(mockEvent); - expect(parseStdoutService.getLineClasses) - .toHaveBeenCalledWith(mockEvent, 'line1', 13); - expect(parseStdoutService.getCollapseIcon) - .toHaveBeenCalledWith(mockEvent, 'line1'); - expect(parseStdoutService.getAnchorTags) - .toHaveBeenCalledWith(mockEvent); - expect(parseStdoutService.prettify) - .toHaveBeenCalledWith('line1'); - expect(parseStdoutService.getStartTimeBadge) - .toHaveBeenCalledWith(mockEvent, 'line1'); - - // get line arr should be called once for the event - expect(parseStdoutService.getLineArr.calls.count()) - .toBe(1); - - // other functions should be called twice (once for each - // line) - expect(parseStdoutService.getLineClasses.calls.count()) - .toBe(2); - expect(parseStdoutService.getCollapseIcon.calls.count()) - .toBe(2); - expect(parseStdoutService.getAnchorTags.calls.count()) - .toBe(2); - expect(parseStdoutService.prettify.calls.count()) - .toBe(2); - }); - - it('returns dom-ified lines', function() { - spyOn(parseStdoutService, 'getLineArr').and - .returnValue([[13, 'line1']]); - spyOn(parseStdoutService, 'getLineClasses').and - .returnValue("line_classes"); - spyOn(parseStdoutService, 'getCollapseIcon').and - .returnValue("collapse_icon_dom"); - spyOn(parseStdoutService, 'getAnchorTags').and - .returnValue(`" anchor_tag_dom`); - spyOn(parseStdoutService, 'prettify').and - .returnValue("prettified_line"); - spyOn(parseStdoutService, 'getStartTimeBadge').and - .returnValue(""); - - var returnedString = parseStdoutService.parseStdout(mockEvent); - - var expectedString = ` -
-
collapse_icon_dom13
-
prettified_line
-
`; - expect(returnedString).toBe(expectedString); - }); - }); -}); From 1362b444f2c39ba13ac643f8209dc86e0f6e939c Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 5 Apr 2018 01:35:42 -0400 Subject: [PATCH 180/379] move search tag test --- .../ui/test/e2e/tests/test-search-tag-add-remove.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename test-search-tag-add-remove.js => awx/ui/test/e2e/tests/test-search-tag-add-remove.js (99%) diff --git a/test-search-tag-add-remove.js b/awx/ui/test/e2e/tests/test-search-tag-add-remove.js similarity index 99% rename from test-search-tag-add-remove.js rename to awx/ui/test/e2e/tests/test-search-tag-add-remove.js index db491712e9..294f7aa582 100644 --- a/test-search-tag-add-remove.js +++ b/awx/ui/test/e2e/tests/test-search-tag-add-remove.js @@ -38,6 +38,7 @@ const checkTags = (client, tags) => { module.exports = { before: (client, done) => { const resources = range(25).map(n => getAdminMachineCredential(`test-search-${n}`)); + Promise.all(resources).then(done); }, 'add and remove search tags': client => { @@ -120,4 +121,4 @@ module.exports = { client.end(); }, -}; \ No newline at end of file +}; From 50794452c8d337a855644fee6f3e69be641d62ba Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 08:01:26 -0400 Subject: [PATCH 181/379] fix bug from new credential property --- awx/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index aaeefad579..763c8f33db 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3201,7 +3201,7 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): data.setdefault('project', job_template.project.pk) data.setdefault('playbook', job_template.playbook) if job_template.credential: - data.setdefault('credential', job_template.credential.pk) + data.setdefault('credential', job_template.credential) data.setdefault('forks', job_template.forks) data.setdefault('limit', job_template.limit) data.setdefault('verbosity', job_template.verbosity) From 133cca14467c492e610cebb665017116dd5d1249 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 07:36:00 -0400 Subject: [PATCH 182/379] fix WFJT user_capabilities special-case --- awx/main/access.py | 7 +++---- .../tests/functional/api/test_rbac_displays.py | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 23f182177d..2bad844201 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -348,10 +348,9 @@ class BaseAccess(object): if obj.validation_errors: user_capabilities[display_method] = False continue - elif isinstance(obj, (WorkflowJobTemplate, WorkflowJob)): - if not feature_enabled('workflows'): - user_capabilities[display_method] = (display_method == 'delete') - continue + elif isinstance(obj, (WorkflowJobTemplate, WorkflowJob)) and (not feature_enabled('workflows')): + user_capabilities[display_method] = (display_method == 'delete') + continue elif display_method == 'copy' and isinstance(obj, WorkflowJobTemplate) and obj.organization_id is None: user_capabilities[display_method] = self.user.is_superuser continue diff --git a/awx/main/tests/functional/api/test_rbac_displays.py b/awx/main/tests/functional/api/test_rbac_displays.py index c0931b50da..01de27bbe7 100644 --- a/awx/main/tests/functional/api/test_rbac_displays.py +++ b/awx/main/tests/functional/api/test_rbac_displays.py @@ -3,8 +3,8 @@ import pytest from awx.api.versioning import reverse from django.test.client import RequestFactory -from awx.main.models import Role, Group, UnifiedJobTemplate, JobTemplate -from awx.main.access import access_registry +from awx.main.models import Role, Group, UnifiedJobTemplate, JobTemplate, WorkflowJobTemplate +from awx.main.access import access_registry, WorkflowJobTemplateAccess from awx.main.utils import prefetch_page_capabilities from awx.api.serializers import JobTemplateSerializer, UnifiedJobTemplateSerializer @@ -322,6 +322,17 @@ def test_prefetch_jt_copy_capability(job_template, project, inventory, rando): assert mapping[job_template.id] == {'copy': True} +@pytest.mark.django_db +def test_workflow_orphaned_capabilities(rando): + wfjt = WorkflowJobTemplate.objects.create(name='test', organization=None) + wfjt.admin_role.members.add(rando) + access = WorkflowJobTemplateAccess(rando) + assert not access.get_user_capabilities( + wfjt, method_list=['edit', 'copy'], + capabilities_cache={'copy': True} + )['copy'] + + @pytest.mark.django_db def test_manual_projects_no_update(manual_project, get, admin_user): response = get(reverse('api:project_detail', kwargs={'pk': manual_project.pk}), admin_user, expect=200) From 524343870b036273b1881078725fd7f4183675b1 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Thu, 5 Apr 2018 09:46:03 -0400 Subject: [PATCH 183/379] Added Project & Inventory signals for JobTemplate RBAC --- awx/main/signals.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/awx/main/signals.py b/awx/main/signals.py index c79b1742aa..cc7f7624f4 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -9,7 +9,13 @@ import json # Django from django.conf import settings -from django.db.models.signals import post_save, pre_delete, post_delete, m2m_changed +from django.db.models.signals import ( + post_init, + post_save, + pre_delete, + post_delete, + m2m_changed, +) from django.dispatch import receiver from django.contrib.auth import SESSION_KEY from django.utils import timezone @@ -229,6 +235,30 @@ def cleanup_detached_labels_on_deleted_parent(sender, instance, **kwargs): if l.is_candidate_for_detach(): l.delete() +def set_original_organization(sender, instance, **kwargs): + '''set_original_organization is used to set the original, or + pre-save organization, so we can later determine if the organization + field is dirty. + ''' + instance.__original_org = instance.organization + +def save_related_job_templates(sender, instance, **kwargs): + '''save_related_job_templates loops through all of the + job templates that use an Inventory or Project that have had their + Organization updated. This triggers the rebuilding of the RBAC hierarchy + and ensures the proper access restrictions. + ''' + if instance.__original_org != instance.organization: + instance.__original_org = instance.organization + jtq = None + if sender == Project: + jtq = JobTemplate.objects.filter(project=instance) + elif sender == Inventory: + jtq = JobTemplate.objects.filter(inventory=instance) + if jtq: + for jt in jtq.all(): + jt.save() + def connect_computed_field_signals(): post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Host) @@ -247,7 +277,10 @@ def connect_computed_field_signals(): connect_computed_field_signals() - +post_init.connect(set_original_organization, sender=Project) +post_init.connect(set_original_organization, sender=Inventory) +post_save.connect(save_related_job_templates, sender=Project) +post_save.connect(save_related_job_templates, sender=Inventory) post_save.connect(emit_job_event_detail, sender=JobEvent) post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent) post_save.connect(emit_project_update_event_detail, sender=ProjectUpdateEvent) From 17f8ec64ce6d7f82646970345467c45c38adde3a Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Thu, 5 Apr 2018 10:32:50 -0400 Subject: [PATCH 184/379] Set `raw=True` when reading passwords from ConfigParser files. Cherry-pick of https://github.com/ansible/ansible/pull/35582 --- awx/plugins/inventory/cloudforms.py | 2 +- awx/plugins/inventory/foreman.py | 2 +- awx/plugins/inventory/ovirt4.py | 2 +- awx/plugins/inventory/vmware_inventory.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/plugins/inventory/cloudforms.py b/awx/plugins/inventory/cloudforms.py index 247d297e3d..c6702dcce1 100755 --- a/awx/plugins/inventory/cloudforms.py +++ b/awx/plugins/inventory/cloudforms.py @@ -138,7 +138,7 @@ class CloudFormsInventory(object): warnings.warn("No username specified, you need to specify a CloudForms username.") if config.has_option('cloudforms', 'password'): - self.cloudforms_pw = config.get('cloudforms', 'password') + self.cloudforms_pw = config.get('cloudforms', 'password', raw=True) else: self.cloudforms_pw = None diff --git a/awx/plugins/inventory/foreman.py b/awx/plugins/inventory/foreman.py index 4d6fd32b80..e1ca8787d6 100755 --- a/awx/plugins/inventory/foreman.py +++ b/awx/plugins/inventory/foreman.py @@ -84,7 +84,7 @@ class ForemanInventory(object): try: self.foreman_url = config.get('foreman', 'url') self.foreman_user = config.get('foreman', 'user') - self.foreman_pw = config.get('foreman', 'password') + self.foreman_pw = config.get('foreman', 'password', raw=True) self.foreman_ssl_verify = config.getboolean('foreman', 'ssl_verify') except (ConfigParser.NoOptionError, ConfigParser.NoSectionError) as e: print("Error parsing configuration: %s" % e, file=sys.stderr) diff --git a/awx/plugins/inventory/ovirt4.py b/awx/plugins/inventory/ovirt4.py index 53499220b9..cf2f7ad3c9 100755 --- a/awx/plugins/inventory/ovirt4.py +++ b/awx/plugins/inventory/ovirt4.py @@ -138,7 +138,7 @@ def create_connection(): return sdk.Connection( url=config.get('ovirt', 'ovirt_url'), username=config.get('ovirt', 'ovirt_username'), - password=config.get('ovirt', 'ovirt_password'), + password=config.get('ovirt', 'ovirt_password', raw=True), ca_file=config.get('ovirt', 'ovirt_ca_file'), insecure=config.get('ovirt', 'ovirt_ca_file') is None, ) diff --git a/awx/plugins/inventory/vmware_inventory.py b/awx/plugins/inventory/vmware_inventory.py index 7f3537bb4e..997f53dcaa 100755 --- a/awx/plugins/inventory/vmware_inventory.py +++ b/awx/plugins/inventory/vmware_inventory.py @@ -268,7 +268,7 @@ class VMWareInventory(object): self.port = int(os.environ.get('VMWARE_PORT', config.get('vmware', 'port'))) self.username = os.environ.get('VMWARE_USERNAME', config.get('vmware', 'username')) self.debugl('username is %s' % self.username) - self.password = os.environ.get('VMWARE_PASSWORD', config.get('vmware', 'password')) + self.password = os.environ.get('VMWARE_PASSWORD', config.get('vmware', 'password', raw=True)) self.validate_certs = os.environ.get('VMWARE_VALIDATE_CERTS', config.get('vmware', 'validate_certs')) if self.validate_certs in ['no', 'false', 'False', False]: self.validate_certs = False From 3411389d00eeaed2b5ca178f71f2fe436532296c Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Thu, 5 Apr 2018 09:46:38 -0400 Subject: [PATCH 185/379] Added JobTemplate ownership change test --- awx/main/signals.py | 2 ++ .../functional/test_rbac_job_templates.py | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/awx/main/signals.py b/awx/main/signals.py index cc7f7624f4..64f053eb23 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -235,6 +235,7 @@ def cleanup_detached_labels_on_deleted_parent(sender, instance, **kwargs): if l.is_candidate_for_detach(): l.delete() + def set_original_organization(sender, instance, **kwargs): '''set_original_organization is used to set the original, or pre-save organization, so we can later determine if the organization @@ -242,6 +243,7 @@ def set_original_organization(sender, instance, **kwargs): ''' instance.__original_org = instance.organization + def save_related_job_templates(sender, instance, **kwargs): '''save_related_job_templates loops through all of the job templates that use an Inventory or Project that have had their diff --git a/awx/main/tests/functional/test_rbac_job_templates.py b/awx/main/tests/functional/test_rbac_job_templates.py index a12bc31b70..0b9d0c46bd 100644 --- a/awx/main/tests/functional/test_rbac_job_templates.py +++ b/awx/main/tests/functional/test_rbac_job_templates.py @@ -11,6 +11,7 @@ from awx.main.access import ( ScheduleAccess ) from awx.main.models.jobs import JobTemplate +from awx.main.models.organization import Organization from awx.main.models.schedules import Schedule @@ -296,3 +297,30 @@ class TestJobTemplateSchedules: mock_change.return_value = True assert access.can_change(schedule, {'inventory': 42}) mock_change.assert_called_once_with(schedule, {'inventory': 42}) + + +@pytest.mark.django_db +def test_jt_org_ownership_change(user, jt_linked): + admin1 = user('admin1') + org1 = jt_linked.project.organization + org1.admin_role.members.add(admin1) + a1_access = JobTemplateAccess(admin1) + + assert a1_access.can_read(jt_linked) + + + admin2 = user('admin2') + org2 = Organization.objects.create(name='mrroboto', description='domo') + org2.admin_role.members.add(admin2) + a2_access = JobTemplateAccess(admin2) + + assert not a2_access.can_read(jt_linked) + + + jt_linked.project.organization = org2 + jt_linked.project.save() + jt_linked.inventory.organization = org2 + jt_linked.inventory.save() + + assert a2_access.can_read(jt_linked) + assert not a1_access.can_read(jt_linked) From 0bd9919108e636a3d7023abccc039ee900f152cf Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Thu, 5 Apr 2018 11:05:48 -0400 Subject: [PATCH 186/379] Make use of callback explicitly for Project and Inventory --- awx/main/signals.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/awx/main/signals.py b/awx/main/signals.py index 64f053eb23..36ad3e21e9 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -250,16 +250,14 @@ def save_related_job_templates(sender, instance, **kwargs): Organization updated. This triggers the rebuilding of the RBAC hierarchy and ensures the proper access restrictions. ''' + if sender not in (Project, Inventory): + raise ValueError('This signal callback is only intended for use with Project or Inventory') + if instance.__original_org != instance.organization: instance.__original_org = instance.organization - jtq = None - if sender == Project: - jtq = JobTemplate.objects.filter(project=instance) - elif sender == Inventory: - jtq = JobTemplate.objects.filter(inventory=instance) - if jtq: - for jt in jtq.all(): - jt.save() + jtq = JobTemplate.objects.filter(**{sender.__name__.lower(): instance}) + for jt in jtq.all(): + jt.save() def connect_computed_field_signals(): From 2f746c9fd9835320ef4c689b9488b087435c2450 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 28 Mar 2018 11:18:00 -0400 Subject: [PATCH 187/379] Complete removal of InitialPlaybookRun --- .../linkout/organizations-linkout.route.js | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index 20f8b6a6c3..deae3ddf13 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -216,6 +216,78 @@ let lists = [{ } ] } +}, { + name: 'organizations.job_templates', + url: '/:organization_id/job_templates', + searchPrefix: 'job_template', + views: { + 'form': { + controller: OrganizationsJobTemplates, + templateProvider: function(OrgJobTemplateList, generateList) { + let html = generateList.build({ + list: OrgJobTemplateList, + mode: 'edit', + cancelButton: true + }); + return generateList.wrapPanel(html); + }, + }, + }, + params: { + template_search: { + value: { + or__project__organization: null, + or__inventory__organization: null, + page_size: 20 + }, + dynamic: true + } + }, + data: { + activityStream: true, + activityStreamTarget: 'organization', + socket: { + "groups": { + "jobs": ["status_changed"] + } + } + }, + ncyBreadcrumb: { + parent: "organizations.edit", + label: N_("JOB TEMPLATES") + }, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }], + OrgJobTemplateList: ['TemplateList', 'GetBasePath', '$stateParams', function(TemplateList) { + let list = _.cloneDeep(TemplateList); + delete list.actions; + // @issue Why is the delete action unavailable in this view? + delete list.fieldActions.delete; + delete list.fields.type; + list.listTitle = N_('Job Templates') + ` | {{ name }}`; + list.emptyListText = "This list is populated by job templates added from the Job Templates section"; + list.iterator = 'template'; + list.name = 'job_templates'; + list.basePath = "job_templates"; + list.fields.smart_status.ngInclude = "'/static/partials/organizations-job-template-smart-status.html'"; + list.fields.name.ngHref = '#/templates/job_template/{{template.id}}'; + list.fieldActions.schedule.ngClick = 'scheduleJob(template.id)'; + list.fieldActions.copy.ngClick = 'copyTemplate(template.id)'; + list.fieldActions.edit.ngClick = "editJobTemplate(template.id)"; + list.fieldActions.view.ngClick = "editJobTemplate(template.id)"; + return list; + }], + OrgJobTemplateDataset: ['OrgJobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath', + function(list, qs, $stateParams, GetBasePath) { + let path = GetBasePath(list.name); + $stateParams.template_search.or__project__organization = $stateParams.organization_id; + $stateParams.template_search.or__inventory__organization = $stateParams.organization_id; + return qs.search(path, $stateParams.template_search); + } + ] + } }, { name: 'organizations.admins', url: '/:organization_id/admins', From 881688dd776f519b3def96cc243f94415f545efa Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Tue, 3 Apr 2018 11:57:47 -0400 Subject: [PATCH 188/379] fix authentication order --- awx/settings/defaults.py | 3 +- .../linkout/organizations-linkout.route.js | 72 ------------------- 2 files changed, 1 insertion(+), 74 deletions(-) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 645947eb60..013f831492 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -289,9 +289,8 @@ REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'awx.api.pagination.Pagination', 'PAGE_SIZE': 25, 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'awx.api.authentication.SessionAuthentication', 'awx.api.authentication.LoggedOAuth2Authentication', - # 'awx.api.authentication.SessionAuthentication', + 'awx.api.authentication.SessionAuthentication', 'awx.api.authentication.LoggedBasicAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index deae3ddf13..20f8b6a6c3 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -216,78 +216,6 @@ let lists = [{ } ] } -}, { - name: 'organizations.job_templates', - url: '/:organization_id/job_templates', - searchPrefix: 'job_template', - views: { - 'form': { - controller: OrganizationsJobTemplates, - templateProvider: function(OrgJobTemplateList, generateList) { - let html = generateList.build({ - list: OrgJobTemplateList, - mode: 'edit', - cancelButton: true - }); - return generateList.wrapPanel(html); - }, - }, - }, - params: { - template_search: { - value: { - or__project__organization: null, - or__inventory__organization: null, - page_size: 20 - }, - dynamic: true - } - }, - data: { - activityStream: true, - activityStreamTarget: 'organization', - socket: { - "groups": { - "jobs": ["status_changed"] - } - } - }, - ncyBreadcrumb: { - parent: "organizations.edit", - label: N_("JOB TEMPLATES") - }, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }], - OrgJobTemplateList: ['TemplateList', 'GetBasePath', '$stateParams', function(TemplateList) { - let list = _.cloneDeep(TemplateList); - delete list.actions; - // @issue Why is the delete action unavailable in this view? - delete list.fieldActions.delete; - delete list.fields.type; - list.listTitle = N_('Job Templates') + ` | {{ name }}`; - list.emptyListText = "This list is populated by job templates added from the Job Templates section"; - list.iterator = 'template'; - list.name = 'job_templates'; - list.basePath = "job_templates"; - list.fields.smart_status.ngInclude = "'/static/partials/organizations-job-template-smart-status.html'"; - list.fields.name.ngHref = '#/templates/job_template/{{template.id}}'; - list.fieldActions.schedule.ngClick = 'scheduleJob(template.id)'; - list.fieldActions.copy.ngClick = 'copyTemplate(template.id)'; - list.fieldActions.edit.ngClick = "editJobTemplate(template.id)"; - list.fieldActions.view.ngClick = "editJobTemplate(template.id)"; - return list; - }], - OrgJobTemplateDataset: ['OrgJobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath', - function(list, qs, $stateParams, GetBasePath) { - let path = GetBasePath(list.name); - $stateParams.template_search.or__project__organization = $stateParams.organization_id; - $stateParams.template_search.or__inventory__organization = $stateParams.organization_id; - return qs.search(path, $stateParams.template_search); - } - ] - } }, { name: 'organizations.admins', url: '/:organization_id/admins', From 78893590d16a146faf7276621cf0a8111521bb66 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 5 Apr 2018 13:34:16 -0400 Subject: [PATCH 189/379] Fixed bug where job/workflow templates add forms were fully disabled for users with add access --- .../job-template-add.controller.js | 59 ++++++++++--------- .../add-workflow/workflow-add.controller.js | 58 +++++++++--------- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js index 10754cb0e7..9efc8e5022 100644 --- a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js +++ b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js @@ -10,14 +10,14 @@ 'ProcessErrors', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON', 'CallbackHelpInit', 'GetChoices', '$state', 'availableLabels', 'CreateSelect2', '$q', 'i18n', 'Inventory', 'Project', 'InstanceGroupsService', - 'MultiCredentialService', 'ConfigData', + 'MultiCredentialService', 'ConfigData', 'resolvedModels', function( $filter, $scope, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, md5Setup, ParseTypeChange, Wait, Empty, ToJSON, CallbackHelpInit, GetChoices, $state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, - MultiCredentialService, ConfigData + MultiCredentialService, ConfigData, resolvedModels ) { // Inject dynamic view @@ -28,37 +28,38 @@ selectPlaybook, checkSCMStatus, callback; - init(); - function init(){ - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); + const jobTemplate = resolvedModels[0]; - $scope.can_edit = true; - $scope.allow_callbacks = false; - $scope.playbook_options = []; - $scope.mode = "add"; - $scope.parseType = 'yaml'; - $scope.credentialNotPresent = false; - $scope.canGetAllRelatedResources = true; + $scope.canAddJobTemplate = jobTemplate.options('actions.POST'); - md5Setup({ - scope: $scope, - master: master, - check_field: 'allow_callbacks', - default_val: false - }); - CallbackHelpInit({ scope: $scope }); + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); - $scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.'); + $scope.can_edit = true; + $scope.allow_callbacks = false; + $scope.playbook_options = []; + $scope.mode = "add"; + $scope.parseType = 'yaml'; + $scope.credentialNotPresent = false; + $scope.canGetAllRelatedResources = true; - MultiCredentialService.getCredentialTypes() - .then(({ data }) => { - $scope.multiCredential = { - credentialTypes: data.results, - selectedCredentials: [] - }; - }); - } + md5Setup({ + scope: $scope, + master: master, + check_field: 'allow_callbacks', + default_val: false + }); + CallbackHelpInit({ scope: $scope }); + + $scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.'); + + MultiCredentialService.getCredentialTypes() + .then(({ data }) => { + $scope.multiCredential = { + credentialTypes: data.results, + selectedCredentials: [] + }; + }); callback = function() { // Make sure the form controller knows there was a change diff --git a/awx/ui/client/src/templates/workflows/add-workflow/workflow-add.controller.js b/awx/ui/client/src/templates/workflows/add-workflow/workflow-add.controller.js index c725dbf1c6..5c998812ee 100644 --- a/awx/ui/client/src/templates/workflows/add-workflow/workflow-add.controller.js +++ b/awx/ui/client/src/templates/workflows/add-workflow/workflow-add.controller.js @@ -8,43 +8,47 @@ export default [ '$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', 'Wait', '$state', 'CreateSelect2', 'TemplatesService', 'ToJSON', 'ParseTypeChange', '$q', 'Rest', 'GetBasePath', 'availableLabels', 'i18n', + 'resolvedModels', function($scope, WorkflowForm, GenerateForm, Alert, ProcessErrors, Wait, $state, CreateSelect2, TemplatesService, ToJSON, - ParseTypeChange, $q, Rest, GetBasePath, availableLabels, i18n) { + ParseTypeChange, $q, Rest, GetBasePath, availableLabels, i18n, + resolvedModels) { // Inject dynamic view let form = WorkflowForm(), generator = GenerateForm; - function init() { - $scope.canEditOrg = true; - $scope.parseType = 'yaml'; - $scope.can_edit = true; - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); + const workflowTemplate = resolvedModels[1]; - // Make the variables textarea look pretty - ParseTypeChange({ - scope: $scope, - field_id: 'workflow_job_template_variables', - onChange: function() { - // Make sure the form controller knows there was a change - $scope[form.name + '_form'].$setDirty(); - } - }); + $scope.canAddWorkflowJobTemplate = workflowTemplate.options('actions.POST'); - $scope.labelOptions = availableLabels - .map((i) => ({label: i.name, value: i.id})); + $scope.canEditOrg = true; + $scope.parseType = 'yaml'; + $scope.can_edit = true; + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); - CreateSelect2({ - element:'#workflow_job_template_labels', - multiple: true, - addNew: true - }); + // Make the variables textarea look pretty + ParseTypeChange({ + scope: $scope, + field_id: 'workflow_job_template_variables', + onChange: function() { + // Make sure the form controller knows there was a change + $scope[form.name + '_form'].$setDirty(); + } + }); - $scope.workflowEditorTooltip = i18n._("Please save before defining the workflow graph."); - $scope.surveyTooltip = i18n._('Please save before adding a survey to this workflow.'); - } + $scope.labelOptions = availableLabels + .map((i) => ({label: i.name, value: i.id})); + + CreateSelect2({ + element:'#workflow_job_template_labels', + multiple: true, + addNew: true + }); + + $scope.workflowEditorTooltip = i18n._("Please save before defining the workflow graph."); + $scope.surveyTooltip = i18n._('Please save before adding a survey to this workflow.'); $scope.formSave = function () { let fld, data = {}; @@ -167,7 +171,5 @@ export default [ $scope.formCancel = function () { $state.transitionTo('templates'); }; - - init(); } ]; From d9c9df73d2b331f5e8a59ac82573b48510ace2a8 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 5 Apr 2018 14:00:13 -0400 Subject: [PATCH 190/379] Fixed unit test failure --- .../workflows/workflow-add.controller-test.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/awx/ui/test/spec/workflows/workflow-add.controller-test.js b/awx/ui/test/spec/workflows/workflow-add.controller-test.js index 0f7e588227..82421e0e6c 100644 --- a/awx/ui/test/spec/workflows/workflow-add.controller-test.js +++ b/awx/ui/test/spec/workflows/workflow-add.controller-test.js @@ -16,7 +16,8 @@ describe('Controller: WorkflowAdd', () => { Wait, ParseTypeChange, ToJSON, - availableLabels; + availableLabels, + resolvedModels; beforeEach(angular.mock.module('awApp')); beforeEach(angular.mock.module('RestServices')); @@ -48,7 +49,16 @@ describe('Controller: WorkflowAdd', () => { name: "foo", id: "1" }]; - + + resolvedModels = [ + {}, + { + options: () => { + return true; + } + } + ]; + Alert = jasmine.createSpy('Alert'); ProcessErrors = jasmine.createSpy('ProcessErrors'); CreateSelect2 = jasmine.createSpy('CreateSelect2'); @@ -65,6 +75,7 @@ describe('Controller: WorkflowAdd', () => { $provide.value('ParseTypeChange', ParseTypeChange); $provide.value('ToJSON', ToJSON); $provide.value('availableLabels', availableLabels); + $provide.value('resolvedModels', resolvedModels); })); beforeEach(angular.mock.inject( ($rootScope, $controller, $q, $httpBackend, _state_, _ConfigService_, _GetChoices_, _Alert_, _GenerateForm_, _ProcessErrors_, _CreateSelect2_, _Wait_, _ParseTypeChange_, _ToJSON_, _availableLabels_) => { From c407cb78b5d6586485d98c3bf13cb07d8b595331 Mon Sep 17 00:00:00 2001 From: Paul Neumann Date: Thu, 5 Apr 2018 19:32:31 +0200 Subject: [PATCH 191/379] Fix trivial UI typo --- awx/ui/client/src/inventory-scripts/inventory-scripts.list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js index 07d8492a77..03b694f460 100644 --- a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js +++ b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js @@ -61,7 +61,7 @@ export default ['i18n', function(i18n){ label: i18n._('Copy'), ngClick: 'copyCustomInv(inventory_script)', "class": 'btn-danger btn-xs', - awToolTip: i18n._('Copy inventory scruot'), + awToolTip: i18n._('Copy inventory script'), dataPlacement: 'top', ngShow: 'inventory_script.summary_fields.user_capabilities.edit' }, From 0f046338ac2cee46c00d4545c50e705b111ea340 Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Fri, 6 Apr 2018 08:47:54 -0400 Subject: [PATCH 192/379] check ParseError fix --- awx/api/generics.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/awx/api/generics.py b/awx/api/generics.py index cf5b2002ee..e12949515b 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -23,7 +23,7 @@ from django.contrib.auth import views as auth_views # Django REST Framework from rest_framework.authentication import get_authorization_header -from rest_framework.exceptions import PermissionDenied, AuthenticationFailed +from rest_framework.exceptions import PermissionDenied, AuthenticationFailed, ParseError from rest_framework import generics from rest_framework.response import Response from rest_framework import status @@ -165,6 +165,9 @@ class APIView(views.APIView): request.drf_request_user = getattr(drf_request, 'user', False) except AuthenticationFailed: request.drf_request_user = None + except ParseError as exc: + request.drf_request_user = None + self.__init_request_error__ = exc return drf_request def finalize_response(self, request, response, *args, **kwargs): @@ -174,6 +177,8 @@ class APIView(views.APIView): if response.status_code >= 400: status_msg = "status %s received by user %s attempting to access %s from %s" % \ (response.status_code, request.user, request.path, request.META.get('REMOTE_ADDR', None)) + if hasattr(self, '__init_request_error__'): + response = self.handle_exception(self.__init_request_error__) if response.status_code == 401: logger.info(status_msg) else: From a344ceda0ee6a475c13924e2a72143e60d54e328 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 14:46:52 -0400 Subject: [PATCH 193/379] User editing permission changes Only allow administrative action for a user who is a system admin or auditor if the the requesting-user is a system admin. Previously a user could be edited if the requesting-user was an admin of ANY of the orgs the user was member of. This is changed to require admin permission to ALL orgs the user is member of. As a special-case, allow org admins to add a user as a member to their organization if the following conditions are met: - the user is not member of any other orgs - the org admin has permissions to all of the roles the user has --- awx/main/access.py | 33 +++++++- awx/main/tests/functional/test_rbac_role.py | 93 +++++++++++++++++++-- 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 172d34df08..d543c00509 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -538,12 +538,37 @@ class UserAccess(BaseAccess): return False return bool(self.user == obj or self.can_admin(obj, data)) + def user_membership_roles(self, u): + return Role.objects.filter( + content_type=ContentType.objects.get_for_model(Organization), + role_field__in=['admin_role', 'member_role'], + members=u + ) + + def is_all_org_admin(self, u): + return not self.user_membership_roles(u).exclude( + ancestors__in=self.user.roles.filter(role_field='admin_role') + ).exists() + + def user_is_orphaned(self, u): + return not self.user_membership_roles(u).exists() + @check_superuser - def can_admin(self, obj, data): + def can_admin(self, obj, data, allow_orphans=False): if not settings.MANAGE_ORGANIZATION_AUTH: return False - return Organization.objects.filter(Q(member_role__members=obj) | Q(admin_role__members=obj), - Q(admin_role__members=self.user)).exists() + if obj.is_superuser or obj.is_system_auditor: + # must be superuser to admin users with system roles + return False + if self.user_is_orphaned(obj): + if not allow_orphans: + # in these cases only superusers can modify orphan users + return False + return not obj.roles.all().exclude( + content_type=ContentType.objects.get_for_model(User) + ).filter(ancestors__in=self.user.roles.all()).exists() + else: + return self.is_all_org_admin(obj) def can_delete(self, obj): if obj == self.user: @@ -2535,7 +2560,7 @@ class RoleAccess(BaseAccess): # unwanted escalations lets ensure that the Organization administartor has the abilty # to admin the user being added to the role. if isinstance(obj.content_object, Organization) and obj.role_field in ['member_role', 'admin_role']: - if not UserAccess(self.user).can_admin(sub_obj, None): + if not UserAccess(self.user).can_admin(sub_obj, None, allow_orphans=True): return False if isinstance(obj.content_object, ResourceMixin) and \ diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 438a72182b..abaa8a4410 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -4,7 +4,7 @@ from awx.main.access import ( RoleAccess, UserAccess, TeamAccess) -from awx.main.models import Role +from awx.main.models import Role, Organization @pytest.mark.django_db @@ -52,13 +52,96 @@ def test_visible_roles(admin_user, system_auditor, rando, organization, project) assert project.admin_role in Role.visible_roles(rando) +# Permissions when adding users to org member/admin @pytest.mark.django_db -def test_org_user_role_attach(user, organization): +def test_org_user_role_attach(user, organization, inventory): + ''' + Org admins must not be able to add arbitrary users to their + organization, because that would give them admin permission to that user + ''' admin = user('admin') nonmember = user('nonmember') + inventory.admin_role.members.add(nonmember) organization.admin_role.members.add(admin) - access = RoleAccess(admin) - assert not access.can_attach(organization.member_role, nonmember, 'members', None) - assert not access.can_attach(organization.admin_role, nonmember, 'members', None) + role_access = RoleAccess(admin) + assert not role_access.can_attach(organization.member_role, nonmember, 'members', None) + assert not role_access.can_attach(organization.admin_role, nonmember, 'members', None) + + +# Singleton user editing restrictions +@pytest.mark.django_db +def test_org_superuser_role_attach(admin_user, org_admin, organization): + ''' + Ideally, you would not add superusers to roles (particularly member_role) + but it has historically been possible + this checks that the situation does not grant unexpected permissions + ''' + organization.member_role.members.add(admin_user) + + role_access = RoleAccess(org_admin) + assert not role_access.can_attach(organization.member_role, admin_user, 'members', None) + assert not role_access.can_attach(organization.admin_role, admin_user, 'members', None) + user_access = UserAccess(org_admin) + assert not user_access.can_change(admin_user, {'last_name': 'Witzel'}) + + +# Sanity check user editing permissions combined with new org roles +@pytest.mark.django_db +def test_org_object_role_not_sufficient(user, organization): + member = user('amember') + obj_admin = user('icontrolallworkflows') + + organization.member_role.members.add(member) + organization.workflow_admin_role.members.add(obj_admin) + + user_access = UserAccess(obj_admin) + assert not user_access.can_change(member, {'last_name': 'Witzel'}) + + +# Org admin user editing permission ANY to ALL change +@pytest.mark.django_db +def test_need_all_orgs_to_admin_user(user): + ''' + Old behavior - org admin to ANY organization that a user is member of + grants permission to admin that user + New behavior enforced here - org admin to ALL organizations that a + user is member of grants permission to admin that user + ''' + org1 = Organization.objects.create(name='org1') + org2 = Organization.objects.create(name='org2') + + org1_admin = user('org1-admin') + org1.admin_role.members.add(org1_admin) + + org12_member = user('org12-member') + org1.member_role.members.add(org12_member) + org2.member_role.members.add(org12_member) + + user_access = UserAccess(org1_admin) + assert not user_access.can_change(org12_member, {'last_name': 'Witzel'}) + + role_access = RoleAccess(org1_admin) + assert not role_access.can_attach(org1.admin_role, org12_member, 'members', None) + assert not role_access.can_attach(org1.member_role, org12_member, 'members', None) + + org2.admin_role.members.add(org1_admin) + assert role_access.can_attach(org1.admin_role, org12_member, 'members', None) + assert role_access.can_attach(org1.member_role, org12_member, 'members', None) + + +# Orphaned user can be added to member role, only in special cases +@pytest.mark.django_db +def test_orphaned_user_allowed(org_admin, rando, organization): + ''' + We still allow adoption of orphaned* users by assigning them to + organization member role, but only in the situation where the + org admin already posesses indirect access to all of the user's roles + *orphaned means user is not a member of any organization + ''' + role_access = RoleAccess(org_admin) + assert role_access.can_attach(organization.member_role, rando, 'members', None) + # Cannot edit the user directly without adding to org first + user_access = UserAccess(org_admin) + assert not user_access.can_change(rando, {'last_name': 'Witzel'}) From cb92f1794a3826b6ba3d3fa52fb07937fd13279c Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 6 Apr 2018 10:15:24 -0400 Subject: [PATCH 194/379] Handle race condition where workflow job status might appear stuck in waiting if we miss the socket event indicating that it got moved to running --- .../workflow-results/workflow-results.controller.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/awx/ui/client/src/workflow-results/workflow-results.controller.js b/awx/ui/client/src/workflow-results/workflow-results.controller.js index be5d26e928..85112cddd8 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.controller.js +++ b/awx/ui/client/src/workflow-results/workflow-results.controller.js @@ -195,6 +195,16 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', if(data.hasOwnProperty('workflow_job_id') && parseInt(data.workflow_job_id, 10) === parseInt($scope.workflow.id,10)){ + // This check ensures that the workflow status icon doesn't get stuck in + // the waiting state due to the UI missing the initial socket message. This + // can happen if the GET request on the workflow job returns "waiting" and + // the sockets aren't established yet so we miss the event that indicates + // the workflow job has moved into a running state. + if (!_.includes(['running', 'successful', 'failed', 'error'], $scope.workflow.status)){ + $scope.workflow.status = 'running'; + runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateWorkflowJobElapsedTimer); + } + WorkflowService.updateStatusOfNode({ treeData: $scope.treeData, nodeId: data.workflow_node_id, From 5fb532c87b31109bd219f187a0474b954ae3009e Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 6 Apr 2018 11:05:54 -0400 Subject: [PATCH 195/379] Updated job results related resources tooltips --- .../features/output/details.directive.js | 21 +++++++++++-------- .../features/output/details.partial.html | 14 ++++++++----- awx/ui/client/features/output/jobs.strings.js | 11 ++++++++++ 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 3c0e677d9b..80251e3d9d 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -106,8 +106,9 @@ function getSourceWorkflowJobDetails () { } const link = `/#/workflows/${sourceWorkflowJob.id}`; + const tooltip = strings.get('resourceTooltips.SOURCE_WORKFLOW_JOB'); - return { link }; + return { link, tooltip }; } function getJobTemplateDetails () { @@ -120,8 +121,9 @@ function getJobTemplateDetails () { const label = 'Job Template'; const link = `/#/templates/job_template/${jobTemplate.id}`; const value = $filter('sanitize')(jobTemplate.name); + const tooltip = strings.get('resourceTooltips.JOB_TEMPLATE'); - return { label, link, value }; + return { label, link, value, tooltip }; } function getLaunchedByDetails () { @@ -142,11 +144,11 @@ function getLaunchedByDetails () { let value; if (createdBy) { - tooltip = 'Edit the User'; + tooltip = strings.get('resourceTooltips.USER'); link = `/#/users/${createdBy.id}`; value = $filter('sanitize')(createdBy.username); } else if (relatedSchedule && jobTemplate) { - tooltip = 'Edit the Schedule'; + tooltip = strings.get('resourceTooltips.SCHEDULE'); link = `/#/templates/job_template/${jobTemplate.id}/schedules/${schedule.id}`; value = $filter('sanitize')(schedule.name); } else { @@ -166,7 +168,7 @@ function getInventoryDetails () { } const label = 'Inventory'; - const tooltip = 'Edit the inventory'; + const tooltip = strings.get('resourceTooltips.INVENTORY'); const value = $filter('sanitize')(inventory.name); let link; @@ -191,18 +193,19 @@ function getProjectDetails () { const label = 'Project'; const link = `/#/projects/${project.id}`; const value = $filter('sanitize')(project.name); + const tooltip = strings.get('resourceTooltips.PROJECT'); if (projectUpdate) { const update = { link: `/#/jobz/project/${projectUpdate.id}`, - tooltip: 'View project checkout results', + tooltip: strings.get('resourceTooltips.PROJECT_UPDATE'), status: projectUpdate.status, }; - return { label, link, value, update }; + return { label, link, value, tooltip, update }; } - return { label, link, value }; + return { label, link, value, tooltip }; } function getSCMRevisionDetails () { @@ -275,7 +278,7 @@ function getCredentialDetails () { } const link = `/#/credentials/${credential.id}`; - const tooltip = 'Edit the Credential'; + const tooltip = strings.get('resourceTooltips.CREDENTIAL'); const value = $filter('sanitize')(credential.name); return { label, link, tooltip, value }; diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index 9417d30670..db5864647f 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -70,14 +70,17 @@
-
+
@@ -143,12 +146,13 @@
-
+
diff --git a/awx/ui/client/features/output/jobs.strings.js b/awx/ui/client/features/output/jobs.strings.js index af52a98472..5e0d9a8cb8 100644 --- a/awx/ui/client/features/output/jobs.strings.js +++ b/awx/ui/client/features/output/jobs.strings.js @@ -20,6 +20,17 @@ function JobsStrings (BaseString) { RUNNING: t.s('The host status bar will update when the job is complete.'), UNAVAILABLE: t.s('Host status information for this job unavailable.'), }; + + ns.resourceTooltips = { + USER: t.s('View the User'), + SCHEDULE: t.s('View the Schedule'), + INVENTORY: t.s('View the Inventory'), + CREDENTIAL: t.s('View the Credential'), + JOB_TEMPLATE: t.s('View the Job Template'), + SOURCE_WORKFLOW_JOB: t.s('View the source Workflow Job'), + PROJECT: t.s('View the Project'), + PROJECT_UPDATE: t.s('View Project checkout results') + }; } JobsStrings.$inject = ['BaseStringService']; From bd7d9db1ceaf647fa0cd2484bedd509234438e9b Mon Sep 17 00:00:00 2001 From: chris meyers Date: Thu, 5 Apr 2018 17:23:09 -0400 Subject: [PATCH 196/379] correctly cascade set null * It's problematic to delete an instance that is referenced by a foreign key; where the referening model is one that has a Polymorphic parent. * Specifically, when Django goes to nullify the relationship it relies on the related instances[0] class type to issue a query to decide what to nullify. So if the foreignkey references multiple different types (i.e. ProjectUpdate, Job) then only 1 of those class types will get nullified. The end result is an IntegrityError when delete() is called. * This changeset ensures that the parent Polymorphic class is queried so that all the foreignkey entries are nullified * Also remove old Django "hack" that doesn't work with Django 1.11 --- .../0030_v330_polymorphic_delete.py | 21 +++++++++++++++++++ awx/main/models/unified_jobs.py | 9 ++------ awx/main/utils/polymorphic.py | 5 +++++ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 awx/main/migrations/0030_v330_polymorphic_delete.py diff --git a/awx/main/migrations/0030_v330_polymorphic_delete.py b/awx/main/migrations/0030_v330_polymorphic_delete.py new file mode 100644 index 0000000000..ad041718ee --- /dev/null +++ b/awx/main/migrations/0030_v330_polymorphic_delete.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.11 on 2018-04-06 13:44 +from __future__ import unicode_literals + +import awx.main.utils.polymorphic +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0029_v330_encrypt_oauth2_secret'), + ] + + operations = [ + migrations.AlterField( + model_name='unifiedjob', + name='instance_group', + field=models.ForeignKey(blank=True, default=None, help_text='The Rampart/Instance group the job was run under', null=True, on_delete=awx.main.utils.polymorphic.SET_NULL, to='main.InstanceGroup'), + ), + ] diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 08e5cecb1c..540d5ee2e7 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -38,6 +38,7 @@ from awx.main.utils import ( copy_model_by_class, copy_m2m_relationships, get_type_for_model, parse_yaml_or_json ) +from awx.main.utils import polymorphic from awx.main.constants import ACTIVE_STATES, CAN_CANCEL from awx.main.redact import UriCleaner, REPLACE_STR from awx.main.consumers import emit_channel_notification @@ -89,9 +90,6 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio ALL_STATUS_CHOICES = OrderedDict(PROJECT_STATUS_CHOICES + INVENTORY_SOURCE_STATUS_CHOICES + JOB_TEMPLATE_STATUS_CHOICES + DEPRECATED_STATUS_CHOICES).items() - # NOTE: Working around a django-polymorphic issue: https://github.com/django-polymorphic/django-polymorphic/issues/229 - base_manager_name = 'base_objects' - class Meta: app_label = 'main' # unique_together here is intentionally commented out. Please make sure sub-classes of this model @@ -536,9 +534,6 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique PASSWORD_FIELDS = ('start_args',) - # NOTE: Working around a django-polymorphic issue: https://github.com/django-polymorphic/django-polymorphic/issues/229 - base_manager_name = 'base_objects' - class Meta: app_label = 'main' @@ -669,7 +664,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique blank=True, null=True, default=None, - on_delete=models.SET_NULL, + on_delete=polymorphic.SET_NULL, help_text=_('The Rampart/Instance group the job was run under'), ) credentials = models.ManyToManyField( diff --git a/awx/main/utils/polymorphic.py b/awx/main/utils/polymorphic.py index 4eabba213e..f8ed1bc160 100644 --- a/awx/main/utils/polymorphic.py +++ b/awx/main/utils/polymorphic.py @@ -1,5 +1,6 @@ from django.contrib.contenttypes.models import ContentType +from django.db import models def build_polymorphic_ctypes_map(cls): @@ -10,3 +11,7 @@ def build_polymorphic_ctypes_map(cls): if ct_model_class and issubclass(ct_model_class, cls): mapping[ct.id] = ct_model_class._camel_to_underscore(ct_model_class.__name__) return mapping + + +def SET_NULL(collector, field, sub_objs, using): + return models.SET_NULL(collector, field, sub_objs.non_polymorphic(), using) From 4c0096a5240f7e6ce8eb8e0a58bd58590cf8670d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 5 Apr 2018 10:43:19 -0400 Subject: [PATCH 197/379] implement celery failure logging using CELERY_ANNOTATIONS see: https://github.com/ansible/awx/issues/1720 see: https://github.com/ansible/tower/issues/1190 --- awx/main/scheduler/tasks.py | 14 ++++-------- awx/main/tasks.py | 45 ++++++++++++++++++++----------------- awx/settings/defaults.py | 10 +++++++++ 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/awx/main/scheduler/tasks.py b/awx/main/scheduler/tasks.py index 89e36f6a93..194b188146 100644 --- a/awx/main/scheduler/tasks.py +++ b/awx/main/scheduler/tasks.py @@ -3,7 +3,7 @@ import logging # Celery -from celery import Task, shared_task +from celery import shared_task # AWX from awx.main.scheduler import TaskManager @@ -15,23 +15,17 @@ logger = logging.getLogger('awx.main.scheduler') # updated model, the call to schedule() may get stale data. -class LogErrorsTask(Task): - def on_failure(self, exc, task_id, args, kwargs, einfo): - logger.exception('Task {} encountered exception.'.format(self.name), exc_info=exc) - super(LogErrorsTask, self).on_failure(exc, task_id, args, kwargs, einfo) - - -@shared_task(base=LogErrorsTask) +@shared_task() def run_job_launch(job_id): TaskManager().schedule() -@shared_task(base=LogErrorsTask) +@shared_task() def run_job_complete(job_id): TaskManager().schedule() -@shared_task(base=LogErrorsTask) +@shared_task() def run_task_manager(): logger.debug("Running Tower task manager.") TaskManager().schedule() diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 6f7d855ab6..36fc1fd57e 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -80,8 +80,8 @@ Try upgrading OpenSSH or providing your private key in an different format. \ logger = logging.getLogger('awx.main.tasks') -class LogErrorsTask(Task): - def on_failure(self, exc, task_id, args, kwargs, einfo): +def log_celery_failure(self, exc, task_id, args, kwargs, einfo): + try: if getattr(exc, 'is_awx_task_error', False): # Error caused by user / tracked in job output logger.warning(six.text_type("{}").format(exc)) @@ -91,7 +91,12 @@ class LogErrorsTask(Task): .format(get_type_for_model(self.model), args[0])) else: logger.exception(six.text_type('Task {} encountered exception.').format(self.name), exc_info=exc) - super(LogErrorsTask, self).on_failure(exc, task_id, args, kwargs, einfo) + except Exception: + # It's fairly critical that this code _not_ raise exceptions on logging + # If you configure external logging in a way that _it_ fails, there's + # not a lot we can do here; sys.stderr.write is a final hail mary + _, _, tb = sys.exc_info() + traceback.print_tb(tb) @celeryd_init.connect @@ -116,7 +121,6 @@ def task_set_logger_pre_run(*args, **kwargs): cache.close() configure_external_logger(settings, is_startup=False) except Exception: - # General exception because LogErrorsTask not used with celery signals logger.exception('Encountered error on initial log configuration.') @@ -129,11 +133,10 @@ def inform_cluster_of_shutdown(*args, **kwargs): logger.warning(six.text_type('Normal shutdown signal for instance {}, ' 'removed self from capacity pool.').format(this_inst.hostname)) except Exception: - # General exception because LogErrorsTask not used with celery signals logger.exception('Encountered problem with normal shutdown signal.') -@shared_task(bind=True, queue='tower_instance_router', base=LogErrorsTask) +@shared_task(bind=True, queue='tower_instance_router') def apply_cluster_membership_policies(self): with advisory_lock('cluster_policy_lock', wait=True): considered_instances = Instance.objects.all().order_by('id') @@ -195,7 +198,7 @@ def apply_cluster_membership_policies(self): handle_ha_toplogy_changes.apply([]) -@shared_task(queue='tower_broadcast_all', bind=True, base=LogErrorsTask) +@shared_task(queue='tower_broadcast_all', bind=True) def handle_setting_changes(self, setting_keys): orig_len = len(setting_keys) for i in range(orig_len): @@ -214,7 +217,7 @@ def handle_setting_changes(self, setting_keys): restart_local_services(['uwsgi']) -@shared_task(bind=True, queue='tower_broadcast_all', base=LogErrorsTask) +@shared_task(bind=True, queue='tower_broadcast_all') def handle_ha_toplogy_changes(self): (changed, instance) = Instance.objects.get_or_register() if changed: @@ -265,7 +268,7 @@ def handle_update_celery_hostname(sender, instance, **kwargs): logger.warn(six.text_type("Set hostname to {}").format(instance.hostname)) -@shared_task(queue='tower', base=LogErrorsTask) +@shared_task(queue='tower') def send_notifications(notification_list, job_id=None): if not isinstance(notification_list, list): raise TypeError("notification_list should be of type list") @@ -289,7 +292,7 @@ def send_notifications(notification_list, job_id=None): notification.save() -@shared_task(bind=True, queue='tower', base=LogErrorsTask) +@shared_task(bind=True, queue='tower') def run_administrative_checks(self): logger.warn("Running administrative checks.") if not settings.TOWER_ADMIN_ALERTS: @@ -311,7 +314,7 @@ def run_administrative_checks(self): fail_silently=True) -@shared_task(bind=True, base=LogErrorsTask) +@shared_task(bind=True) def purge_old_stdout_files(self): nowtime = time.time() for f in os.listdir(settings.JOBOUTPUT_ROOT): @@ -320,7 +323,7 @@ def purge_old_stdout_files(self): logger.info(six.text_type("Removing {}").format(os.path.join(settings.JOBOUTPUT_ROOT,f))) -@shared_task(bind=True, base=LogErrorsTask) +@shared_task(bind=True) def cluster_node_heartbeat(self): logger.debug("Cluster node heartbeat task.") nowtime = now() @@ -393,7 +396,7 @@ def cluster_node_heartbeat(self): logger.exception(six.text_type('Error marking {} as lost').format(other_inst.hostname)) -@shared_task(bind=True, base=LogErrorsTask) +@shared_task(bind=True) def awx_isolated_heartbeat(self): local_hostname = settings.CLUSTER_HOST_ID logger.debug("Controlling node checking for any isolated management tasks.") @@ -417,7 +420,7 @@ def awx_isolated_heartbeat(self): isolated_manager.IsolatedManager.health_check(isolated_instance_qs, awx_application_version) -@shared_task(bind=True, queue='tower', base=LogErrorsTask) +@shared_task(bind=True, queue='tower') def awx_periodic_scheduler(self): run_now = now() state = TowerScheduleState.get_solo() @@ -452,7 +455,7 @@ def awx_periodic_scheduler(self): state.save() -@shared_task(bind=True, queue='tower', base=LogErrorsTask) +@shared_task(bind=True, queue='tower') def handle_work_success(self, result, task_actual): try: instance = UnifiedJob.get_instance_by_type(task_actual['type'], task_actual['id']) @@ -466,7 +469,7 @@ def handle_work_success(self, result, task_actual): run_job_complete.delay(instance.id) -@shared_task(queue='tower', base=LogErrorsTask) +@shared_task(queue='tower') def handle_work_error(task_id, *args, **kwargs): subtasks = kwargs.get('subtasks', None) logger.debug('Executing error task id %s, subtasks: %s' % (task_id, str(subtasks))) @@ -507,7 +510,7 @@ def handle_work_error(task_id, *args, **kwargs): pass -@shared_task(queue='tower', base=LogErrorsTask) +@shared_task(queue='tower') def update_inventory_computed_fields(inventory_id, should_update_hosts=True): ''' Signal handler and wrapper around inventory.update_computed_fields to @@ -527,7 +530,7 @@ def update_inventory_computed_fields(inventory_id, should_update_hosts=True): raise -@shared_task(queue='tower', base=LogErrorsTask) +@shared_task(queue='tower') def update_host_smart_inventory_memberships(): try: with transaction.atomic(): @@ -552,7 +555,7 @@ def update_host_smart_inventory_memberships(): smart_inventory.update_computed_fields(update_groups=False, update_hosts=False) -@shared_task(bind=True, queue='tower', base=LogErrorsTask, max_retries=5) +@shared_task(bind=True, queue='tower', max_retries=5) def delete_inventory(self, inventory_id, user_id): # Delete inventory as user if user_id is None: @@ -597,7 +600,7 @@ def with_path_cleanup(f): return _wrapped -class BaseTask(LogErrorsTask): +class BaseTask(Task): name = None model = None event_model = None @@ -2329,7 +2332,7 @@ def _reconstruct_relationships(copy_mapping): new_obj.save() -@shared_task(bind=True, queue='tower', base=LogErrorsTask) +@shared_task(bind=True, queue='tower') def deep_copy_model_obj( self, model_module, model_name, obj_pk, new_obj_pk, user_pk, sub_obj_list, permission_check_func=None diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 645947eb60..0b179c8515 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -469,6 +469,16 @@ CELERY_QUEUES = ( ) CELERY_ROUTES = {} + +def log_celery_failure(*args): + # Import annotations lazily to avoid polluting the `awx.settings` namespace + # and causing circular imports + from awx.main.tasks import log_celery_failure + return log_celery_failure(*args) + + +CELERY_ANNOTATIONS = {'*': {'on_failure': log_celery_failure}} + CELERYBEAT_SCHEDULER = 'celery.beat.PersistentScheduler' CELERYBEAT_MAX_LOOP_INTERVAL = 60 CELERYBEAT_SCHEDULE = { From 12979260bb1cd78da68694930b5259af399e4b7b Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 6 Apr 2018 12:03:43 -0400 Subject: [PATCH 198/379] include new org roles in permissions fix --- awx/main/access.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/awx/main/access.py b/awx/main/access.py index d543c00509..aeb82ce36e 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -541,7 +541,12 @@ class UserAccess(BaseAccess): def user_membership_roles(self, u): return Role.objects.filter( content_type=ContentType.objects.get_for_model(Organization), - role_field__in=['admin_role', 'member_role'], + role_field__in=[ + 'admin_role', 'member_role', + 'execute_role', 'project_admin_role', 'inventory_admin_role', + 'credential_admin_role', 'workflow_admin_role', + 'notification_admin_role' + ], members=u ) From 7dd4dd00b3216b4239012cafee0088f52afd30bf Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 13:48:04 -0400 Subject: [PATCH 199/379] add min/max to IG fields --- awx/api/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 763c8f33db..4c1dd83b3a 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4581,6 +4581,8 @@ class InstanceGroupSerializer(BaseSerializer): percent_capacity_remaining = serializers.SerializerMethodField() jobs_running = serializers.SerializerMethodField() instances = serializers.SerializerMethodField() + policy_instance_percentage = serializers.IntegerField(min_value=0, max_value=100) + policy_instance_minimum = serializers.IntegerField(min_value=0) class Meta: model = InstanceGroup From 5a380b4437f0b9b9242b8d62ee46f77f0f159ece Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 6 Apr 2018 12:59:53 -0400 Subject: [PATCH 200/379] deprecate jobs creation via sublist --- awx/api/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/awx/api/views.py b/awx/api/views.py index 462a8946b4..812e0168e2 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -3476,6 +3476,13 @@ class JobTemplateJobsList(SubListCreateAPIView): relationship = 'jobs' parent_key = 'job_template' + @property + def allowed_methods(self): + methods = super(JobTemplateJobsList, self).allowed_methods + if get_request_version(getattr(self, 'request', None)) > 1: + methods.remove('POST') + return methods + class JobTemplateInstanceGroupsList(SubListAttachDetachAPIView): From 81fe778676e192240d1a0b33a94cc2192b46a98a Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 6 Apr 2018 13:35:24 -0400 Subject: [PATCH 201/379] Collect roles and update parentage instead of saving JT --- awx/main/signals.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/awx/main/signals.py b/awx/main/signals.py index 36ad3e21e9..f75fa50bf0 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -254,10 +254,22 @@ def save_related_job_templates(sender, instance, **kwargs): raise ValueError('This signal callback is only intended for use with Project or Inventory') if instance.__original_org != instance.organization: - instance.__original_org = instance.organization jtq = JobTemplate.objects.filter(**{sender.__name__.lower(): instance}) - for jt in jtq.all(): - jt.save() + for jt in jtq: + for implicit_role_field in getattr(JobTemplate, '__implicit_role_fields'): + role = getattr(jt, implicit_role_field.name) + original_parents = set(json.loads(role.implicit_parents)) + new_parents = implicit_role_field._resolve_parent_roles(instance) + + role.parents.remove(*list(original_parents - new_parents)) + role.parents.add(*list(new_parents - original_parents)) + + new_parents_list = list(new_parents) + new_parents_list.sort() + new_parents_json = json.dumps(new_parents_list) + if role.implicit_parents != new_parents_json: + role.implicit_parents = new_parents_json + role.save() def connect_computed_field_signals(): From 1195385492ccbf0a8ad596619852d2b83badff71 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 14:46:52 -0400 Subject: [PATCH 202/379] User editing permission changes Only allow administrative action for a user who is a system admin or auditor if the the requesting-user is a system admin. Previously a user could be edited if the requesting-user was an admin of ANY of the orgs the user was member of. This is changed to require admin permission to ALL orgs the user is member of. As a special-case, allow org admins to add a user as a member to their organization if the following conditions are met: - the user is not member of any other orgs - the org admin has permissions to all of the roles the user has --- awx/main/access.py | 33 ++++++- awx/main/tests/functional/test_rbac_role.py | 96 ++++++++++++++++++++- 2 files changed, 121 insertions(+), 8 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index ddecce39e2..73281630bf 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -491,10 +491,35 @@ class UserAccess(BaseAccess): # that a user should be able to edit for themselves. return bool(self.user == obj or self.can_admin(obj, data)) + def user_membership_roles(self, u): + return Role.objects.filter( + content_type=ContentType.objects.get_for_model(Organization), + role_field__in=['admin_role', 'member_role'], + members=u + ) + + def is_all_org_admin(self, u): + return not self.user_membership_roles(u).exclude( + ancestors__in=self.user.roles.filter(role_field='admin_role') + ).exists() + + def user_is_orphaned(self, u): + return not self.user_membership_roles(u).exists() + @check_superuser - def can_admin(self, obj, data): - return Organization.objects.filter(Q(member_role__members=obj) | Q(admin_role__members=obj), - Q(admin_role__members=self.user)).exists() + def can_admin(self, obj, data, allow_orphans=False): + if obj.is_superuser or obj.is_system_auditor: + # must be superuser to admin users with system roles + return False + if self.user_is_orphaned(obj): + if not allow_orphans: + # in these cases only superusers can modify orphan users + return False + return not obj.roles.all().exclude( + content_type=ContentType.objects.get_for_model(User) + ).filter(ancestors__in=self.user.roles.all()).exists() + else: + return self.is_all_org_admin(obj) def can_delete(self, obj): if obj == self.user: @@ -2401,7 +2426,7 @@ class RoleAccess(BaseAccess): # unwanted escalations lets ensure that the Organization administartor has the abilty # to admin the user being added to the role. if isinstance(obj.content_object, Organization) and obj.role_field in ['member_role', 'admin_role']: - if not UserAccess(self.user).can_admin(sub_obj, None): + if not UserAccess(self.user).can_admin(sub_obj, None, allow_orphans=True): return False if isinstance(obj.content_object, ResourceMixin) and \ diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 62b8469abf..7ec80e8d94 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -4,6 +4,7 @@ from awx.main.access import ( RoleAccess, UserAccess, TeamAccess) +from awx.main.models import Role, Organization @pytest.mark.django_db @@ -35,12 +36,99 @@ def test_role_access_attach(rando, inventory): @pytest.mark.django_db -def test_org_user_role_attach(user, organization): +def test_visible_roles(admin_user, system_auditor, rando, organization, project): + ''' + system admin & system auditor fixtures needed to create system roles + ''' + organization.auditor_role.members.add(rando) + access = RoleAccess(rando) + + assert rando not in organization.admin_role + assert access.can_read(organization.admin_role) + assert organization.admin_role in Role.visible_roles(rando) + + assert rando not in project.admin_role + assert access.can_read(project.admin_role) + assert project.admin_role in Role.visible_roles(rando) + + +# Permissions when adding users to org member/admin +@pytest.mark.django_db +def test_org_user_role_attach(user, organization, inventory): + ''' + Org admins must not be able to add arbitrary users to their + organization, because that would give them admin permission to that user + ''' admin = user('admin') nonmember = user('nonmember') + inventory.admin_role.members.add(nonmember) organization.admin_role.members.add(admin) - access = RoleAccess(admin) - assert not access.can_attach(organization.member_role, nonmember, 'members', None) - assert not access.can_attach(organization.admin_role, nonmember, 'members', None) + role_access = RoleAccess(admin) + assert not role_access.can_attach(organization.member_role, nonmember, 'members', None) + assert not role_access.can_attach(organization.admin_role, nonmember, 'members', None) + + +# Singleton user editing restrictions +@pytest.mark.django_db +def test_org_superuser_role_attach(admin_user, org_admin, organization): + ''' + Ideally, you would not add superusers to roles (particularly member_role) + but it has historically been possible + this checks that the situation does not grant unexpected permissions + ''' + organization.member_role.members.add(admin_user) + + role_access = RoleAccess(org_admin) + assert not role_access.can_attach(organization.member_role, admin_user, 'members', None) + assert not role_access.can_attach(organization.admin_role, admin_user, 'members', None) + user_access = UserAccess(org_admin) + assert not user_access.can_change(admin_user, {'last_name': 'Witzel'}) + + +# Org admin user editing permission ANY to ALL change +@pytest.mark.django_db +def test_need_all_orgs_to_admin_user(user): + ''' + Old behavior - org admin to ANY organization that a user is member of + grants permission to admin that user + New behavior enforced here - org admin to ALL organizations that a + user is member of grants permission to admin that user + ''' + org1 = Organization.objects.create(name='org1') + org2 = Organization.objects.create(name='org2') + + org1_admin = user('org1-admin') + org1.admin_role.members.add(org1_admin) + + org12_member = user('org12-member') + org1.member_role.members.add(org12_member) + org2.member_role.members.add(org12_member) + + user_access = UserAccess(org1_admin) + assert not user_access.can_change(org12_member, {'last_name': 'Witzel'}) + + role_access = RoleAccess(org1_admin) + assert not role_access.can_attach(org1.admin_role, org12_member, 'members', None) + assert not role_access.can_attach(org1.member_role, org12_member, 'members', None) + + org2.admin_role.members.add(org1_admin) + assert role_access.can_attach(org1.admin_role, org12_member, 'members', None) + assert role_access.can_attach(org1.member_role, org12_member, 'members', None) + + +# Orphaned user can be added to member role, only in special cases +@pytest.mark.django_db +def test_orphaned_user_allowed(org_admin, rando, organization): + ''' + We still allow adoption of orphaned* users by assigning them to + organization member role, but only in the situation where the + org admin already posesses indirect access to all of the user's roles + *orphaned means user is not a member of any organization + ''' + role_access = RoleAccess(org_admin) + assert role_access.can_attach(organization.member_role, rando, 'members', None) + # Cannot edit the user directly without adding to org first + user_access = UserAccess(org_admin) + assert not user_access.can_change(rando, {'last_name': 'Witzel'}) From 99fb0fa4cd9b0e11563a3fdd39011210c3b0af74 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 6 Apr 2018 14:49:27 -0400 Subject: [PATCH 203/379] Extract update_role_parentage_for_instance --- awx/main/fields.py | 35 +++++++++++++++++++++-------------- awx/main/signals.py | 20 +++++--------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index 3d541d524a..a4519610c3 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -48,7 +48,9 @@ from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role from awx.main import utils -__all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField', 'SmartFilterField'] +__all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField', + 'SmartFilterField', 'update_role_parentage_for_instance', + 'is_implicit_parent'] # Provide a (better) custom error message for enum jsonschema validation @@ -181,6 +183,23 @@ def is_implicit_parent(parent_role, child_role): return False +def update_role_parentage_for_instance(instance): + '''update_role_parentage_for_instance + updates the parents listing for all the roles + of a given instance if they have changed + ''' + for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): + cur_role = getattr(instance, implicit_role_field.name) + new_parents = implicit_role_field._resolve_parent_roles(instance) + cur_role.parents.set(new_parents) + new_parents_list = list(new_parents) + new_parents_list.sort() + new_parents_json = json.dumps(new_parents_list) + if cur_role.implicit_parents != new_parents_json: + cur_role.implicit_parents = new_parents_json + cur_role.save() + + class ImplicitRoleDescriptor(ForwardManyToOneDescriptor): pass @@ -303,19 +322,7 @@ class ImplicitRoleField(models.ForeignKey): type(latest_instance).objects.filter(pk=latest_instance.pk).update(**updates) Role.rebuild_role_ancestor_list(role_ids, []) - # Update parentage if necessary - for implicit_role_field in getattr(latest_instance.__class__, '__implicit_role_fields'): - cur_role = getattr(latest_instance, implicit_role_field.name) - original_parents = set(json.loads(cur_role.implicit_parents)) - new_parents = implicit_role_field._resolve_parent_roles(latest_instance) - cur_role.parents.remove(*list(original_parents - new_parents)) - cur_role.parents.add(*list(new_parents - original_parents)) - new_parents_list = list(new_parents) - new_parents_list.sort() - new_parents_json = json.dumps(new_parents_list) - if cur_role.implicit_parents != new_parents_json: - cur_role.implicit_parents = new_parents_json - cur_role.save() + update_role_parentage_for_instance(latest_instance) instance.refresh_from_db() diff --git a/awx/main/signals.py b/awx/main/signals.py index f75fa50bf0..ea4a5ae927 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -34,7 +34,10 @@ from awx.api.serializers import * # noqa from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore from awx.main.utils import ignore_inventory_computed_fields, ignore_inventory_group_removal, _inventory_updates from awx.main.tasks import update_inventory_computed_fields -from awx.main.fields import is_implicit_parent +from awx.main.fields import ( + is_implicit_parent, + update_role_parentage_for_instance, +) from awx.main import consumers @@ -256,20 +259,7 @@ def save_related_job_templates(sender, instance, **kwargs): if instance.__original_org != instance.organization: jtq = JobTemplate.objects.filter(**{sender.__name__.lower(): instance}) for jt in jtq: - for implicit_role_field in getattr(JobTemplate, '__implicit_role_fields'): - role = getattr(jt, implicit_role_field.name) - original_parents = set(json.loads(role.implicit_parents)) - new_parents = implicit_role_field._resolve_parent_roles(instance) - - role.parents.remove(*list(original_parents - new_parents)) - role.parents.add(*list(new_parents - original_parents)) - - new_parents_list = list(new_parents) - new_parents_list.sort() - new_parents_json = json.dumps(new_parents_list) - if role.implicit_parents != new_parents_json: - role.implicit_parents = new_parents_json - role.save() + update_role_parentage_for_instance(jt) def connect_computed_field_signals(): From 0f07f4f956b3097143733e86dff37e6bd5f1b00f Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 6 Apr 2018 15:52:29 -0400 Subject: [PATCH 204/379] Added generic error handling to promises in relaunch button component --- .../relaunchButton.component.js | 25 +++++++++++++++++++ awx/ui/client/src/shared/Utilities.js | 14 +++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js index 3d7d6b09ff..f30afabf36 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js @@ -117,6 +117,11 @@ function atRelaunchCtrl ( const relaunchType = launchRes.data.type === 'job' ? 'playbook' : launchRes.data.type; $state.go('jobz', { id: launchRes.data.id, type: relaunchType }, { reload: true }); } + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: strings.get('error.HEADER'), + msg: strings.get('error.CALL', { path: `${config.url}`, status }) + }); }); } }); @@ -165,6 +170,11 @@ function atRelaunchCtrl ( if (!$state.includes('jobs')) { $state.go('jobz', { id: postUpdateRes.data.id, type: 'inventory' }, { reload: true }); } + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: strings.get('error.HEADER'), + msg: strings.get('error.CALL', { path: `${config.url}`, status }) + }); }); } else { Alert( @@ -184,6 +194,11 @@ function atRelaunchCtrl ( if (!$state.includes('jobs')) { $state.go('jobz', { id: postUpdateRes.data.id, type: 'project' }, { reload: true }); } + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: strings.get('error.HEADER'), + msg: strings.get('error.CALL', { path: `${config.url}`, status }) + }); }); } else { Alert( @@ -201,6 +216,11 @@ function atRelaunchCtrl ( if (!$state.includes('jobs')) { $state.go('workflowResults', { id: launchRes.data.id }, { reload: true }); } + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: strings.get('error.HEADER'), + msg: strings.get('error.CALL', { path: `${config.url}`, status }) + }); }); } else if (vm.job.type === 'ad_hoc_command') { const adHocCommand = new AdHocCommand(); @@ -220,6 +240,11 @@ function atRelaunchCtrl ( if (!$state.includes('jobs')) { $state.go('jobz', { id: launchRes.data.id, type: 'command' }, { reload: true }); } + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: strings.get('error.HEADER'), + msg: strings.get('error.CALL', { path: `${config.url}`, status }) + }); }); } }); diff --git a/awx/ui/client/src/shared/Utilities.js b/awx/ui/client/src/shared/Utilities.js index 23d593ccd2..301a7eae9d 100644 --- a/awx/ui/client/src/shared/Utilities.js +++ b/awx/ui/client/src/shared/Utilities.js @@ -222,14 +222,14 @@ angular.module('Utilities', ['RestServices', 'Utilities']) } else if (typeof data === 'object' && data !== null) { if (Object.keys(data).length > 0) { keys = Object.keys(data); - if (Array.isArray(data[keys[0]])) { - msg = data[keys[0]][0]; - } else { - msg = ""; - _.forOwn(data, function(value, key) { + msg = ""; + _.forOwn(data, function(value, key) { + if (Array.isArray(data[key])) { + msg += `${key}: ${data[key][0]}`; + } else { msg += `${key} : ${value} `; - }); - } + } + }); Alert(defaultMsg.hdr, msg); } else { Alert(defaultMsg.hdr, defaultMsg.msg); From 639da5de59407530ea1e1b643f7d8676ec07dd19 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Fri, 6 Apr 2018 13:28:24 -0700 Subject: [PATCH 205/379] Changes https flag to false for UI dev env --- awx/ui/build/webpack.watch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/build/webpack.watch.js b/awx/ui/build/webpack.watch.js index 5bf1aad89c..4450024a30 100644 --- a/awx/ui/build/webpack.watch.js +++ b/awx/ui/build/webpack.watch.js @@ -51,7 +51,7 @@ const watch = { stats: 'minimal', publicPath: '/static/', host: '127.0.0.1', - https: true, + https: false, port: 3000, clientLogLevel: 'none', proxy: [{ From ea16bef39b98da9464d78d8eaf774d34e5f31f96 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 6 Apr 2018 17:50:49 -0400 Subject: [PATCH 206/379] ux updates to navigation --- .../lib/components/components.strings.js | 6 +- .../client/lib/components/layout/_index.less | 90 ++++++++++++++++--- .../lib/components/layout/layout.partial.html | 36 +++++--- .../components/layout/side-nav.directive.js | 2 +- awx/ui/client/lib/theme/_variables.less | 19 +++- 5 files changed, 128 insertions(+), 25 deletions(-) diff --git a/awx/ui/client/lib/components/components.strings.js b/awx/ui/client/lib/components/components.strings.js index 015fd5d387..a53da0676f 100644 --- a/awx/ui/client/lib/components/components.strings.js +++ b/awx/ui/client/lib/components/components.strings.js @@ -76,7 +76,11 @@ function ComponentsStrings (BaseString) { APPLICATIONS: t.s('Applications'), SETTINGS: t.s('Settings'), FOOTER_ABOUT: t.s('About'), - FOOTER_COPYRIGHT: t.s('Copyright © 2018 Red Hat, Inc.') + FOOTER_COPYRIGHT: t.s('Copyright © 2018 Red Hat, Inc.'), + VIEWS_HEADER: t.s('Views'), + RESOURCES_HEADER: t.s('Resources'), + ACCESS_HEADER: t.s('Access'), + ADMINISTRATION_HEADER: t.s('Administration') }; ns.relaunch = { diff --git a/awx/ui/client/lib/components/layout/_index.less b/awx/ui/client/lib/components/layout/_index.less index fd4e29c144..73868252e6 100644 --- a/awx/ui/client/lib/components/layout/_index.less +++ b/awx/ui/client/lib/components/layout/_index.less @@ -90,30 +90,47 @@ overflow-y: auto; max-height: 100vh; min-width: @at-width-collapsed-side-nav; + width: @at-width-collapsed-side-nav; + overflow-x: hidden; z-index: @at-z-index-side-nav; + .at-Popover-container { + margin-top: 2px; + margin-left: -11px; + } + + .at-Popover-arrow [ + padding-top: 7px; + margin-left: -10px; + } + .at-Layout-sideNavItem { background: inherit; color: @at-color-side-nav-content; display: flex; cursor: pointer; text-transform: uppercase; - - > i.fa { - padding-left: 20px; - } - + font-size: 12px; i { cursor: pointer; - color: @at-color-side-nav-content; - font-size: @at-height-side-nav-item-icon; + color: @at-color-side-nav-item-icon; + font-size: @at-font-size-side-nav-icon; padding: @at-padding-side-nav-item-icon; + padding-left: @at-padding-left-side-nav-item-icon; } i.fa-cubes { margin-left: -4px; } + i.fa-user { + margin-left: 1px; + } + + i.fa-users { + margin-left: -1px; + } + &:hover, &.is-active { background: @at-color-side-nav-item-background-hover; @@ -127,12 +144,37 @@ i.fa-cubes { margin-left: -9px; } + + i.fa-user { + margin-left: -4px; + } + + i.fa-users { + margin-left: -6px; + } } } + .at-Layout-sideNavToggle { + padding-top: @at-padding-top-side-nav-toggle; + + i { + padding-left: @at-padding-left-side-nav-toggle-icon; + } + } + .at-Layout-sideNavSpacer { - background: inherit; - height: 5px; + border-bottom: 1px solid @at-color-side-nav-space-collapsed-border; + padding: 0; + margin: @at-margin-side-nav-space-collapsed; + } + + .at-Layout-sideNavSpacer--first { + display: none; + } + + .at-Layout-sideNavHeader { + display: none; } &--expanded { @@ -143,11 +185,33 @@ justify-content: flex-start; align-items: center; padding-right: @at-padding-between-side-nav-icon-text; + + i { + padding-left: @at-padding-left-side-nav-item-icon-expanded; + } } + .at-Layout-main { padding-left: @at-width-expanded-side-nav; } + + .at-Layout-sideNavSpacer--first { + display: inherit; + } + + .at-Layout-sideNavSpacer { + height: @at-height-side-nav-spacer; + font-size: @at-font-size-side-nav-space; + color: @at-color-side-nav-item-spacer; + padding: @at-padding-side-nav-item-spacer; + text-transform: uppercase; + border-bottom: 0; + margin: 0; + } + + .at-Layout-sideNavHeader { + display: inherit; + } } } @@ -191,9 +255,13 @@ .at-Layout-sideNavItem.at-Layout-sideNavToggle { display: flex; - height: 40px; + height: @at-height-side-nav-toggle-mobile; align-items: center; - width: 55px; + width: @at-width-side-nav-toggle-mobile; + + i { + padding-bottom: @at-padding-bottom-side-nav-toggle-mobile; + } } .at-Layout-sideNavItem, diff --git a/awx/ui/client/lib/components/layout/layout.partial.html b/awx/ui/client/lib/components/layout/layout.partial.html index b1ada5e000..ada6b2f1ea 100644 --- a/awx/ui/client/lib/components/layout/layout.partial.html +++ b/awx/ui/client/lib/components/layout/layout.partial.html @@ -31,6 +31,11 @@
+
+ + {{:: $parent.layoutVm.getString('VIEWS_HEADER') }} + +
@@ -39,38 +44,49 @@ -
- +
+ + {{:: $parent.layoutVm.getString('RESOURCES_HEADER') }} + +
+ - + - + -
+
+ + {{:: $parent.layoutVm.getString('ACCESS_HEADER') }} + +
-
- +
+ + {{:: $parent.layoutVm.getString('ADMINISTRATION_HEADER') }} + +
+ - -
diff --git a/awx/ui/client/lib/components/layout/side-nav.directive.js b/awx/ui/client/lib/components/layout/side-nav.directive.js index f089d54eee..3fbf7cd86f 100644 --- a/awx/ui/client/lib/components/layout/side-nav.directive.js +++ b/awx/ui/client/lib/components/layout/side-nav.directive.js @@ -16,7 +16,7 @@ function AtSideNavController ($scope, $window) { const vm = this || {}; const breakpoint = 700; - vm.isExpanded = false; + vm.isExpanded = true; vm.toggleExpansion = () => { vm.isExpanded = !vm.isExpanded; diff --git a/awx/ui/client/lib/theme/_variables.less b/awx/ui/client/lib/theme/_variables.less index 918f67342c..ffb37ac96a 100644 --- a/awx/ui/client/lib/theme/_variables.less +++ b/awx/ui/client/lib/theme/_variables.less @@ -174,6 +174,9 @@ @at-color-side-nav-content: @at-white; @at-color-side-nav-item-background-hover: @at-gray-b7; @at-color-side-nav-item-border-hover: @at-white; +@at-color-side-nav-item-icon: @at-white; +@at-color-side-nav-item-spacer: @at-gray-d7; +@at-color-side-nav-space-collapsed-border: @at-gray-b7; @at-color-footer-background: @at-gray-fc; @at-color-footer: @at-gray-70; @@ -208,6 +211,8 @@ @at-font-size-navigation: @at-font-size-3x; @at-font-size-table-heading: @at-font-size-3x; @at-font-size-menu-icon: @at-font-size-5x; +@at-font-size-side-nav-icon: 19px; +@at-font-size-side-nav-space: 11px; @at-font-size-list-row-item-tag: 10px; @at-font-size-list-row-action: 19px; @at-font-size-list-row-action-icon: 19px; @@ -228,6 +233,13 @@ @at-padding-input: @at-space-2x; @at-padding-top-nav-item-sides: @at-space-4x; @at-padding-side-nav-item-icon: @at-space-3x; +@at-padding-side-nav-item-icon: 10px 15px; +@at-padding-side-nav-item-spacer: 10px 10px 25px 15px; +@at-padding-bottom-side-nav-toggle-mobile: 15px; +@at-padding-top-side-nav-toggle: 5px; +@at-padding-left-side-nav-toggle-icon: 15px; +@at-padding-left-side-nav-item-icon: 10px; +@at-padding-left-side-nav-item-icon-expanded: 15px; @at-padding-between-side-nav-icon-text: @at-space-3x; @at-padding-footer-right: @at-space-4x; @at-padding-footer-bottom: @at-space-4x; @@ -246,6 +258,7 @@ @at-margin-form-label-hint: @at-space-2x; @at-margin-top-nav-item-between-icon-and-name: @at-space-2x; @at-margin-top-nav-item-icon-socket-top-makeup: -3px; +@at-margin-side-nav-space-collapsed: 10px 0; @at-margin-after-footer-link: @at-space; @at-margin-footer-top: @at-space-4x; @@ -274,7 +287,7 @@ @at-height-top-nav: 60px; @at-height-top-nav-item-icon: 21px; @at-height-top-nav-item-icon-socket: 18px; -@at-height-side-nav-item-icon: 20px; +@at-height-side-nav-item-icon: 19px; @at-height-side-nav-spacer: 20px; @at-height-top-side-nav-makeup: 55px; @at-height-list-empty: 200px; @@ -282,13 +295,15 @@ @at-height-list-row-item: 27px; @at-height-list-row-item-tag: 15px; @at-height-list-row-action: 30px; +@at-height-side-nav-toggle-mobile: 40px; @at-width-input-button-sm: 72px; @at-width-input-button-md: 84px; @at-width-collapsed-side-nav: 50px; -@at-width-expanded-side-nav: 200px; +@at-width-expanded-side-nav: 180px; @at-width-list-row-item-label: 120px; @at-width-list-row-action: 30px; +@at-width-side-nav-toggle-mobile: 50px; @at-line-height-list-row-item-header: @at-space-3x; @at-line-height-list-row-item-labels: 17px; From 177b771826554bcb2fb53d8a983ce5baf99d8f7c Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 6 Apr 2018 18:04:10 -0400 Subject: [PATCH 207/379] fix less syntax arrow --- awx/ui/client/lib/components/layout/_index.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/lib/components/layout/_index.less b/awx/ui/client/lib/components/layout/_index.less index 73868252e6..ad2dc00958 100644 --- a/awx/ui/client/lib/components/layout/_index.less +++ b/awx/ui/client/lib/components/layout/_index.less @@ -99,7 +99,7 @@ margin-left: -11px; } - .at-Popover-arrow [ + .at-Popover-arrow { padding-top: 7px; margin-left: -10px; } From 60d311c1a924f1cd02f2566235919cb4e1eadd4e Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 5 Apr 2018 16:41:34 -0500 Subject: [PATCH 208/379] don't try to use stats events for adhoc commands --- .../features/output/index.controller.js | 4 +-- .../client/features/output/status.service.js | 36 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 0fee736561..17dfb63c4c 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -95,7 +95,7 @@ function init () { } }); - $scope.$on(resource.ws.events, handleSocketEvent); + $scope.$on(resource.ws.events, handleJobEvent); $scope.$on(resource.ws.status, handleStatusEvent); if (!status.isRunning()) { @@ -107,7 +107,7 @@ function handleStatusEvent (scope, data) { status.pushStatusEvent(data); } -function handleSocketEvent (scope, data) { +function handleJobEvent (scope, data) { engine.pushJobEvent(data); status.pushJobEvent(data); diff --git a/awx/ui/client/features/output/status.service.js b/awx/ui/client/features/output/status.service.js index a3d80dbc01..4d16dc3d07 100644 --- a/awx/ui/client/features/output/status.service.js +++ b/awx/ui/client/features/output/status.service.js @@ -2,7 +2,9 @@ const JOB_START = 'playbook_on_start'; const JOB_END = 'playbook_on_stats'; const PLAY_START = 'playbook_on_play_start'; const TASK_START = 'playbook_on_task_start'; + const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped']; +const FINISHED = ['running', 'successful', 'failed', 'error']; let moment; @@ -14,6 +16,7 @@ function JobStatusService (_moment_) { this.created = resource.model.get('created'); this.job = resource.model.get('id'); + this.jobType = resource.model.get('type'); this.project = resource.model.get('project'); this.elapsed = resource.model.get('elapsed'); this.started = resource.model.get('started'); @@ -21,6 +24,7 @@ function JobStatusService (_moment_) { this.jobStatus = resource.model.get('status'); this.projectStatus = resource.model.get('summary_fields.project_update.status'); + this.latestTime = null; this.playCount = null; this.taskCount = null; this.hostCount = null; @@ -40,6 +44,15 @@ function JobStatusService (_moment_) { } else if (isProjectEvent) { this.setProjectStatus(data.status); } + + if (this.isCommand()) { + if (_.includes(FINISHED, data.status)) { + if (!this.started && this.latestJobEventTime) { + this.started = moment(this.latestJobEventTime) + .subtract(this.elapsed, 'seconds'); + } + } + } }; this.pushJobEvent = data => { @@ -52,8 +65,8 @@ function JobStatusService (_moment_) { if (isLatest) { this.counter = data.counter; + this.latestTime = data.created; this.elapsed = moment(data.created).diff(this.created, 'seconds'); - this.jobStatus = _.get(data, ['summary_fields', 'job', 'status']); } if (data.event === JOB_START) { @@ -97,14 +110,12 @@ function JobStatusService (_moment_) { }; this.updateStats = () => { - if (!this.statsEvent) { - return; - } - this.updateHostCounts(); - this.setFinished(this.statsEvent.created); - this.setJobStatus(this.statsEvent.failed ? 'failed' : 'successful'); + if (this.statsEvent) { + this.setFinished(this.statsEvent.created); + this.setJobStatus(this.statsEvent.failed ? 'failed' : 'successful'); + } }; this.isRunning = () => (Boolean(this.started) && !this.finished) || @@ -112,6 +123,7 @@ function JobStatusService (_moment_) { (this.jobStatus === 'pending') || (this.jobStatus === 'waiting'); + this.isCommand = () => (this.jobType === 'ad_hoc_command'); this.getPlayCount = () => this.playCount; this.getTaskCount = () => this.taskCount; this.getHostCount = () => this.hostCount; @@ -125,6 +137,16 @@ function JobStatusService (_moment_) { this.setJobStatus = status => { this.jobStatus = status; + + if (this.isCommand() && _.includes(FINISHED, status)) { + if (this.latestTime) { + this.finished = this.latestTime; + + if (!this.started) { + this.started = moment(this.latestTime).subtract(this.elapsed, 'seconds'); + } + } + } }; this.setProjectStatus = status => { From 56935fef94f9a4fefe73715faf03a0f4d4079cbe Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Fri, 6 Apr 2018 14:12:26 -0500 Subject: [PATCH 209/379] account for existence of 'check' project update jobs --- .../features/output/details.directive.js | 51 ++++++++++++------- .../features/output/details.partial.html | 8 +-- awx/ui/client/features/output/jobs.strings.js | 2 +- .../client/features/output/status.service.js | 7 +++ 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index 80251e3d9d..e0015d0b94 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -184,7 +184,6 @@ function getInventoryDetails () { function getProjectDetails () { const project = resource.model.get('summary_fields.project'); - const projectUpdate = resource.model.get('summary_fields.project_update'); if (!project) { return null; @@ -195,17 +194,32 @@ function getProjectDetails () { const value = $filter('sanitize')(project.name); const tooltip = strings.get('resourceTooltips.PROJECT'); - if (projectUpdate) { - const update = { - link: `/#/jobz/project/${projectUpdate.id}`, - tooltip: strings.get('resourceTooltips.PROJECT_UPDATE'), - status: projectUpdate.status, - }; + return { label, link, value, tooltip }; +} - return { label, link, value, tooltip, update }; +function getProjectStatusDetails (projectStatus) { + const project = resource.model.get('summary_fields.project'); + const jobStatus = projectStatus || resource.model.get('summary_fields.project_update.status'); + + if (!project) { + return null; } - return { label, link, value, tooltip }; + return jobStatus; +} + +function getProjectUpdateDetails (updateId) { + const project = resource.model.get('summary_fields.project'); + const jobId = updateId || resource.model.get('summary_fields.project_update.id'); + + if (!project) { + return null; + } + + const link = `/#/jobz/project/${jobId}`; + const tooltip = strings.get('resourceTooltips.PROJECT_UPDATE'); + + return { link, tooltip }; } function getSCMRevisionDetails () { @@ -495,6 +509,8 @@ function AtJobDetailsController ( vm.sourceWorkflowJob = getSourceWorkflowJobDetails(); vm.inventory = getInventoryDetails(); vm.project = getProjectDetails(); + vm.projectUpdate = getProjectUpdateDetails(); + vm.projectStatus = getProjectStatusDetails(); vm.scmRevision = getSCMRevisionDetails(); vm.playbook = getPlaybookDetails(); vm.resultTraceback = getResultTracebackDetails(); @@ -533,16 +549,15 @@ function AtJobDetailsController ( vm.deleteJob = deleteJob; vm.toggleLabels = toggleLabels; - $scope.$watch(status.getStarted, value => { vm.started = getStartDetails(value); }); - $scope.$watch(status.getJobStatus, value => { vm.status = getStatusDetails(value); }); - $scope.$watch(status.getFinished, value => { vm.finished = getFinishDetails(value); }); + const observe = (getter, transform, key) => { + $scope.$watch(getter, value => { vm[key] = transform(value); }); + }; - $scope.$watch(status.getProjectStatus, value => { - if (!value) return; - - vm.project.update = vm.project.update || {}; - vm.project.update.status = value; - }); + observe(status.getStarted, getStartDetails, 'started'); + observe(status.getJobStatus, getStatusDetails, 'status'); + observe(status.getFinished, getFinishDetails, 'finished'); + observe(status.getProjectUpdateId, getProjectUpdateDetails, 'projectUpdate'); + observe(status.getProjectStatus, getProjectStatusDetails, 'projectStatus'); }; } diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index db5864647f..cc3f18b19d 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -119,11 +119,11 @@
- - + this.hostStatusCounts || {}; this.getJobStatus = () => this.jobStatus; this.getProjectStatus = () => this.projectStatus; + this.getProjectUpdateId = () => this.projectUpdateId; this.getElapsed = () => this.elapsed; this.getStatsEvent = () => this.statsEvent; this.getStarted = () => this.started; @@ -153,6 +156,10 @@ function JobStatusService (_moment_) { this.projectStatus = status; }; + this.setProjectUpdateId = id => { + this.projectUpdateId = id; + }; + this.setFinished = time => { this.finished = time; }; From f369e3ba0fd1963db3377426ecb67a2fb1926bd5 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 6 Apr 2018 14:04:51 -0400 Subject: [PATCH 210/379] handle 400 error creating sys auditor --- awx/api/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awx/api/views.py b/awx/api/views.py index 812e0168e2..05c15b9243 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -995,6 +995,8 @@ class OrganizationInventoriesList(SubListAPIView): class BaseUsersList(SubListCreateAttachDetachAPIView): def post(self, request, *args, **kwargs): ret = super(BaseUsersList, self).post( request, *args, **kwargs) + if ret.status_code != 201: + return ret try: if ret.data is not None and request.data.get('is_system_auditor', False): # This is a faux-field that just maps to checking the system From 78e0c02a08615ef150687308f64274f243f81e23 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 9 Apr 2018 08:36:21 -0400 Subject: [PATCH 211/379] no stats event expected for inventory updates --- .../client/features/output/status.service.js | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/awx/ui/client/features/output/status.service.js b/awx/ui/client/features/output/status.service.js index 6af3bce91d..638d2ff399 100644 --- a/awx/ui/client/features/output/status.service.js +++ b/awx/ui/client/features/output/status.service.js @@ -4,7 +4,7 @@ const PLAY_START = 'playbook_on_play_start'; const TASK_START = 'playbook_on_task_start'; const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped']; -const FINISHED = ['running', 'successful', 'failed', 'error']; +const FINISHED = ['successful', 'failed', 'error']; let moment; @@ -46,15 +46,6 @@ function JobStatusService (_moment_) { this.setProjectStatus(data.status); this.setProjectUpdateId(data.unified_job_id); } - - if (this.isCommand()) { - if (_.includes(FINISHED, data.status)) { - if (!this.started && this.latestJobEventTime) { - this.started = moment(this.latestJobEventTime) - .subtract(this.elapsed, 'seconds'); - } - } - } }; this.pushJobEvent = data => { @@ -72,7 +63,7 @@ function JobStatusService (_moment_) { } if (data.event === JOB_START) { - this.started = data.created; + this.started = this.started || data.created; } if (data.event === PLAY_START) { @@ -125,7 +116,9 @@ function JobStatusService (_moment_) { (this.jobStatus === 'pending') || (this.jobStatus === 'waiting'); - this.isCommand = () => (this.jobType === 'ad_hoc_command'); + this.isExpectingStatsEvent = () => (this.jobType === 'job') || + (this.jobType === 'project_update'); + this.getPlayCount = () => this.playCount; this.getTaskCount = () => this.taskCount; this.getHostCount = () => this.hostCount; @@ -141,11 +134,11 @@ function JobStatusService (_moment_) { this.setJobStatus = status => { this.jobStatus = status; - if (this.isCommand() && _.includes(FINISHED, status)) { + if (!this.isExpectingStatsEvent() && _.includes(FINISHED, status)) { if (this.latestTime) { - this.finished = this.latestTime; + this.setFinished(this.latestTime); - if (!this.started) { + if (!this.started && this.elapsed) { this.started = moment(this.latestTime).subtract(this.elapsed, 'seconds'); } } From c88621f5fbcd801059c1891d11e86ca853df8ca5 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Apr 2018 08:42:09 -0400 Subject: [PATCH 212/379] more accurate handling of serializer cred versioning --- awx/api/serializers.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 4c1dd83b3a..0ac84bea4a 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3097,7 +3097,6 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO def get_summary_fields(self, obj): summary_fields = super(JobTemplateSerializer, self).get_summary_fields(obj) all_creds = [] - extra_creds = [] if obj.pk: for cred in obj.credentials.all(): summarized_cred = { @@ -3108,20 +3107,31 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO 'credential_type_id': cred.credential_type_id } all_creds.append(summarized_cred) - if self.is_detail_view: - for summarized_cred in all_creds: - if summarized_cred['kind'] in ('cloud', 'net'): - extra_creds.append(summarized_cred) - elif summarized_cred['kind'] == 'ssh': - summary_fields['credential'] = summarized_cred - elif summarized_cred['kind'] == 'vault': - summary_fields['vault_credential'] = summarized_cred + # Organize credential data into multitude of deprecated fields + extra_creds = [] + vault_credential = None + credential = None + for summarized_cred in all_creds: + if summarized_cred['kind'] in ('cloud', 'net'): + extra_creds.append(summarized_cred) + elif summarized_cred['kind'] == 'ssh': + credential = summarized_cred + elif summarized_cred['kind'] == 'vault': + vault_credential = summarized_cred + # Selectively apply those fields, depending on view deetails + if (self.is_detail_view or self.version == 1) and credential: + summary_fields['credential'] = credential + else: + # Credential could be an empty dictionary in this case + summary_fields.pop('credential', None) + if (self.is_detail_view or self.version == 1) and vault_credential: + summary_fields['vault_credential'] = vault_credential + else: + # vault credential could be empty dictionary + summary_fields.pop('vault_credential', None) if self.version > 1: if self.is_detail_view: summary_fields['extra_credentials'] = extra_creds - else: - # Credential would be an empty dictionary in this case - summary_fields.pop('credential', None) summary_fields['credentials'] = all_creds return summary_fields From ba66996addb6591f1a8170991c430756d3170c5b Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 9 Apr 2018 09:10:50 -0400 Subject: [PATCH 213/379] missing import for celery failure handler --- awx/main/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 36fc1fd57e..b01e5331f6 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -13,6 +13,7 @@ import os import re import shutil import stat +import sys import tempfile import time import traceback From c1f192199558f22a3434552044e240d971d08b4d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 9 Apr 2018 09:56:43 -0400 Subject: [PATCH 214/379] add test for JT credential summary_fields --- .../tests/functional/api/test_job_template.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index fe729b8c84..3bd337f10f 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -116,6 +116,51 @@ def test_create_v1_rbac_check(get, post, project, credential, net_credential, ra post(reverse('api:job_template_list', kwargs={'version': 'v1'}), base_kwargs, rando, expect=403) +# TODO: remove as each field tested has support removed +@pytest.mark.django_db +def test_jt_deprecated_summary_fields( + project, inventory, + machine_credential, net_credential, vault_credential, + mocker): + jt = JobTemplate.objects.create( + project=project, + inventory=inventory, + playbook='helloworld.yml' + ) + + class MockView: + kwargs = {} + request = None + + class MockRequest: + version = 'v1' + user = None + + view = MockView() + request = MockRequest() + view.request = request + serializer = JobTemplateSerializer(instance=jt, context={'view': view, 'request': request}) + + for kwargs in [{}, {'pk': 1}]: # detail vs. list view + for version in ['v1', 'v2']: + view.kwargs = kwargs + request.version = version + sf = serializer.get_summary_fields(jt) + assert 'credential' not in sf + assert 'vault_credential' not in sf + + jt.credentials.add(machine_credential, net_credential, vault_credential) + + view.kwargs = {'pk': 1} + for version in ['v1', 'v2']: + request.version = version + sf = serializer.get_summary_fields(jt) + assert 'credential' in sf + assert sf['credential'] # not empty dict + assert 'vault_credential' in sf + assert sf['vault_credential'] + + @pytest.mark.django_db def test_extra_credential_creation(get, post, organization_factory, job_template_factory, credentialtype_aws): objs = organization_factory("org", superusers=['admin']) From 37546d64952e4a40ea1e52a102cfd00df7991fcb Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 9 Apr 2018 11:20:26 -0400 Subject: [PATCH 215/379] move invalid list row properties to row component --- .../client/features/templates/list.view.html | 123 ------------------ .../templates/templatesList.view.html | 7 +- .../lib/components/list/row.directive.js | 4 +- .../lib/components/list/row.partial.html | 6 +- 4 files changed, 11 insertions(+), 129 deletions(-) delete mode 100644 awx/ui/client/features/templates/list.view.html diff --git a/awx/ui/client/features/templates/list.view.html b/awx/ui/client/features/templates/list.view.html deleted file mode 100644 index ba45081856..0000000000 --- a/awx/ui/client/features/templates/list.view.html +++ /dev/null @@ -1,123 +0,0 @@ -
- - - {{:: vm.strings.get('list.PANEL_TITLE') }} -
- {{ template_dataset.count }} -
-
- - -
- - - -
- -
-
- - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - -
-
-
- - - - diff --git a/awx/ui/client/features/templates/templatesList.view.html b/awx/ui/client/features/templates/templatesList.view.html index 9b344955a9..206cce7acf 100644 --- a/awx/ui/client/features/templates/templatesList.view.html +++ b/awx/ui/client/features/templates/templatesList.view.html @@ -40,10 +40,9 @@ -
- -
+ template-id="{{ template.id }}" + invalid="vm.isInvalid(template)" + invalid-tooltip="vm.invalidTooltip">
+
+
+ +
+
From 27e8e55d15785fd3a64dfce7b8271c43472d71ca Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 9 Apr 2018 11:06:32 -0400 Subject: [PATCH 216/379] add e2e test for auth form rendering after tab switch --- awx/ui/test/e2e/objects/configuration.js | 40 ++++++++++++ awx/ui/test/e2e/objects/dashboard.js | 24 ++++++++ .../test/e2e/objects/sections/navigation.js | 3 +- .../tests/test-configuration-ldap-fields.js | 61 +++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 awx/ui/test/e2e/objects/configuration.js create mode 100644 awx/ui/test/e2e/objects/dashboard.js create mode 100644 awx/ui/test/e2e/tests/test-configuration-ldap-fields.js diff --git a/awx/ui/test/e2e/objects/configuration.js b/awx/ui/test/e2e/objects/configuration.js new file mode 100644 index 0000000000..fdf45677a9 --- /dev/null +++ b/awx/ui/test/e2e/objects/configuration.js @@ -0,0 +1,40 @@ +import breadcrumb from './sections/breadcrumb'; +import header from './sections/header'; +import navigation from './sections/navigation'; + +const sections = { + header, + navigation, + breadcrumb, +}; + +const commands = [{ + load () { + this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724 + return this.navigate(); + }, + selectSubcategory (name) { + const spinny = 'div.spinny'; + const select = '#configure-dropdown-nav'; + const arrow = `${select} + span span[class$="arrow"]`; + const option = `//li[contains(text(), "${name}")]`; + + this.api.waitForElementVisible(arrow); + this.api.click(arrow); + + this.api.useXpath(); + this.api.waitForElementVisible(option); + this.api.click(option); + this.api.useCss(); + + return this; + }, +}]; + +module.exports = { + url () { + return `${this.api.globals.launch_url}/#/configuration`; + }, + sections, + commands, +}; diff --git a/awx/ui/test/e2e/objects/dashboard.js b/awx/ui/test/e2e/objects/dashboard.js new file mode 100644 index 0000000000..ca6695ca5c --- /dev/null +++ b/awx/ui/test/e2e/objects/dashboard.js @@ -0,0 +1,24 @@ +import breadcrumb from './sections/breadcrumb'; +import header from './sections/header'; +import navigation from './sections/navigation'; + +const sections = { + header, + navigation, + breadcrumb, +}; + +const commands = [{ + load () { + this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724 + return this.navigate(); + } +}]; + +module.exports = { + url () { + return `${this.api.globals.launch_url}/#/home`; + }, + sections, + commands, +}; diff --git a/awx/ui/test/e2e/objects/sections/navigation.js b/awx/ui/test/e2e/objects/sections/navigation.js index 62f3359202..2ac257f1dd 100644 --- a/awx/ui/test/e2e/objects/sections/navigation.js +++ b/awx/ui/test/e2e/objects/sections/navigation.js @@ -17,7 +17,8 @@ const navigation = { inventoryScripts: 'i[class$="fa-code"]', notifications: 'i[class$="fa-bell"]', managementJobs: 'i[class$="fa-wrench"]', - instanceGroups: 'i[class$="fa-server"]' + instanceGroups: 'i[class$="fa-server"]', + settings: 'i[class$="fa-cog"]', } }; diff --git a/awx/ui/test/e2e/tests/test-configuration-ldap-fields.js b/awx/ui/test/e2e/tests/test-configuration-ldap-fields.js new file mode 100644 index 0000000000..181e33f509 --- /dev/null +++ b/awx/ui/test/e2e/tests/test-configuration-ldap-fields.js @@ -0,0 +1,61 @@ +module.exports = { + 'expected LDAP codemirror fields are rendered when returning from another tab': client => { + const authTab = '#auth_tab'; + const authView = 'div[ui-view="auth"]'; + const ldapForm = '#configuration_ldap_template_form'; + const systemTab = '#system_tab'; + const systemView = 'div[ui-view="system"]'; + + const { navigation } = client.page.dashboard().section; + const configuration = client.page.configuration(); + + client.login(); + client.waitForAngular(); + + navigation + .waitForElementVisible('@settings') + .click('@settings'); + + configuration.waitForElementVisible(authView); + + configuration.waitForElementVisible(systemTab); + configuration.click(systemTab); + + configuration.waitForElementNotVisible(authView); + configuration.waitForElementVisible(systemView); + + configuration.waitForElementVisible(authTab); + configuration.click(authTab); + + configuration.waitForElementNotVisible(systemView); + configuration.waitForElementVisible(authView); + + configuration.selectSubcategory('LDAP'); + configuration.waitForElementVisible(ldapForm); + + const expectedCodemirrorFields = [ + 'AUTH_LDAP_USER_SEARCH', + 'AUTH_LDAP_GROUP_SEARCH', + 'AUTH_LDAP_USER_ATTR_MAP', + 'AUTH_LDAP_GROUP_TYPE_PARAMS', + 'AUTH_LDAP_USER_FLAGS_BY_GROUP', + 'AUTH_LDAP_ORGANIZATION_MAP', + 'AUTH_LDAP_TEAM_MAP', + ]; + + const ldapCodeMirrors = `${ldapForm} div[class^="CodeMirror"] textarea`; + + client.elements('css selector', ldapCodeMirrors, ({ value }) => { + client.assert.equal(value.length, expectedCodemirrorFields.length); + }); + + expectedCodemirrorFields.forEach(fieldName => { + const codemirror = `#cm-${fieldName}-container div[class^="CodeMirror"]`; + + configuration.expect.element(codemirror).visible; + configuration.expect.element(codemirror).enabled; + }); + + client.end(); + }, +}; From 93e6d06bca987b48892b3854b6eb9e82dc42badc Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 9 Apr 2018 11:53:59 -0400 Subject: [PATCH 217/379] Fixed host/groups pagination issue --- awx/ui/client/src/shared/paginate/paginate.controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/shared/paginate/paginate.controller.js b/awx/ui/client/src/shared/paginate/paginate.controller.js index 34a3031a0e..5c4a9f1090 100644 --- a/awx/ui/client/src/shared/paginate/paginate.controller.js +++ b/awx/ui/client/src/shared/paginate/paginate.controller.js @@ -41,7 +41,8 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q queryset = _.merge(origQuerySet, { page: page }); } else { - queryset = _.merge($stateParams[`${$scope.iterator}_search`], { page: page }); + let origStateParams = _.cloneDeep($stateParams[`${$scope.iterator}_search`]); + queryset = _.merge(origStateParams, { page: page }); } if (!$scope.querySet) { $state.go('.', { From 3ab255bda8ff4600e116fb3510d32912d5093c58 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 9 Apr 2018 11:57:32 -0400 Subject: [PATCH 218/379] remove todo messages --- awx/ui/client/features/templates/templatesList.view.html | 1 - 1 file changed, 1 deletion(-) diff --git a/awx/ui/client/features/templates/templatesList.view.html b/awx/ui/client/features/templates/templatesList.view.html index 206cce7acf..7713e153db 100644 --- a/awx/ui/client/features/templates/templatesList.view.html +++ b/awx/ui/client/features/templates/templatesList.view.html @@ -37,7 +37,6 @@
- Date: Mon, 9 Apr 2018 11:45:28 -0400 Subject: [PATCH 219/379] show module arg details for command jobs --- awx/ui/client/features/output/details.directive.js | 12 ++++++++++++ awx/ui/client/features/output/details.partial.html | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index e0015d0b94..ea20d73cdd 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -68,6 +68,17 @@ function getFinishDetails (finished) { return { label, value }; } +function getModuleArgDetails () { + const value = resource.model.get('module_args'); + const label = 'Module Args'; + + if (!value) { + return null; + } + + return { label, value }; +} + function getJobTypeDetails () { const unmapped = resource.model.get('job_type'); @@ -504,6 +515,7 @@ function AtJobDetailsController ( vm.status = getStatusDetails(); vm.started = getStartDetails(); vm.finished = getFinishDetails(); + vm.moduleArgs = getModuleArgDetails(); vm.jobType = getJobTypeDetails(); vm.jobTemplate = getJobTemplateDetails(); vm.sourceWorkflowJob = getSourceWorkflowJobDetails(); diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index cc3f18b19d..7e923b5758 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -63,6 +63,12 @@
+ +
+ +
{{ vm.moduleArgs.value }}
+
+
@@ -70,7 +76,7 @@
-
+
Date: Mon, 9 Apr 2018 13:04:57 -0400 Subject: [PATCH 220/379] update collapse nav spacer padding amount --- awx/ui/client/lib/theme/_variables.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/lib/theme/_variables.less b/awx/ui/client/lib/theme/_variables.less index ffb37ac96a..9f3d5470fa 100644 --- a/awx/ui/client/lib/theme/_variables.less +++ b/awx/ui/client/lib/theme/_variables.less @@ -258,7 +258,7 @@ @at-margin-form-label-hint: @at-space-2x; @at-margin-top-nav-item-between-icon-and-name: @at-space-2x; @at-margin-top-nav-item-icon-socket-top-makeup: -3px; -@at-margin-side-nav-space-collapsed: 10px 0; +@at-margin-side-nav-space-collapsed: 5px 0; @at-margin-after-footer-link: @at-space; @at-margin-footer-top: @at-space-4x; From 67ba534097cfbac117272fbdb1c70e6afeaf684a Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 9 Apr 2018 11:07:34 -0400 Subject: [PATCH 221/379] ensure correct system and auth forms are loaded --- .../configuration-auth.controller.js | 2 +- .../configuration/configuration.controller.js | 10 ++++- .../configuration/configuration.partial.html | 43 ++++++++++++++++--- .../configuration-system.controller.js | 2 +- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js b/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js index 5b80e40ddb..66d2f8818d 100644 --- a/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js +++ b/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js @@ -82,7 +82,7 @@ export default [ }; var activeForm = function() { - if(!$scope.$parent[formTracker.currentFormName()].$dirty) { + if(!_.get($scope.$parent, [formTracker.currentFormName(), '$dirty'])) { authVm.activeAuthForm = getActiveAuthForm(); formTracker.setCurrentAuth(authVm.activeAuthForm); startCodeMirrors(); diff --git a/awx/ui/client/src/configuration/configuration.controller.js b/awx/ui/client/src/configuration/configuration.controller.js index 9752522e9d..db0696ddaf 100644 --- a/awx/ui/client/src/configuration/configuration.controller.js +++ b/awx/ui/client/src/configuration/configuration.controller.js @@ -169,7 +169,7 @@ export default [ setCurrentSystem: function(form) { this.currentSystem = form; this.setCurrent(this.currentSystem); - } + }, }; // Default to auth form and tab @@ -275,7 +275,13 @@ export default [ vm.activeTab = setForm; if (setForm !== 'license') { - formTracker.setCurrent(setForm); + if (setForm === 'auth') { + formTracker.setCurrentAuth(formTracker.currentAuth); + } else if (setForm === 'system') { + formTracker.setCurrentSystem(formTracker.currenSystem); + } else { + formTracker.setCurrent(setForm); + } $state.go('configuration', { currentTab: setForm diff --git a/awx/ui/client/src/configuration/configuration.partial.html b/awx/ui/client/src/configuration/configuration.partial.html index c7f65fd05e..47f9abfe10 100644 --- a/awx/ui/client/src/configuration/configuration.partial.html +++ b/awx/ui/client/src/configuration/configuration.partial.html @@ -7,16 +7,47 @@
-
CONFIGURE {{BRAND_NAME}}
+
CONFIGURE {{ BRAND_NAME }}
-
Authentication
-
Jobs
-
System
-
User Interface
-
License
+
+ Authentication +
+
+ Jobs +
+
+ System +
+
+ User Interface +
+
+ License +
diff --git a/awx/ui/client/src/configuration/system-form/configuration-system.controller.js b/awx/ui/client/src/configuration/system-form/configuration-system.controller.js index 11a631aca3..68a01f6a2c 100644 --- a/awx/ui/client/src/configuration/system-form/configuration-system.controller.js +++ b/awx/ui/client/src/configuration/system-form/configuration-system.controller.js @@ -47,7 +47,7 @@ export default [ } var activeForm = function() { - if(!$scope.$parent[formTracker.currentFormName()].$dirty) { + if(!_.get($scope.$parent, [formTracker.currentFormName(), '$dirty'])) { systemVm.activeSystemForm = systemVm.dropdownValue; formTracker.setCurrentSystem(systemVm.activeSystemForm); } else { From 18c95bf706230ba9e3ace70646daa1eeaa085853 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 9 Apr 2018 11:26:20 -0400 Subject: [PATCH 222/379] add exception handling to deprecated v1 credential support see: https://github.com/ansible/tower/issues/1268 --- awx/api/fields.py | 19 +++++++++++ awx/api/serializers.py | 33 +++++-------------- .../test_deprecated_credential_assignment.py | 32 ++++++++++++++++++ 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/awx/api/fields.py b/awx/api/fields.py index 6a1ddb6018..5276ef4dec 100644 --- a/awx/api/fields.py +++ b/awx/api/fields.py @@ -3,12 +3,14 @@ # Django from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ObjectDoesNotExist # Django REST Framework from rest_framework import serializers # AWX from awx.conf import fields +from awx.main.models import Credential __all__ = ['BooleanNullField', 'CharNullField', 'ChoiceNullField', 'VerbatimField'] @@ -87,3 +89,20 @@ class OAuth2ProviderField(fields.DictField): if invalid_flags: self.fail('invalid_key_names', invalid_key_names=', '.join(list(invalid_flags))) return data + + +class DeprecatedCredentialField(serializers.IntegerField): + + def __init__(self, **kwargs): + kwargs['allow_null'] = True + kwargs['default'] = None + kwargs['min_value'] = 1 + kwargs['help_text'] = 'This resource has been deprecated and will be removed in a future release' + super(DeprecatedCredentialField, self).__init__(**kwargs) + + def to_internal_value(self, pk): + try: + Credential.objects.get(pk=pk) + except ObjectDoesNotExist: + raise serializers.ValidationError(_('Credential {} does not exist').format(pk)) + return pk diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 0ac84bea4a..bd17ff1126 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -56,12 +56,11 @@ from awx.main.validators import vars_validate_or_raise from awx.conf.license import feature_enabled from awx.api.versioning import reverse, get_request_version -from awx.api.fields import BooleanNullField, CharNullField, ChoiceNullField, VerbatimField +from awx.api.fields import (BooleanNullField, CharNullField, ChoiceNullField, + VerbatimField, DeprecatedCredentialField) logger = logging.getLogger('awx.api.serializers') -DEPRECATED = 'This resource has been deprecated and will be removed in a future release' - # Fields that should be summarized regardless of object type. DEFAULT_SUMMARY_FIELDS = ('id', 'name', 'description')# , 'created_by', 'modified_by')#, 'type') @@ -1957,9 +1956,7 @@ class CustomInventoryScriptSerializer(BaseSerializer): class InventorySourceOptionsSerializer(BaseSerializer): - credential = models.PositiveIntegerField( - blank=True, null=True, default=None, - help_text='This resource has been deprecated and will be removed in a future release') + credential = DeprecatedCredentialField() class Meta: fields = ('*', 'source', 'source_path', 'source_script', 'source_vars', 'credential', @@ -2818,15 +2815,11 @@ class V1JobOptionsSerializer(BaseSerializer): model = Credential fields = ('*', 'cloud_credential', 'network_credential') - V1_FIELDS = { - 'cloud_credential': models.PositiveIntegerField(blank=True, null=True, default=None, help_text=DEPRECATED), - 'network_credential': models.PositiveIntegerField(blank=True, null=True, default=None, help_text=DEPRECATED), - } + V1_FIELDS = ('cloud_credential', 'network_credential',) def build_field(self, field_name, info, model_class, nested_depth): if field_name in self.V1_FIELDS: - return self.build_standard_field(field_name, - self.V1_FIELDS[field_name]) + return (DeprecatedCredentialField, {}) return super(V1JobOptionsSerializer, self).build_field(field_name, info, model_class, nested_depth) @@ -2837,15 +2830,11 @@ class LegacyCredentialFields(BaseSerializer): model = Credential fields = ('*', 'credential', 'vault_credential') - LEGACY_FIELDS = { - 'credential': models.PositiveIntegerField(blank=True, null=True, default=None, help_text=DEPRECATED), - 'vault_credential': models.PositiveIntegerField(blank=True, null=True, default=None, help_text=DEPRECATED), - } + LEGACY_FIELDS = ('credential', 'vault_credential',) def build_field(self, field_name, info, model_class, nested_depth): if field_name in self.LEGACY_FIELDS: - return self.build_standard_field(field_name, - self.LEGACY_FIELDS[field_name]) + return (DeprecatedCredentialField, {}) return super(LegacyCredentialFields, self).build_field(field_name, info, model_class, nested_depth) @@ -3718,9 +3707,7 @@ class LaunchConfigurationBaseSerializer(BaseSerializer): class WorkflowJobTemplateNodeSerializer(LaunchConfigurationBaseSerializer): - credential = models.PositiveIntegerField( - blank=True, null=True, default=None, - help_text='This resource has been deprecated and will be removed in a future release') + credential = DeprecatedCredentialField() 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) @@ -3811,9 +3798,7 @@ class WorkflowJobTemplateNodeSerializer(LaunchConfigurationBaseSerializer): class WorkflowJobNodeSerializer(LaunchConfigurationBaseSerializer): - credential = models.PositiveIntegerField( - blank=True, null=True, default=None, - help_text='This resource has been deprecated and will be removed in a future release') + credential = DeprecatedCredentialField() 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) diff --git a/awx/main/tests/functional/api/test_deprecated_credential_assignment.py b/awx/main/tests/functional/api/test_deprecated_credential_assignment.py index e9090630e2..f6466affd7 100644 --- a/awx/main/tests/functional/api/test_deprecated_credential_assignment.py +++ b/awx/main/tests/functional/api/test_deprecated_credential_assignment.py @@ -1,3 +1,4 @@ +import json import mock import pytest @@ -5,6 +6,14 @@ from awx.main.models import Credential, Job from awx.api.versioning import reverse +@pytest.fixture +def ec2_source(inventory, project): + with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): + return inventory.inventory_sources.create( + name='some_source', update_on_project_update=True, source='ec2', + source_project=project, scm_last_revision=project.scm_revision) + + @pytest.fixture def job_template(job_template, project, inventory): job_template.playbook = 'helloworld.yml' @@ -34,6 +43,14 @@ def test_ssh_credential_access(get, job_template, admin, machine_credential): assert resp.data['summary_fields']['credential']['kind'] == 'ssh' +@pytest.mark.django_db +@pytest.mark.parametrize('key', ('credential', 'vault_credential', 'cloud_credential', 'network_credential')) +def test_invalid_credential_update(get, patch, job_template, admin, key): + url = reverse('api:job_template_detail', kwargs={'pk': job_template.pk, 'version': 'v1'}) + resp = patch(url, {key: 999999}, admin, expect=400) + assert 'Credential 999999 does not exist' in json.loads(resp.content)[key] + + @pytest.mark.django_db def test_ssh_credential_update(get, patch, job_template, admin, machine_credential): url = reverse('api:job_template_detail', kwargs={'pk': job_template.pk}) @@ -362,3 +379,18 @@ def test_rbac_default_credential_usage(get, post, job_template, alice, machine_c new_cred.use_role.members.add(alice) url = reverse('api:job_template_launch', kwargs={'pk': job_template.pk}) post(url, {'credential': new_cred.pk}, alice, expect=201) + + +@pytest.mark.django_db +def test_inventory_source_deprecated_credential(get, patch, admin, ec2_source, credential): + url = reverse('api:inventory_source_detail', kwargs={'pk': ec2_source.pk}) + patch(url, {'credential': credential.pk}, admin, expect=200) + resp = get(url, admin, expect=200) + assert json.loads(resp.content)['credential'] == credential.pk + + +@pytest.mark.django_db +def test_inventory_source_invalid_deprecated_credential(patch, admin, ec2_source, credential): + url = reverse('api:inventory_source_detail', kwargs={'pk': ec2_source.pk}) + resp = patch(url, {'credential': 999999}, admin, expect=400) + assert 'Credential 999999 does not exist' in resp.content From 23d14546466caf70a3f759a4860d7be2a94bac02 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 9 Apr 2018 15:01:16 -0400 Subject: [PATCH 223/379] Fixed inventory source form error message. Leveraged inv src model for adding/editing existing object --- .../sources/add/sources-add.controller.js | 145 ++++++++-------- .../sources/edit/sources-edit.controller.js | 157 +++++++++--------- .../sources/edit/sources-edit.route.js | 4 +- 3 files changed, 158 insertions(+), 148 deletions(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js index 10865e4823..685fe3aa1b 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js @@ -9,24 +9,78 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'rbacUiControlService', 'ToJSON', 'SourcesService', 'Empty', 'Wait', 'Rest', 'Alert', 'ProcessErrors', 'inventorySourcesOptions', - '$rootScope', 'i18n', + '$rootScope', 'i18n', 'InventorySourceModel', 'InventoryHostsStrings', function($state, $stateParams, $scope, SourcesFormDefinition, ParseTypeChange, GenerateForm, inventoryData, GroupsService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, rbacUiControlService, ToJSON, SourcesService, Empty, Wait, Rest, Alert, ProcessErrors, - inventorySourcesOptions,$rootScope, i18n) { + inventorySourcesOptions,$rootScope, i18n, InventorySource, InventoryHostsStrings) { let form = SourcesFormDefinition; - init(); + $scope.mode = 'add'; + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope, true); + $scope.canAdd = inventorySourcesOptions.actions.POST; + $scope.envParseType = 'yaml'; - function init() { - $scope.mode = 'add'; - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope, true); - $scope.canAdd = inventorySourcesOptions.actions.POST; - $scope.envParseType = 'yaml'; - initSources(); - } + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'rax_regions', + choice_name: 'rax_region_choices', + options: inventorySourcesOptions + }); + + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'ec2_regions', + choice_name: 'ec2_region_choices', + options: inventorySourcesOptions + }); + + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'gce_regions', + choice_name: 'gce_region_choices', + options: inventorySourcesOptions + }); + + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'azure_regions', + choice_name: 'azure_rm_region_choices', + options: inventorySourcesOptions + }); + + // Load options for group_by + GetChoices({ + scope: $scope, + field: 'group_by', + variable: 'ec2_group_by', + choice_name: 'ec2_group_by_choices', + options: inventorySourcesOptions + }); + + initRegionSelect(); + + GetChoices({ + scope: $scope, + field: 'verbosity', + variable: 'verbosity_options', + options: inventorySourcesOptions + }); + + initVerbositySelect(); + + GetSourceTypeOptions({ + scope: $scope, + variable: 'source_type_options' + }); + + const inventorySource = new InventorySource(); var getInventoryFiles = function (project) { var url; @@ -225,65 +279,6 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', $scope.verbosity = $scope.verbosity_options[1]; } - function initSources(){ - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'rax_regions', - choice_name: 'rax_region_choices', - options: inventorySourcesOptions - }); - - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'ec2_regions', - choice_name: 'ec2_region_choices', - options: inventorySourcesOptions - }); - - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'gce_regions', - choice_name: 'gce_region_choices', - options: inventorySourcesOptions - }); - - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'azure_regions', - choice_name: 'azure_rm_region_choices', - options: inventorySourcesOptions - }); - - // Load options for group_by - GetChoices({ - scope: $scope, - field: 'group_by', - variable: 'ec2_group_by', - choice_name: 'ec2_group_by_choices', - options: inventorySourcesOptions - }); - - initRegionSelect(); - - GetChoices({ - scope: $scope, - field: 'verbosity', - variable: 'verbosity_options', - options: inventorySourcesOptions - }); - - initVerbositySelect(); - - GetSourceTypeOptions({ - scope: $scope, - variable: 'source_type_options' - }); - } - $scope.formCancel = function() { $state.go('^'); }; @@ -325,9 +320,17 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', } else { params.source = null; } - SourcesService.post(params).then((response) => { + + inventorySource.request('post', { + data: params + }).then((response) => { let inventory_source_id = response.data.id; $state.go('^.edit', {inventory_source_id: inventory_source_id}, {reload: true}); + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: InventoryHostsStrings.get('error.CALL', { path: `${config.url}`, status }) + }); }); }; } diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js index 8218c35fbb..e2e9ca3968 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js @@ -7,85 +7,87 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', 'rbacUiControlService', 'ToJSON', 'ParseTypeChange', 'GroupsService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', - 'inventorySourceData', 'SourcesService', 'inventoryData', 'inventorySourcesOptions', 'Empty', - 'Wait', 'Rest', 'Alert', '$rootScope', 'i18n', + 'SourcesService', 'inventoryData', 'inventorySourcesOptions', 'Empty', + 'Wait', 'Rest', 'Alert', '$rootScope', 'i18n', 'InventoryHostsStrings', + 'ProcessErrors', 'inventorySource', function($state, $stateParams, $scope, ParseVariableString, rbacUiControlService, ToJSON,ParseTypeChange, GroupsService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, - inventorySourceData, SourcesService, inventoryData, inventorySourcesOptions, Empty, - Wait, Rest, Alert, $rootScope, i18n) { + SourcesService, inventoryData, inventorySourcesOptions, Empty, + Wait, Rest, Alert, $rootScope, i18n, InventoryHostsStrings, + ProcessErrors, inventorySource) { - function init() { - $scope.projectBasePath = GetBasePath('projects') + '?not__status=never updated'; - $scope.canAdd = inventorySourcesOptions.actions.POST; - // instantiate expected $scope values from inventorySourceData - _.assign($scope, - {credential: inventorySourceData.credential}, - {overwrite: inventorySourceData.overwrite}, - {overwrite_vars: inventorySourceData.overwrite_vars}, - {update_on_launch: inventorySourceData.update_on_launch}, - {update_cache_timeout: inventorySourceData.update_cache_timeout}, - {instance_filters: inventorySourceData.instance_filters}, - {inventory_script: inventorySourceData.source_script}, - {verbosity: inventorySourceData.verbosity}); + const inventorySourceData = inventorySource.get(); - $scope.inventory_source_obj = inventorySourceData; - if (inventorySourceData.credential) { - $scope.credential_name = inventorySourceData.summary_fields.credential.name; + $scope.projectBasePath = GetBasePath('projects') + '?not__status=never updated'; + $scope.canAdd = inventorySourcesOptions.actions.POST; + // instantiate expected $scope values from inventorySourceData + _.assign($scope, + {credential: inventorySourceData.credential}, + {overwrite: inventorySourceData.overwrite}, + {overwrite_vars: inventorySourceData.overwrite_vars}, + {update_on_launch: inventorySourceData.update_on_launch}, + {update_cache_timeout: inventorySourceData.update_cache_timeout}, + {instance_filters: inventorySourceData.instance_filters}, + {inventory_script: inventorySourceData.source_script}, + {verbosity: inventorySourceData.verbosity}); + + $scope.inventory_source_obj = inventorySourceData; + if (inventorySourceData.credential) { + $scope.credential_name = inventorySourceData.summary_fields.credential.name; + } + + if(inventorySourceData.source === 'scm') { + $scope.project = inventorySourceData.source_project; + $scope.project_name = inventorySourceData.summary_fields.source_project.name; + updateSCMProject(); + } + + // display custom inventory_script name + if (inventorySourceData.source === 'custom' && inventorySourceData.summary_fields.source_script) { + $scope.inventory_script_name = inventorySourceData.summary_fields.source_script.name; + } + $scope = angular.extend($scope, inventorySourceData); + + $scope.$watch('summary_fields.user_capabilities.edit', function(val) { + $scope.canAdd = val; + }); + + $scope.$on('sourceTypeOptionsReady', function() { + initSourceSelect(); + }); + + $scope.envParseType = 'yaml'; + + initSources(); + + GetChoices({ + scope: $scope, + field: 'verbosity', + variable: 'verbosity_options', + options: inventorySourcesOptions + }); + + var i; + for (i = 0; i < $scope.verbosity_options.length; i++) { + if ($scope.verbosity_options[i].value === $scope.verbosity) { + $scope.verbosity = $scope.verbosity_options[i]; } + } - if(inventorySourceData.source === 'scm') { - $scope.project = inventorySourceData.source_project; - $scope.project_name = inventorySourceData.summary_fields.source_project.name; + initVerbositySelect(); + + $scope.$watch('verbosity', initVerbositySelect); + + // Register a watcher on project_name + if ($scope.getInventoryFilesUnregister) { + $scope.getInventoryFilesUnregister(); + } + $scope.getInventoryFilesUnregister = $scope.$watch('project', function (newValue, oldValue) { + if (newValue !== oldValue) { updateSCMProject(); } - - // display custom inventory_script name - if (inventorySourceData.source === 'custom' && inventorySourceData.summary_fields.source_script) { - $scope.inventory_script_name = inventorySourceData.summary_fields.source_script.name; - } - $scope = angular.extend($scope, inventorySourceData); - - $scope.$watch('summary_fields.user_capabilities.edit', function(val) { - $scope.canAdd = val; - }); - - $scope.$on('sourceTypeOptionsReady', function() { - initSourceSelect(); - }); - - $scope.envParseType = 'yaml'; - - initSources(); - - GetChoices({ - scope: $scope, - field: 'verbosity', - variable: 'verbosity_options', - options: inventorySourcesOptions - }); - - var i; - for (i = 0; i < $scope.verbosity_options.length; i++) { - if ($scope.verbosity_options[i].value === $scope.verbosity) { - $scope.verbosity = $scope.verbosity_options[i]; - } - } - - initVerbositySelect(); - - $scope.$watch('verbosity', initVerbositySelect); - - // Register a watcher on project_name - if ($scope.getInventoryFilesUnregister) { - $scope.getInventoryFilesUnregister(); - } - $scope.getInventoryFilesUnregister = $scope.$watch('project', function (newValue, oldValue) { - if (newValue !== oldValue) { - updateSCMProject(); - } - }); - } + }); function initVerbositySelect(){ CreateSelect2({ @@ -366,9 +368,16 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', params.source = null; } - SourcesService - .put(params) - .then(() => $state.go('.', null, { reload: true })); + inventorySource.request('put', { + data: params + }).then(() => { + $state.go('.', null, { reload: true }); + }).catch(({ data, status, config }) => { + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: InventoryHostsStrings.get('error.CALL', { path: `${config.url}`, status }) + }); + }); }; $scope.sourceChange = function(source) { @@ -419,7 +428,5 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', initRegionSelect(); }; - - init(); } ]; diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.route.js index c73c7840b4..2402b9fb08 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.route.js @@ -20,8 +20,8 @@ export default { } }, resolve: { - inventorySourceData: ['$stateParams', 'SourcesService', function($stateParams, SourcesService) { - return SourcesService.get({id: $stateParams.inventory_source_id }).then(response => response.data.results[0]); + inventorySource: ['InventorySourceModel', '$stateParams', (InventorySource, $stateParams) => { + return new InventorySource('get', $stateParams.inventory_source_id); }], inventorySourcesOptions: ['InventoriesService', '$stateParams', function(InventoriesService, $stateParams) { return InventoriesService.inventorySourcesOptions($stateParams.inventory_id) From 04d8642daf5ec1f4cafc302e0dc799b15962db23 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 9 Apr 2018 15:04:52 -0400 Subject: [PATCH 224/379] update badge count on search --- awx/ui/client/features/templates/index.controller.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/templates/index.controller.js b/awx/ui/client/features/templates/index.controller.js index fd0850d582..5f305345a9 100644 --- a/awx/ui/client/features/templates/index.controller.js +++ b/awx/ui/client/features/templates/index.controller.js @@ -1,12 +1,19 @@ -function IndexTemplatesController (strings, dataset) { +function IndexTemplatesController ($scope, strings, dataset) { let vm = this; vm.strings = strings; vm.count = dataset.data.count; + + $scope.$on('updateDataset', (e, { count }) => { + if (count) { + vm.count = count; + } + }); } IndexTemplatesController.$inject = [ + '$scope', 'TemplatesStrings', - 'Dataset' + 'Dataset', ]; export default IndexTemplatesController; From 453a8507b024e268f3b546c4919dbae1868a6444 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 9 Apr 2018 15:07:27 -0400 Subject: [PATCH 225/379] Function/code cleanup --- .../sources/add/sources-add.controller.js | 52 ++--- .../sources/edit/sources-edit.controller.js | 184 ++++++++---------- 2 files changed, 102 insertions(+), 134 deletions(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js index 685fe3aa1b..4415dc386d 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/add/sources-add.controller.js @@ -73,7 +73,12 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', options: inventorySourcesOptions }); - initVerbositySelect(); + CreateSelect2({ + element: '#inventory_source_verbosity', + multiple: false + }); + + $scope.verbosity = $scope.verbosity_options[1]; GetSourceTypeOptions({ scope: $scope, @@ -93,7 +98,14 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', .then(({data}) => { $scope.inventory_files = data; $scope.inventory_files.push("/ (project root)"); - sync_inventory_file_select2(); + CreateSelect2({ + element:'#inventory-file-select', + addNew: true, + multiple: false, + scope: $scope, + options: 'inventory_files', + model: 'inventory_file' + }); Wait('stop'); }) .catch(() => { @@ -113,17 +125,6 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', } }); - function sync_inventory_file_select2() { - CreateSelect2({ - element:'#inventory-file-select', - addNew: true, - multiple: false, - scope: $scope, - options: 'inventory_files', - model: 'inventory_file' - }); - } - $scope.lookupCredential = function(){ if($scope.source.value !== "scm" && $scope.source.value !== "custom") { let kind = ($scope.source.value === "ec2") ? "aws" : $scope.source.value; @@ -205,7 +206,10 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', // region / source options callback $scope.$on('sourceTypeOptionsReady', function() { - initSourceSelect(); + CreateSelect2({ + element: '#inventory_source_source', + multiple: false + }); }); function initRegionSelect(){ @@ -214,10 +218,6 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', multiple: true }); - initGroupBySelect(); - } - - function initGroupBySelect(){ let add_new = false; if( _.get($scope, 'source') === 'ec2' || _.get($scope.source, 'value') === 'ec2') { $scope.group_by_choices = $scope.ec2_group_by; @@ -263,22 +263,6 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', }); } - function initSourceSelect(){ - CreateSelect2({ - element: '#inventory_source_source', - multiple: false - }); - } - - function initVerbositySelect(){ - CreateSelect2({ - element: '#inventory_source_verbosity', - multiple: false - }); - - $scope.verbosity = $scope.verbosity_options[1]; - } - $scope.formCancel = function() { $state.go('^'); }; diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js index e2e9ca3968..104ff2574a 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js @@ -54,12 +54,94 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', }); $scope.$on('sourceTypeOptionsReady', function() { - initSourceSelect(); + $scope.source = _.find($scope.source_type_options, { value: inventorySourceData.source }); + var source = $scope.source && $scope.source.value ? $scope.source.value : null; + $scope.cloudCredentialRequired = source !== '' && source !== 'scm' && source !== 'custom' && source !== 'ec2' ? true : false; + CreateSelect2({ + element: '#inventory_source_source', + multiple: false + }); + + if (source === 'ec2' || source === 'custom' || + source === 'vmware' || source === 'openstack' || + source === 'scm' || source === 'cloudforms' || + source === 'satellite6') { + + var varName; + if (source === 'scm') { + varName = 'custom_variables'; + } else { + varName = source + '_variables'; + } + + $scope[varName] = ParseVariableString(inventorySourceData + .source_vars); + + ParseTypeChange({ + scope: $scope, + field_id: varName, + variable: varName, + parse_variable: 'envParseType', + }); + } }); $scope.envParseType = 'yaml'; - initSources(); + GetSourceTypeOptions({ + scope: $scope, + variable: 'source_type_options' + }); + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'rax_regions', + choice_name: 'rax_region_choices', + options: inventorySourcesOptions + }); + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'ec2_regions', + choice_name: 'ec2_region_choices', + options: inventorySourcesOptions + }); + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'gce_regions', + choice_name: 'gce_region_choices', + options: inventorySourcesOptions + }); + GetChoices({ + scope: $scope, + field: 'source_regions', + variable: 'azure_regions', + choice_name: 'azure_rm_region_choices', + options: inventorySourcesOptions + }); + GetChoices({ + scope: $scope, + field: 'group_by', + variable: 'ec2_group_by', + choice_name: 'ec2_group_by_choices', + options: inventorySourcesOptions + }); + + var source = $scope.source === 'azure_rm' ? 'azure' : $scope.source; + var regions = inventorySourceData.source_regions.split(','); + // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint + $scope.source_region_choices = $scope[source + '_regions']; + + // the API stores azure regions as all-lowercase strings - but the azure regions received from OPTIONS are Snake_Cased + if (source === 'azure') { + $scope.source_regions = _.map(regions, (region) => _.find($scope[source + '_regions'], (o) => o.value.toLowerCase() === region)); + } + // all other regions are 1-1 + else { + $scope.source_regions = _.map(regions, (region) => _.find($scope[source + '_regions'], (o) => o.value === region)); + } + initRegionSelect(); GetChoices({ scope: $scope, @@ -140,110 +222,12 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', } } - function initSourceSelect() { - $scope.source = _.find($scope.source_type_options, { value: inventorySourceData.source }); - var source = $scope.source && $scope.source.value ? $scope.source.value : null; - $scope.cloudCredentialRequired = source !== '' && source !== 'scm' && source !== 'custom' && source !== 'ec2' ? true : false; - CreateSelect2({ - element: '#inventory_source_source', - multiple: false - }); - - if (source === 'ec2' || source === 'custom' || - source === 'vmware' || source === 'openstack' || - source === 'scm' || source === 'cloudforms' || - source === 'satellite6') { - - var varName; - if (source === 'scm') { - varName = 'custom_variables'; - } else { - varName = source + '_variables'; - } - - $scope[varName] = ParseVariableString(inventorySourceData - .source_vars); - - ParseTypeChange({ - scope: $scope, - field_id: varName, - variable: varName, - parse_variable: 'envParseType', - }); - } - } - - function initRegionData() { - var source = $scope.source === 'azure_rm' ? 'azure' : $scope.source; - var regions = inventorySourceData.source_regions.split(','); - // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint - $scope.source_region_choices = $scope[source + '_regions']; - - // the API stores azure regions as all-lowercase strings - but the azure regions received from OPTIONS are Snake_Cased - if (source === 'azure') { - $scope.source_regions = _.map(regions, (region) => _.find($scope[source + '_regions'], (o) => o.value.toLowerCase() === region)); - } - // all other regions are 1-1 - else { - $scope.source_regions = _.map(regions, (region) => _.find($scope[source + '_regions'], (o) => o.value === region)); - } - initRegionSelect(); - } - - function initSources() { - GetSourceTypeOptions({ - scope: $scope, - variable: 'source_type_options' - }); - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'rax_regions', - choice_name: 'rax_region_choices', - options: inventorySourcesOptions - }); - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'ec2_regions', - choice_name: 'ec2_region_choices', - options: inventorySourcesOptions - }); - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'gce_regions', - choice_name: 'gce_region_choices', - options: inventorySourcesOptions - }); - GetChoices({ - scope: $scope, - field: 'source_regions', - variable: 'azure_regions', - choice_name: 'azure_rm_region_choices', - options: inventorySourcesOptions - }); - GetChoices({ - scope: $scope, - field: 'group_by', - variable: 'ec2_group_by', - choice_name: 'ec2_group_by_choices', - options: inventorySourcesOptions - }); - - initRegionData(); - } - function initRegionSelect() { CreateSelect2({ element: '#inventory_source_source_regions', multiple: true }); - initGroupBySelect(); - } - - function initGroupBySelect(){ let add_new = false; if( _.get($scope, 'source') === 'ec2' || _.get($scope.source, 'value') === 'ec2') { $scope.group_by_choices = $scope.ec2_group_by; From 13759fd8ce421b9b2515926f1e68cadcd1962403 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 9 Apr 2018 15:48:26 -0400 Subject: [PATCH 226/379] prevent OverflowError in SESSION_COOKIE_AGE --- awx/api/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/api/conf.py b/awx/api/conf.py index b91cdae254..34bf305f20 100644 --- a/awx/api/conf.py +++ b/awx/api/conf.py @@ -10,6 +10,7 @@ register( 'SESSION_COOKIE_AGE', field_class=fields.IntegerField, min_value=60, + max_value=30000000000, # approx 1,000 years, higher values give OverflowError label=_('Idle Time Force Log Out'), help_text=_('Number of seconds that a user is inactive before they will need to login again.'), category=_('Authentication'), From dd5a34ce3b5b03d1af0931bd7d22a1c1869fad2b Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 9 Apr 2018 16:30:47 -0400 Subject: [PATCH 227/379] pin container images --- awx/ui/test/e2e/cluster/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/test/e2e/cluster/docker-compose.yml b/awx/ui/test/e2e/cluster/docker-compose.yml index de4a7f920b..4576ed804e 100644 --- a/awx/ui/test/e2e/cluster/docker-compose.yml +++ b/awx/ui/test/e2e/cluster/docker-compose.yml @@ -2,11 +2,11 @@ version: '2' services: hub: - image: selenium/hub + image: selenium/hub:3.8.1-erbium ports: - 4444:4444 chrome: - image: selenium/node-chrome + image: selenium/node-chrome:3.8.1-erbium # uncomment the two lines below to make tests watchable at vnc://localhost:secret@localhost:5900 # image: selenium/node-chrome-debug # ports: ['5900:5900'] From b1028a2e0abac480b8b4c0177dc8026de3d9b81c Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 9 Apr 2018 17:03:22 -0400 Subject: [PATCH 228/379] fix a bug preventing custom credential templates from including unicode see: https://github.com/ansible/tower/issues/1266 --- awx/main/models/credential/__init__.py | 2 +- awx/main/tests/unit/test_tasks.py | 30 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index c109f28b65..b390043765 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -632,7 +632,7 @@ class CredentialType(CommonModelNameNotUnique): data = Template(file_tmpl).render(**namespace) _, path = tempfile.mkstemp(dir=private_data_dir) with open(path, 'w') as f: - f.write(data) + f.write(data.encode('utf-8')) os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) # determine if filename indicates single file or many diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 90023d9076..9a17a12239 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from contextlib import contextmanager from datetime import datetime from functools import partial @@ -12,6 +14,7 @@ from backports.tempfile import TemporaryDirectory import fcntl import mock import pytest +import six import yaml from django.conf import settings @@ -1319,6 +1322,33 @@ class TestJobCredentials(TestJobExecution): self.run_pexpect.side_effect = run_pexpect_side_effect self.task.run(self.pk) + def test_custom_environment_injectors_with_unicode_content(self): + value = six.u('Iñtërnâtiônàlizætiøn') + some_cloud = CredentialType( + kind='cloud', + name='SomeCloud', + managed_by_tower=False, + inputs={'fields': []}, + injectors={ + 'file': {'template': value}, + 'env': {'MY_CLOUD_INI_FILE': '{{tower.filename}}'} + } + ) + credential = Credential( + pk=1, + credential_type=some_cloud, + ) + self.instance.credentials.add(credential) + self.task.run(self.pk) + + def run_pexpect_side_effect(*args, **kwargs): + args, cwd, env, stdout = args + assert open(env['MY_CLOUD_INI_FILE'], 'rb').read() == value.encode('utf-8') + return ['successful', 0] + + self.run_pexpect.side_effect = run_pexpect_side_effect + self.task.run(self.pk) + def test_custom_environment_injectors_with_files(self): some_cloud = CredentialType( kind='cloud', From 6e2133aa5271411d034d38771e583077c6039ebf Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 9 Apr 2018 17:10:15 -0400 Subject: [PATCH 229/379] Limit adhoc credential lookup to only machine credentials --- .../inventories/adhoc/adhoc-credential.route.js | 3 ++- .../inventories/adhoc/adhoc.controller.js | 16 ++++++++++++++-- .../inventories/adhoc/adhoc.form.js | 1 + .../inventories/adhoc/adhoc.route.js | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc-credential.route.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc-credential.route.js index 173c77f445..82f3321652 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc-credential.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc-credential.route.js @@ -7,7 +7,8 @@ export default { params: { credential_search: { value: { - page_size: '5' + page_size: '5', + credential_type: null }, squash: true, dynamic: true diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js index e5e020fc6d..33f3a0583f 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js @@ -12,7 +12,7 @@ function adhocController($q, $scope, $stateParams, $state, CheckPasswords, PromptForPasswords, CreateLaunchDialog, CreateSelect2, adhocForm, GenerateForm, Rest, ProcessErrors, GetBasePath, GetChoices, - KindChange, Wait, ParseTypeChange) { + KindChange, Wait, ParseTypeChange, credentialTypes) { // this is done so that we can access private functions for testing, but // we don't want to populate the "public" scope with these internal @@ -301,11 +301,23 @@ function adhocController($q, $scope, $stateParams, }); }; + $scope.lookupCredential = function(){ + let credType = _.filter(credentialTypes, function(credType){ + return credType.kind === "ssh"; + }); + $state.go('.credential', { + credential_search: { + credential_type: credType[0].id, + page_size: '5', + page: '1' + } + }); + }; } export default ['$q', '$scope', '$stateParams', '$state', 'CheckPasswords', 'PromptForPasswords', 'CreateLaunchDialog', 'CreateSelect2', 'adhocForm', 'GenerateForm', 'Rest', 'ProcessErrors', 'GetBasePath', - 'GetChoices', 'KindChange', 'Wait', 'ParseTypeChange', + 'GetChoices', 'KindChange', 'Wait', 'ParseTypeChange', 'credentialTypes', adhocController]; diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.form.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.form.js index 0799ff54c2..e3cd25263d 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.form.js +++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.form.js @@ -64,6 +64,7 @@ export default ['i18n', function(i18n) { sourceModel: 'credential', sourceField: 'name', class: 'squeeze', + ngClick: 'lookupCredential()', awPopOver: '

Select the credential you want to use when ' + 'accessing the remote hosts to run the command. ' + 'Choose the credential containing ' + diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.route.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.route.js index 3b96425d93..30d1f400a5 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.route.js @@ -26,5 +26,14 @@ export default { }, ncyBreadcrumb: { label: N_("RUN COMMAND") + }, + resolve: { + credentialTypes: ['CredentialTypeModel', (CredentialType) => + new CredentialType('get') + .then((model) => { + const credentialTypeRes = model.get(); + return credentialTypeRes.results; + }) + ] } }; From a6dd7dbf070c7c57f5cb38dd6d6df2a239475c2e Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 10 Apr 2018 10:08:49 -0400 Subject: [PATCH 230/379] Fixed credential prompting bug where only one password field is ever shown even if multiple passwords are required --- .../steps/credential/prompt-credential.controller.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js index 55ede39e7e..9c25eaa724 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js @@ -74,11 +74,14 @@ export default }; if(cred.inputs.password && cred.inputs.password === "ASK") { scope.promptData.prompts.credentials.passwords.ssh = credPassObj; - } else if(cred.inputs.become_password && cred.inputs.become_password === "ASK") { + } + if(cred.inputs.become_password && cred.inputs.become_password === "ASK") { scope.promptData.prompts.credentials.passwords.become = credPassObj; - } else if(cred.inputs.ssh_key_unlock && cred.inputs.ssh_key_unlock === "ASK") { + } + if(cred.inputs.ssh_key_unlock && cred.inputs.ssh_key_unlock === "ASK") { scope.promptData.prompts.credentials.passwords.ssh_key_unlock = credPassObj; - } else if(cred.inputs.vault_password && cred.inputs.vault_password === "ASK") { + } + if(cred.inputs.vault_password && cred.inputs.vault_password === "ASK") { credPassObj.vault_id = cred.inputs.vault_id; if(!scope.promptData.prompts.credentials.passwords.vault) { scope.promptData.prompts.credentials.passwords.vault = []; From c8b12ed23ea302bc8a8df76a561ba6c89cc19405 Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 10 Apr 2018 10:26:56 -0400 Subject: [PATCH 231/379] Fixed bug where typing in a password would update all password inputs --- .../prompt-credential.controller.js | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js index 9c25eaa724..9a5c8feec3 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js @@ -51,11 +51,14 @@ export default } else if(cred.inputs && !_.isEmpty(cred.inputs)) { if(cred.inputs.password && cred.inputs.password === "ASK") { delete scope.promptData.prompts.credentials.passwords.ssh; - } else if(cred.inputs.become_password && cred.inputs.become_password === "ASK") { + } + if(cred.inputs.become_password && cred.inputs.become_password === "ASK") { delete scope.promptData.prompts.credentials.passwords.become; - } else if(cred.inputs.ssh_key_unlock && cred.inputs.ssh_key_unlock === "ASK") { + } + if(cred.inputs.ssh_key_unlock && cred.inputs.ssh_key_unlock === "ASK") { delete scope.promptData.prompts.credentials.passwords.ssh_key_unlock; - } else if(cred.inputs.vault_password && cred.inputs.vault_password === "ASK") { + } + if(cred.inputs.vault_password && cred.inputs.vault_password === "ASK") { for (let i = scope.promptData.prompts.credentials.passwords.vault.length - 1; i >= 0; i--) { if(cred.id === scope.promptData.prompts.credentials.passwords.vault[i].id) { scope.promptData.prompts.credentials.passwords.vault.splice(i, 1); @@ -68,25 +71,33 @@ export default let updateNeededPasswords = (cred) => { if(cred.inputs) { - let credPassObj = { - id: cred.id, - name: cred.name - }; if(cred.inputs.password && cred.inputs.password === "ASK") { - scope.promptData.prompts.credentials.passwords.ssh = credPassObj; + scope.promptData.prompts.credentials.passwords.ssh = { + id: cred.id, + name: cred.name + }; } if(cred.inputs.become_password && cred.inputs.become_password === "ASK") { - scope.promptData.prompts.credentials.passwords.become = credPassObj; + scope.promptData.prompts.credentials.passwords.become = { + id: cred.id, + name: cred.name + }; } if(cred.inputs.ssh_key_unlock && cred.inputs.ssh_key_unlock === "ASK") { - scope.promptData.prompts.credentials.passwords.ssh_key_unlock = credPassObj; + scope.promptData.prompts.credentials.passwords.ssh_key_unlock = { + id: cred.id, + name: cred.name + }; } if(cred.inputs.vault_password && cred.inputs.vault_password === "ASK") { - credPassObj.vault_id = cred.inputs.vault_id; if(!scope.promptData.prompts.credentials.passwords.vault) { scope.promptData.prompts.credentials.passwords.vault = []; } - scope.promptData.prompts.credentials.passwords.vault.push(credPassObj); + scope.promptData.prompts.credentials.passwords.vault.push({ + id: cred.id, + name: cred.name, + vault_id: cred.inputs.vault_id + }); } } }; From 50a1a5707ebb6601bec507e6861965fec99f950d Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 10 Apr 2018 10:51:56 -0400 Subject: [PATCH 232/379] Fixed nested group name link --- .../groups/related/nested-groups/group-nested-groups.list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups.list.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups.list.js index cd920675cf..95df7e4348 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups.list.js @@ -31,7 +31,7 @@ name: { label: i18n._('Groups'), key: true, - uiSref: "inventories.edit.groups.edit.nested_groups({group_id:nested_group.id)}", + uiSref: "inventories.edit.groups.edit({group_id:nested_group.id})", columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6', class: 'InventoryManage-breakWord', } From 02f9fd0ca0b1e7b505a76e3dc22c3de342b4d75c Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 10 Apr 2018 10:58:33 -0400 Subject: [PATCH 233/379] fix cancel-delete urls --- awx/ui/client/features/output/details.directive.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index ea20d73cdd..45b6b64f84 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -436,7 +436,7 @@ function cancelJob () { const resourceName = `#${id} ${name}`; const method = 'POST'; - const url = `${resource.model.path}/${id}/cancel/`; + const url = `${resource.model.path}${id}/cancel/`; const errorHandler = createErrorHandler('cancel job', method); @@ -465,7 +465,7 @@ function deleteJob () { const resourceName = `#${id} ${name}`; const method = 'DELETE'; - const url = `${resource.model.path}/${id}/`; + const url = `${resource.model.path}${id}/`; const errorHandler = createErrorHandler('delete job', method); From 4f65b283df84839dbaae123c4c32bcf5afc7dc2a Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 10 Apr 2018 11:00:37 -0400 Subject: [PATCH 234/379] do not server error if this value is None --- awx/main/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index a4519610c3..b56dcc0e30 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -839,6 +839,6 @@ class OAuth2ClientSecretField(models.CharField): ) def from_db_value(self, value, expression, connection, context): - if value.startswith('$encrypted$'): + if value and value.startswith('$encrypted$'): return decrypt_value(get_encryption_key('value', pk=None), value) return value From 704a2a73c722dffd809cca4fa5d6d6925f67554e Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Wed, 4 Apr 2018 11:03:07 -0400 Subject: [PATCH 235/379] Fix error where controller was accessing wrong prompt_for_days_form scope. --- .../management-jobs/card/card.controller.js | 69 ++++++++----------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/awx/ui/client/src/management-jobs/card/card.controller.js b/awx/ui/client/src/management-jobs/card/card.controller.js index 12dceb22b9..e2c1b1a844 100644 --- a/awx/ui/client/src/management-jobs/card/card.controller.js +++ b/awx/ui/client/src/management-jobs/card/card.controller.js @@ -30,41 +30,27 @@ export default }); }; getManagementJobs(); - var scope = $rootScope.$new(); - scope.cleanupJob = true; + $scope.cleanupJob = true; // This handles the case where the user refreshes the management job notifications page. if($state.current.name === 'managementJobsList.notifications') { $scope.activeCard = parseInt($state.params.management_id); $scope.cardAction = "notifications"; } - // Cancel - scope.cancelConfigure = function () { - try { - $('#configure-dialog').dialog('close'); - $("#configure-save-button").remove(); - } - catch(e) { - //ignore - } - - Wait('stop'); - }; - $scope.submitCleanupJob = function(id, name){ defaultUrl = GetBasePath('system_job_templates')+id+'/launch/'; CreateDialog({ id: 'prompt-for-days-facts', title: name, - scope: scope, + scope: $scope, width: 500, height: 470, minWidth: 200, callback: 'PromptForDaysFacts', resizable: false, onOpen: function(){ - scope.$watch('prompt_for_days_facts_form.$invalid', function(invalid) { + $scope.$watch('prompt_for_days_facts_form.$invalid', function(invalid) { if (invalid === true) { $('#prompt-for-days-facts-launch').prop("disabled", true); } else { @@ -72,10 +58,11 @@ export default } }); - var fieldScope = scope.$parent; + var fieldScope = $scope.$parent; + + // set these form elements up on the $scope where the form + // is the parent of the current $scope - // set these form elements up on the scope where the form - // is the parent of the current scope fieldScope.keep_unit_choices = [{ "label" : "Days", "value" : "d" @@ -100,9 +87,9 @@ export default "label" : "Years", "value" : "y" }]; - scope.prompt_for_days_facts_form.$setPristine(); - scope.prompt_for_days_facts_form.$invalid = false; - fieldScope.keep_unit = fieldScope.keep_unit_choices[0]; + $scope.prompt_for_days_facts_form.$setPristine(); + $scope.prompt_for_days_facts_form.$invalid = false; + fieldScope.keep_unit = fieldScope.keep_unit_choices[0]; fieldScope.granularity_keep_unit = fieldScope.granularity_keep_unit_choices[1]; fieldScope.keep_amount = 30; fieldScope.granularity_keep_amount = 1; @@ -120,8 +107,8 @@ export default "label": "Launch", "onClick": function() { var extra_vars = { - "older_than": scope.keep_amount+scope.keep_unit.value, - "granularity": scope.granularity_keep_amount+scope.granularity_keep_unit.value + "older_than": $scope.keep_amount+$scope.keep_unit.value, + "granularity": $scope.granularity_keep_amount+$scope.granularity_keep_unit.value }, data = {}; data.extra_vars = JSON.stringify(extra_vars); @@ -135,9 +122,9 @@ export default $state.go('jobz', { id: data.system_job, type: 'system' }, { reload: true }); }) .catch(({data, status}) => { - let template_id = scope.job_template_id; + let template_id = $scope.job_template_id; template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); - ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'), + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); }); }, @@ -147,10 +134,10 @@ export default ] }); - if (scope.removePromptForDays) { - scope.removePromptForDays(); + if ($scope.removePromptForDays) { + $scope.removePromptForDays(); } - scope.removePromptForDays = scope.$on('PromptForDaysFacts', function() { + $scope.removePromptForDays = $scope.$on('PromptForDaysFacts', function() { // $('#configure-dialog').dialog('close'); $('#prompt-for-days-facts').show(); $('#prompt-for-days-facts').dialog('open'); @@ -179,14 +166,14 @@ export default CreateDialog({ id: 'prompt-for-days' , title: name, - scope: scope, + scope: $scope, width: 500, height: 300, minWidth: 200, callback: 'PromptForDays', resizable: false, onOpen: function(){ - scope.$watch('prompt_for_days_form.$invalid', function(invalid) { + $scope.$watch('prompt_for_days_form.$invalid', function(invalid) { if (invalid === true) { $('#prompt-for-days-launch').prop("disabled", true); } else { @@ -194,10 +181,10 @@ export default } }); - var fieldScope = scope.$parent; + let fieldScope = $scope.$parent; fieldScope.days_to_keep = 30; - scope.prompt_for_days_form.$setPristine(); - scope.prompt_for_days_form.$invalid = false; + $scope.prompt_for_days_form.$setPristine(); + $scope.prompt_for_days_form.$invalid = false; }, buttons: [ { @@ -212,7 +199,7 @@ export default { "label": "Launch", "onClick": function() { - var extra_vars = {"days": scope.days_to_keep }, + const extra_vars = {"days": $scope.days_to_keep }, data = {}; data.extra_vars = JSON.stringify(extra_vars); @@ -225,9 +212,9 @@ export default $state.go('jobz', { id: data.system_job, type: 'system' }, { reload: true }); }) .catch(({data, status}) => { - let template_id = scope.job_template_id; + let template_id = $scope.job_template_id; template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); - ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'), + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); }); }, @@ -237,10 +224,10 @@ export default ] }); - if (scope.removePromptForDays) { - scope.removePromptForDays(); + if ($scope.removePromptForDays) { + $scope.removePromptForDays(); } - scope.removePromptForDays = scope.$on('PromptForDays', function() { + $scope.removePromptForDays = $scope.$on('PromptForDays', function() { // $('#configure-dialog').dialog('close'); $('#prompt-for-days').show(); $('#prompt-for-days').dialog('open'); From 69822391b34e62a646b6eff9dae63da40d0fcc15 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 10 Apr 2018 11:51:05 -0400 Subject: [PATCH 236/379] fix silent traceback tests were causing --- awx/main/tests/unit/test_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 9a17a12239..75a307dbb9 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -222,7 +222,7 @@ class TestJobExecution: self.run_pexpect.return_value = ['successful', 0] self.patches = [ - mock.patch.object(CallbackQueueDispatcher, 'dispatch', lambda obj: None), + mock.patch.object(CallbackQueueDispatcher, 'dispatch', lambda self, obj: None), mock.patch.object(Project, 'get_project_path', lambda *a, **kw: self.project_path), # don't emit websocket statuses; they use the DB and complicate testing mock.patch.object(UnifiedJob, 'websocket_emit_status', mock.Mock()), From 6768b10638d8b2b2704abbba7f1d27aa01bcbc99 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 10 Apr 2018 12:05:10 -0400 Subject: [PATCH 237/379] use ordained list of statuses for showing cancel/delete --- awx/ui/client/features/output/details.partial.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index 7e923b5758..b3f04428e7 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -12,7 +12,10 @@ class="List-actionButton List-actionButton--delete" data-placement="top" ng-click="vm.cancelJob()" - ng-show="vm.status.value === 'Running'|| vm.status.value ==='Pending'" + ng-show="vm.status.value === 'New' || + vm.status.value === 'Pending' || + vm.status.value === 'Waiting' || + vm.status.value === 'Running'" aw-tool-tip="{{'Cancel' | translate }}" data-original-title="" title=""> @@ -24,9 +27,11 @@ class="List-actionButton List-actionButton--delete" data-placement="top" ng-click="vm.deleteJob()" - ng-hide="!vm.canDelete - || vm.status.value === 'Running' - || vm.status.value === 'Pending'" + ng-show="vm.canDelete && ( + vm.status.value === 'Successful' || + vm.status.value === 'Failed' || + vm.status.value === 'Error' || + vm.status.value === 'Canceled')" aw-tool-tip="{{ 'Delete' | translate }}" data-original-title="" title=""> From 2f96169f077c3afb994a056b9d3e1cdd94c4a5a5 Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 10 Apr 2018 12:27:09 -0400 Subject: [PATCH 238/379] Force on/off toggle to be disabled on smart inventory host list --- .../inventories/smart-inventory/smart-inventory-hosts.route.js | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory-hosts.route.js b/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory-hosts.route.js index befc2c2d66..41090d5cb4 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory-hosts.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory-hosts.route.js @@ -38,6 +38,7 @@ export default { delete list.fieldActions.delete; delete list.fieldActions.edit; delete list.fieldActions.view.ngShow; + list.fields.toggleHost.ngDisabled = '{{1 === 1}}'; list.fields.name.columnClass = 'col-lg-8 col-md-11 col-sm-8 col-xs-7'; return list; }], From f0e0a8a33855416d60a3df0e55d0891cda81cc75 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 10 Apr 2018 12:25:12 -0400 Subject: [PATCH 239/379] use newly ordained list of statuses for showing cancel/delete --- awx/ui/client/features/output/details.partial.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index b3f04428e7..55398f6864 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -12,8 +12,7 @@ class="List-actionButton List-actionButton--delete" data-placement="top" ng-click="vm.cancelJob()" - ng-show="vm.status.value === 'New' || - vm.status.value === 'Pending' || + ng-show="vm.status.value === 'Pending' || vm.status.value === 'Waiting' || vm.status.value === 'Running'" aw-tool-tip="{{'Cancel' | translate }}" @@ -28,6 +27,7 @@ data-placement="top" ng-click="vm.deleteJob()" ng-show="vm.canDelete && ( + vm.status.value === 'New' || vm.status.value === 'Successful' || vm.status.value === 'Failed' || vm.status.value === 'Error' || From 02d7ce97c66cc1b62f6f40d4911c037d86bd819f Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 10 Apr 2018 13:38:07 -0400 Subject: [PATCH 240/379] use copy capability field for copy action --- awx/ui/client/src/credentials/credentials.list.js | 2 +- .../client/src/inventories-hosts/inventories/inventory.list.js | 2 +- awx/ui/client/src/inventory-scripts/inventory-scripts.list.js | 2 +- awx/ui/client/src/notifications/notificationTemplates.list.js | 2 +- awx/ui/client/src/projects/projects.list.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/awx/ui/client/src/credentials/credentials.list.js b/awx/ui/client/src/credentials/credentials.list.js index f381927a89..fbc4c826d5 100644 --- a/awx/ui/client/src/credentials/credentials.list.js +++ b/awx/ui/client/src/credentials/credentials.list.js @@ -75,7 +75,7 @@ export default ['i18n', function(i18n) { "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy credential'), dataPlacement: 'top', - ngShow: 'credential.summary_fields.user_capabilities.edit' + ngShow: 'credential.summary_fields.user_capabilities.copy' }, view: { ngClick: "editCredential(credential.id)", diff --git a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js index a8edfa27eb..07991c8397 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js @@ -113,7 +113,7 @@ export default ['i18n', function(i18n) { "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy inventory'), dataPlacement: 'top', - ngShow: '!inventory.pending_deletion && inventory.summary_fields.user_capabilities.edit' + ngShow: '!inventory.pending_deletion && inventory.summary_fields.user_capabilities.copy' }, view: { label: i18n._('View'), diff --git a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js index 03b694f460..9c0ffd2a1d 100644 --- a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js +++ b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js @@ -63,7 +63,7 @@ export default ['i18n', function(i18n){ "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy inventory script'), dataPlacement: 'top', - ngShow: 'inventory_script.summary_fields.user_capabilities.edit' + ngShow: 'inventory_script.summary_fields.user_capabilities.copy' }, view: { ngClick: "editCustomInv(inventory_script.id)", diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js index e8bf90ffa6..0e7182be9a 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.list.js +++ b/awx/ui/client/src/notifications/notificationTemplates.list.js @@ -83,7 +83,7 @@ export default ['i18n', function(i18n){ "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy notification'), dataPlacement: 'top', - ngShow: 'notification_template.summary_fields.user_capabilities.edit' + ngShow: 'notification_template.summary_fields.user_capabilities.copy' }, view: { ngClick: "editNotification(notification_template.id)", diff --git a/awx/ui/client/src/projects/projects.list.js b/awx/ui/client/src/projects/projects.list.js index b05d923a8b..f4d504304c 100644 --- a/awx/ui/client/src/projects/projects.list.js +++ b/awx/ui/client/src/projects/projects.list.js @@ -106,7 +106,7 @@ export default ['i18n', function(i18n) { "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy project'), dataPlacement: 'top', - ngShow: 'project.summary_fields.user_capabilities.edit' + ngShow: 'project.summary_fields.user_capabilities.copy' }, edit: { ngClick: "editProject(project.id)", From 6beaa4b1668e9746b93a54dd7e70c0798d8935c5 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Tue, 10 Apr 2018 13:46:55 -0400 Subject: [PATCH 241/379] Fix infinite Wait spinner when deleting instance group --- .../list/instance-groups-list.controller.js | 59 +++++++++++-------- .../list/instance-groups-list.partial.html | 10 +--- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js index 1660b0b0da..3a48660c62 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js @@ -1,10 +1,9 @@ -export default ['$scope', 'resolvedModels', 'Dataset', '$state', 'ComponentsStrings', 'ProcessErrors', 'Wait', - function($scope, resolvedModels, Dataset, $state, strings, ProcessErrors, Wait) { +export default ['$scope', '$filter', '$state', 'Alert', 'resolvedModels', 'Dataset', 'ComponentsStrings', 'ProcessErrors', 'Prompt', 'Wait', + function($scope, $filter, $state, Alert, resolvedModels, Dataset, strings, ProcessErrors, Prompt, Wait) { const vm = this; const { instanceGroup } = resolvedModels; vm.strings = strings; - $scope.selection = {}; init(); @@ -33,31 +32,43 @@ export default ['$scope', 'resolvedModels', 'Dataset', '$state', 'ComponentsStri vm.activeId = parseInt($state.params.instance_group_id); }); - vm.delete = () => { - Wait('start'); - let deletables = $scope.selection; - deletables = Object.keys(deletables).filter((n) => deletables[n]); + vm.deleteInstanceGroup = instance_group => { + if (!instance_group) { + Alert(strings.get('error.DELETE'), strings.get('alert.MISSING_PARAMETER')); + return; + } - deletables.forEach((data) => { - let promise = instanceGroup.http.delete({resource: data}); - Promise.resolve(promise).then(vm.onSaveSuccess) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Call failed. Return status: ' + status - }); - }) - .finally(() => { - Wait('stop'); - }); + Prompt({ + action() { + $('#prompt-modal').modal('hide'); + Wait('start'); + instanceGroup + .request('delete', instance_group.id) + .then(() => handleSuccessfulDelete(instance_group)) + .catch(createErrorHandler('delete instance group', 'DELETE')) + .finally(() => Wait('stop')); + }, + hdr: strings.get('DELETE'), + resourceName: $filter('sanitize')(instance_group.name), + body: `${strings.get('deleteResource.CONFIRM', 'instance group')}` }); }; - vm.onSaveSuccess = () => { - $state.transitionTo($state.current, $state.params, { - reload: true, location: true, inherit: false, notify: true - }); - }; + function handleSuccessfulDelete(instance_group) { + if (parseInt($state.params.instance_group_id, 0) === instance_group.id) { + $state.go('instanceGroups', $state.params, { reload: true }); + } else { + $state.go('.', $state.params, { reload: true }); + } + } + + function createErrorHandler(path, action) { + return ({ data, status }) => { + const hdr = strings.get('error.HEADER'); + const msg = strings.get('error.CALL', { path, action, status }); + ProcessErrors($scope, data, status, null, { hdr, msg }); + }; + } $scope.createInstanceGroup = () => { $state.go('instanceGroups.add'); diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html b/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html index cc40d32050..2600ce4094 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html @@ -19,10 +19,6 @@ search-tags="searchTags">

-
- -
+
+
SETTING CATEGORY
+
+
+
+
SETTING NAME
+
+
CHANGES
-
+ +
diff --git a/awx/ui/client/lib/components/_index.less b/awx/ui/client/lib/components/_index.less index ae992079c9..72c14fe4b5 100644 --- a/awx/ui/client/lib/components/_index.less +++ b/awx/ui/client/lib/components/_index.less @@ -10,3 +10,4 @@ @import 'tabs/_index'; @import 'truncate/_index'; @import 'utility/_index'; +@import 'code-mirror/_index'; diff --git a/awx/ui/client/lib/components/code-mirror/_index.less b/awx/ui/client/lib/components/code-mirror/_index.less new file mode 100644 index 0000000000..770546b151 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/_index.less @@ -0,0 +1,76 @@ +.noselect { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + not supported by any browser */ +} + +.atCodeMirror-label{ + display: flex; + width: 100%; + margin-bottom: 5px; +} + +.atCodeMirror-labelLeftSide{ + flex: 1 0 auto; +} + +.atCodeMirror-labelText{ + text-transform: uppercase; + color: #707070; + font-weight: normal; + font-size: small; + padding-right: 5px; + width: 100%; +} + +.atCodeMirror-toggleContainer{ + margin: 0 0 0 10px; + display: initial; + padding-bottom: 5px; +} + +.atCodeMirror-expandTextContainer{ + flex: 1 0 auto; + text-align: right; + font-weight: normal; + color: @default-link; + cursor: pointer; + font-size: 12px; +} + +.CodeMirror-modal .modal-dialog{ + width: calc(~"100% - 200px"); + height: calc(~"100vh - 80px"); +} + + +@media screen and (min-width: 768px){ + .NetworkingExtraVars .modal-dialog{ + width: 700px; + } +} + +.CodeMirror-modal .modal-dialog{ + width: calc(~"100% - 200px"); + height: calc(~"100vh - 80px"); +} + +.CodeMirror-modal .modal-content{ + height: 100%; +} + +.NetworkingExtraVars .CodeMirror{ + overflow-x: hidden; +} + +.CodeMirror-modalControls{ + float: right; + margin-top: 15px; + button { + margin-left: 10px; + } +} diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js new file mode 100644 index 0000000000..4eba4d2393 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js @@ -0,0 +1,78 @@ +const templateUrl = require('~components/code-mirror/code-mirror.partial.html'); + +const CodeMirrorEventListener = 'CodeMirror-init'; +const CodeMirrorID = 'codemirror-extra-vars'; +const CodeMirrorModalID = '#CodeMirror-modal'; +const ParseVariable = 'parseType'; +const CodeMirrorVar = 'variables'; +const ParseType = 'yaml'; + +function atCodeMirrorController ( + $scope, + strings, + ParseTypeChange, + ParseVariableString +) { + const vm = this; + + function init (vars) { + $scope.variables = ParseVariableString(_.cloneDeep(vars)); + $scope.parseType = ParseType; + const options = { + scope: $scope, + variable: CodeMirrorVar, + parse_variable: ParseVariable, + field_id: CodeMirrorID, + readOnly: $scope.disabled + }; + ParseTypeChange(options); + } + + function expand () { + vm.expanded = true; + } + + function close () { + $(CodeMirrorModalID).off('hidden.bs.modal'); + $(CodeMirrorModalID).modal('hide'); + $('.popover').popover('hide'); + vm.expanded = false; + } + + vm.strings = strings; + vm.expanded = false; + vm.close = close; + vm.expand = expand; + init($scope.variables); + $scope.$on(CodeMirrorEventListener, (e, vars) => { + init(vars); + }); +} + +atCodeMirrorController.$inject = [ + '$scope', + 'CodeMirrorStrings', + 'ParseTypeChange', + 'ParseVariableString' +]; + +function atCodeMirrorTextarea () { + return { + restrict: 'E', + replace: true, + transclude: true, + templateUrl, + controller: atCodeMirrorController, + controllerAs: 'vm', + scope: { + disabled: '@', + label: '@', + labelClass: '@', + tooltip: '@', + tooltipPlacement: '@', + variables: '@' + } + }; +} + +export default atCodeMirrorTextarea; diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html b/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html new file mode 100644 index 0000000000..98349fd3a3 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html @@ -0,0 +1,60 @@ +
+
+
+ + {{ label || vm.strings.get('code_mirror.label.VARIABLES') }} + + + + +
+
+ + +
+
+
+
{{ vm.strings.get('label.EXPAND') }}
+
+ + + +
diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js b/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js new file mode 100644 index 0000000000..0dae4bc6a5 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js @@ -0,0 +1,55 @@ +function CodeMirrorStrings (BaseString) { + BaseString.call(this, 'code_mirror'); + + const { t } = this; + const ns = this.code_mirror; + + ns.label = { + EXTRA_VARIABLES: t.s('EXTRA VARIABLES'), + VARIABLES: t.s('VARIABLES'), + EXPAND: t.s('EXPAND'), + YAML: t.s('YAML'), + JSON: t.s('JSON') + + }; + + ns.tooltip = { + TOOLTIP: t.s(` +

+ Enter inventory variables using either JSON or YAML + syntax. Use the radio button to toggle between the two. +

+ JSON: +
+
+ { +
"somevar": "somevalue", +
"password": "magic" +
+ } +
+ YAML: +
+
+ --- +
somevar: somevalue +
password: magic +
+
+

+ View JSON examples at + www.json.org +

+

+ View YAML examples at + + docs.ansible.com +

`), + TOOLTIP_TITLE: t.s('EXTRA VARIABLES'), + JOB_RESULTS: t.s('Read-only view of extra variables added to the job template.') + }; +} + +CodeMirrorStrings.$inject = ['BaseStringService']; + +export default CodeMirrorStrings; diff --git a/awx/ui/client/lib/components/code-mirror/index.js b/awx/ui/client/lib/components/code-mirror/index.js new file mode 100644 index 0000000000..21d4aedc09 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/index.js @@ -0,0 +1,12 @@ +import codemirror from './code-mirror.directive'; +import modal from './modal/code-mirror-modal.directive'; +import strings from './code-mirror.strings'; + +const MODULE_NAME = 'at.code.mirror'; + +angular.module(MODULE_NAME, []) + .directive('atCodeMirror', codemirror) + .directive('atCodeMirrorModal', modal) + .service('CodeMirrorStrings', strings); + +export default MODULE_NAME; diff --git a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js new file mode 100644 index 0000000000..8899e28bdd --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js @@ -0,0 +1,79 @@ +const templateUrl = require('~components/code-mirror/modal/code-mirror-modal.partial.html'); + +const CodeMirrorModalID = '#CodeMirror-modal'; +const CodeMirrorID = 'codemirror-extra-vars-modal'; +const ParseVariable = 'parseType'; +const CodeMirrorVar = 'extra_variables'; +const ParseType = 'yaml'; +const ModalHeight = '#CodeMirror-modal .modal-dialog'; +const ModalHeader = '.atCodeMirror-label'; +const ModalFooter = '.CodeMirror-modalControls'; + +function atCodeMirrorModalController ( + $scope, + strings, + ParseTypeChange, + ParseVariableString +) { + const vm = this; + + function resize () { + const editor = $(`${CodeMirrorModalID} .CodeMirror`)[0].CodeMirror; + const height = $(ModalHeight).height() - $(ModalHeader).height() - + $(ModalFooter).height() - 100; + editor.setSize('100%', height); + } + + function toggle () { + $scope.parseTypeChange('parseType', 'extra_variables'); + setTimeout(resize, 0); + } + + function init () { + $(CodeMirrorModalID).modal('show'); + $scope.extra_variables = ParseVariableString(_.cloneDeep($scope.variables)); + $scope.parseType = ParseType; + const options = { + scope: $scope, + variable: CodeMirrorVar, + parse_variable: ParseVariable, + field_id: CodeMirrorID, + readOnly: $scope.disabled + }; + ParseTypeChange(options); + resize(); + $(CodeMirrorModalID).on('hidden.bs.modal', $scope.closeFn); + } + + vm.strings = strings; + vm.toggle = toggle; + init(); +} + +atCodeMirrorModalController.$inject = [ + '$scope', + 'CodeMirrorStrings', + 'ParseTypeChange', + 'ParseVariableString', +]; + +function atCodeMirrorModal () { + return { + restrict: 'E', + replace: true, + transclude: true, + templateUrl, + controller: atCodeMirrorModalController, + controllerAs: 'vm', + scope: { + disabled: '@', + label: '@', + labelClass: '@', + tooltip: '@', + variables: '@', + closeFn: '&' + } + }; +} + +export default atCodeMirrorModal; diff --git a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html new file mode 100644 index 0000000000..056672a2b5 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html @@ -0,0 +1,68 @@ + diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index 9ac933628c..35b0e0193d 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -34,6 +34,7 @@ import tab from '~components/tabs/tab.directive'; import tabGroup from '~components/tabs/group.directive'; import topNavItem from '~components/layout/top-nav-item.directive'; import truncate from '~components/truncate/truncate.directive'; +import atCodeMirror from '~components/code-mirror'; import BaseInputController from '~components/input/base.controller'; import ComponentsStrings from '~components/components.strings'; @@ -42,7 +43,8 @@ const MODULE_NAME = 'at.lib.components'; angular .module(MODULE_NAME, [ - atLibServices + atLibServices, + atCodeMirror ]) .directive('atActionGroup', actionGroup) .directive('atDivider', divider) diff --git a/awx/ui/client/src/network-ui/network-details/details.controller.js b/awx/ui/client/src/network-ui/network-details/details.controller.js index cf629ffa62..9e7ceab874 100644 --- a/awx/ui/client/src/network-ui/network-details/details.controller.js +++ b/awx/ui/client/src/network-ui/network-details/details.controller.js @@ -5,10 +5,9 @@ *************************************************/ export default - ['$scope', '$state', '$stateParams', 'GenerateForm', 'ParseTypeChange', 'HostsService', - function($scope, $state, $stateParams, GenerateForm, ParseTypeChange, HostsService){ + ['$scope', 'HostsService', + function($scope, HostsService){ - $scope.parseType = 'yaml'; $scope.formCancel = function(){ $scope.$parent.$broadcast('awxNet-closeDetailsPanel'); }; @@ -30,7 +29,6 @@ $scope.saveConfirmed = false; }, 3000); }); - }; $scope.$parent.$on('awxNet-showDetails', (e, data, canAdd) => { @@ -40,5 +38,6 @@ } else { $scope.item = data; } + $scope.$broadcast('CodeMirror-init', $scope.item.variables); }); }]; diff --git a/awx/ui/client/src/network-ui/network-details/details.partial.html b/awx/ui/client/src/network-ui/network-details/details.partial.html index 5b52ae1e6a..a7460576ce 100644 --- a/awx/ui/client/src/network-ui/network-details/details.partial.html +++ b/awx/ui/client/src/network-ui/network-details/details.partial.html @@ -31,7 +31,11 @@
- + +
diff --git a/awx/ui/client/src/network-ui/network-details/main.js b/awx/ui/client/src/network-ui/network-details/main.js index 1827ce7297..ffa06f41e4 100644 --- a/awx/ui/client/src/network-ui/network-details/main.js +++ b/awx/ui/client/src/network-ui/network-details/main.js @@ -5,9 +5,7 @@ *************************************************/ import awxNetDetailsPanel from './details.directive'; -import awxNetExtraVars from './network-extra-vars/network-extra-vars.directive'; export default angular.module('networkDetailsDirective', []) - .directive('awxNetDetailsPanel', awxNetDetailsPanel) - .directive('awxNetExtraVars', awxNetExtraVars); + .directive('awxNetDetailsPanel', awxNetDetailsPanel); diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less deleted file mode 100644 index 9e340cccec..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less +++ /dev/null @@ -1,168 +0,0 @@ -.NetworkingExtraVarsLabel{ - display: flex; - width: 100%; -} - -.NetworkingExtraVars-extraVarsLabelContainer{ - flex: 1 0 auto; -} - -.NetworkingExtraVars-expandTextContainer{ - flex: 1 0 auto; - text-align: right; - font-weight: normal; - color: @default-link; - cursor: pointer; - font-size: 12px; -} - -.noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -khtml-user-select: none; /* Konqueror */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - not supported by any browser */ -} - -@media screen and (min-width: 768px){ - .NetworkingExtraVars .modal-dialog{ - width: 700px; - } -} - -.NetworkingExtraVarsModal .modal-dialog{ - width: calc(~"100% - 200px"); - height: calc(~"100vh - 80px"); -} - -.NetworkingExtraVarsModal .modal-content{ - height: 100%; -} - -.NetworkingExtraVars .CodeMirror{ - overflow-x: hidden; -} - -.NetworkingExtraVars-close:hover{ - color: @btn-txt; - background-color: @btn-bg-hov; -} - -.NetworkingExtraVars-body{ - margin-bottom: 20px; -} - -.NetworkingExtraVars-tab:hover { - color: @btn-txt; - background-color: @btn-bg-hov; - cursor: pointer; -} -.NetworkingExtraVars-tab--selected{ - color: @btn-txt-sel!important; - background-color: @default-icon!important; - border-color: @default-icon!important; -} -.NetworkingExtraVars-view--container{ - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; -} -.NetworkingExtraVars .modal-footer{ - border: 0; - margin-top: 0px; - padding-top: 5px; -} -.NetworkingExtraVars-controls{ - float: right; - margin-top: 15px; - button { - margin-left: 10px; - } -} - -.NetworkingExtraVars-header{ - padding-bottom: 15px; -} -.NetworkingExtraVars-title{ - color: @default-interface-txt; - font-weight: 600; - margin-bottom: 8px; -} -.NetworkingExtraVarsModal .modal-body{ - padding: 0px!important; - overflow-y: auto; -} -.NetworkingExtraVars-nav{ - padding-top: 12px; - padding-bottom: 20px; -} -.NetworkingExtraVars-field{ - margin-bottom: 8px; - flex: 0 1 12em; -} -.NetworkingExtraVars-field--label{ - text-transform: uppercase; - flex: 0 1 80px; - max-width: 80px; - min-width: 80px; - font-size: 12px; - word-wrap: break-word; -} -.NetworkingExtraVars-field{ - .OnePlusTwo-left--detailsRow; -} -.NetworkingExtraVars-field--content{ - word-wrap: break-word; -} -.NetworkingExtraVars-field--monospaceContent{ - font-family: monospace; -} -.NetworkingExtraVars-button:disabled { - pointer-events: all!important; -} - -.NetworkingExtraVars-numberColumnPreload { - background-color: @default-list-header-bg; - height: 198px; - border-right: 1px solid #ccc; - width: 30px; - position: fixed; -} - -.NetworkingExtraVars-numberColumn { - background-color: @default-list-header-bg; - border-right: 1px solid #ccc; - border-bottom-left-radius: 5px; - color: #999; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - position: fixed; - padding: 4px 3px 0 5px; - text-align: right; - white-space: nowrap; - width: 30px; -} - -.NetworkingExtraVars-numberColumn--second{ - padding-top:0px; -} - -.NetworkingExtraVars-noJson{ - align-items: center; - background-color: @default-no-items-bord; - border: 1px solid @default-icon-hov; - border-radius: 5px; - color: @b7grey; - display: flex; - height: 200px; - justify-content: center; - text-transform: uppercase; - width: 100%; -} - -.NetworkingExtraVarsModal .CodeMirror{ - max-height: none; -} diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js deleted file mode 100644 index 35cf89539d..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js +++ /dev/null @@ -1,70 +0,0 @@ -/************************************************* - * Copyright (c) 2018 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -const templateUrl = require('~network-ui/network-details/network-extra-vars/network-extra-vars.partial.html'); - -export default [ 'ParseTypeChange', 'ParseVariableString', - function(ParseTypeChange, ParseVariableString) { - return { - scope:{ - item: "=" - }, - templateUrl, - restrict: 'E', - link(scope){ - scope.networkingExtraVarsModalOpen = true; - function init(){ - if(scope.item && scope.item.host_id){ - scope.variables = ParseVariableString(scope.item.variables); - scope.parseType = 'yaml'; - ParseTypeChange({ - scope: scope, - field_id: 'network_host_variables', - variable: 'variables', - readOnly: true - }); - } - } - - scope.$watch('item', function(){ - init(); - }); - - scope.closeExtraVarModal = function() { - // Unbind the listener so it doesn't fire when we close the modal via navigation - $('.CodeMirror')[1].remove(); - $('#NetworkingExtraVarsModal').off('hidden.bs.modal'); - $('#NetworkingExtraVarsModal').modal('hide'); - scope.networkingExtraVarsModalOpen = false; - }; - - scope.openExtraVarsModal = function(){ - scope.networkingExtraVarsModalOpen = true; - $('#NetworkingExtraVarsModal').modal('show'); - - $('.modal-dialog').on('resize', function(){ - resize(); - }); - scope.extra_variables = ParseVariableString(_.cloneDeep(scope.item.variables)); - scope.parseType = 'yaml'; - ParseTypeChange({ - scope: scope, - field_id: 'NetworkingExtraVars-codemirror', - variable: 'extra_variables', - readOnly: true - }); - resize(); - }; - - function resize(){ - let editor = $('.CodeMirror')[1].CodeMirror; - let height = $('#NetworkingExtraVarsModalDialog').height() - $('.NetworkingExtraVars-header').height() - $('.NetworkingExtraVars-controls').height() - 110; - editor.setSize("100%", height); - } - - } - }; -}]; diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html deleted file mode 100644 index 1ac026747e..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/awx/ui/client/src/network-ui/style.less b/awx/ui/client/src/network-ui/style.less index 0eddbaa9e7..b49b66c8f8 100644 --- a/awx/ui/client/src/network-ui/style.less +++ b/awx/ui/client/src/network-ui/style.less @@ -1,7 +1,6 @@ /* Copyright (c) 2017 Red Hat, Inc. */ @import 'network-nav/network.nav.block.less'; @import 'network-details/details.block.less'; -@import 'network-details/network-extra-vars/network-extra-vars.block.less'; @import 'zoom-widget/zoom.block.less'; @font-face { From 0b4d88a57d88733f17f0b02d397e333f398a80a9 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Fri, 20 Apr 2018 17:14:18 -0700 Subject: [PATCH 340/379] Adds ability for parent controller to call atCodeMirror's init() function. This enables the parent controller to re-instantiate the CodeMirror instance on the fly, when necessary. This was necessary on the NetworkUI to update the CodeMirror instance on the Host Detail panel. --- .../components/code-mirror/code-mirror.directive.js | 10 +++++----- .../network-ui/network-details/details.controller.js | 10 +++++++++- .../network-ui/network-details/details.partial.html | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js index 4eba4d2393..6d74f2a6aa 100644 --- a/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js @@ -1,6 +1,5 @@ const templateUrl = require('~components/code-mirror/code-mirror.partial.html'); -const CodeMirrorEventListener = 'CodeMirror-init'; const CodeMirrorID = 'codemirror-extra-vars'; const CodeMirrorModalID = '#CodeMirror-modal'; const ParseVariable = 'parseType'; @@ -43,10 +42,10 @@ function atCodeMirrorController ( vm.expanded = false; vm.close = close; vm.expand = expand; + if ($scope.init) { + $scope.init = init; + } init($scope.variables); - $scope.$on(CodeMirrorEventListener, (e, vars) => { - init(vars); - }); } atCodeMirrorController.$inject = [ @@ -70,7 +69,8 @@ function atCodeMirrorTextarea () { labelClass: '@', tooltip: '@', tooltipPlacement: '@', - variables: '@' + variables: '@', + init: '=' } }; } diff --git a/awx/ui/client/src/network-ui/network-details/details.controller.js b/awx/ui/client/src/network-ui/network-details/details.controller.js index 9e7ceab874..ff30b97e9f 100644 --- a/awx/ui/client/src/network-ui/network-details/details.controller.js +++ b/awx/ui/client/src/network-ui/network-details/details.controller.js @@ -8,6 +8,12 @@ ['$scope', 'HostsService', function($scope, HostsService){ + function codemirror () { + return { + init:{} + }; + } + $scope.codeMirror = new codemirror(); $scope.formCancel = function(){ $scope.$parent.$broadcast('awxNet-closeDetailsPanel'); }; @@ -38,6 +44,8 @@ } else { $scope.item = data; } - $scope.$broadcast('CodeMirror-init', $scope.item.variables); + if ($scope.codeMirror.init) { + $scope.codeMirror.init($scope.item.variables); + } }); }]; diff --git a/awx/ui/client/src/network-ui/network-details/details.partial.html b/awx/ui/client/src/network-ui/network-details/details.partial.html index a7460576ce..f7771e8916 100644 --- a/awx/ui/client/src/network-ui/network-details/details.partial.html +++ b/awx/ui/client/src/network-ui/network-details/details.partial.html @@ -34,7 +34,8 @@ + tooltip-placement="left" + init="codeMirror.init">
From 929129e8adcdd81c0719b8b85299abe3a62e6567 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 23 Apr 2018 18:05:47 -0700 Subject: [PATCH 341/379] Makes CodeMirror Modal resizable --- .../code-mirror/modal/code-mirror-modal.directive.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js index 8899e28bdd..6a1837272c 100644 --- a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js +++ b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js @@ -43,6 +43,11 @@ function atCodeMirrorModalController ( ParseTypeChange(options); resize(); $(CodeMirrorModalID).on('hidden.bs.modal', $scope.closeFn); + $(`${CodeMirrorModalID} .modal-dialog`).resizable({ + minHeight: 523, + minWidth: 600 + }); + $(`${CodeMirrorModalID} .modal-dialog`).on('resize', resize); } vm.strings = strings; From 298af25babeea0f73a544dc746624022fc2fc9db Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 23 Apr 2018 18:13:26 -0700 Subject: [PATCH 342/379] Points the host event modal's Standard Out tab at `event_data.res.stdout` instead of `event_data.res.results.stdout`. Same for stderr. This was some stale copy pasta from the host event modal rework --- .../features/output/host-event/host-event.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/output/host-event/host-event.controller.js b/awx/ui/client/features/output/host-event/host-event.controller.js index 67105ba7a0..a688e59a64 100644 --- a/awx/ui/client/features/output/host-event/host-event.controller.js +++ b/awx/ui/client/features/output/host-event/host-event.controller.js @@ -26,7 +26,7 @@ function HostEventsController ( $scope.module_name = 'No result found'; } - if (_.has(hostEvent.event_data, 'res.result.stdout')) { + if (_.has(hostEvent.event_data, 'res.stdout')) { if (hostEvent.event_data.res.stdout === '') { $scope.stdout = ' '; } else { @@ -34,7 +34,7 @@ function HostEventsController ( } } - if (_.has(hostEvent.event_data, 'res.result.stderr')) { + if (_.has(hostEvent.event_data, 'res.stderr')) { if (hostEvent.event_data.res.stderr === '') { $scope.stderr = ' '; } else { From 99fb8e6d83b92a43bd446ca583d39a2d9854aa04 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 23 Apr 2018 19:15:12 -0700 Subject: [PATCH 343/379] Updates URL for host events for adhoc commands --- .../output/host-event/host-event.route.js | 2 +- .../output/host-event/host-event.service.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/features/output/host-event/host-event.route.js b/awx/ui/client/features/output/host-event/host-event.route.js index 06f3eeac51..105881c778 100644 --- a/awx/ui/client/features/output/host-event/host-event.route.js +++ b/awx/ui/client/features/output/host-event/host-event.route.js @@ -14,7 +14,7 @@ function exit () { } function HostEventResolve (HostEventService, $stateParams) { - return HostEventService.getRelatedJobEvents($stateParams.id, { + return HostEventService.getRelatedJobEvents($stateParams.id, $stateParams.type, { id: $stateParams.eventId }).then((response) => response.data.results[0]); } diff --git a/awx/ui/client/features/output/host-event/host-event.service.js b/awx/ui/client/features/output/host-event/host-event.service.js index a1e6952725..4454bde27b 100644 --- a/awx/ui/client/features/output/host-event/host-event.service.js +++ b/awx/ui/client/features/output/host-event/host-event.service.js @@ -4,13 +4,22 @@ function HostEventService ( GetBasePath, $rootScope ) { + this.getUrl = (id, type, params) => { + let url; + if (type === 'playbook') { + url = `${GetBasePath('jobs')}${id}/job_events/?${this.stringifyParams(params)}`; + } else if (type === 'command') { + url = `${GetBasePath('ad_hoc_commands')}${id}/events/?${this.stringifyParams(params)}`; + } + return url; + }; + // GET events related to a job run // e.g. // ?event=playbook_on_stats // ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter - this.getRelatedJobEvents = (id, params) => { - let url = GetBasePath('jobs'); - url = `${url}${id}/job_events/?${this.stringifyParams(params)}`; + this.getRelatedJobEvents = (id, type, params) => { + const url = this.getUrl(id, type, params); Rest.setUrl(url); return Rest.get() .then(response => response) From 6f1774f1b1e5a0024c3e3ce25282d8fee919aae9 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 23 Apr 2018 19:49:18 -0700 Subject: [PATCH 344/379] Adds NgNonBindable directive to stdout lines in order to prevent stdout code from running angular snippets --- awx/ui/client/features/output/render.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 12ca797b32..aa86913133 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -169,7 +169,7 @@ function JobRenderService ($q, $sce, $window) { } if (current.isHost) { - tdEvent = `${content}`; + tdEvent = `${content}`; } if (current.time && current.line === ln) { From 155daf47ea01b60b1998e7e9c45ea1cc6503b683 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 23 Apr 2018 16:32:02 -0400 Subject: [PATCH 345/379] don't anticipate spinny on navbar click to current page --- awx/ui/test/e2e/tests/smoke.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/awx/ui/test/e2e/tests/smoke.js b/awx/ui/test/e2e/tests/smoke.js index 931ef9f6d5..b77b23d69d 100644 --- a/awx/ui/test/e2e/tests/smoke.js +++ b/awx/ui/test/e2e/tests/smoke.js @@ -190,9 +190,6 @@ module.exports = { credentials.section.navigation.expect.element('@credentials').enabled; credentials.section.navigation.click('@credentials'); - credentials.waitForElementVisible('div.spinny'); - credentials.waitForElementNotVisible('div.spinny'); - credentials.section.list.waitForElementVisible('@add'); credentials.section.list.expect.element('@add').enabled; credentials.section.list.click('@add'); @@ -219,7 +216,6 @@ module.exports = { credentials.section.navigation.expect.element('@credentials').enabled; credentials.section.navigation.click('@credentials'); - credentials.waitForElementVisible('div.spinny'); credentials.waitForElementNotVisible('div.spinny'); credentials.section.list.waitForElementVisible('@add'); From 68975572f37484004916abc704a02e0f5b1e2aa8 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 24 Apr 2018 09:23:08 -0400 Subject: [PATCH 346/379] do not update modified_by for system fields --- awx/main/models/base.py | 12 +++++++++--- awx/main/tests/functional/models/test_job.py | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/awx/main/models/base.py b/awx/main/models/base.py index 7639bc4548..fcca82474c 100644 --- a/awx/main/models/base.py +++ b/awx/main/models/base.py @@ -256,6 +256,7 @@ class PrimordialModel(CreatedModifiedModel): def save(self, *args, **kwargs): update_fields = kwargs.get('update_fields', []) + fields_are_specified = bool(update_fields) user = get_current_user() if user and not user.id: user = None @@ -263,9 +264,14 @@ class PrimordialModel(CreatedModifiedModel): self.created_by = user if 'created_by' not in update_fields: update_fields.append('created_by') - self.modified_by = user - if 'modified_by' not in update_fields: - update_fields.append('modified_by') + # Update modified_by if not called with update_fields, or if any + # editable fields are present in update_fields + if ( + (not fields_are_specified) or + any(getattr(self._meta.get_field(name), 'editable', True) for name in update_fields)): + self.modified_by = user + if 'modified_by' not in update_fields: + update_fields.append('modified_by') super(PrimordialModel, self).save(*args, **kwargs) def clean_description(self): diff --git a/awx/main/tests/functional/models/test_job.py b/awx/main/tests/functional/models/test_job.py index bc166fd77d..ec23045fea 100644 --- a/awx/main/tests/functional/models/test_job.py +++ b/awx/main/tests/functional/models/test_job.py @@ -1,6 +1,7 @@ import pytest from awx.main.models import JobTemplate, Job +from crum import impersonate @pytest.mark.django_db @@ -49,3 +50,18 @@ def test_awx_custom_virtualenv_without_jt(project): job = Job.objects.get(pk=job.id) assert job.ansible_virtualenv_path == '/venv/fancy-proj' + + +@pytest.mark.django_db +def test_update_parent_instance(job_template, alice): + # jobs are launched as a particular user, user not saved as modified_by + with impersonate(alice): + assert job_template.current_job is None + assert job_template.status == 'never updated' + assert job_template.modified_by is None + job = job_template.jobs.create(status='new') + job.status = 'pending' + job.save() + assert job_template.current_job == job + assert job_template.status == 'pending' + assert job_template.modified_by is None From 7c621a91ee60e9820feb543fc44cc23d5ad1f18d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 24 Apr 2018 11:26:36 -0400 Subject: [PATCH 347/379] use named formatting in error messages --- awx/api/serializers.py | 4 +++- awx/main/fields.py | 12 ++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index ca9464f93b..bba331fa23 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2673,7 +2673,9 @@ class CredentialSerializer(BaseSerializer): for field in set(data.keys()) - valid_fields - set(credential_type.defined_fields): if data.get(field): raise serializers.ValidationError( - {"detail": _("'%s' is not a valid field for %s") % (field, credential_type.name)} + {"detail": _("'{field_name}' is not a valid field for {credential_type_name}").format( + field_name=field, credential_type_name=credential_type.name + )} ) value.pop('kind', None) return value diff --git a/awx/main/fields.py b/awx/main/fields.py index 44389f3879..14e1cc6ad0 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -59,7 +59,8 @@ __all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField', def __enum_validate__(validator, enums, instance, schema): if instance not in enums: yield jsonschema.exceptions.ValidationError( - _("'%s' is not one of ['%s']") % (instance, "', '".join(enums)) + _("'{value}' is not one of ['{allowed_values}']").format( + value=instance, allowed_values="', '".join(enums)) ) @@ -729,7 +730,8 @@ class CredentialTypeInputField(JSONSchemaField): for key in ('choices', 'multiline', 'format', 'secret',): if key in field and field['type'] != 'string': raise django_exceptions.ValidationError( - _('%s not allowed for %s type (%s)' % (key, field['type'], field['id'])), + _('{sub_key} not allowed for {element_type} type ({element_id})'.format( + sub_key=key, element_type=field['type'], element_id=field['id'])), code='invalid', params={'value': value}, ) @@ -826,13 +828,15 @@ class CredentialTypeInjectorField(JSONSchemaField): ).from_string(tmpl).render(valid_namespace) except UndefinedError as e: raise django_exceptions.ValidationError( - _('%s uses an undefined field (%s)') % (key, e), + _('{sub_key} uses an undefined field ({error_msg})').format( + sub_key=key, error_msg=e), code='invalid', params={'value': value}, ) except TemplateSyntaxError as e: raise django_exceptions.ValidationError( - _('Syntax error rendering template for %s inside of %s (%s)') % (key, type_, e), + _('Syntax error rendering template for {sub_key} inside of {type} ({error_msg})').format( + sub_key=key, type=type_, error_msg=e), code='invalid', params={'value': value}, ) From 13550acb9105cdd224776430bb55222bfc2cddeb Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 24 Apr 2018 10:49:04 -0400 Subject: [PATCH 348/379] fix cross-talk between JT-proj due to arg mutability --- awx/main/models/unified_jobs.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index ab03389535..943956f7ac 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -263,14 +263,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio if field not in update_fields: update_fields.append(field) # Do the actual save. - try: - super(UnifiedJobTemplate, self).save(*args, **kwargs) - except ValueError: - # A fix for https://trello.com/c/S4rU1F21 - # Does not resolve the root cause. Tis merely a bandaid. - if 'scm_delete_on_next_update' in update_fields: - update_fields.remove('scm_delete_on_next_update') - super(UnifiedJobTemplate, self).save(*args, **kwargs) + super(UnifiedJobTemplate, self).save(*args, **kwargs) def _get_current_status(self): @@ -722,7 +715,10 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique def _get_parent_instance(self): return getattr(self, self._get_parent_field_name(), None) - def _update_parent_instance_no_save(self, parent_instance, update_fields=[]): + def _update_parent_instance_no_save(self, parent_instance, update_fields=None): + if update_fields is None: + update_fields = [] + def parent_instance_set(key, val): setattr(parent_instance, key, val) if key not in update_fields: From 619ec905b28e8bf5a0544dec15e72db396e78bac Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 24 Apr 2018 14:05:38 -0400 Subject: [PATCH 349/379] policy fields not required * They were previously not required until a min/max was enforced. This caused the fields to, unintentionally, be required. * This fix makes the policy fields not required and provides sane defaults. --- awx/api/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index bba331fa23..99bdf339ea 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4587,8 +4587,8 @@ class InstanceGroupSerializer(BaseSerializer): percent_capacity_remaining = serializers.SerializerMethodField() jobs_running = serializers.SerializerMethodField() instances = serializers.SerializerMethodField() - policy_instance_percentage = serializers.IntegerField(min_value=0, max_value=100) - policy_instance_minimum = serializers.IntegerField(min_value=0) + policy_instance_percentage = serializers.IntegerField(min_value=0, max_value=100, required=False, initial=0) + policy_instance_minimum = serializers.IntegerField(min_value=0, required=False, initial=0) class Meta: model = InstanceGroup From c98ede2f271401bc3a7f893e2f1358d15654f10e Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 24 Apr 2018 16:53:10 -0400 Subject: [PATCH 350/379] fix a bug in custom virtualenv when Project.organization is None see: https://github.com/ansible/tower/issues/1490 --- awx/main/models/jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index d60833edeb..51394aa830 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -538,7 +538,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana for virtualenv in ( self.job_template.custom_virtualenv if self.job_template else None, self.project.custom_virtualenv, - self.project.organization.custom_virtualenv + self.project.organization.custom_virtualenv if self.project.organization else None ): if virtualenv: return virtualenv From 05419d010bb9353bfce2c5f8346a78151b866814 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Tue, 24 Apr 2018 21:40:11 -0400 Subject: [PATCH 351/379] Update group cluster policies on save, not just created Currently updating policy settings doesn't trigger a re-evaluation of instance group policies, this makes sure we re-evaluate in the event that anything changes. --- awx/main/models/ha.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index b7e50ec2b4..471276c560 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -192,9 +192,8 @@ class JobOrigin(models.Model): @receiver(post_save, sender=InstanceGroup) def on_instance_group_saved(sender, instance, created=False, raw=False, **kwargs): - if created: - from awx.main.tasks import apply_cluster_membership_policies - connection.on_commit(lambda: apply_cluster_membership_policies.apply_async()) + from awx.main.tasks import apply_cluster_membership_policies + connection.on_commit(lambda: apply_cluster_membership_policies.apply_async()) @receiver(post_save, sender=Instance) From 4af8a532321d783382f80a59491ae4e9256ebd2c Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Tue, 24 Apr 2018 16:31:08 -0400 Subject: [PATCH 352/379] Remove Instance Group concept/usage from WorkflowJobs This also relaxes some of the task manager rules on Instance Groups down the full stack such that workflow jobs tend to shortcut the processing or omit it altogether. This lets the workflow job spawning logic exist outside of the instance group queues, which it doesn't need to participate in in the first place. --- awx/main/managers.py | 2 -- awx/main/models/workflow.py | 2 +- awx/main/scheduler/task_manager.py | 10 +++++++--- .../task_management/test_rampart_groups.py | 14 +++++++++++++- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/awx/main/managers.py b/awx/main/managers.py index 1adb75e913..274a0ef774 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -178,8 +178,6 @@ class InstanceGroupManager(models.Manager): if t.status == 'waiting' or not t.execution_node: # Subtract capacity from any peer groups that share instances if not t.instance_group: - logger.warning('Excluded %s from capacity algorithm ' - '(missing instance_group).', t.log_format) impacted_groups = [] elif t.instance_group.name not in ig_ig_mapping: # Waiting job in group with 0 capacity has no collateral impact diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py index f43a43cd24..c63bbc6f1f 100644 --- a/awx/main/models/workflow.py +++ b/awx/main/models/workflow.py @@ -474,7 +474,7 @@ class WorkflowJob(UnifiedJob, WorkflowJobOptions, SurveyJobMixin, JobNotificatio @property def preferred_instance_groups(self): - return self.global_instance_groups + return [] ''' A WorkflowJob is a virtual job. It doesn't result in a celery task. diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index f988e76fd3..b3d5ed14f1 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -259,7 +259,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' - if not task.supports_isolation() and rampart_group.controller_id: + elif not task.supports_isolation() and rampart_group.controller_id: # non-Ansible jobs on isolated instances run on controller task.instance_group = rampart_group.controller logger.info('Submitting isolated %s to queue %s via %s.', @@ -271,7 +271,8 @@ class TaskManager(): task.celery_task_id = str(uuid.uuid4()) task.save() - self.consume_capacity(task, rampart_group.name) + if rampart_group is not None: + self.consume_capacity(task, rampart_group.name) def post_commit(): task.websocket_emit_status(task.status) @@ -281,7 +282,7 @@ class TaskManager(): connection.on_commit(post_commit) def process_running_tasks(self, running_tasks): - map(lambda task: self.graph[task.instance_group.name]['graph'].add_job(task), running_tasks) + map(lambda task: self.graph[task.instance_group.name]['graph'].add_job(task) if task.instance_group else None, running_tasks) def create_project_update(self, task): project_task = Project.objects.get(id=task.project_id).create_project_update( @@ -447,6 +448,9 @@ class TaskManager(): continue preferred_instance_groups = task.preferred_instance_groups found_acceptable_queue = False + if isinstance(task, WorkflowJob): + self.start_task(task, None, task.get_jobs_fail_chain()) + continue for rampart_group in preferred_instance_groups: remaining_capacity = self.get_remaining_capacity(rampart_group.name) if remaining_capacity <= 0: diff --git a/awx/main/tests/functional/task_management/test_rampart_groups.py b/awx/main/tests/functional/task_management/test_rampart_groups.py index 9b4b3eac44..ce79b78003 100644 --- a/awx/main/tests/functional/task_management/test_rampart_groups.py +++ b/awx/main/tests/functional/task_management/test_rampart_groups.py @@ -2,7 +2,7 @@ import pytest import mock from datetime import timedelta from awx.main.scheduler import TaskManager -from awx.main.models import InstanceGroup +from awx.main.models import InstanceGroup, WorkflowJob from awx.main.tasks import apply_cluster_membership_policies @@ -77,6 +77,18 @@ def test_multi_group_with_shared_dependency(instance_factory, default_instance_g assert TaskManager.start_task.call_count == 2 +@pytest.mark.django_db +def test_workflow_job_no_instancegroup(workflow_job_template_factory, default_instance_group, mocker): + wfjt = workflow_job_template_factory('anicedayforawalk').workflow_job_template + wfj = WorkflowJob.objects.create(workflow_job_template=wfjt) + wfj.status = "pending" + wfj.save() + with mocker.patch("awx.main.scheduler.TaskManager.start_task"): + TaskManager().schedule() + TaskManager.start_task.assert_called_once_with(wfj, None, []) + assert wfj.instance_group is None + + @pytest.mark.django_db def test_overcapacity_blocking_other_groups_unaffected(instance_factory, default_instance_group, mocker, instance_group_factory, job_template_factory): From c691d16b1195aafee5ab4dab5f583932ed2635eb Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 11 Apr 2018 15:45:14 -0400 Subject: [PATCH 353/379] validate instance group host list --- awx/api/serializers.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 99bdf339ea..c774898d0d 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4587,8 +4587,22 @@ class InstanceGroupSerializer(BaseSerializer): percent_capacity_remaining = serializers.SerializerMethodField() jobs_running = serializers.SerializerMethodField() instances = serializers.SerializerMethodField() - policy_instance_percentage = serializers.IntegerField(min_value=0, max_value=100, required=False, initial=0) - policy_instance_minimum = serializers.IntegerField(min_value=0, required=False, initial=0) + # NOTE: help_text is duplicated from field definitions, no obvious way of + # both defining field details here and also getting the field's help_text + policy_instance_percentage = serializers.IntegerField( + default=0, min_value=0, max_value=100, required=False, initial=0, + help_text=_("Minimum percentage of all instances that will be automatically assigned to " + "this group when new instances come online.") + ) + policy_instance_minimum = serializers.IntegerField( + default=0, min_value=0, required=False, initial=0, + help_text=_("Static minimum number of Instances that will be automatically assign to " + "this group when new instances come online.") + ) + policy_instance_list = serializers.ListField( + child=serializers.CharField(), + help_text=_("List of exact-match Instances that will be assigned to this group") + ) class Meta: model = InstanceGroup @@ -4605,6 +4619,14 @@ class InstanceGroupSerializer(BaseSerializer): res['controller'] = self.reverse('api:instance_group_detail', kwargs={'pk': obj.controller_id}) return res + def validate_policy_instance_list(self, value): + for instance_name in value: + if value.count(instance_name) > 1: + raise serializers.ValidationError(_('Duplicate entry {}.').format(instance_name)) + if not Instance.objects.filter(hostname=instance_name).exists(): + raise serializers.ValidationError(_('{} is not a valid hostname of an existing instance.').format(instance_name)) + return value + def get_jobs_qs(self): # Store running jobs queryset in context, so it will be shared in ListView if 'running_jobs' not in self.context: From 14c6265b270ff47dbb95656f401b39018636d0c0 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 24 Apr 2018 12:00:55 -0400 Subject: [PATCH 354/379] ensure instance policy percentages round up --- awx/main/tests/functional/test_instances.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/awx/main/tests/functional/test_instances.py b/awx/main/tests/functional/test_instances.py index 11484dfc6e..91dee86b9e 100644 --- a/awx/main/tests/functional/test_instances.py +++ b/awx/main/tests/functional/test_instances.py @@ -60,6 +60,21 @@ def test_policy_instance_few_instances(mock, instance_factory, instance_group_fa assert i2 in ig_4.instances.all() +@pytest.mark.django_db +@mock.patch('awx.main.tasks.handle_ha_toplogy_changes', return_value=None) +def test_policy_instance_distribution_round_up(mock, instance_factory, instance_group_factory): + i1 = instance_factory("i1") + i2 = instance_factory("i2") + i3 = instance_factory("i3") + i4 = instance_factory("i4") + i5 = instance_factory("i5") + ig_1 = instance_group_factory("ig1", percentage=79) + apply_cluster_membership_policies() + assert len(ig_1.instances.all()) == 4 + assert set([i1, i2, i3, i4]) == set(ig_1.instances.all()) + assert i5 not in ig_1.instances.all() + + @pytest.mark.django_db @mock.patch('awx.main.tasks.handle_ha_toplogy_changes', return_value=None) def test_policy_instance_distribution_uneven(mock, instance_factory, instance_group_factory): From b7f280588c8a1b2739f38ff557daa57a2b84bc4d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 25 Apr 2018 09:35:45 -0400 Subject: [PATCH 355/379] add handling for missing related items of events --- awx/main/models/events.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/awx/main/models/events.py b/awx/main/models/events.py index 6f240cfdf4..21dcd90a24 100644 --- a/awx/main/models/events.py +++ b/awx/main/models/events.py @@ -2,7 +2,7 @@ import datetime import logging from django.conf import settings -from django.db import models +from django.db import models, DatabaseError from django.utils.dateparse import parse_datetime from django.utils.timezone import utc from django.utils.translation import ugettext_lazy as _ @@ -15,6 +15,8 @@ from awx.main.utils import ignore_inventory_computed_fields analytics_logger = logging.getLogger('awx.analytics.job_events') +logger = logging.getLogger('awx.main.models.events') + __all__ = ['JobEvent', 'ProjectUpdateEvent', 'AdHocCommandEvent', 'InventoryUpdateEvent', 'SystemJobEvent'] @@ -323,7 +325,10 @@ class BasePlaybookEvent(CreatedModifiedModel): hostnames = self._hostnames() self._update_host_summary_from_stats(hostnames) - self.job.inventory.update_computed_fields() + try: + self.job.inventory.update_computed_fields() + except DatabaseError: + logger.exception('Computed fields database error saving event {}'.format(self.pk)) @@ -441,6 +446,9 @@ class JobEvent(BasePlaybookEvent): def _update_host_summary_from_stats(self, hostnames): with ignore_inventory_computed_fields(): + if not self.job or not self.job.inventory: + logger.info('Event {} missing job or inventory, host summaries not updated'.format(self.pk)) + return qs = self.job.inventory.hosts.filter(name__in=hostnames) job = self.job for host in hostnames: From ea80fb84975c06e075ce9af351edfc8169e7ca34 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 25 Apr 2018 10:50:09 -0400 Subject: [PATCH 356/379] Hold off on refreshing lists when ws-jobs events come in and the launch modal is open to prevent the modal from getting wiped --- .../features/jobs/jobsList.controller.js | 28 ++++++-- .../templates/templatesList.controller.js | 34 +++++++-- .../lib/components/modal/modal.directive.js | 5 ++ awx/ui/client/src/home/home.controller.js | 70 ++++++++++++------- .../src/templates/prompt/prompt.controller.js | 6 ++ 5 files changed, 106 insertions(+), 37 deletions(-) diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js index 6715c84e99..4959d41b5e 100644 --- a/awx/ui/client/features/jobs/jobsList.controller.js +++ b/awx/ui/client/features/jobs/jobsList.controller.js @@ -29,6 +29,9 @@ function ListJobsController ( const iterator = 'job'; const key = 'job_dataset'; + let launchModalOpen = false; + let refreshAfterLaunchClose = false; + $scope.list = { iterator, name }; $scope.collection = { iterator, basePath: 'unified_jobs' }; $scope[key] = Dataset.data; @@ -38,10 +41,20 @@ function ListJobsController ( $scope[name] = dataset.results; }); $scope.$on('ws-jobs', () => { - qs.search(unifiedJob.path, $state.params.job_search) - .then(({ data }) => { - $scope.$emit('updateDataset', data); - }); + if (!launchModalOpen) { + refreshJobs(); + } else { + refreshAfterLaunchClose = true; + } + }); + + $scope.$on('launchModalOpen', (evt, isOpen) => { + evt.stopPropagation(); + if (!isOpen && refreshAfterLaunchClose) { + refreshAfterLaunchClose = false; + refreshJobs(); + } + launchModalOpen = isOpen; }); if ($state.includes('instanceGroups')) { @@ -164,6 +177,13 @@ function ListJobsController ( actionText: strings.get('CANCEL') }); }; + + function refreshJobs () { + qs.search(unifiedJob.path, $state.params.job_search) + .then(({ data }) => { + $scope.$emit('updateDataset', data); + }); + } } ListJobsController.$inject = [ diff --git a/awx/ui/client/features/templates/templatesList.controller.js b/awx/ui/client/features/templates/templatesList.controller.js index 0253fabdb0..6ab8712e40 100644 --- a/awx/ui/client/features/templates/templatesList.controller.js +++ b/awx/ui/client/features/templates/templatesList.controller.js @@ -30,6 +30,9 @@ function ListTemplatesController( const choices = workflowTemplate.options('actions.GET.type.choices') .concat(jobTemplate.options('actions.GET.type.choices')); + let launchModalOpen = false; + let refreshAfterLaunchClose = false; + vm.strings = strings; vm.templateTypes = mapChoices(choices); vm.activeId = parseInt($state.params.job_template_id || $state.params.workflow_template_id); @@ -48,7 +51,7 @@ function ListTemplatesController( $scope.canAdd = ($scope.canAddJobTemplate || $scope.canAddWorkflowJobTemplate); // smart-search - $scope.list = { + $scope.list = { iterator: 'template', name: 'templates' }; @@ -64,12 +67,20 @@ function ListTemplatesController( }); $scope.$on(`ws-jobs`, () => { - let path = GetBasePath('unified_job_templates'); - qs.search(path, $state.params.template_search) - .then(function(searchResponse) { - $scope.template_dataset = searchResponse.data; - $scope.templates = $scope.template_dataset.results; - }); + if (!launchModalOpen) { + refreshTemplates(); + } else { + refreshAfterLaunchClose = true; + } + }); + + $scope.$on('launchModalOpen', (evt, isOpen) => { + evt.stopPropagation(); + if (!isOpen && refreshAfterLaunchClose) { + refreshAfterLaunchClose = false; + refreshTemplates(); + } + launchModalOpen = isOpen; }); vm.isInvalid = (template) => { @@ -163,6 +174,15 @@ function ListTemplatesController( return html; }; + function refreshTemplates() { + let path = GetBasePath('unified_job_templates'); + qs.search(path, $state.params.template_search) + .then(function(searchResponse) { + $scope.template_dataset = searchResponse.data; + $scope.templates = $scope.template_dataset.results; + }); + } + function createErrorHandler(path, action) { return ({ data, status }) => { const hdr = strings.get('error.HEADER'); diff --git a/awx/ui/client/lib/components/modal/modal.directive.js b/awx/ui/client/lib/components/modal/modal.directive.js index f3def99885..3f77d374e7 100644 --- a/awx/ui/client/lib/components/modal/modal.directive.js +++ b/awx/ui/client/lib/components/modal/modal.directive.js @@ -26,6 +26,7 @@ function AtModalController ($timeout, eventService, strings) { vm.modal = scope[scope.ns].modal; vm.modal.show = vm.show; vm.modal.hide = vm.hide; + vm.modal.onClose = scope.onClose; }; vm.show = (title, message) => { @@ -48,6 +49,10 @@ function AtModalController ($timeout, eventService, strings) { setTimeout(() => { overlay.style.display = 'none'; }, DEFAULT_ANIMATION_DURATION); + + if (vm.modal.onClose) { + vm.modal.onClose(); + } }; vm.clickToHide = event => { diff --git a/awx/ui/client/src/home/home.controller.js b/awx/ui/client/src/home/home.controller.js index 42ec5ec5b0..17b6d4e314 100644 --- a/awx/ui/client/src/home/home.controller.js +++ b/awx/ui/client/src/home/home.controller.js @@ -10,35 +10,24 @@ export default ['$scope', '$rootScope','Wait', Rest, GetBasePath, ProcessErrors, graphData) { var dataCount = 0; + let launchModalOpen = false; + let refreshAfterLaunchClose = false; $scope.$on('ws-jobs', function () { - Rest.setUrl(GetBasePath('dashboard')); - Rest.get() - .then(({data}) => { - $scope.dashboardData = data; - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard host graph data: ' + status }); - }); - - Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job"); - Rest.get() - .then(({data}) => { - $scope.dashboardJobsListData = data.results; - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); - }); - - Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); - Rest.get() - .then(({data}) => { - $scope.dashboardJobTemplatesListData = data.results; - }) - .catch(({data, status}) => { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); - }); + if (!launchModalOpen) { + refreshLists(); + } else { + refreshAfterLaunchClose = true; + } + }); + $scope.$on('launchModalOpen', (evt, isOpen) => { + evt.stopPropagation(); + if (!isOpen && refreshAfterLaunchClose) { + refreshAfterLaunchClose = false; + refreshLists(); + } + launchModalOpen = isOpen; }); if ($scope.removeDashboardDataLoadComplete) { @@ -119,5 +108,34 @@ export default ['$scope', '$rootScope','Wait', $scope.refresh(); + function refreshLists () { + Rest.setUrl(GetBasePath('dashboard')); + Rest.get() + .then(({data}) => { + $scope.dashboardData = data; + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard host graph data: ' + status }); + }); + + Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job"); + Rest.get() + .then(({data}) => { + $scope.dashboardJobsListData = data.results; + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); + }); + + Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); + Rest.get() + .then(({data}) => { + $scope.dashboardJobTemplatesListData = data.results; + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); + }); + } + } ]; diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 5b6f035dd5..12c1760195 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -140,6 +140,12 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', vm.steps.preview.tab.order = order; modal.show('PROMPT'); vm.promptData.triggerModalOpen = false; + + modal.onClose = () => { + scope.$emit('launchModalOpen', false); + }; + + scope.$emit('launchModalOpen', true); }) .catch(({data, status}) => { ProcessErrors(scope, data, status, null, { From 895ad70a123b40262233d5d9e08f6d13d3c8f2a4 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 16 Apr 2018 15:38:32 -0400 Subject: [PATCH 357/379] Prevent users from attempting to create a template schedule or workflow node with credentials that require passwords --- .../launchTemplateButton.partial.html | 2 +- .../relaunchButton/relaunchButton.partial.html | 2 +- .../job-templates/job-templates-list.partial.html | 2 +- .../client/src/portal-mode/portal-mode.route.js | 2 +- .../src/scheduler/schedulerAdd.controller.js | 2 ++ .../src/scheduler/schedulerEdit.controller.js | 2 ++ .../src/scheduler/schedulerForm.partial.html | 2 +- .../src/templates/prompt/prompt.controller.js | 1 + .../src/templates/prompt/prompt.directive.js | 3 ++- .../src/templates/prompt/prompt.partial.html | 15 ++++++++++++--- .../credential/prompt-credential.directive.js | 3 ++- .../credential/prompt-credential.partial.html | 14 +++++++++++++- .../workflow-maker/workflow-maker.controller.js | 1 + .../workflow-maker/workflow-maker.partial.html | 2 +- 14 files changed, 41 insertions(+), 12 deletions(-) diff --git a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html index 9ac0fc2e1b..fc5c90de57 100644 --- a/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html +++ b/awx/ui/client/lib/components/launchTemplateButton/launchTemplateButton.partial.html @@ -5,5 +5,5 @@ data-placement="top"> - +
diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html index 69b3eea711..280380dcbc 100644 --- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html @@ -30,5 +30,5 @@ ng-if="!vm.showDropdown"> - +
diff --git a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html index c80c4f955e..e60a7e5c71 100644 --- a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html +++ b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html @@ -56,4 +56,4 @@ You can create a job template here.

- + diff --git a/awx/ui/client/src/portal-mode/portal-mode.route.js b/awx/ui/client/src/portal-mode/portal-mode.route.js index cb347821b3..8812a489d6 100644 --- a/awx/ui/client/src/portal-mode/portal-mode.route.js +++ b/awx/ui/client/src/portal-mode/portal-mode.route.js @@ -50,7 +50,7 @@ export default { list: PortalJobTemplateList, mode: 'edit' }); - return html + ''; + return html + ''; }, controller: PortalModeJobTemplatesController } diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index 4e34389a7e..4a6ad12c9c 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -32,6 +32,8 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.$parent.schedulerEndDt = month + '/' + day + '/' + dt.getFullYear(); }; + $scope.preventCredsWithPasswords = true; + /* * This is a workaround for the angular-scheduler library inserting `ll` into fields after an * invalid entry and never unsetting them. Presumably null is being truncated down to 2 chars diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index f82d89cd46..5893d7394e 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -10,6 +10,8 @@ function($filter, $state, $stateParams, Wait, $scope, moment, let schedule, scheduler, scheduleCredentials = []; + $scope.preventCredsWithPasswords = true; + // initial end @ midnight values $scope.schedulerEndHour = "00"; $scope.schedulerEndMinute = "00"; diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html index bea1d9eade..bb68bfbaaf 100644 --- a/awx/ui/client/src/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -686,5 +686,5 @@ ng-disabled="!schedulerIsValid || promptModalMissingReqFields"> Save
- +
diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 5b6f035dd5..95e756fd78 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -16,6 +16,7 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', ({ modal } = scope[scope.ns]); scope.$watch('vm.promptData.triggerModalOpen', () => { + vm.actionButtonClicked = false; if(vm.promptData && vm.promptData.triggerModalOpen) { diff --git a/awx/ui/client/src/templates/prompt/prompt.directive.js b/awx/ui/client/src/templates/prompt/prompt.directive.js index 501d89f79a..b09a3bcc84 100644 --- a/awx/ui/client/src/templates/prompt/prompt.directive.js +++ b/awx/ui/client/src/templates/prompt/prompt.directive.js @@ -5,7 +5,8 @@ export default [ 'templateUrl', scope: { promptData: '=', onFinish: '&', - actionText: '@actionText' + actionText: '@actionText', + preventCredsWithPasswords: '<' }, templateUrl: templateUrl('templates/prompt/prompt'), replace: true, diff --git a/awx/ui/client/src/templates/prompt/prompt.partial.html b/awx/ui/client/src/templates/prompt/prompt.partial.html index 98ca4f9644..a25bef7e0b 100644 --- a/awx/ui/client/src/templates/prompt/prompt.partial.html +++ b/awx/ui/client/src/templates/prompt/prompt.partial.html @@ -12,7 +12,11 @@
- + +
@@ -26,8 +30,13 @@
+ +
+
+ + Credentials that require passwords on launch are not permitted for template schedules and workflow nodes. The following credentials must be removed or replaced to proceed: +
+
+
{{promptData.prompts.credentials.passwords.ssh.name || promptData.prompts.credentials.passwords.become.name || promptData.prompts.credentials.passwords.ssh_key_unlock.name}}
+
{{vaultCred.name}}
+
+
+
{{:: vm.strings.get('prompt.CREDENTIAL_TYPE') }}: @@ -53,7 +65,7 @@
-
+
{{:: vm.strings.get('prompt.PASSWORDS_REQUIRED_HELP') }}
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index c27ecde1d7..a4119b4a79 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -14,6 +14,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let promptWatcher, surveyQuestionWatcher; $scope.strings = TemplatesStrings; + $scope.preventCredsWithPasswords = true; $scope.workflowMakerFormConfig = { nodeMode: "idle", diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html index ad0a5fc9d5..8fb59e1349 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html @@ -129,5 +129,5 @@
- +
From eaa2227890438655cbaeba626e01b281265f8677 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 19 Apr 2018 11:28:11 -0400 Subject: [PATCH 358/379] Replace styles with classes and move string to strings file --- awx/ui/client/features/templates/templates.strings.js | 1 + awx/ui/client/src/templates/prompt/prompt.block.less | 7 +++++++ .../prompt/steps/credential/prompt-credential.partial.html | 6 +++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js index 0b4e6be732..f275e522a0 100644 --- a/awx/ui/client/features/templates/templates.strings.js +++ b/awx/ui/client/features/templates/templates.strings.js @@ -33,6 +33,7 @@ function TemplatesStrings (BaseString) { NO_INVENTORY_SELECTED: t.s('No inventory selected'), REVERT: t.s('REVERT'), CREDENTIAL_TYPE: t.s('Credential Type'), + CREDENTIAL_PASSWORD_WARNING: t.s('Credentials that require passwords on launch are not permitted for template schedules and workflow nodes. The following credentials must be removed or replaced to proceed:'), PASSWORDS_REQUIRED_HELP: t.s('Launching this job requires the passwords listed below. Enter and confirm each password before continuing.'), PLEASE_ENTER_PASSWORD: t.s('Please enter a password.'), credential_passwords: { diff --git a/awx/ui/client/src/templates/prompt/prompt.block.less b/awx/ui/client/src/templates/prompt/prompt.block.less index 62275ecc0b..4b2814d5f5 100644 --- a/awx/ui/client/src/templates/prompt/prompt.block.less +++ b/awx/ui/client/src/templates/prompt/prompt.block.less @@ -169,3 +169,10 @@ margin-bottom: 20px; color: @default-err; } +.Prompt-credsWarning { + margin-bottom: 5px; + color: @default-err; +} +.Prompt-credsWarningList { + margin-bottom: 20px; +} diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html index bcafc859d8..214401b55f 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html @@ -46,11 +46,11 @@
-
+
- Credentials that require passwords on launch are not permitted for template schedules and workflow nodes. The following credentials must be removed or replaced to proceed: + {{ vm.strings.get('prompt.CREDENTIAL_PASSWORD_WARNING')}}
-
+
{{promptData.prompts.credentials.passwords.ssh.name || promptData.prompts.credentials.passwords.become.name || promptData.prompts.credentials.passwords.ssh_key_unlock.name}}
{{vaultCred.name}}
From d44d28beba4b12744395634697e7e7bd890599fc Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 23 Apr 2018 15:27:52 -0400 Subject: [PATCH 359/379] Populate JT schedule add with default extra vars when promptable. Hide schedule vars for everything except JT's with promptable extra vars --- .../src/scheduler/schedulerAdd.controller.js | 46 +++++++++---------- .../src/scheduler/schedulerEdit.controller.js | 45 +++++++++--------- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index 4e34389a7e..347aab9727 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -89,7 +89,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', // extra_data field is not manifested in the UI when scheduling a Management Job if ($state.current.name === 'jobTemplateSchedules.add'){ $scope.parseType = 'yaml'; - $scope.extraVars = '---'; + $scope.extraVars = ParentObject.extra_vars === '' ? '---' : ParentObject.extra_vars; ParseTypeChange({ scope: $scope, @@ -114,20 +114,20 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.$watchGroup(promptValuesToWatch, function() { let missingPromptValue = false; - if($scope.missingSurveyValue) { + if ($scope.missingSurveyValue) { missingPromptValue = true; - } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { missingPromptValue = true; } $scope.promptModalMissingReqFields = missingPromptValue; }); }; - if(!launchConf.ask_variables_on_launch) { + if (!launchConf.ask_variables_on_launch) { $scope.noVars = true; } - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -149,11 +149,11 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', // Promptable variables will happen in the schedule form launchConf.ignore_ask_variables = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { $scope.promptModalMissingReqFields = true; } - if(launchConf.survey_enabled) { + if (launchConf.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions(ParentObject.id) .then((surveyQuestionRes) => { @@ -178,7 +178,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -187,8 +187,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', watchForPromptChanges(); }); - } - else { + } else { $scope.promptData = { launchConf: responses[1].data, launchOptions: responses[0].data, @@ -272,18 +271,18 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', } }); } - else if ($state.current.name === 'projectSchedules.add'){ - $scope.noVars = true; - } - else if ($state.current.name === 'inventories.edit.inventory_sources.edit.schedules.add'){ + + if ($state.current.name === 'workflowJobTemplateSchedules.add' || + $state.current.name === 'projectSchedules.add' || + $state.current.name === 'inventories.edit.inventory_sources.edit.schedules.add' + ){ $scope.noVars = true; } job_type = $scope.parentObject.job_type; if (!Empty($stateParams.id) && base !== 'system_job_templates' && base !== 'inventories' && !schedule_url) { schedule_url = GetBasePath(base) + $stateParams.id + '/schedules/'; - } - else if(base === "inventories"){ + } else if (base === "inventories"){ if (!schedule_url){ Rest.setUrl(GetBasePath('groups') + $stateParams.id + '/'); Rest.get() @@ -297,10 +296,9 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', }); }); } - } - else if (base === 'system_job_templates') { + } else if (base === 'system_job_templates') { schedule_url = GetBasePath(base) + $stateParams.id + '/schedules/'; - if(job_type === "cleanup_facts"){ + if (job_type === "cleanup_facts"){ $scope.isFactCleanup = true; $scope.keep_unit_choices = [{ "label" : "Days", @@ -330,8 +328,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1); $scope.keep_unit = $scope.keep_unit_choices[0]; $scope.granularity_keep_unit = $scope.granularity_keep_unit_choices[1]; - } - else { + } else { $scope.cleanupJob = true; } } @@ -350,15 +347,14 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', }); $scope.scheduleTimeChange(); }); - if($scope.schedulerUTCTime) { + if ($scope.schedulerUTCTime) { // The UTC time is already set processSchedulerEndDt(); - } - else { + } else { // We need to wait for it to be set by angular-scheduler because the following function depends // on it var schedulerUTCTimeWatcher = $scope.$watch('schedulerUTCTime', function(newVal) { - if(newVal) { + if (newVal) { // Remove the watcher schedulerUTCTimeWatcher(); processSchedulerEndDt(); diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index f82d89cd46..11edac1c29 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -127,7 +127,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, $scope.extraVars = (data.extra_data === '' || _.isEmpty(data.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(data.extra_data); - if(schedule.extra_data.hasOwnProperty('granularity')){ + if (schedule.extra_data.hasOwnProperty('granularity')){ $scope.isFactCleanup = true; } if (schedule.extra_data.hasOwnProperty('days')){ @@ -196,16 +196,15 @@ function($filter, $state, $stateParams, Wait, $scope, moment, scheduler.setRRule(schedule.rrule); scheduler.setName(schedule.name); - if($scope.isFactCleanup || $scope.cleanupJob){ + if ($scope.isFactCleanup || $scope.cleanupJob){ var a,b, prompt_for_days, keep_unit, granularity, granularity_keep_unit; - if($scope.cleanupJob){ + if ($scope.cleanupJob){ $scope.schedulerPurgeDays = Number(schedule.extra_data.days); - } - else if($scope.isFactCleanup){ + } else if ($scope.isFactCleanup){ $scope.keep_unit_choices = [{ "label" : "Days", "value" : "d" @@ -272,9 +271,9 @@ function($filter, $state, $stateParams, Wait, $scope, moment, $scope.$watchGroup(promptValuesToWatch, function() { let missingPromptValue = false; - if($scope.missingSurveyValue) { + if ($scope.missingSurveyValue) { missingPromptValue = true; - } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { missingPromptValue = true; } $scope.promptModalMissingReqFields = missingPromptValue; @@ -292,8 +291,8 @@ function($filter, $state, $stateParams, Wait, $scope, moment, const credentialHasScheduleOverride = (templateDefaultCred) => { let credentialHasOverride = false; scheduleCredentials.forEach((scheduleCred) => { - if(templateDefaultCred.credential_type === scheduleCred.credential_type) { - if( + if (templateDefaultCred.credential_type === scheduleCred.credential_type) { + if ( (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) || (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id) ) { @@ -305,9 +304,9 @@ function($filter, $state, $stateParams, Wait, $scope, moment, return credentialHasOverride; }; - if(_.has(launchConf, 'defaults.credentials')) { + if (_.has(launchConf, 'defaults.credentials')) { launchConf.defaults.credentials.forEach((defaultCred) => { - if(!credentialHasScheduleOverride(defaultCred)) { + if (!credentialHasScheduleOverride(defaultCred)) { defaultCredsWithoutOverrides.push(defaultCred); } }); @@ -315,11 +314,11 @@ function($filter, $state, $stateParams, Wait, $scope, moment, prompts.credentials.value = defaultCredsWithoutOverrides.concat(scheduleCredentials); - if(!launchConf.ask_variables_on_launch) { + if (!launchConf.ask_variables_on_launch) { $scope.noVars = true; } - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -341,11 +340,11 @@ function($filter, $state, $stateParams, Wait, $scope, moment, // Promptable variables will happen in the schedule form launchConf.ignore_ask_variables = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has(data, 'summary_fields.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has(data, 'summary_fields.inventory')) { $scope.promptModalMissingReqFields = true; } - if(responses[1].data.survey_enabled) { + if (responses[1].data.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions(ParentObject.id) .then((surveyQuestionRes) => { @@ -378,7 +377,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -387,8 +386,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, watchForPromptChanges(); }); - } - else { + } else { $scope.promptData = { launchConf: launchConf, launchOptions: launchOptions, @@ -472,13 +470,12 @@ function($filter, $state, $stateParams, Wait, $scope, moment, // extra_data field is not manifested in the UI when scheduling a Management Job if ($state.current.name !== 'managementJobsList.schedule.add' && $state.current.name !== 'managementJobsList.schedule.edit'){ - if ($state.current.name === 'projectSchedules.edit'){ + if ($state.current.name === 'projectSchedules.edit' || + $state.current.name === 'inventories.edit.inventory_sources.edit.schedules.edit' || + $state.current.name === 'workflowJobTemplateSchedules.add' + ){ $scope.noVars = true; - } - else if ($state.current.name === 'inventories.edit.inventory_sources.edit.schedules.edit'){ - $scope.noVars = true; - } - else { + } else { ParseTypeChange({ scope: $scope, variable: 'extraVars', From 404b47657688dc472ef22c9c83240a2bb9206054 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 25 Apr 2018 11:46:21 -0400 Subject: [PATCH 360/379] Allow real null to be searched in host_filter --- awx/main/tests/unit/utils/test_filters.py | 8 ++++++-- awx/main/utils/filters.py | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/awx/main/tests/unit/utils/test_filters.py b/awx/main/tests/unit/utils/test_filters.py index 964466cb73..c0b38d294c 100644 --- a/awx/main/tests/unit/utils/test_filters.py +++ b/awx/main/tests/unit/utils/test_filters.py @@ -39,6 +39,7 @@ class TestSmartFilterQueryFromString(): ('a__b__c=3.14', Q(**{u"a__b__c": 3.14})), ('a__b__c=true', Q(**{u"a__b__c": True})), ('a__b__c=false', Q(**{u"a__b__c": False})), + ('a__b__c=null', Q(**{u"a__b__c": None})), ('ansible_facts__a="true"', Q(**{u"ansible_facts__contains": {u"a": u"true"}})), #('"a__b\"__c"="true"', Q(**{u"a__b\"__c": "true"})), #('a__b\"__c="true"', Q(**{u"a__b\"__c": "true"})), @@ -114,7 +115,7 @@ class TestSmartFilterQueryFromString(): assert six.text_type(q) == six.text_type(q_expected) @pytest.mark.parametrize("filter_string,q_expected", [ - ('ansible_facts__a=null', Q(**{u"ansible_facts__contains": {u"a": u"null"}})), + ('ansible_facts__a=null', Q(**{u"ansible_facts__contains": {u"a": None}})), ('ansible_facts__c="null"', Q(**{u"ansible_facts__contains": {u"c": u"\"null\""}})), ]) def test_contains_query_generated_null(self, mock_get_host_model, filter_string, q_expected): @@ -130,7 +131,10 @@ class TestSmartFilterQueryFromString(): Q(**{u"group__name__contains": u"foo"}) | Q(**{u"group__description__contains": u"foo"}))), ('search=foo or ansible_facts__a=null', Q(Q(**{u"name__contains": u"foo"}) | Q(**{u"description__contains": u"foo"})) | - Q(**{u"ansible_facts__contains": {u"a": u"null"}})), + Q(**{u"ansible_facts__contains": {u"a": None}})), + ('search=foo or ansible_facts__a="null"', + Q(Q(**{u"name__contains": u"foo"}) | Q(**{u"description__contains": u"foo"})) | + Q(**{u"ansible_facts__contains": {u"a": u"\"null\""}})), ]) def test_search_related_fields(self, mock_get_host_model, filter_string, q_expected): q = SmartFilter.query_from_string(filter_string) diff --git a/awx/main/utils/filters.py b/awx/main/utils/filters.py index a1d316ba72..81f91a0b0a 100644 --- a/awx/main/utils/filters.py +++ b/awx/main/utils/filters.py @@ -19,6 +19,8 @@ __all__ = ['SmartFilter'] def string_to_type(t): + if t == u'null': + return None if t == u'true': return True elif t == u'false': From 82f2783c979f32fcc2061a3e8f66067ed18a363a Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 24 Apr 2018 10:26:54 -0400 Subject: [PATCH 361/379] Mark prompt strings for translation --- awx/ui/client/features/templates/templates.strings.js | 2 ++ awx/ui/client/src/scheduler/schedulerAdd.controller.js | 6 ++++-- awx/ui/client/src/scheduler/schedulerEdit.controller.js | 6 ++++-- awx/ui/client/src/scheduler/schedulerForm.partial.html | 2 +- .../steps/other-prompts/prompt-other-prompts.partial.html | 2 +- .../workflows/workflow-maker/workflow-maker.controller.js | 2 ++ .../workflows/workflow-maker/workflow-maker.partial.html | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js index f275e522a0..896cfd87bd 100644 --- a/awx/ui/client/features/templates/templates.strings.js +++ b/awx/ui/client/features/templates/templates.strings.js @@ -28,6 +28,7 @@ function TemplatesStrings (BaseString) { SURVEY: t.s('Survey'), PREVIEW: t.s('Preview'), LAUNCH: t.s('LAUNCH'), + CONFIRM: t.s('CONFIRM'), SELECTED: t.s('SELECTED'), NO_CREDENTIALS_SELECTED: t.s('No credentials selected'), NO_INVENTORY_SELECTED: t.s('No inventory selected'), @@ -51,6 +52,7 @@ function TemplatesStrings (BaseString) { CHOOSE_JOB_TYPE: t.s('Choose a job type'), CHOOSE_VERBOSITY: t.s('Choose a verbosity'), EXTRA_VARIABLES: t.s('Extra Variables'), + EXTRA_VARIABLES_HELP: t.s('

Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON.

JSON:
{
"somevar": "somevalue",
"password": "magic"
}
YAML:
---
somevar: somevalue
password: magic
'), PLEASE_ENTER_ANSWER: t.s('Please enter an answer.'), PLEASE_SELECT_VALUE: t.s('Please select a value'), VALID_INTEGER: t.s('Please enter an answer that is a valid integer.'), diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index cd55b73ea0..33d7a2a622 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -8,12 +8,12 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', '$scope', '$rootScope', 'CreateSelect2', 'ParseTypeChange', 'GetBasePath', 'Rest', 'ParentObject', 'JobTemplateModel', '$q', 'Empty', 'SchedulePost', 'ProcessErrors', 'SchedulerInit', '$location', 'PromptService', 'RRuleToAPI', 'moment', - 'WorkflowJobTemplateModel', + 'WorkflowJobTemplateModel', 'TemplatesStrings', function($filter, $state, $stateParams, $http, Wait, $scope, $rootScope, CreateSelect2, ParseTypeChange, GetBasePath, Rest, ParentObject, JobTemplate, $q, Empty, SchedulePost, ProcessErrors, SchedulerInit, $location, PromptService, RRuleToAPI, moment, - WorkflowJobTemplate + WorkflowJobTemplate, TemplatesStrings ) { var base = $scope.base || $location.path().replace(/^\//, '').split('/')[0], @@ -88,6 +88,8 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.hideForm = true; + $scope.promptActionText = TemplatesStrings.get('prompt.CONFIRM'); + // extra_data field is not manifested in the UI when scheduling a Management Job if ($state.current.name === 'jobTemplateSchedules.add'){ $scope.parseType = 'yaml'; diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index 6376664145..8b1d3c083a 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -1,11 +1,11 @@ export default ['$filter', '$state', '$stateParams', 'Wait', '$scope', 'moment', '$rootScope', '$http', 'CreateSelect2', 'ParseTypeChange', 'ParentObject', 'ProcessErrors', 'Rest', 'GetBasePath', 'SchedulerInit', 'SchedulePost', 'JobTemplateModel', '$q', 'Empty', 'PromptService', 'RRuleToAPI', -'WorkflowJobTemplateModel', +'WorkflowJobTemplateModel', 'TemplatesStrings', function($filter, $state, $stateParams, Wait, $scope, moment, $rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest, GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI, - WorkflowJobTemplate + WorkflowJobTemplate, TemplatesStrings ) { let schedule, scheduler, scheduleCredentials = []; @@ -21,6 +21,8 @@ function($filter, $state, $stateParams, Wait, $scope, moment, $scope.hideForm = true; $scope.parseType = 'yaml'; + $scope.promptActionText = TemplatesStrings.get('prompt.CONFIRM'); + $scope.processSchedulerEndDt = function(){ // set the schedulerEndDt to be equal to schedulerStartDt + 1 day @ midnight var dt = new Date($scope.schedulerUTCTime); diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html index bb68bfbaaf..8930245501 100644 --- a/awx/ui/client/src/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -686,5 +686,5 @@ ng-disabled="!schedulerIsValid || promptModalMissingReqFields"> Save
- +
diff --git a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html index dcbf7f5a92..9d731d185f 100644 --- a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html @@ -93,7 +93,7 @@
From cf38faa899043b89081ce31d7c0f7d55e7ea9438 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 25 Apr 2018 11:41:20 -0400 Subject: [PATCH 362/379] Use string interpolation rather than passing action text in via a var --- awx/ui/client/src/scheduler/schedulerAdd.controller.js | 3 +-- awx/ui/client/src/scheduler/schedulerEdit.controller.js | 2 +- awx/ui/client/src/scheduler/schedulerForm.partial.html | 2 +- awx/ui/client/src/templates/prompt/prompt.directive.js | 2 +- .../workflows/workflow-maker/workflow-maker.controller.js | 2 -- .../workflows/workflow-maker/workflow-maker.partial.html | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index 33d7a2a622..c00df03e27 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -33,6 +33,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', }; $scope.preventCredsWithPasswords = true; + $scope.strings = TemplatesStrings; /* * This is a workaround for the angular-scheduler library inserting `ll` into fields after an @@ -88,8 +89,6 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.hideForm = true; - $scope.promptActionText = TemplatesStrings.get('prompt.CONFIRM'); - // extra_data field is not manifested in the UI when scheduling a Management Job if ($state.current.name === 'jobTemplateSchedules.add'){ $scope.parseType = 'yaml'; diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index 8b1d3c083a..ec0a24842a 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -21,7 +21,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, $scope.hideForm = true; $scope.parseType = 'yaml'; - $scope.promptActionText = TemplatesStrings.get('prompt.CONFIRM'); + $scope.strings = TemplatesStrings; $scope.processSchedulerEndDt = function(){ // set the schedulerEndDt to be equal to schedulerStartDt + 1 day @ midnight diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html index 8930245501..9313db7f71 100644 --- a/awx/ui/client/src/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -686,5 +686,5 @@ ng-disabled="!schedulerIsValid || promptModalMissingReqFields"> Save
- +
diff --git a/awx/ui/client/src/templates/prompt/prompt.directive.js b/awx/ui/client/src/templates/prompt/prompt.directive.js index b09a3bcc84..e151760bb7 100644 --- a/awx/ui/client/src/templates/prompt/prompt.directive.js +++ b/awx/ui/client/src/templates/prompt/prompt.directive.js @@ -5,7 +5,7 @@ export default [ 'templateUrl', scope: { promptData: '=', onFinish: '&', - actionText: '@actionText', + actionText: '@', preventCredsWithPasswords: '<' }, templateUrl: templateUrl('templates/prompt/prompt'), diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index aa6a321f3b..a4119b4a79 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -16,8 +16,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.strings = TemplatesStrings; $scope.preventCredsWithPasswords = true; - $scope.promptActionText = $scope.strings.get('prompt.CONFIRM'); - $scope.workflowMakerFormConfig = { nodeMode: "idle", activeTab: "jobs", diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html index 3fa1abff37..aca4d30f13 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html @@ -129,5 +129,5 @@
- +
From 7151071779525ad9f1c0b0256ab8c1d50a29fe88 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 25 Apr 2018 11:55:57 -0400 Subject: [PATCH 363/379] Add awx-link make target --- Makefile | 7 ++++++- tools/docker-compose/bootstrap_development.sh | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7d4c242693..14564df430 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ UI_RELEASE_FLAG_FILE = awx/ui/.release_built I18N_FLAG_FILE = .i18n_built -.PHONY: clean clean-tmp clean-venv requirements requirements_dev \ +.PHONY: awx-link clean clean-tmp clean-venv requirements requirements_dev \ develop refresh adduser migrate dbchange dbshell runserver celeryd \ receiver test test_unit test_ansible test_coverage coverage_html \ dev_build release_build release_clean sdist \ @@ -367,6 +367,11 @@ swagger: reports check: flake8 pep8 # pyflakes pylint +awx-link: + cp -R /tmp/awx.egg-info /awx_devel/ || true + sed -i "s/placeholder/$(shell git describe --long | sed 's/\./\\./g')/" /awx_devel/awx.egg-info/PKG-INFO + cp /tmp/awx.egg-link /venv/awx/lib/python2.7/site-packages/awx.egg-link + TEST_DIRS ?= awx/main/tests/unit awx/main/tests/functional awx/conf/tests awx/sso/tests # Run all API unit tests. test: diff --git a/tools/docker-compose/bootstrap_development.sh b/tools/docker-compose/bootstrap_development.sh index df6ecb2637..17629a334f 100755 --- a/tools/docker-compose/bootstrap_development.sh +++ b/tools/docker-compose/bootstrap_development.sh @@ -21,9 +21,7 @@ else echo "Failed to find awx source tree, map your development tree volume" fi -cp -R /tmp/awx.egg-info /awx_devel/ || true -sed -i "s/placeholder/$(git describe --long | sed 's/\./\\./g')/" /awx_devel/awx.egg-info/PKG-INFO -cp /tmp/awx.egg-link /venv/awx/lib/python2.7/site-packages/awx.egg-link +make awx-link ln -s /awx_devel/tools/rdb.py /venv/awx/lib/python2.7/site-packages/rdb.py || true yes | cp -rf /awx_devel/tools/docker-compose/supervisor.conf /supervisor.conf From ac4697e93b658727ac0584580eee9440b056efb8 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 25 Apr 2018 12:01:10 -0400 Subject: [PATCH 364/379] Fixed bug displaying default cred with password when creating a schedule --- .../src/templates/prompt/prompt.controller.js | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 1a20ca1d2c..59cc2092db 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -73,16 +73,40 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', if(vm.promptDataClone.launchConf.passwords_needed_to_start) { vm.promptDataClone.launchConf.passwords_needed_to_start.forEach((passwordNeeded) => { - if(passwordNeeded === "ssh_password") { - vm.promptDataClone.prompts.credentials.passwords.ssh = {}; - } - if(passwordNeeded === "become_password") { - vm.promptDataClone.prompts.credentials.passwords.become = {}; - } - if(passwordNeeded === "ssh_key_unlock") { - vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock = {}; - } - if(passwordNeeded.startsWith("vault_password")) { + if (passwordNeeded === "ssh_password") { + vm.promptDataClone.prompts.credentials.value.forEach((defaultCredential) => { + defaultCredential.passwords_needed.forEach((neededPassword) => { + if (neededPassword === "ssh_password") { + vm.promptDataClone.prompts.credentials.passwords.ssh = { + id: defaultCredential.id, + name: defaultCredential.name + }; + } + }); + }); + } else if (passwordNeeded === "become_password") { + vm.promptDataClone.prompts.credentials.value.forEach((defaultCredential) => { + defaultCredential.passwords_needed.forEach((neededPassword) => { + if (neededPassword === "become_password") { + vm.promptDataClone.prompts.credentials.passwords.become = { + id: defaultCredential.id, + name: defaultCredential.name + }; + } + }); + }); + } else if (passwordNeeded === "ssh_key_unlock") { + vm.promptDataClone.prompts.credentials.value.forEach((defaultCredential) => { + defaultCredential.passwords_needed.forEach((neededPassword) => { + if (neededPassword === "ssh_key_unlock") { + vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock = { + id: defaultCredential.id, + name: defaultCredential.name + }; + } + }); + }); + } else if (passwordNeeded.startsWith("vault_password")) { let vault_id = null; if (passwordNeeded.includes('.')) { vault_id = passwordNeeded.split(/\.(.+)/)[1]; From 9192829de2c8850e5affef9669b69b5a1e5471ac Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Wed, 25 Apr 2018 12:54:08 -0400 Subject: [PATCH 365/379] [UI] Org Admin permissions for Instance Groups --- .../capacity-adjuster/capacity-adjuster.directive.js | 4 ++-- .../capacity-adjuster/capacity-adjuster.partial.html | 1 + .../instances/instances-list.partial.html | 12 ++++++++---- .../instances/instances.controller.js | 10 ++++++++++ .../list/instance-groups-list.controller.js | 7 +++++++ .../list/instance-groups-list.partial.html | 3 ++- .../client/src/scheduler/scheduleToggle.block.less | 3 ++- 7 files changed, 32 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.directive.js b/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.directive.js index 5600502578..e36916aca8 100644 --- a/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.directive.js +++ b/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.directive.js @@ -1,7 +1,8 @@ function CapacityAdjuster (templateUrl, ProcessErrors, Wait) { return { scope: { - state: '=' + state: '=', + disabled: '@' }, templateUrl: templateUrl('instance-groups/capacity-adjuster/capacity-adjuster'), restrict: 'E', @@ -17,7 +18,6 @@ function CapacityAdjuster (templateUrl, ProcessErrors, Wait) { scope.min_capacity = _.min(adjustment_values, 'value'); scope.max_capacity = _.max(adjustment_values, 'value'); - }, controller: function($http) { const vm = this || {}; diff --git a/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.partial.html b/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.partial.html index c6a5bca81b..f486745fae 100644 --- a/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.partial.html +++ b/awx/ui/client/src/instance-groups/capacity-adjuster/capacity-adjuster.partial.html @@ -8,6 +8,7 @@ min="0" max="1" step="0.1" + ng-disabled="{{disabled}}" ng-change="vm.slide(state)"/>

{{max_capacity.label}} {{max_capacity.value}}

diff --git a/awx/ui/client/src/instance-groups/instances/instances-list.partial.html b/awx/ui/client/src/instance-groups/instances/instances-list.partial.html index 59d459a70b..5a7a26021d 100644 --- a/awx/ui/client/src/instance-groups/instances/instances-list.partial.html +++ b/awx/ui/client/src/instance-groups/instances/instances-list.partial.html @@ -27,6 +27,7 @@ type="button" ng-click="$state.go('instanceGroups.instances.modal.add')" class="at-Button--add" + ng-show="vm.isSuperuser" aria-expanded="false">
@@ -37,15 +38,18 @@ ng-class="{'at-Row--active': (instance.id === vm.activeId)}">
+ ng-class="{'is-on': instance.enabled, + 'ScheduleToggle--disabled': vm.rowAction.toggle._disabled}">
@@ -63,7 +67,7 @@
- +
diff --git a/awx/ui/client/src/instance-groups/instances/instances.controller.js b/awx/ui/client/src/instance-groups/instances/instances.controller.js index 26d6ef3e71..87281efae1 100644 --- a/awx/ui/client/src/instance-groups/instances/instances.controller.js +++ b/awx/ui/client/src/instance-groups/instances/instances.controller.js @@ -5,6 +5,7 @@ function InstancesController ($scope, $state, $http, models, Instance, strings, vm.panelTitle = instanceGroup.get('name'); vm.instances = instanceGroup.get('related.instances.results'); vm.instance_group_id = instanceGroup.get('id'); + vm.isSuperuser = $scope.$root.user_is_superuser; init(); @@ -47,6 +48,15 @@ function InstancesController ($scope, $state, $http, models, Instance, strings, } }; + vm.rowAction = { + toggle: { + _disabled: !vm.isSuperuser + }, + capacity_adjustment: { + _disabled: !vm.isSuperuser + } + }; + vm.toggle = (toggled) => { const instance = _.find(vm.instances, 'id', toggled.id); instance.enabled = !instance.enabled; diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js index 97990c8679..b3eccaffab 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js @@ -4,6 +4,7 @@ export default ['$scope', '$filter', '$state', 'Alert', 'resolvedModels', 'Datas const { instanceGroup } = resolvedModels; vm.strings = strings; + vm.isSuperuser = $scope.$root.user_is_superuser; init(); @@ -32,6 +33,12 @@ export default ['$scope', '$filter', '$state', 'Alert', 'resolvedModels', 'Datas vm.activeId = parseInt($state.params.instance_group_id); }); + vm.rowAction = { + trash: instance_group => { + return vm.isSuperuser && instance_group.name !== 'tower'; + } + }; + vm.deleteInstanceGroup = instance_group => { if (!instance_group) { Alert(strings.get('error.DELETE'), strings.get('alert.MISSING_PARAMETER')); diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html b/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html index e09f28cbe5..8d75d6bc72 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.partial.html @@ -23,6 +23,7 @@ type="button" ui-sref="instanceGroups.add" class="at-Button--add" + ng-show="vm.isSuperuser" aria-haspopup="true" aria-expanded="false"> @@ -60,7 +61,7 @@
- +
diff --git a/awx/ui/client/src/scheduler/scheduleToggle.block.less b/awx/ui/client/src/scheduler/scheduleToggle.block.less index 49bcc954f7..b234a0b5f2 100644 --- a/awx/ui/client/src/scheduler/scheduleToggle.block.less +++ b/awx/ui/client/src/scheduler/scheduleToggle.block.less @@ -20,7 +20,8 @@ &.ScheduleToggle--disabled { cursor: not-allowed; - border-color: @default-link !important; + background-color: none; + border-color: @d7grey !important; .ScheduleToggle-switch { background-color: @d7grey !important; cursor: not-allowed; From b3ca7acb41d1d9215e9f5171d41d4c82e37794b0 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Wed, 25 Apr 2018 13:46:46 -0400 Subject: [PATCH 366/379] Only pass --force if we have an scm_result (i.e., we ran an actual checkout/revision change.) --- awx/playbooks/project_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 5d6682d499..d9067bac45 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -139,7 +139,7 @@ register: doesRequirementsExist - name: fetch galaxy roles from requirements.yml - command: ansible-galaxy install -r requirements.yml -p {{project_path|quote}}/roles/ --force + command: ansible-galaxy install -r requirements.yml -p {{project_path|quote}}/roles/ {{ scm_result is defined|ternary('--force',omit) }} args: chdir: "{{project_path|quote}}/roles" when: doesRequirementsExist.stat.exists From 0bc5665e92f5a807b133fe90b9865e4e44730d0f Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 25 Apr 2018 14:20:19 -0400 Subject: [PATCH 367/379] remove the legacy fact cleanup system template see: https://github.com/ansible/tower/issues/1021 --- .../api/system_job_template_launch.md | 7 ------- .../0037_v330_remove_legacy_fact_cleanup.py | 18 ++++++++++++++++++ awx/main/migrations/_scan_jobs.py | 8 ++++++++ awx/main/tasks.py | 9 ++------- 4 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 awx/main/migrations/0037_v330_remove_legacy_fact_cleanup.py diff --git a/awx/api/templates/api/system_job_template_launch.md b/awx/api/templates/api/system_job_template_launch.md index 95d0f6b378..ee05d38cb9 100644 --- a/awx/api/templates/api/system_job_template_launch.md +++ b/awx/api/templates/api/system_job_template_launch.md @@ -12,12 +12,6 @@ For example on `cleanup_jobs` and `cleanup_activitystream`: Which will act on data older than 30 days. -For `cleanup_facts`: - -`{"extra_vars": {"older_than": "4w", "granularity": "3d"}}` - -Which will reduce the granularity of scan data to one scan per 3 days when the data is older than 4w. - For `cleanup_activitystream` and `cleanup_jobs` commands, providing `"dry_run": true` inside of `extra_vars` will show items that will be removed without deleting them. @@ -27,7 +21,6 @@ applicable either when running it from the command line or launching its system job template with empty `extra_vars`. - Defaults for `cleanup_activitystream`: days=90 - - Defaults for `cleanup_facts`: older_than="30d", granularity="1w" - Defaults for `cleanup_jobs`: days=90 If successful, the response status code will be 202. If the job cannot be diff --git a/awx/main/migrations/0037_v330_remove_legacy_fact_cleanup.py b/awx/main/migrations/0037_v330_remove_legacy_fact_cleanup.py new file mode 100644 index 0000000000..da2423bdcf --- /dev/null +++ b/awx/main/migrations/0037_v330_remove_legacy_fact_cleanup.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +# AWX +from awx.main.migrations._scan_jobs import remove_legacy_fact_cleanup + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0036_v330_credtype_remove_become_methods'), + ] + + operations = [ + migrations.RunPython(remove_legacy_fact_cleanup), + ] diff --git a/awx/main/migrations/_scan_jobs.py b/awx/main/migrations/_scan_jobs.py index 0d91e3ef23..993b28c5d1 100644 --- a/awx/main/migrations/_scan_jobs.py +++ b/awx/main/migrations/_scan_jobs.py @@ -102,3 +102,11 @@ def remove_scan_type_nodes(apps, schema_editor): prompts.pop('job_type') node.char_prompts = prompts node.save() + + +def remove_legacy_fact_cleanup(apps, schema_editor): + SystemJobTemplate = apps.get_model('main', 'SystemJobTemplate') + for job in SystemJobTemplate.objects.filter(job_type='cleanup_facts').all(): + for sched in job.schedules.all(): + sched.delete() + job.delete() diff --git a/awx/main/tasks.py b/awx/main/tasks.py index cb42464462..470da6f1f2 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -2278,19 +2278,14 @@ class RunSystemJob(BaseTask): json_vars = {} else: json_vars = json.loads(system_job.extra_vars) - if 'days' in json_vars and system_job.job_type != 'cleanup_facts': + if 'days' in json_vars: args.extend(['--days', str(json_vars.get('days', 60))]) - if 'dry_run' in json_vars and json_vars['dry_run'] and system_job.job_type != 'cleanup_facts': + if 'dry_run' in json_vars and json_vars['dry_run']: args.extend(['--dry-run']) if system_job.job_type == 'cleanup_jobs': args.extend(['--jobs', '--project-updates', '--inventory-updates', '--management-jobs', '--ad-hoc-commands', '--workflow-jobs', '--notifications']) - if system_job.job_type == 'cleanup_facts': - if 'older_than' in json_vars: - args.extend(['--older_than', str(json_vars['older_than'])]) - if 'granularity' in json_vars: - args.extend(['--granularity', str(json_vars['granularity'])]) except Exception: logger.exception(six.text_type("{} Failed to parse system job").format(system_job.log_format)) return args From 90308066af9000bf72cc1167a8ebb977eb43fc6a Mon Sep 17 00:00:00 2001 From: adamscmRH Date: Wed, 25 Apr 2018 15:04:41 -0400 Subject: [PATCH 368/379] update radius backend for dr1.3.3 --- awx/sso/backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/sso/backends.py b/awx/sso/backends.py index 4b20ec165f..d4a2cc2280 100644 --- a/awx/sso/backends.py +++ b/awx/sso/backends.py @@ -182,13 +182,13 @@ class RADIUSBackend(BaseRADIUSBackend): Custom Radius backend to verify license status ''' - def authenticate(self, username, password): + def authenticate(self, request, username, password): if not django_settings.RADIUS_SERVER: return None if not feature_enabled('enterprise_auth'): logger.error("Unable to authenticate, license does not support RADIUS authentication") return None - return super(RADIUSBackend, self).authenticate(username, password) + return super(RADIUSBackend, self).authenticate(request, username, password) def get_user(self, user_id): if not django_settings.RADIUS_SERVER: From f0d3713e99bbcb60805b03100108cc65c696e2f8 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 25 Apr 2018 16:28:51 -0400 Subject: [PATCH 369/379] use select.poll() instead of select.select() for file descriptors see: https://github.com/ansible/tower/issues/861 see: https://github.com/pexpect/pexpect/pull/474 --- awx/main/expect/run.py | 2 +- requirements/requirements.in | 2 +- requirements/requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/awx/main/expect/run.py b/awx/main/expect/run.py index ce685325c7..0c8881a85c 100755 --- a/awx/main/expect/run.py +++ b/awx/main/expect/run.py @@ -101,7 +101,7 @@ def run_pexpect(args, cwd, env, logfile, child = pexpect.spawn( args[0], args[1:], cwd=cwd, env=env, ignore_sighup=True, - encoding='utf-8', echo=False, + encoding='utf-8', echo=False, use_poll=True ) child.logfile_read = logfile canceled = False diff --git a/requirements/requirements.in b/requirements/requirements.in index 3dbd0aed63..f1ff3cd0bb 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -30,7 +30,7 @@ jsonschema==2.6.0 M2Crypto==0.29.0 Markdown==2.6.11 ordereddict==1.1 -pexpect==4.4.0 +pexpect==4.5.0 psphere==0.5.2 psutil==5.4.3 psycopg2==2.7.3.2 # problems with Segmentation faults / wheels on upgrade diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a938b9323e..d81700b47d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -170,7 +170,7 @@ os-service-types==1.2.0 # via openstacksdk packaging==17.1 # via deprecation pathlib2==2.3.0 # via azure-datalake-store pbr==3.1.1 # via keystoneauth1, openstacksdk, os-service-types, shade, stevedore -pexpect==4.4.0 +pexpect==4.5.0 psphere==0.5.2 psutil==5.4.3 psycopg2==2.7.3.2 From fe7a0a63b9c7a7cf55a44e4c096a61240c8f0553 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 25 Apr 2018 16:55:35 -0400 Subject: [PATCH 370/379] Implemented expand/collapse job results --- .../features/output/index.controller.js | 4 ++++ awx/ui/client/features/output/index.view.html | 9 ++++++--- awx/ui/client/features/output/jobs.strings.js | 5 +++++ .../client/features/output/stats.directive.js | 17 +++++++++++++++- .../client/features/output/stats.partial.html | 20 +++++++++---------- .../client/lib/components/input/_index.less | 6 +++++- 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 17dfb63c4c..488d994dc2 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -55,6 +55,10 @@ function JobsIndexController ( up: scrollPageUp }; + vm.fullscreen = { + isFullscreen: false + }; + render.requestAnimationFrame(() => init()); } diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 0c4a92de07..52d34ccb66 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,14 +1,17 @@
-
+
-
+
{{ vm.title }}
- + +
diff --git a/awx/ui/client/features/output/jobs.strings.js b/awx/ui/client/features/output/jobs.strings.js index e17722da3f..f1f3ace11d 100644 --- a/awx/ui/client/features/output/jobs.strings.js +++ b/awx/ui/client/features/output/jobs.strings.js @@ -31,6 +31,11 @@ function JobsStrings (BaseString) { PROJECT: t.s('View the Project'), PROJECT_UPDATE: t.s('View Project checkout results') }; + + ns.expandCollapse = { + EXPAND: t.s('Expand Output'), + COLLAPSE: t.s('Collapse Output') + }; } JobsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/output/stats.directive.js b/awx/ui/client/features/output/stats.directive.js index 51fbd89afb..789fc29de6 100644 --- a/awx/ui/client/features/output/stats.directive.js +++ b/awx/ui/client/features/output/stats.directive.js @@ -30,8 +30,12 @@ function AtJobStatsController (_strings_, _status_) { vm.init = scope => { const { resource } = scope; + vm.fullscreen = scope.fullscreen; + vm.download = resource.model.get('related.stdout'); + vm.toggleStdoutFullscreenTooltip = strings.get('expandCollapse.EXPAND'); + vm.setHostStatusCounts(status.getHostStatusCounts()); scope.$watch(status.getPlayCount, value => { vm.plays = value; }); @@ -55,6 +59,13 @@ function AtJobStatsController (_strings_, _status_) { vm.statsAreAvailable = Boolean(status.getStatsEvent()); }; + + vm.toggleFullscreen = () => { + vm.fullscreen.isFullscreen = !vm.fullscreen.isFullscreen; + vm.toggleStdoutFullscreenTooltip = vm.fullscreen.isFullscreen ? + strings.get('expandCollapse.COLLAPSE') : + strings.get('expandCollapse.EXPAND'); + }; } function atJobStats () { @@ -67,9 +78,13 @@ function atJobStats () { controller: [ 'JobStrings', 'JobStatusService', + '$scope', AtJobStatsController ], - scope: { resource: '=', }, + scope: { + resource: '=', + fullscreen: '=' + } }; } diff --git a/awx/ui/client/features/output/stats.partial.html b/awx/ui/client/features/output/stats.partial.html index 95668d4e2b..70d980ed33 100644 --- a/awx/ui/client/features/output/stats.partial.html +++ b/awx/ui/client/features/output/stats.partial.html @@ -19,20 +19,20 @@ - +
diff --git a/awx/ui/client/lib/components/input/_index.less b/awx/ui/client/lib/components/input/_index.less index 1f8962bccd..adcc2f2505 100644 --- a/awx/ui/client/lib/components/input/_index.less +++ b/awx/ui/client/lib/components/input/_index.less @@ -70,6 +70,10 @@ height: @at-height-textarea; } +.at-Input-button--active { + .at-mixin-ButtonColor(at-color-info, at-color-default); +} + .at-Input--focus { border-color: @at-color-input-focus; } @@ -269,4 +273,4 @@ cursor: not-allowed; } } -} \ No newline at end of file +} From 8fa224f8467a1b3c9ed219359a4285e58f81f718 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 26 Apr 2018 00:03:53 -0400 Subject: [PATCH 371/379] ensure host modal can be visible when details panel is collapsed --- awx/ui/client/features/output/details.partial.html | 1 - awx/ui/client/features/output/index.view.html | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index 25a8f7cbb5..d873c4fbb5 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -1,5 +1,4 @@ -
DETAILS
diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 52d34ccb66..b3bb6e95bc 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,4 +1,5 @@
+
From 2f3d7b17f669bcae66f364db31fd627ac7084ed1 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 26 Apr 2018 09:11:08 -0400 Subject: [PATCH 372/379] Fix an issue where missing instance group would cause an error We'll now default to queue submission to the basic management queue --- awx/main/scheduler/task_manager.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index b3d5ed14f1..34aa80fe6f 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -277,7 +277,11 @@ class TaskManager(): def post_commit(): task.websocket_emit_status(task.status) if task.status != 'failed': - task.start_celery_task(opts, error_callback=error_handler, success_callback=success_handler, queue=rampart_group.name) + if rampart_group is not None: + actual_queue=rampart_group.name + else: + actual_queue=settings.CELERY_DEFAULT_QUEUE + task.start_celery_task(opts, error_callback=error_handler, success_callback=success_handler, queue=actual_queue) connection.on_commit(post_commit) From 84d9273012c3b2327354b15685fa499eab24646d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 26 Apr 2018 09:30:19 -0400 Subject: [PATCH 373/379] add corresponding log for WorkflowJob submission --- awx/main/scheduler/task_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 34aa80fe6f..943d4960e6 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -259,6 +259,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' + logger.info('Transitioning %s to running status.', task.log_format) elif not task.supports_isolation() and rampart_group.controller_id: # non-Ansible jobs on isolated instances run on controller task.instance_group = rampart_group.controller From 52d8d851feb938a033ebe153fcd3b1982421b0b7 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 26 Apr 2018 09:36:43 -0400 Subject: [PATCH 374/379] avoid unrelated errors saving notification --- awx/main/tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 470da6f1f2..7e4afac382 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -270,6 +270,7 @@ def send_notifications(notification_list, job_id=None): job_actual.notifications.add(*notifications) for notification in notifications: + update_fields = ['status', 'notifications_sent'] try: sent = notification.notification_template.send(notification.subject, notification.body) notification.status = "successful" @@ -278,8 +279,9 @@ def send_notifications(notification_list, job_id=None): logger.error(six.text_type("Send Notification Failed {}").format(e)) notification.status = "failed" notification.error = smart_str(e) + update_fields.append('error') finally: - notification.save() + notification.save(update_fields=update_fields) @shared_task(bind=True, queue=settings.CELERY_DEFAULT_QUEUE) From dff0f2f9ed75ad88350488f33cb00f0e59163322 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 26 Apr 2018 12:21:50 -0400 Subject: [PATCH 375/379] Revert "update tests for org members seeing teams" This reverts commit fe04f69e891078dd3e6eabd2171796c20ff08114. --- awx/main/tests/functional/api/test_organization_counts.py | 4 ++-- awx/main/tests/functional/test_projects.py | 6 +++--- awx/main/tests/functional/test_rbac_api.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/awx/main/tests/functional/api/test_organization_counts.py b/awx/main/tests/functional/api/test_organization_counts.py index 67bbb81858..9c4f536b09 100644 --- a/awx/main/tests/functional/api/test_organization_counts.py +++ b/awx/main/tests/functional/api/test_organization_counts.py @@ -92,7 +92,7 @@ def test_org_counts_detail_member(resourced_organization, user, get): 'job_templates': 0, 'projects': 0, 'inventories': 0, - 'teams': 5 + 'teams': 0 } @@ -123,7 +123,7 @@ def test_org_counts_list_member(resourced_organization, user, get): 'job_templates': 0, 'projects': 0, 'inventories': 0, - 'teams': 5 + 'teams': 0 } diff --git a/awx/main/tests/functional/test_projects.py b/awx/main/tests/functional/test_projects.py index dab22a4d45..55cc484006 100644 --- a/awx/main/tests/functional/test_projects.py +++ b/awx/main/tests/functional/test_projects.py @@ -176,9 +176,9 @@ def test_team_project_list(get, team_project_list): @pytest.mark.django_db -def test_team_project_list_fail1(get, team, rando): - # user not in organization not allowed to see team-based views - res = get(reverse('api:team_projects_list', kwargs={'pk':team.pk,}), rando) +def test_team_project_list_fail1(get, team_project_list): + objects = team_project_list + res = get(reverse('api:team_projects_list', kwargs={'pk':objects.teams.team2.pk,}), objects.users.alice) assert res.status_code == 403 diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/test_rbac_api.py index 4dca2e2ce2..c4114a81b0 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/test_rbac_api.py @@ -57,9 +57,9 @@ def test_get_roles_list_user(organization, inventory, team, get, user): assert organization.admin_role.id in role_hash assert organization.member_role.id in role_hash assert custom_role.id in role_hash - assert team.member_role.id in role_hash assert inventory.admin_role.id not in role_hash + assert team.member_role.id not in role_hash @pytest.mark.django_db @@ -150,7 +150,7 @@ def test_user_view_other_user_roles(organization, inventory, team, get, alice, b assert custom_role.id not in role_hash # doesn't show up in the user roles list, not an explicit grant assert Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id not in role_hash assert inventory.admin_role.id not in role_hash - assert team.member_role.id in role_hash # alice can see team in her org + assert team.member_role.id not in role_hash # alice can't see this # again but this time alice is part of the team, and should be able to see the team role team.member_role.members.add(alice) From b15494963962d9f245d6ce0463109f55549810d9 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 25 Apr 2018 17:36:43 -0700 Subject: [PATCH 376/379] Removes dupclicate Angular import, and removes tower.app.js module from network UI --- awx/ui/client/src/app.js | 4 ++-- awx/ui/client/src/network-ui/main.js | 5 ----- awx/ui/client/src/network-ui/network.ui.app.js | 6 ++---- awx/ui/client/src/network-ui/network.ui.controller.js | 1 - awx/ui/client/src/network-ui/tower.app.js | 8 -------- 5 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 awx/ui/client/src/network-ui/main.js delete mode 100644 awx/ui/client/src/network-ui/tower.app.js diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 376e4589e7..8ed9133251 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -43,7 +43,7 @@ import atLibComponents from '~components'; import atLibModels from '~models'; import atLibServices from '~services'; -import networkUI from '~network-ui/main'; +import networkUI from '~network-ui/network.ui.app'; start.bootstrap(() => { angular.bootstrap(document.body, ['awApp']); @@ -91,7 +91,7 @@ angular users.name, projects.name, scheduler.name, - networkUI.tower.name, + networkUI.name, 'Utilities', 'templates', diff --git a/awx/ui/client/src/network-ui/main.js b/awx/ui/client/src/network-ui/main.js deleted file mode 100644 index 0a9e8d5120..0000000000 --- a/awx/ui/client/src/network-ui/main.js +++ /dev/null @@ -1,5 +0,0 @@ -/* Copyright (c) 2017 Red Hat, Inc. */ -var networkUI = require('./network.ui.app.js'); -var tower = require('./tower.app.js'); -exports.networkUI = networkUI.networkUI; -exports.tower = tower.tower; diff --git a/awx/ui/client/src/network-ui/network.ui.app.js b/awx/ui/client/src/network-ui/network.ui.app.js index bc71fd4a3e..7fb414950d 100644 --- a/awx/ui/client/src/network-ui/network.ui.app.js +++ b/awx/ui/client/src/network-ui/network.ui.app.js @@ -5,7 +5,6 @@ import networkDetailsDirective from './network-details/main'; import networkZoomWidget from './zoom-widget/main'; //console.log = function () { }; -var angular = require('angular'); var NetworkUIController = require('./network.ui.controller.js'); var cursor = require('./cursor.directive.js'); var router = require('./router.directive.js'); @@ -21,7 +20,8 @@ var debug = require('./debug.directive.js'); var test_results = require('./test_results.directive.js'); var awxNetworkUI = require('./network.ui.directive.js'); -var networkUI = angular.module('networkUI', [ +export default + angular.module('networkUI', [ 'monospaced.mousewheel', atFeaturesNetworking, networkDetailsDirective.name, @@ -41,5 +41,3 @@ var networkUI = angular.module('networkUI', [ .directive('awxNetInventoryToolbox', inventoryToolbox.inventoryToolbox) .directive('awxNetTestResults', test_results.test_results) .directive('awxNetworkUi', awxNetworkUI.awxNetworkUI); - -exports.networkUI = networkUI; diff --git a/awx/ui/client/src/network-ui/network.ui.controller.js b/awx/ui/client/src/network-ui/network.ui.controller.js index de30650caa..a0875b0d64 100644 --- a/awx/ui/client/src/network-ui/network.ui.controller.js +++ b/awx/ui/client/src/network-ui/network.ui.controller.js @@ -1,5 +1,4 @@ /* Copyright (c) 2017 Red Hat, Inc. */ -var angular = require('angular'); var fsm = require('./fsm.js'); var mode_fsm = require('./mode.fsm.js'); var hotkeys = require('./hotkeys.fsm.js'); diff --git a/awx/ui/client/src/network-ui/tower.app.js b/awx/ui/client/src/network-ui/tower.app.js deleted file mode 100644 index 200d5078ce..0000000000 --- a/awx/ui/client/src/network-ui/tower.app.js +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright (c) 2017 Red Hat, Inc. */ - -var angular = require('angular'); - -var tower = angular.module('tower', ['networkUI', 'ui.router']); - -exports.tower = tower; - From 5f38b4fde4692a9bfde0926573963429032db543 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 25 Apr 2018 16:41:48 -0700 Subject: [PATCH 377/379] removes code/references related to cleanup_facts mgmt jobs, and their schedules --- .../management-jobs/card/card.controller.js | 124 ------------------ .../management-jobs/card/card.partial.html | 4 +- .../management-jobs/card/mgmtcards.block.less | 2 +- .../scheduler/schedulerForm.partial.html | 40 +----- .../factories/schedule-post.factory.js | 8 +- .../src/scheduler/schedulerAdd.controller.js | 36 +---- .../src/scheduler/schedulerEdit.controller.js | 67 ++-------- 7 files changed, 18 insertions(+), 263 deletions(-) diff --git a/awx/ui/client/src/management-jobs/card/card.controller.js b/awx/ui/client/src/management-jobs/card/card.controller.js index e2c1b1a844..846b95a6ff 100644 --- a/awx/ui/client/src/management-jobs/card/card.controller.js +++ b/awx/ui/client/src/management-jobs/card/card.controller.js @@ -38,121 +38,6 @@ export default $scope.cardAction = "notifications"; } - $scope.submitCleanupJob = function(id, name){ - defaultUrl = GetBasePath('system_job_templates')+id+'/launch/'; - CreateDialog({ - id: 'prompt-for-days-facts', - title: name, - scope: $scope, - width: 500, - height: 470, - minWidth: 200, - callback: 'PromptForDaysFacts', - resizable: false, - onOpen: function(){ - $scope.$watch('prompt_for_days_facts_form.$invalid', function(invalid) { - if (invalid === true) { - $('#prompt-for-days-facts-launch').prop("disabled", true); - } else { - $('#prompt-for-days-facts-launch').prop("disabled", false); - } - }); - - var fieldScope = $scope.$parent; - - // set these form elements up on the $scope where the form - // is the parent of the current $scope - - fieldScope.keep_unit_choices = [{ - "label" : "Days", - "value" : "d" - }, - { - "label": "Weeks", - "value" : "w" - }, - { - "label" : "Years", - "value" : "y" - }]; - fieldScope.granularity_keep_unit_choices = [{ - "label" : "Days", - "value" : "d" - }, - { - "label": "Weeks", - "value" : "w" - }, - { - "label" : "Years", - "value" : "y" - }]; - $scope.prompt_for_days_facts_form.$setPristine(); - $scope.prompt_for_days_facts_form.$invalid = false; - fieldScope.keep_unit = fieldScope.keep_unit_choices[0]; - fieldScope.granularity_keep_unit = fieldScope.granularity_keep_unit_choices[1]; - fieldScope.keep_amount = 30; - fieldScope.granularity_keep_amount = 1; - }, - buttons: [ - { - "label": "Cancel", - "onClick": function() { - $(this).dialog('close'); - }, - "class": "btn btn-default", - "id": "prompt-for-days-facts-cancel" - }, - { - "label": "Launch", - "onClick": function() { - var extra_vars = { - "older_than": $scope.keep_amount+$scope.keep_unit.value, - "granularity": $scope.granularity_keep_amount+$scope.granularity_keep_unit.value - }, - data = {}; - data.extra_vars = JSON.stringify(extra_vars); - - Rest.setUrl(defaultUrl); - Rest.post(data) - .then(({data}) => { - Wait('stop'); - $("#prompt-for-days-facts").dialog("close"); - $("#configure-dialog").dialog('close'); - $state.go('jobz', { id: data.system_job, type: 'system' }, { reload: true }); - }) - .catch(({data, status}) => { - let template_id = $scope.job_template_id; - template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); - }); - }, - "class": "btn btn-primary", - "id": "prompt-for-days-facts-launch", - } - ] - }); - - if ($scope.removePromptForDays) { - $scope.removePromptForDays(); - } - $scope.removePromptForDays = $scope.$on('PromptForDaysFacts', function() { - // $('#configure-dialog').dialog('close'); - $('#prompt-for-days-facts').show(); - $('#prompt-for-days-facts').dialog('open'); - CreateSelect2({ - element: '#keep_unit', - multiple: false - }); - CreateSelect2({ - element: '#granularity_keep_unit', - multiple: false - }); - Wait('stop'); - }); - }; - $scope.goToNotifications = function(card){ $state.transitionTo('managementJobsList.notifications',{ card: card, @@ -235,15 +120,6 @@ export default }); }; - $scope.chooseRunJob = function(id, name) { - if(this.card.job_type === "cleanup_facts") { - // Run only for 'Cleanup Fact Details' - $scope.submitCleanupJob(id, name); - } else { - $scope.submitJob(id, name); - } - }; - $scope.configureSchedule = function(id) { $state.transitionTo('managementJobsList.schedule', { id: id diff --git a/awx/ui/client/src/management-jobs/card/card.partial.html b/awx/ui/client/src/management-jobs/card/card.partial.html index 66aa4befbe..008786dc7f 100644 --- a/awx/ui/client/src/management-jobs/card/card.partial.html +++ b/awx/ui/client/src/management-jobs/card/card.partial.html @@ -18,7 +18,7 @@

{{ card.name }}

-
\ No newline at end of file +
diff --git a/awx/ui/client/src/management-jobs/card/mgmtcards.block.less b/awx/ui/client/src/management-jobs/card/mgmtcards.block.less index 75a463dc82..57c09e84e3 100644 --- a/awx/ui/client/src/management-jobs/card/mgmtcards.block.less +++ b/awx/ui/client/src/management-jobs/card/mgmtcards.block.less @@ -1,7 +1,6 @@ .MgmtCards { display: flex; flex-flow: row wrap; - justify-content: space-between; } .MgmtCards-card { @@ -11,6 +10,7 @@ border: 1px solid @b7grey; align-items: baseline; margin-top: 20px; + margin-right: 20px; width: 32%; } diff --git a/awx/ui/client/src/management-jobs/scheduler/schedulerForm.partial.html b/awx/ui/client/src/management-jobs/scheduler/schedulerForm.partial.html index 9723193d39..a735e4c825 100644 --- a/awx/ui/client/src/management-jobs/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/management-jobs/scheduler/schedulerForm.partial.html @@ -161,7 +161,7 @@ ng-show="sheduler_frequency_error">
-
+
A value is required.
@@ -521,44 +521,6 @@
- -
- Note: For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept. - Caution: Setting both numerical variables to "0" will delete all facts.
- -
- -
- - -
- - -
-
Please enter the number of days you would like to keep this data.
-
Please enter a valid number.
-
Please enter a non-negative number.
-
Please enter a number smaller than 9999.
-
-
- -
- -
- - -
- - -
-
Please enter the number of days you would like to keep this data.
-
Please enter a valid number.
-
Please enter a non-negative number.
-
Please enter a number smaller than 9999.
-
-
-
{ scheduler.scope.timeZones = data; scheduler.scope.schedulerTimeZone = _.find(data, function(x) { - let tz = $scope.schedule_obj.rrule.match(/TZID=\s*(.*?)\s*:/)[1]; - return x.name === tz; + let tz = $scope.schedule_obj.rrule.match(/TZID=\s*(.*?)\s*:/); + if (_.has(tz, '1')) { + return x.name === tz[1]; + } else { + return false; + } + }); }); scheduler.inject('form-container', false); @@ -200,56 +203,8 @@ function($filter, $state, $stateParams, Wait, $scope, moment, scheduler.setRRule(schedule.rrule); scheduler.setName(schedule.name); - if ($scope.isFactCleanup || $scope.cleanupJob){ - var a,b, prompt_for_days, - keep_unit, - granularity, - granularity_keep_unit; - - if ($scope.cleanupJob){ - $scope.schedulerPurgeDays = Number(schedule.extra_data.days); - } else if ($scope.isFactCleanup){ - $scope.keep_unit_choices = [{ - "label" : "Days", - "value" : "d" - }, - { - "label": "Weeks", - "value" : "w" - }, - { - "label" : "Years", - "value" : "y" - }]; - $scope.granularity_keep_unit_choices = [{ - "label" : "Days", - "value" : "d" - }, - { - "label": "Weeks", - "value" : "w" - }, - { - "label" : "Years", - "value" : "y" - }]; - // the API returns something like 20w or 1y - a = schedule.extra_data.older_than; // "20y" - b = schedule.extra_data.granularity; // "1w" - prompt_for_days = Number(_.initial(a,1).join('')); // 20 - keep_unit = _.last(a); // "y" - granularity = Number(_.initial(b,1).join('')); // 1 - granularity_keep_unit = _.last(b); // "w" - - $scope.keep_amount = prompt_for_days; - $scope.granularity_keep_amount = granularity; - $scope.keep_unit = _.find($scope.keep_unit_choices, function(i){ - return i.value === keep_unit; - }); - $scope.granularity_keep_unit =_.find($scope.granularity_keep_unit_choices, function(i){ - return i.value === granularity_keep_unit; - }); - } + if ($scope.cleanupJob){ + $scope.schedulerPurgeDays = Number(schedule.extra_data.days); } if ($state.current.name === 'jobTemplateSchedules.edit'){ From c88303ca67952fb45aa360adf2df078ab24efadb Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 26 Apr 2018 12:33:38 -0400 Subject: [PATCH 378/379] revert change to allow org members to see teams --- .../0029_v330_members_can_see_teams.py | 31 ------------------- .../0030_v330_modify_application.py | 2 +- awx/main/models/organization.py | 2 +- 3 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 awx/main/migrations/0029_v330_members_can_see_teams.py diff --git a/awx/main/migrations/0029_v330_members_can_see_teams.py b/awx/main/migrations/0029_v330_members_can_see_teams.py deleted file mode 100644 index a314bda418..0000000000 --- a/awx/main/migrations/0029_v330_members_can_see_teams.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.11 on 2018-04-02 19:18 -from __future__ import unicode_literals - -from django.db import migrations -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - -import awx.main.fields - -from awx.main.migrations import ActivityStreamDisabledMigration -from awx.main.migrations import _rbac as rbac -from awx.main.migrations import _migration_utils as migration_utils - - -class Migration(ActivityStreamDisabledMigration): - - dependencies = [ - ('main', '0028_v330_add_tower_verify'), - ] - - operations = [ - migrations.AlterField( - model_name='team', - name='read_role', - field=awx.main.fields.ImplicitRoleField(null=b'True', on_delete=django.db.models.deletion.CASCADE, parent_role=[b'organization.auditor_role', b'organization.member_role', b'member_role'], related_name='+', to='main.Role'), - ), - migrations.RunPython(migration_utils.set_current_apps_for_migrations), - migrations.RunPython(rbac.rebuild_role_hierarchy), - ] diff --git a/awx/main/migrations/0030_v330_modify_application.py b/awx/main/migrations/0030_v330_modify_application.py index 54d190292b..7725ffeaff 100644 --- a/awx/main/migrations/0030_v330_modify_application.py +++ b/awx/main/migrations/0030_v330_modify_application.py @@ -11,7 +11,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('main', '0029_v330_members_can_see_teams'), + ('main', '0028_v330_add_tower_verify'), ] operations = [ diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index 8bf0701821..db406fd2ed 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -112,7 +112,7 @@ class Team(CommonModelNameNotUnique, ResourceMixin): parent_role='admin_role', ) read_role = ImplicitRoleField( - parent_role=['organization.auditor_role', 'organization.member_role', 'member_role'], + parent_role=['organization.auditor_role', 'member_role'], ) def get_absolute_url(self, request=None): From 23a009f8bb72abe83cf3065f54f82f57eee88205 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Thu, 26 Apr 2018 14:03:57 -0400 Subject: [PATCH 379/379] Fix another stray 2017 in the UI. --- awx/ui/client/src/about/about.partial.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/about/about.partial.html b/awx/ui/client/src/about/about.partial.html index 32e32b72e8..3f49a15514 100644 --- a/awx/ui/client/src/about/about.partial.html +++ b/awx/ui/client/src/about/about.partial.html @@ -25,7 +25,7 @@ Ansible {{ ansible_version }}
- Copyright © 2017 Red Hat, Inc.
+ Copyright © 2018 Red Hat, Inc.
Visit Ansible.com for more information.