mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 01:57:35 -03:30
Merge branch 'release_3.0.3' into devel
* release_3.0.3: (55 commits) Revert "Revert "Add needed types for selinux change"" Revert "Add needed types for selinux change" interpret backslash escapes when displaying url in welcome message Bump the SELinux policy version Add needed types for selinux change Update SELinux policy to allow httpd_t to execute files in lib_t and var_lib_t Bumping changelog for 3.0.3 Update rax.py inventory Revert "filter internal User.admin_roles from the /roles API list view" fix spelling of disassociated Resolves 404 when assigning resources/users to organizations in card view. Sidesteps a bug in the Refresh() utility, where pagination calculations are not made against filtered results. Sync azure changes to Tower virtual environment Add regions here as well. Also bump boto for new regions, per ryansb. More regions! Revert "bump shade version" bump shade version Hack copying of job_template.related.survey_spec into ui job copy flow, resolves #3737 Revert "bump shade version" bump shade version ...
This commit is contained in:
commit
c6cf02a602
@ -14,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
# Django REST Framework
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.exceptions import ParseError, PermissionDenied
|
||||
from rest_framework.filters import BaseFilterBackend
|
||||
|
||||
# Ansible Tower
|
||||
@ -97,7 +97,10 @@ class FieldLookupBackend(BaseFilterBackend):
|
||||
|
||||
new_parts.append(name)
|
||||
|
||||
if name == 'pk':
|
||||
|
||||
if name in getattr(model, 'PASSWORD_FIELDS', ()):
|
||||
raise PermissionDenied('Filtering on password fields is not allowed.')
|
||||
elif name == 'pk':
|
||||
field = model._meta.pk
|
||||
else:
|
||||
field = model._meta.get_field_by_name(name)[0]
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Labels not associated with any other resources are deleted. A label can become disassociated with a resource as a result of 3 events.
|
||||
|
||||
1. A label is explicitly diassociated with a related job template
|
||||
1. A label is explicitly disassociated with a related job template
|
||||
2. A job is deleted with labels
|
||||
3. A cleanup job deletes a job with labels
|
||||
|
||||
|
||||
@ -764,7 +764,6 @@ class CredentialAccess(BaseAccess):
|
||||
or (not organization_pk and obj.organization):
|
||||
return False
|
||||
|
||||
print(self.user in obj.admin_role)
|
||||
return self.user in obj.admin_role
|
||||
|
||||
def can_delete(self, obj):
|
||||
@ -1237,17 +1236,32 @@ class JobAccess(BaseAccess):
|
||||
if self.user.is_superuser:
|
||||
return True
|
||||
|
||||
# If a user can launch the job template then they can relaunch a job from that
|
||||
# job template
|
||||
inventory_access = obj.inventory and self.user in obj.inventory.use_role
|
||||
credential_access = obj.credential and self.user in obj.credential.use_role
|
||||
|
||||
# Check if JT execute access (and related prompts) is sufficient
|
||||
if obj.job_template is not None:
|
||||
return self.user in obj.job_template.execute_role
|
||||
prompts_access = True
|
||||
job_fields = {}
|
||||
for fd in obj.job_template._ask_for_vars_dict():
|
||||
job_fields[fd] = getattr(obj, fd)
|
||||
accepted_fields, ignored_fields = obj.job_template._accept_or_ignore_job_kwargs(**job_fields)
|
||||
for fd in ignored_fields:
|
||||
if fd != 'extra_vars' and job_fields[fd] != getattr(obj.job_template, fd):
|
||||
# Job has field that is not promptable
|
||||
prompts_access = False
|
||||
if obj.credential != obj.job_template.credential and not credential_access:
|
||||
prompts_access = False
|
||||
if obj.inventory != obj.job_template.inventory and not inventory_access:
|
||||
prompts_access = False
|
||||
if prompts_access and self.user in obj.job_template.execute_role:
|
||||
return True
|
||||
|
||||
inventory_access = self.user in obj.inventory.use_role
|
||||
credential_access = self.user in obj.credential.use_role
|
||||
|
||||
org_access = self.user in obj.inventory.organization.admin_role
|
||||
org_access = obj.inventory and self.user in obj.inventory.organization.admin_role
|
||||
project_access = obj.project is None or self.user in obj.project.admin_role
|
||||
|
||||
# job can be relaunched if user could make an equivalent JT
|
||||
return inventory_access and credential_access and (org_access or project_access)
|
||||
|
||||
def can_cancel(self, obj):
|
||||
|
||||
@ -51,7 +51,6 @@ class CallbackBrokerWorker(ConsumerMixin):
|
||||
logger.error('Callback Task Processor Raised Exception: %r', exc)
|
||||
message.ack()
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
'''
|
||||
Save Job Callback receiver (see awx.plugins.callbacks.job_event_callback)
|
||||
|
||||
23
awx/main/migrations/0033_v303_v245_host_variable_fix.py
Normal file
23
awx/main/migrations/0033_v303_v245_host_variable_fix.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
from awx.main.migrations import _migration_utils as migration_utils
|
||||
|
||||
|
||||
def update_dashed_host_variables(apps, schema_editor):
|
||||
Host = apps.get_model('main', 'Host')
|
||||
for host in Host.objects.filter(variables='---'):
|
||||
host.variables = ''
|
||||
host.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0032_v302_credential_permissions_update'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
|
||||
migrations.RunPython(update_dashed_host_variables),
|
||||
]
|
||||
@ -551,7 +551,7 @@ class BaseTask(Task):
|
||||
output_replacements=output_replacements)
|
||||
job_start = time.time()
|
||||
while child.isalive():
|
||||
result_id = child.expect(expect_list, timeout=pexpect_timeout)
|
||||
result_id = child.expect(expect_list, timeout=pexpect_timeout, searchwindowsize=100)
|
||||
if result_id in expect_passwords:
|
||||
child.sendline(expect_passwords[result_id])
|
||||
if logfile_pos != logfile.tell():
|
||||
|
||||
@ -2,11 +2,7 @@ import pytest
|
||||
|
||||
from awx.main.models.inventory import Inventory
|
||||
from awx.main.models.credential import Credential
|
||||
from awx.main.models.jobs import JobTemplate
|
||||
|
||||
@pytest.fixture
|
||||
def machine_credential():
|
||||
return Credential.objects.create(name='machine-cred', kind='ssh', username='test_user', password='pas4word')
|
||||
from awx.main.models.jobs import JobTemplate, Job
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.job_permissions
|
||||
@ -45,3 +41,52 @@ def test_inventory_use_access(inventory, user):
|
||||
inventory.use_role.members.add(common_user)
|
||||
|
||||
assert common_user.can_access(Inventory, 'use', inventory)
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestJobRelaunchAccess:
|
||||
@pytest.fixture
|
||||
def job_no_prompts(self, machine_credential, inventory):
|
||||
jt = JobTemplate.objects.create(name='test-job_template', credential=machine_credential, inventory=inventory)
|
||||
return jt.create_unified_job()
|
||||
|
||||
@pytest.fixture
|
||||
def job_with_prompts(self, machine_credential, inventory, organization):
|
||||
jt = JobTemplate.objects.create(
|
||||
name='test-job-template-prompts', credential=machine_credential, inventory=inventory,
|
||||
ask_tags_on_launch=True, ask_variables_on_launch=True, ask_skip_tags_on_launch=True,
|
||||
ask_limit_on_launch=True, ask_job_type_on_launch=True, ask_inventory_on_launch=True,
|
||||
ask_credential_on_launch=True)
|
||||
new_cred = Credential.objects.create(name='new-cred', kind='ssh', username='test_user', password='pas4word')
|
||||
new_inv = Inventory.objects.create(name='new-inv', organization=organization)
|
||||
return jt.create_unified_job(credential=new_cred, inventory=new_inv)
|
||||
|
||||
def test_normal_relaunch_via_job_template(self, job_no_prompts, rando):
|
||||
"Has JT execute_role, job unchanged relative to JT"
|
||||
job_no_prompts.job_template.execute_role.members.add(rando)
|
||||
assert rando.can_access(Job, 'start', job_no_prompts)
|
||||
|
||||
def test_no_relaunch_without_prompted_fields_access(self, job_with_prompts, rando):
|
||||
"Has JT execute_role but no use_role on inventory & credential - deny relaunch"
|
||||
job_with_prompts.job_template.execute_role.members.add(rando)
|
||||
assert not rando.can_access(Job, 'start', job_with_prompts)
|
||||
|
||||
def test_can_relaunch_with_prompted_fields_access(self, job_with_prompts, rando):
|
||||
"Has use_role on the prompted inventory & credential - allow relaunch"
|
||||
job_with_prompts.job_template.execute_role.members.add(rando)
|
||||
job_with_prompts.credential.use_role.members.add(rando)
|
||||
job_with_prompts.inventory.use_role.members.add(rando)
|
||||
assert rando.can_access(Job, 'start', job_with_prompts)
|
||||
|
||||
def test_no_relaunch_after_limit_change(self, job_no_prompts, rando):
|
||||
"State of the job contradicts the JT state - deny relaunch"
|
||||
job_no_prompts.job_template.execute_role.members.add(rando)
|
||||
job_no_prompts.limit = 'webservers'
|
||||
job_no_prompts.save()
|
||||
assert not rando.can_access(Job, 'start', job_no_prompts)
|
||||
|
||||
def test_can_relaunch_if_limit_was_prompt(self, job_with_prompts, rando):
|
||||
"Job state differs from JT, but only on prompted fields - allow relaunch"
|
||||
job_with_prompts.job_template.execute_role.members.add(rando)
|
||||
job_with_prompts.limit = 'webservers'
|
||||
job_with_prompts.save()
|
||||
assert not rando.can_access(Job, 'start', job_with_prompts)
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from awx.api.filters import FieldLookupBackend
|
||||
from awx.main.models import JobTemplate
|
||||
from awx.main.models import Credential, JobTemplate
|
||||
|
||||
@pytest.mark.parametrize(u"empty_value", [u'', ''])
|
||||
def test_empty_in(empty_value):
|
||||
@ -15,3 +16,21 @@ def test_valid_in(valid_value):
|
||||
field_lookup = FieldLookupBackend()
|
||||
value, new_lookup = field_lookup.value_to_python(JobTemplate, 'project__in', valid_value)
|
||||
assert 'foo' in value
|
||||
|
||||
@pytest.mark.parametrize('lookup_suffix', ['', 'contains', 'startswith', 'in'])
|
||||
@pytest.mark.parametrize('password_field', Credential.PASSWORD_FIELDS)
|
||||
def test_filter_on_password_field(password_field, lookup_suffix):
|
||||
field_lookup = FieldLookupBackend()
|
||||
lookup = '__'.join(filter(None, [password_field, lookup_suffix]))
|
||||
with pytest.raises(PermissionDenied) as excinfo:
|
||||
field, new_lookup = field_lookup.get_field_from_lookup(Credential, lookup)
|
||||
assert 'not allowed' in str(excinfo.value)
|
||||
|
||||
@pytest.mark.parametrize('lookup_suffix', ['', 'contains', 'startswith', 'in'])
|
||||
@pytest.mark.parametrize('password_field', Credential.PASSWORD_FIELDS)
|
||||
def test_filter_on_related_password_field(password_field, lookup_suffix):
|
||||
field_lookup = FieldLookupBackend()
|
||||
lookup = '__'.join(filter(None, ['credential', password_field, lookup_suffix]))
|
||||
with pytest.raises(PermissionDenied) as excinfo:
|
||||
field, new_lookup = field_lookup.get_field_from_lookup(JobTemplate, lookup)
|
||||
assert 'not allowed' in str(excinfo.value)
|
||||
|
||||
@ -9,6 +9,15 @@ from awx.main.access import (
|
||||
check_superuser,
|
||||
JobTemplateAccess,
|
||||
WorkflowJobTemplateAccess,
|
||||
SystemJobTemplateAccess,
|
||||
)
|
||||
|
||||
from awx.main.models import (
|
||||
Credential,
|
||||
Inventory,
|
||||
Project,
|
||||
Role,
|
||||
Organization,
|
||||
)
|
||||
from awx.conf.license import LicenseForbids
|
||||
from awx.main.models import Credential, Inventory, Project, Role, Organization, Instance
|
||||
@ -124,7 +133,6 @@ def test_jt_can_add_bad_data(user_unit):
|
||||
access = JobTemplateAccess(user_unit)
|
||||
assert not access.can_add({'asdf': 'asdf'})
|
||||
|
||||
|
||||
class TestWorkflowAccessMethods:
|
||||
@pytest.fixture
|
||||
def workflow(self, workflow_job_template_factory):
|
||||
@ -172,3 +180,12 @@ def test_user_capabilities_method():
|
||||
'copy': 'foobar'
|
||||
}
|
||||
|
||||
def test_system_job_template_can_start(mocker):
|
||||
user = mocker.MagicMock(spec=User, id=1, is_system_auditor=True, is_superuser=False)
|
||||
assert user.is_system_auditor
|
||||
access = SystemJobTemplateAccess(user)
|
||||
assert not access.can_start(None)
|
||||
|
||||
user.is_superuser = True
|
||||
access = SystemJobTemplateAccess(user)
|
||||
assert access.can_start(None)
|
||||
|
||||
11
awx/main/tests/unit/test_settings.py
Normal file
11
awx/main/tests/unit/test_settings.py
Normal file
@ -0,0 +1,11 @@
|
||||
from split_settings.tools import include
|
||||
|
||||
def test_postprocess_auth_basic_enabled():
|
||||
locals().update({'__file__': __file__})
|
||||
|
||||
include('../../../settings/defaults.py', scope=locals())
|
||||
assert 'awx.api.authentication.LoggedBasicAuthentication' in locals()['REST_FRAMEWORK']['DEFAULT_AUTHENTICATION_CLASSES']
|
||||
|
||||
locals().update({'AUTH_BASIC_ENABLED': False})
|
||||
include('../../../settings/postprocess.py', scope=locals())
|
||||
assert 'awx.api.authentication.LoggedBasicAuthentication' not in locals()['REST_FRAMEWORK']['DEFAULT_AUTHENTICATION_CLASSES']
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Configuration file for azure_rm_invetory.py
|
||||
# Configuration file for azure_rm.py
|
||||
#
|
||||
[azure]
|
||||
# Control which resource groups are included. By default all resources groups are included.
|
||||
@ -9,11 +9,14 @@
|
||||
# Control which tags are included. Set tags to a comma separated list of keys or key:value pairs
|
||||
#tags=
|
||||
|
||||
# Control which locations are included. Set locations to a comma separated list (e.g. eastus,eastus2,westus)
|
||||
#locations=
|
||||
|
||||
# Include powerstate. If you don't need powerstate information, turning it off improves runtime performance.
|
||||
include_powerstate=yes
|
||||
|
||||
# Control grouping with the following boolean flags. Valid values: yes, no, true, false, True, False, 0, 1.
|
||||
group_by_resource_group=yes
|
||||
group_by_location=yes
|
||||
group_by_security_group=no
|
||||
group_by_security_group=yes
|
||||
group_by_tag=yes
|
||||
|
||||
@ -76,7 +76,7 @@ required. For a specific host, this script returns the following variables:
|
||||
"version": "latest"
|
||||
},
|
||||
"location": "westus",
|
||||
"mac_address": "00-0D-3A-31-2C-EC",
|
||||
"mac_address": "00-00-5E-00-53-FE",
|
||||
"name": "object-name",
|
||||
"network_interface": "interface-name",
|
||||
"network_interface_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkInterfaces/object-name1",
|
||||
@ -115,7 +115,7 @@ When run in --list mode, instances are grouped by the following categories:
|
||||
- tag key
|
||||
- tag key_value
|
||||
|
||||
Control groups using azure_rm_inventory.ini or set environment variables:
|
||||
Control groups using azure_rm.ini or set environment variables:
|
||||
|
||||
AZURE_GROUP_BY_RESOURCE_GROUP=yes
|
||||
AZURE_GROUP_BY_LOCATION=yes
|
||||
@ -130,6 +130,10 @@ Select hosts for specific tag key by assigning a comma separated list of tag key
|
||||
|
||||
AZURE_TAGS=key1,key2,key3
|
||||
|
||||
Select hosts for specific locations:
|
||||
|
||||
AZURE_LOCATIONS=eastus,westus,eastus2
|
||||
|
||||
Or, select hosts for specific tag key:value pairs by assigning a comma separated list key:value pairs to:
|
||||
|
||||
AZURE_TAGS=key1:value1,key2:value2
|
||||
@ -137,12 +141,14 @@ AZURE_TAGS=key1:value1,key2:value2
|
||||
If you don't need the powerstate, you can improve performance by turning off powerstate fetching:
|
||||
AZURE_INCLUDE_POWERSTATE=no
|
||||
|
||||
azure_rm_inventory.ini
|
||||
----------------------
|
||||
As mentioned above you can control execution using environment variables or an .ini file. A sample
|
||||
azure_rm_inventory.ini is included. The name of the .ini file is the basename of the inventory script (in this case
|
||||
'azure_rm_inventory') with a .ini extension. This provides you with the flexibility of copying and customizing this
|
||||
script and having matching .ini files. Go forth and customize your Azure inventory!
|
||||
azure_rm.ini
|
||||
------------
|
||||
As mentioned above, you can control execution using environment variables or a .ini file. A sample
|
||||
azure_rm.ini is included. The name of the .ini file is the basename of the inventory script (in this case
|
||||
'azure_rm') with a .ini extension. It also assumes the .ini file is alongside the script. To specify
|
||||
a different path for the .ini file, define the AZURE_INI_PATH environment variable:
|
||||
|
||||
export AZURE_INI_PATH=/path/to/custom.ini
|
||||
|
||||
Powerstate:
|
||||
-----------
|
||||
@ -152,13 +158,13 @@ up. If the value is anything other than 'running', the machine is down, and will
|
||||
Examples:
|
||||
---------
|
||||
Execute /bin/uname on all instances in the galaxy-qa resource group
|
||||
$ ansible -i azure_rm_inventory.py galaxy-qa -m shell -a "/bin/uname -a"
|
||||
$ ansible -i azure_rm.py galaxy-qa -m shell -a "/bin/uname -a"
|
||||
|
||||
Use the inventory script to print instance specific information
|
||||
$ contrib/inventory/azure_rm_inventory.py --host my_instance_host_name --pretty
|
||||
$ contrib/inventory/azure_rm.py --host my_instance_host_name --pretty
|
||||
|
||||
Use with a playbook
|
||||
$ ansible-playbook -i contrib/inventory/azure_rm_inventory.py my_playbook.yml --limit galaxy-qa
|
||||
$ ansible-playbook -i contrib/inventory/azure_rm.py my_playbook.yml --limit galaxy-qa
|
||||
|
||||
|
||||
Insecure Platform Warning
|
||||
@ -180,11 +186,13 @@ Version: 1.0.0
|
||||
|
||||
import argparse
|
||||
import ConfigParser
|
||||
import json
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from os.path import expanduser
|
||||
|
||||
HAS_AZURE = True
|
||||
@ -195,12 +203,9 @@ try:
|
||||
from azure.mgmt.compute import __version__ as azure_compute_version
|
||||
from azure.common import AzureMissingResourceHttpError, AzureHttpError
|
||||
from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials
|
||||
from azure.mgmt.network.network_management_client import NetworkManagementClient,\
|
||||
NetworkManagementClientConfiguration
|
||||
from azure.mgmt.resource.resources.resource_management_client import ResourceManagementClient,\
|
||||
ResourceManagementClientConfiguration
|
||||
from azure.mgmt.compute.compute_management_client import ComputeManagementClient,\
|
||||
ComputeManagementClientConfiguration
|
||||
from azure.mgmt.network.network_management_client import NetworkManagementClient
|
||||
from azure.mgmt.resource.resources.resource_management_client import ResourceManagementClient
|
||||
from azure.mgmt.compute.compute_management_client import ComputeManagementClient
|
||||
except ImportError as exc:
|
||||
HAS_AZURE_EXC = exc
|
||||
HAS_AZURE = False
|
||||
@ -219,6 +224,7 @@ AZURE_CREDENTIAL_ENV_MAPPING = dict(
|
||||
AZURE_CONFIG_SETTINGS = dict(
|
||||
resource_groups='AZURE_RESOURCE_GROUPS',
|
||||
tags='AZURE_TAGS',
|
||||
locations='AZURE_LOCATIONS',
|
||||
include_powerstate='AZURE_INCLUDE_POWERSTATE',
|
||||
group_by_resource_group='AZURE_GROUP_BY_RESOURCE_GROUP',
|
||||
group_by_location='AZURE_GROUP_BY_LOCATION',
|
||||
@ -226,7 +232,7 @@ AZURE_CONFIG_SETTINGS = dict(
|
||||
group_by_tag='AZURE_GROUP_BY_TAG'
|
||||
)
|
||||
|
||||
AZURE_MIN_VERSION = "2016-03-30"
|
||||
AZURE_MIN_VERSION = "0.30.0rc5"
|
||||
|
||||
|
||||
def azure_id_to_dict(id):
|
||||
@ -362,8 +368,7 @@ class AzureRM(object):
|
||||
def network_client(self):
|
||||
self.log('Getting network client')
|
||||
if not self._network_client:
|
||||
self._network_client = NetworkManagementClient(
|
||||
NetworkManagementClientConfiguration(self.azure_credentials, self.subscription_id))
|
||||
self._network_client = NetworkManagementClient(self.azure_credentials, self.subscription_id)
|
||||
self._register('Microsoft.Network')
|
||||
return self._network_client
|
||||
|
||||
@ -371,16 +376,14 @@ class AzureRM(object):
|
||||
def rm_client(self):
|
||||
self.log('Getting resource manager client')
|
||||
if not self._resource_client:
|
||||
self._resource_client = ResourceManagementClient(
|
||||
ResourceManagementClientConfiguration(self.azure_credentials, self.subscription_id))
|
||||
self._resource_client = ResourceManagementClient(self.azure_credentials, self.subscription_id)
|
||||
return self._resource_client
|
||||
|
||||
@property
|
||||
def compute_client(self):
|
||||
self.log('Getting compute client')
|
||||
if not self._compute_client:
|
||||
self._compute_client = ComputeManagementClient(
|
||||
ComputeManagementClientConfiguration(self.azure_credentials, self.subscription_id))
|
||||
self._compute_client = ComputeManagementClient(self.azure_credentials, self.subscription_id)
|
||||
self._register('Microsoft.Compute')
|
||||
return self._compute_client
|
||||
|
||||
@ -403,6 +406,7 @@ class AzureInventory(object):
|
||||
|
||||
self.resource_groups = []
|
||||
self.tags = None
|
||||
self.locations = None
|
||||
self.replace_dash_in_groups = False
|
||||
self.group_by_resource_group = True
|
||||
self.group_by_location = True
|
||||
@ -425,6 +429,9 @@ class AzureInventory(object):
|
||||
if self._args.tags:
|
||||
self.tags = self._args.tags.split(',')
|
||||
|
||||
if self._args.locations:
|
||||
self.locations = self._args.locations.split(',')
|
||||
|
||||
if self._args.no_powerstate:
|
||||
self.include_powerstate = False
|
||||
|
||||
@ -462,6 +469,8 @@ class AzureInventory(object):
|
||||
help='Return inventory for comma separated list of resource group names')
|
||||
parser.add_argument('--tags', action='store',
|
||||
help='Return inventory for comma separated list of tag key:value pairs')
|
||||
parser.add_argument('--locations', action='store',
|
||||
help='Return inventory for comma separated list of locations')
|
||||
parser.add_argument('--no-powerstate', action='store_true', default=False,
|
||||
help='Do not include the power state of each virtual host')
|
||||
return parser.parse_args()
|
||||
@ -487,7 +496,7 @@ class AzureInventory(object):
|
||||
except Exception as exc:
|
||||
sys.exit("Error: fetching virtual machines - {0}".format(str(exc)))
|
||||
|
||||
if self._args.host or self.tags > 0:
|
||||
if self._args.host or self.tags or self.locations:
|
||||
selected_machines = self._selected_machines(virtual_machines)
|
||||
self._load_machines(selected_machines)
|
||||
else:
|
||||
@ -524,7 +533,7 @@ class AzureInventory(object):
|
||||
resource_group=resource_group,
|
||||
mac_address=None,
|
||||
plan=(machine.plan.name if machine.plan else None),
|
||||
virtual_machine_size=machine.hardware_profile.vm_size.value,
|
||||
virtual_machine_size=machine.hardware_profile.vm_size,
|
||||
computer_name=machine.os_profile.computer_name,
|
||||
provisioning_state=machine.provisioning_state,
|
||||
)
|
||||
@ -576,7 +585,7 @@ class AzureInventory(object):
|
||||
host_vars['mac_address'] = network_interface.mac_address
|
||||
for ip_config in network_interface.ip_configurations:
|
||||
host_vars['private_ip'] = ip_config.private_ip_address
|
||||
host_vars['private_ip_alloc_method'] = ip_config.private_ip_allocation_method.value
|
||||
host_vars['private_ip_alloc_method'] = ip_config.private_ip_allocation_method
|
||||
if ip_config.public_ip_address:
|
||||
public_ip_reference = self._parse_ref_id(ip_config.public_ip_address.id)
|
||||
public_ip_address = self._network_client.public_ip_addresses.get(
|
||||
@ -585,7 +594,7 @@ class AzureInventory(object):
|
||||
host_vars['ansible_host'] = public_ip_address.ip_address
|
||||
host_vars['public_ip'] = public_ip_address.ip_address
|
||||
host_vars['public_ip_name'] = public_ip_address.name
|
||||
host_vars['public_ip_alloc_method'] = public_ip_address.public_ip_allocation_method.value
|
||||
host_vars['public_ip_alloc_method'] = public_ip_address.public_ip_allocation_method
|
||||
host_vars['public_ip_id'] = public_ip_address.id
|
||||
if public_ip_address.dns_settings:
|
||||
host_vars['fqdn'] = public_ip_address.dns_settings.fqdn
|
||||
@ -599,6 +608,8 @@ class AzureInventory(object):
|
||||
selected_machines.append(machine)
|
||||
if self.tags and self._tags_match(machine.tags, self.tags):
|
||||
selected_machines.append(machine)
|
||||
if self.locations and machine.location in self.locations:
|
||||
selected_machines.append(machine)
|
||||
return selected_machines
|
||||
|
||||
def _get_security_groups(self, resource_group):
|
||||
@ -676,17 +687,17 @@ class AzureInventory(object):
|
||||
file_settings = self._load_settings()
|
||||
if file_settings:
|
||||
for key in AZURE_CONFIG_SETTINGS:
|
||||
if key in ('resource_groups', 'tags') and file_settings.get(key, None) is not None:
|
||||
if key in ('resource_groups', 'tags', 'locations') and file_settings.get(key):
|
||||
values = file_settings.get(key).split(',')
|
||||
if len(values) > 0:
|
||||
setattr(self, key, values)
|
||||
elif file_settings.get(key, None) is not None:
|
||||
elif file_settings.get(key):
|
||||
val = self._to_boolean(file_settings[key])
|
||||
setattr(self, key, val)
|
||||
else:
|
||||
env_settings = self._get_env_settings()
|
||||
for key in AZURE_CONFIG_SETTINGS:
|
||||
if key in('resource_groups', 'tags') and env_settings.get(key, None) is not None:
|
||||
if key in('resource_groups', 'tags', 'locations') and env_settings.get(key):
|
||||
values = env_settings.get(key).split(',')
|
||||
if len(values) > 0:
|
||||
setattr(self, key, values)
|
||||
@ -719,7 +730,8 @@ class AzureInventory(object):
|
||||
|
||||
def _load_settings(self):
|
||||
basename = os.path.splitext(os.path.basename(__file__))[0]
|
||||
path = basename + '.ini'
|
||||
default_path = os.path.join(os.path.dirname(__file__), (basename + '.ini'))
|
||||
path = os.path.expanduser(os.path.expandvars(os.environ.get('AZURE_INI_PATH', default_path)))
|
||||
config = None
|
||||
settings = None
|
||||
try:
|
||||
@ -774,11 +786,11 @@ class AzureInventory(object):
|
||||
|
||||
def main():
|
||||
if not HAS_AZURE:
|
||||
sys.exit("The Azure python sdk is not installed (try 'pip install azure') - {0}".format(HAS_AZURE_EXC))
|
||||
sys.exit("The Azure python sdk is not installed (try 'pip install azure==2.0.0rc5') - {0}".format(HAS_AZURE_EXC))
|
||||
|
||||
if azure_compute_version < AZURE_MIN_VERSION:
|
||||
sys.exit("Expecting azure.mgmt.compute.__version__ to be >= {0}. Found version {1} "
|
||||
"Do you have Azure >= 2.0.0rc2 installed?".format(AZURE_MIN_VERSION, azure_compute_version))
|
||||
if LooseVersion(azure_compute_version) != LooseVersion(AZURE_MIN_VERSION):
|
||||
sys.exit("Expecting azure.mgmt.compute.__version__ to be {0}. Found version {1} "
|
||||
"Do you have Azure == 2.0.0rc5 installed?".format(AZURE_MIN_VERSION, azure_compute_version))
|
||||
|
||||
AzureInventory()
|
||||
|
||||
|
||||
@ -155,8 +155,6 @@ import ConfigParser
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from ansible.constants import get_config, mk_boolean
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
@ -166,11 +164,12 @@ try:
|
||||
import pyrax
|
||||
from pyrax.utils import slugify
|
||||
except ImportError:
|
||||
print('pyrax is required for this module')
|
||||
sys.exit(1)
|
||||
sys.exit('pyrax is required for this module')
|
||||
|
||||
from time import time
|
||||
|
||||
from ansible.constants import get_config, mk_boolean
|
||||
|
||||
|
||||
NON_CALLABLES = (basestring, bool, dict, int, list, type(None))
|
||||
|
||||
@ -227,12 +226,21 @@ def _list_into_cache(regions):
|
||||
|
||||
prefix = get_config(p, 'rax', 'meta_prefix', 'RAX_META_PREFIX', 'meta')
|
||||
|
||||
networks = get_config(p, 'rax', 'access_network', 'RAX_ACCESS_NETWORK',
|
||||
'public', islist=True)
|
||||
try:
|
||||
ip_versions = map(int, get_config(p, 'rax', 'access_ip_version',
|
||||
'RAX_ACCESS_IP_VERSION', 4,
|
||||
islist=True))
|
||||
# Ansible 2.3+
|
||||
networks = get_config(p, 'rax', 'access_network',
|
||||
'RAX_ACCESS_NETWORK', 'public', value_type='list')
|
||||
except TypeError:
|
||||
# Ansible 2.2.x and below
|
||||
networks = get_config(p, 'rax', 'access_network',
|
||||
'RAX_ACCESS_NETWORK', 'public', islist=True)
|
||||
try:
|
||||
try:
|
||||
ip_versions = map(int, get_config(p, 'rax', 'access_ip_version',
|
||||
'RAX_ACCESS_IP_VERSION', 4, value_type='list'))
|
||||
except TypeError:
|
||||
ip_versions = map(int, get_config(p, 'rax', 'access_ip_version',
|
||||
'RAX_ACCESS_IP_VERSION', 4, islist=True))
|
||||
except:
|
||||
ip_versions = [4]
|
||||
else:
|
||||
@ -406,10 +414,9 @@ def setup():
|
||||
if os.path.isfile(default_creds_file):
|
||||
creds_file = default_creds_file
|
||||
elif not keyring_username:
|
||||
sys.stderr.write('No value in environment variable %s and/or no '
|
||||
'credentials file at %s\n'
|
||||
% ('RAX_CREDS_FILE', default_creds_file))
|
||||
sys.exit(1)
|
||||
sys.exit('No value in environment variable %s and/or no '
|
||||
'credentials file at %s'
|
||||
% ('RAX_CREDS_FILE', default_creds_file))
|
||||
|
||||
identity_type = pyrax.get_setting('identity_type')
|
||||
pyrax.set_setting('identity_type', identity_type or 'rackspace')
|
||||
@ -422,23 +429,28 @@ def setup():
|
||||
else:
|
||||
pyrax.set_credential_file(creds_file, region=region)
|
||||
except Exception as e:
|
||||
sys.stderr.write("%s: %s\n" % (e, e.message))
|
||||
sys.exit(1)
|
||||
sys.exit("%s: %s" % (e, e.message))
|
||||
|
||||
regions = []
|
||||
if region:
|
||||
regions.append(region)
|
||||
else:
|
||||
region_list = get_config(p, 'rax', 'regions', 'RAX_REGION', 'all',
|
||||
islist=True)
|
||||
try:
|
||||
# Ansible 2.3+
|
||||
region_list = get_config(p, 'rax', 'regions', 'RAX_REGION', 'all',
|
||||
value_type='list')
|
||||
except TypeError:
|
||||
# Ansible 2.2.x and below
|
||||
region_list = get_config(p, 'rax', 'regions', 'RAX_REGION', 'all',
|
||||
islist=True)
|
||||
|
||||
for region in region_list:
|
||||
region = region.strip().upper()
|
||||
if region == 'ALL':
|
||||
regions = pyrax.regions
|
||||
break
|
||||
elif region not in pyrax.regions:
|
||||
sys.stderr.write('Unsupported region %s' % region)
|
||||
sys.exit(1)
|
||||
sys.exit('Unsupported region %s' % region)
|
||||
elif region not in regions:
|
||||
regions.append(region)
|
||||
|
||||
|
||||
@ -494,6 +494,9 @@ AWX_TASK_ENV = {}
|
||||
# before it recycles
|
||||
JOB_EVENT_RECYCLE_THRESHOLD = 3000
|
||||
|
||||
# Number of workers used to proecess job events in parallel
|
||||
JOB_EVENT_WORKERS = 4
|
||||
|
||||
# Maximum number of job events that can be waiting on a single worker queue before
|
||||
# it can be skipped as too busy
|
||||
JOB_EVENT_MAX_QUEUE_SIZE = 100
|
||||
@ -592,6 +595,7 @@ INV_ENV_VARIABLE_BLACKLIST = ("HOME", "USER", "_", "TERM")
|
||||
# http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region
|
||||
EC2_REGION_NAMES = {
|
||||
'us-east-1': 'US East (Northern Virginia)',
|
||||
'us-east-2': 'US East (Ohio)',
|
||||
'us-west-2': 'US West (Oregon)',
|
||||
'us-west-1': 'US West (Northern California)',
|
||||
'eu-central-1': 'EU (Frankfurt)',
|
||||
@ -600,6 +604,7 @@ EC2_REGION_NAMES = {
|
||||
'ap-southeast-2': 'Asia Pacific (Sydney)',
|
||||
'ap-northeast-1': 'Asia Pacific (Tokyo)',
|
||||
'ap-northeast-2': 'Asia Pacific (Seoul)',
|
||||
'ap-south-1': 'Asia Pacific (Mumbai)',
|
||||
'sa-east-1': 'South America (Sao Paulo)',
|
||||
'us-gov-west-1': 'US West (GovCloud)',
|
||||
'cn-north-1': 'China (Beijing)',
|
||||
@ -739,7 +744,7 @@ OPENSTACK_INSTANCE_ID_VAR = 'openstack.id'
|
||||
# ----- Foreman -----
|
||||
# ---------------------
|
||||
SATELLITE6_ENABLED_VAR = 'foreman.enabled'
|
||||
SATELLITE6_ENABLED_VALUE = 'true'
|
||||
SATELLITE6_ENABLED_VALUE = 'True'
|
||||
SATELLITE6_GROUP_FILTER = r'^.+$'
|
||||
SATELLITE6_HOST_FILTER = r'^.+$'
|
||||
SATELLITE6_EXCLUDE_EMPTY_GROUPS = True
|
||||
|
||||
@ -438,6 +438,16 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.pathsReadyRemove) {
|
||||
$scope.pathsReadyRemove();
|
||||
}
|
||||
$scope.pathsReadyRemove = $scope.$on('pathsReady', function () {
|
||||
CreateSelect2({
|
||||
element: '#local-path-select',
|
||||
multiple: false
|
||||
});
|
||||
});
|
||||
|
||||
// After the project is loaded, retrieve each related set
|
||||
if ($scope.projectLoadedRemove) {
|
||||
$scope.projectLoadedRemove();
|
||||
@ -455,6 +465,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$scope.project_local_paths = opts;
|
||||
$scope.local_path = $scope.project_local_paths[0];
|
||||
$scope.base_dir = 'You do not have access to view this property';
|
||||
$scope.$emit('pathsReady');
|
||||
}
|
||||
|
||||
$scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false;
|
||||
@ -524,11 +535,6 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: '#local-path-select',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch';
|
||||
$scope.scm_update_tooltip = "Start an SCM update";
|
||||
$scope.scm_type_class = "";
|
||||
|
||||
@ -157,7 +157,7 @@ export default
|
||||
open: false,
|
||||
index: false,
|
||||
actions: {},
|
||||
|
||||
emptyListText: 'This user is not a member of any teams',
|
||||
fields: {
|
||||
name: {
|
||||
key: true,
|
||||
|
||||
@ -80,6 +80,7 @@ export default
|
||||
// trigger display of alert block when scm_type == manual
|
||||
scope.showMissingPlaybooksAlert = true;
|
||||
}
|
||||
scope.$emit('pathsReady');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
*************************************************/
|
||||
|
||||
export default ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'ParseTypeChange', 'GenerateForm', 'inventoryData',
|
||||
'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'rbacUiControlService',
|
||||
function($state, $stateParams, $scope, GroupForm, CredentialList, ParseTypeChange, GenerateForm, inventoryData,
|
||||
GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, rbacUiControlService) {
|
||||
var form = GroupForm();
|
||||
|
||||
'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'rbacUiControlService', 'ToJSON',
|
||||
function($state, $stateParams, $scope, GroupForm, CredentialList, ParseTypeChange, GenerateForm, inventoryData,
|
||||
GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, rbacUiControlService, ToJSON) {
|
||||
var generator = GenerateForm,
|
||||
form = GroupForm();
|
||||
init();
|
||||
|
||||
function init() {
|
||||
function init() {n
|
||||
// apply form definition's default field values
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
@ -46,9 +46,10 @@ export default ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList
|
||||
|
||||
$scope.formSave = function() {
|
||||
var params, source;
|
||||
json_data = ToJSON($scope.parseType, $scope.variables, true);
|
||||
// group fields
|
||||
var group = {
|
||||
variables: $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
|
||||
variables: json_data,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
inventory: inventoryData.id
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
*************************************************/
|
||||
|
||||
export default ['$state', '$stateParams', '$scope', 'ToggleNotification', 'ParseVariableString',
|
||||
'ParseTypeChange', 'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'groupData', 'inventorySourceData',
|
||||
function($state, $stateParams, $scope, ToggleNotification, ParseVariableString,
|
||||
ParseTypeChange, GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, groupData, inventorySourceData) {
|
||||
'ParseTypeChange', 'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'groupData', 'inventorySourceData', 'ToJSON',
|
||||
function($state, $stateParams, $scope, ToggleNotification, ParseVariableString,
|
||||
ParseTypeChange, GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, groupData, inventorySourceData, ToJSON) {
|
||||
|
||||
init();
|
||||
|
||||
@ -58,9 +58,10 @@ export default ['$state', '$stateParams', '$scope', 'ToggleNotification', 'Parse
|
||||
};
|
||||
$scope.formSave = function() {
|
||||
var params, source;
|
||||
json_data = ToJSON($scope.parseType, $scope.variables, true);
|
||||
// group fields
|
||||
var group = {
|
||||
variables: $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
|
||||
variables: json_data,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
inventory: $scope.inventory,
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
*************************************************/
|
||||
|
||||
export default ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange',
|
||||
'GenerateForm', 'HostManageService', 'rbacUiControlService', 'GetBasePath',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange,
|
||||
GenerateForm, HostManageService, rbacUiControlService, GetBasePath) {
|
||||
'GenerateForm', 'HostManageService', 'rbacUiControlService', 'GetBasePath', 'ToJSON',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange,
|
||||
GenerateForm, HostManageService, rbacUiControlService, GetBasePath, ToJSON) {
|
||||
|
||||
init();
|
||||
|
||||
@ -36,9 +36,10 @@ export default ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange
|
||||
$scope.toggleHostEnabled = function() {
|
||||
$scope.host.enabled = !$scope.host.enabled;
|
||||
};
|
||||
$scope.formSave = function() {
|
||||
var params = {
|
||||
variables: $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
|
||||
$scope.formSave = function(){
|
||||
var json_data = ToJSON($scope.parseType, $scope.variables, true),
|
||||
params = {
|
||||
variables: json_data,// $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
enabled: $scope.host.enabled,
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'HostManageService', 'host',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, HostManageService, host){
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'HostManageService', 'host', 'ToJSON',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, HostManageService, host, ToJSON){
|
||||
|
||||
init();
|
||||
|
||||
@ -34,9 +34,10 @@
|
||||
$scope.host.enabled = !$scope.host.enabled;
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
var host = {
|
||||
var json_data = ToJSON($scope.parseType, $scope.variables, true),
|
||||
host = {
|
||||
id: $scope.host.id,
|
||||
variables: $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
|
||||
variables: json_data,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
enabled: $scope.host.enabled
|
||||
|
||||
@ -125,6 +125,8 @@
|
||||
.OnePlusTwo-left--detailsRow;
|
||||
}
|
||||
.HostEvent-field--content{
|
||||
word-wrap: break-word;
|
||||
max-width: 13em;
|
||||
flex: 0 1 13em;
|
||||
}
|
||||
.HostEvent-details--left, .HostEvent-details--right{
|
||||
@ -138,6 +140,7 @@
|
||||
flex: 0 1 25em;
|
||||
}
|
||||
.HostEvent-field--content{
|
||||
max-width: 15em;
|
||||
flex: 0 1 15em;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
@ -130,12 +130,20 @@ export default
|
||||
goToJobDetails('managementJobStdout');
|
||||
}
|
||||
else if(_.has(data, 'project_update')) {
|
||||
if($state.current.name !== 'projects') {
|
||||
// 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')) {
|
||||
goToJobDetails('scmUpdateStdout');
|
||||
}
|
||||
}
|
||||
else if(_.has(data, 'inventory_update')) {
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
// If we are on the inventory manage page or any child state of that
|
||||
// page then we want to stay on that page. Otherwise go to the stdout
|
||||
// view.
|
||||
if(!$state.includes('inventoryManage')) {
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
}
|
||||
}
|
||||
}
|
||||
if(scope.clearDialog) {
|
||||
|
||||
@ -5,39 +5,56 @@
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$rootScope', 'Rest', 'ProcessErrors', 'GetBasePath', 'moment',
|
||||
function($rootScope, Rest, ProcessErrors, GetBasePath, moment){
|
||||
return {
|
||||
get: function(id){
|
||||
var defaultUrl = GetBasePath('job_templates') + '?id=' + id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
return Rest.get()
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(res, status){
|
||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
||||
});
|
||||
},
|
||||
set: function(data){
|
||||
var defaultUrl = GetBasePath('job_templates');
|
||||
Rest.setUrl(defaultUrl);
|
||||
var name = this.buildName(data.results[0].name);
|
||||
data.results[0].name = name + ' @ ' + moment().format('h:mm:ss a'); // 2:49:11 pm
|
||||
return Rest.post(data.results[0])
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(res, status){
|
||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
||||
});
|
||||
},
|
||||
buildName: function(name){
|
||||
var result = name.split('@')[0];
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
['$rootScope', 'Rest', 'ProcessErrors', 'GetBasePath', 'moment',
|
||||
function($rootScope, Rest, ProcessErrors, GetBasePath, moment){
|
||||
return {
|
||||
get: function(id){
|
||||
var defaultUrl = GetBasePath('job_templates') + '?id=' + id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
return Rest.get()
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(res, status){
|
||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
||||
});
|
||||
},
|
||||
getSurvey: function(endpoint){
|
||||
Rest.setUrl(endpoint);
|
||||
return Rest.get();
|
||||
},
|
||||
copySurvey: function(source, target){
|
||||
return this.getSurvey(source.related.survey_spec).success( (data) => {
|
||||
Rest.setUrl(target.related.survey_spec);
|
||||
return Rest.post(data);
|
||||
});
|
||||
},
|
||||
set: function(data){
|
||||
var defaultUrl = GetBasePath('job_templates');
|
||||
var self = this;
|
||||
Rest.setUrl(defaultUrl);
|
||||
var name = this.buildName(data.results[0].name);
|
||||
data.results[0].name = name + ' @ ' + moment().format('h:mm:ss a'); // 2:49:11 pm
|
||||
return Rest.post(data.results[0])
|
||||
.success(function(job_template_res){
|
||||
// also copy any associated survey_spec
|
||||
if (data.results[0].related.survey_spec){
|
||||
return self.copySurvey(data.results[0], job_template_res).success( () => job_template_res);
|
||||
}
|
||||
else{
|
||||
return job_template_res;
|
||||
}
|
||||
})
|
||||
.error(function(res, status){
|
||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
||||
});
|
||||
},
|
||||
buildName: function(name){
|
||||
var result = name.split('@')[0];
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
@ -13,6 +13,7 @@ export default
|
||||
return {
|
||||
setPendoOptions: function (config) {
|
||||
var tower_version = config.version.split('-')[0],
|
||||
trial = (config.trial) ? config.trial : false,
|
||||
options = {
|
||||
visitor: {
|
||||
id: null,
|
||||
@ -24,7 +25,7 @@ export default
|
||||
planLevel: config.license_type,
|
||||
planPrice: config.instance_count,
|
||||
creationDate: config.license_date,
|
||||
trial: config.trial,
|
||||
trial: trial,
|
||||
tower_version: tower_version,
|
||||
ansible_version: config.ansible_version
|
||||
}
|
||||
@ -92,49 +93,18 @@ export default
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
getConfig: function () {
|
||||
var config = ConfigService.get(),
|
||||
deferred = $q.defer();
|
||||
if(_.isEmpty(config)){
|
||||
var url = GetBasePath('config');
|
||||
Rest.setUrl(url);
|
||||
var promise = Rest.get();
|
||||
promise.then(function (response) {
|
||||
config = response.data.license_info;
|
||||
config.analytics_status = response.data.analytics_status;
|
||||
config.version = response.data.version;
|
||||
config.ansible_version = response.data.ansible_version;
|
||||
if(config.analytics_status === 'detailed' || config.analytics_status === 'anonymous'){
|
||||
$pendolytics.bootstrap();
|
||||
deferred.resolve(config);
|
||||
}
|
||||
else {
|
||||
deferred.reject('Pendo is turned off.');
|
||||
}
|
||||
});
|
||||
promise.catch(function (response) {
|
||||
ProcessErrors($rootScope, response.data, response.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get inventory name. GET returned status: ' +
|
||||
response.status });
|
||||
deferred.reject('Could not resolve pendo config.');
|
||||
});
|
||||
}
|
||||
else if(config.analytics_status === 'detailed' || config.analytics_status === 'anonymous'){
|
||||
$pendolytics.bootstrap();
|
||||
deferred.resolve(config);
|
||||
}
|
||||
else {
|
||||
deferred.reject('Pendo is turned off.');
|
||||
}
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
issuePendoIdentity: function () {
|
||||
var that = this;
|
||||
this.getConfig().then(function(config){
|
||||
var options = that.setPendoOptions(config);
|
||||
that.setRole(options).then(function(options){
|
||||
var config,
|
||||
options,
|
||||
c = ConfigService.get(),
|
||||
config = c.license_info;
|
||||
config.analytics_status = c.analytics_status;
|
||||
config.version = c.version;
|
||||
config.ansible_version = c.ansible_version;
|
||||
if(config.analytics_status === 'detailed' || config.analytics_status === 'anonymous'){
|
||||
$pendolytics.bootstrap();
|
||||
options = this.setPendoOptions(config);
|
||||
this.setRole(options).then(function(options){
|
||||
$log.debug('Pendo status is '+ config.analytics_status + '. Object below:');
|
||||
$log.debug(options);
|
||||
$pendolytics.identify(options);
|
||||
@ -142,10 +112,10 @@ export default
|
||||
// reject function for setRole
|
||||
$log.debug(reason);
|
||||
});
|
||||
}, function(reason){
|
||||
// reject function for getConfig
|
||||
$log.debug(reason);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$log.debug('Pendo is turned off.')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -44,7 +44,6 @@ export default [{
|
||||
value: {
|
||||
order_by: 'username'
|
||||
}
|
||||
},
|
||||
add_user_search: {
|
||||
value: {
|
||||
order_by: 'username',
|
||||
@ -54,17 +53,15 @@ export default [{
|
||||
squash: true
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "USERS"
|
||||
},
|
||||
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'organization'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
label: "USERS"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
@ -129,10 +126,7 @@ export default [{
|
||||
activityStreamTarget: 'organization'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
parent: "organizations.edit",
|
||||
label: "TEAMS"
|
||||
},
|
||||
resolve: {
|
||||
@ -178,10 +172,7 @@ export default [{
|
||||
activityStreamTarget: 'organization'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
parent: "organizations.edit",
|
||||
label: "INVENTORIES"
|
||||
},
|
||||
resolve: {
|
||||
@ -232,10 +223,7 @@ export default [{
|
||||
},
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
parent: "organizations.edit",
|
||||
label: "PROJECTS"
|
||||
},
|
||||
resolve: {
|
||||
@ -293,10 +281,7 @@ export default [{
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
parent: "organizations.edit",
|
||||
label: "JOB TEMPLATES"
|
||||
},
|
||||
resolve: {
|
||||
@ -367,10 +352,7 @@ export default [{
|
||||
activityStreamTarget: 'organization'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: function($scope) {
|
||||
$scope.$parent.$emit("ReloadOrgListView");
|
||||
return "organizations.edit";
|
||||
},
|
||||
parent: "organizations.edit",
|
||||
label: "ADMINS"
|
||||
},
|
||||
resolve: {
|
||||
|
||||
@ -12,7 +12,6 @@ export default ['$stateParams', '$scope', '$rootScope', '$location',
|
||||
$log, $compile, Rest, OrganizationList, Alert, Prompt, ClearScope,
|
||||
ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset) {
|
||||
|
||||
|
||||
ClearScope();
|
||||
|
||||
var defaultUrl = GetBasePath('organizations'),
|
||||
@ -85,6 +84,27 @@ export default ['$stateParams', '$scope', '$rootScope', '$location',
|
||||
});
|
||||
return val;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("ReloadOrgListView", function() {
|
||||
Rest.setUrl($scope.current_url);
|
||||
Rest.get()
|
||||
.success((data) => $scope.organizations = data.results)
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + defaultUrl + ' failed. DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$scope.$watchCollection('organizations', function(value){
|
||||
$scope.orgCards = parseCardData(value);
|
||||
});
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
|
||||
$scope.$watchCollection(`${list.iterator}_dataset`, function(data) {
|
||||
@ -129,5 +149,50 @@ export default ['$stateParams', '$scope', '$rootScope', '$location',
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
};
|
||||
var init = function(){
|
||||
// Pagination depends on html appended by list generator
|
||||
view.inject(list, {
|
||||
id: 'organizations-list',
|
||||
scope: $scope,
|
||||
mode: 'edit'
|
||||
});
|
||||
// grab the pagination elements, move, destroy list generator elements
|
||||
$('#organization-pagination').appendTo('#OrgCards');
|
||||
$('#organizations tag-search').appendTo('.OrgCards-search');
|
||||
$('#organizations-list').remove();
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl,
|
||||
set: 'organizations'
|
||||
});
|
||||
|
||||
$scope.list = list;
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
$scope.search(list.iterator);
|
||||
var getOrgCount = function() {
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.orgCount = data.count;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + defaultUrl + ' failed. DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
getOrgCount();
|
||||
};
|
||||
init();
|
||||
}
|
||||
];
|
||||
|
||||
@ -2,11 +2,11 @@ git+https://github.com/chrismeyersfsu/ansiconv.git@tower_1.0.0#egg=ansiconv
|
||||
amqp==1.4.9
|
||||
anyjson==0.3.3
|
||||
appdirs==1.4.0
|
||||
azure==2.0.0rc2
|
||||
azure==2.0.0rc5
|
||||
Babel==2.2.0
|
||||
baron==0.6.2
|
||||
billiard==3.3.0.16
|
||||
boto==2.40.0
|
||||
boto==2.43.0
|
||||
celery==3.1.23
|
||||
cliff==1.15.0
|
||||
cmd2==0.6.8
|
||||
@ -116,7 +116,7 @@ rax-default-network-flags-python-novaclient-ext==0.3.2
|
||||
rax-scheduled-images-python-novaclient-ext==0.3.1
|
||||
redbaron==0.6.1
|
||||
requests-oauthlib==0.5.0
|
||||
requests==2.9.1
|
||||
requests==2.11.0
|
||||
requestsexceptions==1.1.1
|
||||
rply==0.7.4
|
||||
shade==1.4.0
|
||||
|
||||
@ -3,7 +3,7 @@ apache-libcloud==0.20.1
|
||||
appdirs==1.4.0
|
||||
azure==2.0.0rc5
|
||||
Babel==2.2.0
|
||||
boto==2.40.0
|
||||
boto==2.43.0
|
||||
cliff==1.15.0
|
||||
cmd2==0.6.8
|
||||
cryptography==1.3.2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user