move code linting to a stricter pep8-esque auto-formatting tool, black

This commit is contained in:
Ryan Petrello
2021-03-19 12:44:51 -04:00
parent 9b702e46fe
commit c2ef0a6500
671 changed files with 20538 additions and 21924 deletions

View File

@@ -1,2 +1,2 @@
from .pages import * # NOQA
from .client import * # NOQA
from .pages import * # NOQA
from .client import * # NOQA

View File

@@ -49,8 +49,7 @@ class Connection(object):
_next = kwargs.get('next')
if _next:
headers = self.session.headers.copy()
self.post('/api/login/', headers=headers,
data=dict(username=username, password=password, next=_next))
self.post('/api/login/', headers=headers, data=dict(username=username, password=password, next=_next))
self.session_id = self.session.cookies.get('sessionid')
self.uses_session_cookie = True
else:
@@ -79,8 +78,7 @@ class Connection(object):
use_endpoint = use_endpoint[1:]
url = '/'.join([self.server, use_endpoint])
kwargs = dict(verify=self.verify, params=query_parameters, json=json, data=data,
hooks=dict(response=log_elapsed))
kwargs = dict(verify=self.verify, params=query_parameters, json=json, data=data, hooks=dict(response=log_elapsed))
if headers is not None:
kwargs['headers'] = headers

View File

@@ -3,7 +3,6 @@ from awxkit.utils import random_title
class HasCopy(object):
def can_copy(self):
return self.get_related('copy').can_copy

View File

@@ -24,7 +24,7 @@ def dependency_graph(page, *provided_dependencies):
return graph
def optional_dependency_graph(page, *provided_dependencies):
def optional_dependency_graph(page, *provided_dependencies):
"""Creates a dependency graph for a page including all dependencies and optional_dependencies
Any optional provided_dependencies will be included as if they were dependencies,
without affecting the value of each keyed page.
@@ -104,8 +104,7 @@ def all_instantiated_dependencies(*potential_parents):
"""
scope_provided_dependencies = []
instantiated = set([x for x in potential_parents
if not isinstance(x, type) and not isinstance(x, tuple)])
instantiated = set([x for x in potential_parents if not isinstance(x, type) and not isinstance(x, tuple)])
for potential_parent in [x for x in instantiated if hasattr(x, '_dependency_store')]:
for dependency in potential_parent._dependency_store.values():
@@ -178,7 +177,6 @@ class DSAdapter(object):
# Hijack json.dumps and simplejson.dumps (used by requests)
# to allow HasCreate.create_payload() serialization without impacting payload.ds access
def filter_ds_from_payload(dumps):
def _filter_ds_from_payload(obj, *a, **kw):
if hasattr(obj, 'get') and isinstance(obj.get('ds'), DSAdapter):
filtered = obj.copy()
@@ -191,10 +189,12 @@ def filter_ds_from_payload(dumps):
import json # noqa
json.dumps = filter_ds_from_payload(json.dumps)
try:
import simplejson # noqa
simplejson.dumps = filter_ds_from_payload(simplejson.dumps)
except ImportError:
pass
@@ -299,8 +299,7 @@ class HasCreate(object):
# remove falsy values
provided_and_desired_dependencies = [x for x in provided_and_desired_dependencies if x]
# (HasCreate(), True) tells HasCreate._update_dependencies to link
provided_dependencies = [(x, True) for x in provided_and_desired_dependencies
if not isinstance(x, type) and not isinstance(x, tuple)]
provided_dependencies = [(x, True) for x in provided_and_desired_dependencies if not isinstance(x, type) and not isinstance(x, tuple)]
# Since dependencies are often declared at runtime, we need to use some introspection
# to determine previously created ones for proper dependency store linking.
@@ -374,12 +373,7 @@ class HasCreate(object):
to_teardown = all_instantiated_dependencies(self)
to_teardown_types = set(map(get_class_if_instance, to_teardown))
order = [
set(
[
potential for potential in (
get_class_if_instance(x) for x in group) if potential in to_teardown_types
]
)
set([potential for potential in (get_class_if_instance(x) for x in group) if potential in to_teardown_types])
for group in page_creation_order(self, *to_teardown)
]
order.reverse()

View File

@@ -3,7 +3,6 @@ import awxkit.exceptions as exc
class HasInstanceGroups(object):
def add_instance_group(self, instance_group):
with suppress(exc.NoContent):
self.related['instance_groups'].post(dict(id=instance_group.id))

View File

@@ -2,29 +2,25 @@ from awxkit.utils import suppress
import awxkit.exceptions as exc
notification_endpoints = ("notification_templates", "notification_templates_started", "notification_templates_error",
"notification_templates_success")
notification_endpoints = ("notification_templates", "notification_templates_started", "notification_templates_error", "notification_templates_success")
wfjt_notification_endpoints = notification_endpoints + ('notification_templates_approvals',)
class HasNotifications(object):
def add_notification_template(self, notification_template, endpoint="notification_templates_success"):
from awxkit.api.pages.workflow_job_templates import WorkflowJobTemplate
supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) \
else notification_endpoints
supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) else notification_endpoints
if endpoint not in supported_endpoints:
raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'
.format(endpoint, notification_endpoints))
raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'.format(endpoint, notification_endpoints))
with suppress(exc.NoContent):
self.related[endpoint].post(dict(id=notification_template.id))
def remove_notification_template(self, notification_template, endpoint="notification_templates_success"):
from awxkit.api.pages.workflow_job_templates import WorkflowJobTemplate
supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) \
else notification_endpoints
supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) else notification_endpoints
if endpoint not in supported_endpoints:
raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'
.format(endpoint, notification_endpoints))
raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'.format(endpoint, notification_endpoints))
with suppress(exc.NoContent):
self.related[endpoint].post(dict(id=notification_template.id, disassociate=notification_template.id))

View File

@@ -40,8 +40,7 @@ class HasStatus(object):
if not getattr(self, 'event_processing_finished', True):
elapsed = datetime.utcnow() - start_time
time_left = timeout - elapsed.total_seconds()
poll_until(lambda: getattr(self.get(), 'event_processing_finished', True),
interval=interval, timeout=time_left, **kwargs)
poll_until(lambda: getattr(self.get(), 'event_processing_finished', True), interval=interval, timeout=time_left, **kwargs)
return self
def wait_until_started(self, interval=1, timeout=60):
@@ -65,9 +64,7 @@ class HasStatus(object):
msg = ''
else:
msg += '\n'
msg += '{0}-{1} has status of {2}, which is not in {3}.'.format(
self.type.title(), self.id, self.status, status_list
)
msg += '{0}-{1} has status of {2}, which is not in {3}.'.format(self.type.title(), self.id, self.status, status_list)
if getattr(self, 'job_explanation', ''):
msg += '\njob_explanation: {}'.format(bytes_to_str(self.job_explanation))
if getattr(self, 'result_traceback', ''):
@@ -79,10 +76,8 @@ class HasStatus(object):
try:
data = json.loads(self.job_explanation.replace('Previous Task Failed: ', ''))
dep_output = self.connection.get(
'{0}/api/v2/{1}s/{2}/stdout/'.format(
self.endpoint.split('/api')[0], data['job_type'], data['job_id']
),
query_parameters=dict(format='txt_download')
'{0}/api/v2/{1}s/{2}/stdout/'.format(self.endpoint.split('/api')[0], data['job_type'], data['job_id']),
query_parameters=dict(format='txt_download'),
).content
msg += '\nDependency output:\n{}'.format(bytes_to_str(dep_output))
except Exception as e:

View File

@@ -3,13 +3,11 @@ from awxkit.utils import random_title
class HasSurvey(object):
def add_survey(self, spec=None, name=None, description=None, required=False, enabled=True):
payload = dict(name=name or 'Survey - {}'.format(random_title()),
description=description or random_title(10),
spec=spec or [dict(required=required,
question_name="What's the password?",
variable="secret",
type="password",
default="foo")])
payload = dict(
name=name or 'Survey - {}'.format(random_title()),
description=description or random_title(10),
spec=spec or [dict(required=required, question_name="What's the password?", variable="secret", type="password", default="foo")],
)
if enabled != self.survey_enabled:
self.patch(survey_enabled=enabled)
return self.related.survey_spec.post(payload).get()

View File

@@ -4,7 +4,6 @@ from awxkit.utils import PseudoNamespace
class HasVariables(object):
@property
def variables(self):
return PseudoNamespace(yaml.safe_load(self.json.variables))

View File

@@ -33,7 +33,7 @@ from .workflow_job_templates import * # NOQA
from .workflow_job_template_nodes import * # NOQA
from .workflow_jobs import * # NOQA
from .workflow_job_nodes import * # NOQA
from .workflow_approvals import * # NOQA
from .workflow_approvals import * # NOQA
from .settings import * # NOQA
from .instances import * # NOQA
from .instance_groups import * # NOQA

View File

@@ -8,11 +8,16 @@ class AccessList(page.PageList, users.User):
pass
page.register_page([resources.organization_access_list,
resources.user_access_list,
resources.inventory_access_list,
resources.group_access_list,
resources.credential_access_list,
resources.project_access_list,
resources.job_template_access_list,
resources.team_access_list], AccessList)
page.register_page(
[
resources.organization_access_list,
resources.user_access_list,
resources.inventory_access_list,
resources.group_access_list,
resources.credential_access_list,
resources.project_access_list,
resources.job_template_access_list,
resources.team_access_list,
],
AccessList,
)

View File

@@ -16,5 +16,4 @@ class ActivityStreams(page.PageList, ActivityStream):
pass
page.register_page([resources.activity_stream,
resources.object_activity_stream], ActivityStreams)
page.register_page([resources.activity_stream, resources.object_activity_stream], ActivityStreams)

View File

@@ -24,31 +24,40 @@ class AdHocCommand(HasCreate, UnifiedJob):
return self.walk(result.url)
def payload(self, inventory, credential, module_name='ping', **kwargs):
payload = PseudoNamespace(inventory=inventory.id,
credential=credential.id,
module_name=module_name)
payload = PseudoNamespace(inventory=inventory.id, credential=credential.id, module_name=module_name)
optional_fields = ('diff_mode', 'extra_vars', 'module_args', 'job_type', 'limit', 'forks',
'verbosity')
optional_fields = ('diff_mode', 'extra_vars', 'module_args', 'job_type', 'limit', 'forks', 'verbosity')
return update_payload(payload, optional_fields, kwargs)
def create_payload(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np,
inventory=Inventory, credential=Credential, **kwargs):
def create_payload(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np, inventory=Inventory, credential=Credential, **kwargs):
self.create_and_update_dependencies(inventory, credential)
payload = self.payload(module_name=module_name, module_args=module_args, job_type=job_type, limit=limit,
verbosity=verbosity, inventory=self.ds.inventory, credential=self.ds.credential,
**kwargs)
payload = self.payload(
module_name=module_name,
module_args=module_args,
job_type=job_type,
limit=limit,
verbosity=verbosity,
inventory=self.ds.inventory,
credential=self.ds.credential,
**kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np,
inventory=Inventory, credential=Credential, **kwargs):
def create(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np, inventory=Inventory, credential=Credential, **kwargs):
payload = self.create_payload(module_name=module_name, module_args=module_args,
job_type=job_type, limit=limit, verbosity=verbosity,
inventory=inventory, credential=credential, **kwargs)
payload = self.create_payload(
module_name=module_name,
module_args=module_args,
job_type=job_type,
limit=limit,
verbosity=verbosity,
inventory=inventory,
credential=credential,
**kwargs
)
return self.update_identity(AdHocCommands(self.connection).post(payload))
@@ -60,7 +69,7 @@ class AdHocCommands(page.PageList, AdHocCommand):
pass
page.register_page([resources.ad_hoc_commands,
resources.inventory_related_ad_hoc_commands,
resources.group_related_ad_hoc_commands,
resources.host_related_ad_hoc_commands], AdHocCommands)
page.register_page(
[resources.ad_hoc_commands, resources.inventory_related_ad_hoc_commands, resources.group_related_ad_hoc_commands, resources.host_related_ad_hoc_commands],
AdHocCommands,
)

View File

@@ -90,18 +90,14 @@ class ApiV2(base.Base):
return None
# Note: doing _page[key] automatically parses json blob strings, which can be a problem.
fields = {
key: _page.json[key] for key in post_fields
if key in _page.json and key not in _page.related and key != 'id'
}
fields = {key: _page.json[key] for key in post_fields if key in _page.json and key not in _page.related and key != 'id'}
for key in post_fields:
if key in _page.related:
related = _page.related[key]
else:
if post_fields[key]['type'] == 'id' and _page.json.get(key) is not None:
log.warning("Related link %r missing from %s, attempting to reconstruct endpoint.",
key, _page.endpoint)
log.warning("Related link %r missing from %s, attempting to reconstruct endpoint.", key, _page.endpoint)
resource = getattr(self, key, None)
if resource is None:
log.error("Unable to infer endpoint for %r on %s.", key, _page.endpoint)
@@ -119,8 +115,7 @@ class ApiV2(base.Base):
continue
rel_natural_key = rel_endpoint.get_natural_key(self._cache)
if rel_natural_key is None:
log.error("Unable to construct a natural key for foreign key %r of object %s.",
key, _page.endpoint)
log.error("Unable to construct a natural key for foreign key %r of object %s.", key, _page.endpoint)
return None # This foreign key has unresolvable dependencies
fields[key] = rel_natural_key
@@ -154,10 +149,7 @@ class ApiV2(base.Base):
continue
if 'results' in rel_page:
results = (
x.get_natural_key(self._cache) if by_natural_key else self._export(x, rel_post_fields)
for x in rel_page.results
)
results = (x.get_natural_key(self._cache) if by_natural_key else self._export(x, rel_post_fields) for x in rel_page.results)
related[key] = [x for x in results if x is not None]
else:
related[key] = rel_page.json
@@ -190,8 +182,7 @@ class ApiV2(base.Base):
if isinstance(value, int) or value.isdecimal():
return endpoint.get(id=int(value))
options = self._cache.get_options(endpoint)
identifier = next(field for field in options['search_fields']
if field in ('name', 'username', 'hostname'))
identifier = next(field for field in options['search_fields'] if field in ('name', 'username', 'hostname'))
return endpoint.get(**{identifier: value})
def export_assets(self, **kwargs):
@@ -214,8 +205,7 @@ class ApiV2(base.Base):
# Import methods
def _dependent_resources(self, data):
page_resource = {getattr(self, resource)._create().__item_class__: resource
for resource in self.json}
page_resource = {getattr(self, resource)._create().__item_class__: resource for resource in self.json}
data_pages = [getattr(self, resource)._create().__item_class__ for resource in EXPORTABLE_RESOURCES]
for page_cls in itertools.chain(*has_create.page_creation_order(*data_pages)):

View File

@@ -12,10 +12,12 @@ class OAuth2Application(HasCreate, base.Base):
dependencies = [Organization]
def payload(self, **kwargs):
payload = PseudoNamespace(name=kwargs.get('name') or 'OAuth2Application - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
client_type=kwargs.get('client_type', 'public'),
authorization_grant_type=kwargs.get('authorization_grant_type', 'password'))
payload = PseudoNamespace(
name=kwargs.get('name') or 'OAuth2Application - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
client_type=kwargs.get('client_type', 'public'),
authorization_grant_type=kwargs.get('authorization_grant_type', 'password'),
)
if kwargs.get('organization'):
payload.organization = kwargs['organization'].id
@@ -35,8 +37,7 @@ class OAuth2Application(HasCreate, base.Base):
return self.update_identity(OAuth2Applications(self.connection).post(payload))
page.register_page((resources.application,
(resources.applications, 'post')), OAuth2Application)
page.register_page((resources.application, (resources.applications, 'post')), OAuth2Application)
class OAuth2Applications(page.PageList, OAuth2Application):
@@ -51,8 +52,7 @@ class OAuth2AccessToken(HasCreate, base.Base):
optional_dependencies = [OAuth2Application]
def payload(self, **kwargs):
payload = PseudoNamespace(description=kwargs.get('description') or random_title(10),
scope=kwargs.get('scope', 'write'))
payload = PseudoNamespace(description=kwargs.get('description') or random_title(10), scope=kwargs.get('scope', 'write'))
if kwargs.get('oauth_2_application'):
payload.application = kwargs['oauth_2_application'].id
@@ -73,8 +73,7 @@ class OAuth2AccessToken(HasCreate, base.Base):
return self.update_identity(OAuth2AccessTokens(self.connection).post(payload))
page.register_page((resources.token,
(resources.tokens, 'post')), OAuth2AccessToken)
page.register_page((resources.token, (resources.tokens, 'post')), OAuth2AccessToken)
class OAuth2AccessTokens(page.PageList, OAuth2AccessToken):

View File

@@ -3,11 +3,7 @@ import logging
from requests.auth import HTTPBasicAuth
from awxkit.api.pages import (
Page,
get_registered_page,
exception_from_status_code
)
from awxkit.api.pages import Page, get_registered_page, exception_from_status_code
from awxkit.config import config
from awxkit.api.resources import resources
import awxkit.exceptions as exc
@@ -17,7 +13,6 @@ log = logging.getLogger(__name__)
class Base(Page):
def silent_delete(self):
"""Delete the object. If it's already deleted, ignore the error"""
try:
@@ -129,14 +124,14 @@ class Base(Page):
@property
def object_roles(self):
from awxkit.api.pages import Roles, Role
url = self.get().json.related.object_roles
for obj_role in Roles(self.connection, endpoint=url).get().json.results:
yield Role(self.connection, endpoint=obj_role.url).get()
def get_authtoken(self, username='', password=''):
default_cred = config.credentials.default
payload = dict(username=username or default_cred.username,
password=password or default_cred.password)
payload = dict(username=username or default_cred.username, password=password or default_cred.password)
auth_url = resources.authtoken
return get_registered_page(auth_url)(self.connection, endpoint=auth_url).post(payload).token
@@ -146,9 +141,7 @@ class Base(Page):
load_default_authtoken = load_authtoken
def get_oauth2_token(self, username='', password='', client_id=None,
description='AWX CLI',
client_secret=None, scope='write'):
def get_oauth2_token(self, username='', password='', client_id=None, description='AWX CLI', client_secret=None, scope='write'):
default_cred = config.credentials.default
username = username or default_cred.username
password = password or default_cred.password
@@ -157,38 +150,21 @@ class Base(Page):
HTTPBasicAuth(client_id, client_secret)(req)
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
resp = self.connection.post(
'/api/o/token/',
data={
"grant_type": "password",
"username": username,
"password": password,
"scope": scope
},
headers=req.headers
'/api/o/token/', data={"grant_type": "password", "username": username, "password": password, "scope": scope}, headers=req.headers
)
elif client_id:
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
resp = self.connection.post(
'/api/o/token/',
data={
"grant_type": "password",
"username": username,
"password": password,
"client_id": client_id,
"scope": scope
},
headers=req.headers
data={"grant_type": "password", "username": username, "password": password, "client_id": client_id, "scope": scope},
headers=req.headers,
)
else:
HTTPBasicAuth(username, password)(req)
resp = self.connection.post(
'/api/v2/users/{}/personal_tokens/'.format(username),
json={
"description": description,
"application": None,
"scope": scope
},
headers=req.headers
json={"description": description, "application": None, "scope": scope},
headers=req.headers,
)
if resp.ok:
result = resp.json()
@@ -201,9 +177,9 @@ class Base(Page):
def load_session(self, username='', password=''):
default_cred = config.credentials.default
self.connection.login(username=username or default_cred.username,
password=password or default_cred.password,
**self.connection.get_session_requirements())
self.connection.login(
username=username or default_cred.username, password=password or default_cred.password, **self.connection.get_session_requirements()
)
return self
def cleanup(self):

View File

@@ -4,22 +4,17 @@ from . import page
class Config(base.Base):
@property
def is_aws_license(self):
return self.license_info.get('is_aws', False) or \
'ami-id' in self.license_info or \
'instance-id' in self.license_info
return self.license_info.get('is_aws', False) or 'ami-id' in self.license_info or 'instance-id' in self.license_info
@property
def is_valid_license(self):
return self.license_info.get('valid_key', False) and \
'instance_count' in self.license_info
return self.license_info.get('valid_key', False) and 'instance_count' in self.license_info
@property
def is_trial_license(self):
return self.is_valid_license and \
self.license_info.get('trial', False)
return self.is_valid_license and self.license_info.get('trial', False)
@property
def is_awx_license(self):
@@ -27,8 +22,7 @@ class Config(base.Base):
@property
def is_enterprise_license(self):
return self.is_valid_license and \
self.license_info.get('license_type', None) == 'enterprise'
return self.is_valid_license and self.license_info.get('license_type', None) == 'enterprise'
@property
def features(self):
@@ -37,7 +31,6 @@ class Config(base.Base):
class ConfigAttach(page.Page):
def attach(self, **kwargs):
return self.post(json=kwargs).json

View File

@@ -16,5 +16,4 @@ class CredentialInputSources(page.PageList, CredentialInputSource):
pass
page.register_page([resources.credential_input_sources,
resources.related_input_sources], CredentialInputSources)
page.register_page([resources.credential_input_sources, resources.related_input_sources], CredentialInputSources)

View File

@@ -44,7 +44,8 @@ credential_input_fields = (
'tenant',
'username',
'vault_password',
'vault_id')
'vault_id',
)
def generate_private_key():
@@ -52,15 +53,9 @@ def generate_private_key():
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
backend=default_backend()
)
key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend())
return key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()
).decode('utf-8')
@@ -98,11 +93,10 @@ credential_type_name_to_config_kind_map = {
'source control': 'scm',
'machine': 'ssh',
'vault': 'vault',
'vmware vcenter': 'vmware'}
'vmware vcenter': 'vmware',
}
config_kind_to_credential_type_name_map = {
kind: name
for name, kind in credential_type_name_to_config_kind_map.items()}
config_kind_to_credential_type_name_map = {kind: name for name, kind in credential_type_name_to_config_kind_map.items()}
def kind_and_config_cred_from_credential_type(credential_type):
@@ -115,8 +109,7 @@ def kind_and_config_cred_from_credential_type(credential_type):
config_cred = config.credentials.network
kind = 'net'
elif credential_type.kind == 'cloud':
kind = credential_type_name_to_config_kind_map[credential_type.name.lower(
)]
kind = credential_type_name_to_config_kind_map[credential_type.name.lower()]
config_kind = kind if kind != 'azure_rm' else 'azure'
config_cred = config.credentials.cloud[config_kind]
else:
@@ -127,11 +120,8 @@ def kind_and_config_cred_from_credential_type(credential_type):
return kind, PseudoNamespace()
def get_payload_field_and_value_from_kwargs_or_config_cred(
field, kind, kwargs, config_cred):
if field in (
'project_id',
'project_name'): # Needed to prevent Project kwarg collision
def get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, kwargs, config_cred):
if field in ('project_id', 'project_name'): # Needed to prevent Project kwarg collision
config_field = 'project'
elif field == 'subscription' and 'azure' in kind:
config_field = 'subscription_id'
@@ -159,10 +149,8 @@ class CredentialType(HasCreate, base.Base):
def payload(self, kind='cloud', **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'CredentialType - {}'.format(
random_title()),
description=kwargs.get('description') or random_title(10),
kind=kind)
name=kwargs.get('name') or 'CredentialType - {}'.format(random_title()), description=kwargs.get('description') or random_title(10), kind=kind
)
fields = ('inputs', 'injectors')
update_payload(payload, fields, kwargs)
return payload
@@ -174,17 +162,13 @@ class CredentialType(HasCreate, base.Base):
def create(self, kind='cloud', **kwargs):
payload = self.create_payload(kind=kind, **kwargs)
return self.update_identity(
CredentialTypes(
self.connection).post(payload))
return self.update_identity(CredentialTypes(self.connection).post(payload))
def test(self, data):
"""Test the credential type endpoint."""
response = self.connection.post(urljoin(str(self.url), 'test/'), data)
exception = exception_from_status_code(response.status_code)
exc_str = "%s (%s) received" % (
http.responses[response.status_code], response.status_code
)
exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
if exception:
raise exception(exc_str, response.json())
elif response.status_code == http.FORBIDDEN:
@@ -192,8 +176,7 @@ class CredentialType(HasCreate, base.Base):
return response
page.register_page([resources.credential_type,
(resources.credential_types, 'post')], CredentialType)
page.register_page([resources.credential_type, (resources.credential_types, 'post')], CredentialType)
class CredentialTypes(page.PageList, CredentialType):
@@ -210,27 +193,19 @@ class Credential(HasCopy, HasCreate, base.Base):
optional_dependencies = [Organization, User, Team]
NATURAL_KEY = ('organization', 'name', 'credential_type')
def payload(
self,
credential_type,
user=None,
team=None,
organization=None,
inputs=None,
**kwargs):
def payload(self, credential_type, user=None, team=None, organization=None, inputs=None, **kwargs):
if not any((user, team, organization)):
raise TypeError(
'{0.__class__.__name__} requires user, team, and/or organization instances.'.format(self))
raise TypeError('{0.__class__.__name__} requires user, team, and/or organization instances.'.format(self))
if inputs is None:
inputs = {}
payload = PseudoNamespace(
name=kwargs.get('name') or 'Credential - {}'.format(
random_title()),
name=kwargs.get('name') or 'Credential - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
credential_type=credential_type.id,
inputs=inputs)
inputs=inputs,
)
if user:
payload.user = user.id
if team:
@@ -238,38 +213,26 @@ class Credential(HasCopy, HasCreate, base.Base):
if organization:
payload.organization = organization.id
kind, config_cred = kind_and_config_cred_from_credential_type(
credential_type)
kind, config_cred = kind_and_config_cred_from_credential_type(credential_type)
for field in credential_input_fields:
field, value = get_payload_field_and_value_from_kwargs_or_config_cred(
field, kind, inputs or kwargs, config_cred)
field, value = get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, inputs or kwargs, config_cred)
if value != not_provided:
payload.inputs[field] = value
if kind == 'net':
payload.inputs.authorize = inputs.get(
'authorize', bool(inputs.get('authorize_password')))
payload.inputs.authorize = inputs.get('authorize', bool(inputs.get('authorize_password')))
if kind in ('ssh', 'net') and 'ssh_key_data' not in payload.inputs:
payload.inputs.ssh_key_data = inputs.get(
'ssh_key_data', generate_private_key())
payload.inputs.ssh_key_data = inputs.get('ssh_key_data', generate_private_key())
return payload
def create_payload(
self,
credential_type=CredentialType,
user=None,
team=None,
organization=Organization,
inputs=None,
**kwargs):
def create_payload(self, credential_type=CredentialType, user=None, team=None, organization=Organization, inputs=None, **kwargs):
if isinstance(credential_type, int):
# if an int was passed, it is assumed to be the pk id of a
# credential type
credential_type = CredentialTypes(
self.connection).get(id=credential_type).results.pop()
credential_type = CredentialTypes(self.connection).get(id=credential_type).results.pop()
if credential_type == CredentialType:
kind = kwargs.pop('kind', 'ssh')
@@ -282,57 +245,29 @@ class Credential(HasCopy, HasCreate, base.Base):
inputs = config.credentials.cloud['openstack']
else:
credential_type_name = config_kind_to_credential_type_name_map[kind]
credential_type = CredentialTypes(
self.connection).get(
managed_by_tower=True,
name__icontains=credential_type_name).results.pop()
credential_type = CredentialTypes(self.connection).get(managed_by_tower=True, name__icontains=credential_type_name).results.pop()
credential_type, organization, user, team = filter_by_class(
(credential_type, CredentialType), (organization, Organization), (user, User), (team, Team))
credential_type, organization, user, team = filter_by_class((credential_type, CredentialType), (organization, Organization), (user, User), (team, Team))
if not any((user, team, organization)):
organization = Organization
self.create_and_update_dependencies(
credential_type, organization, user, team)
self.create_and_update_dependencies(credential_type, organization, user, team)
user = self.ds.user if user else None
team = self.ds.team if team else None
organization = self.ds.organization if organization else None
payload = self.payload(
self.ds.credential_type,
user=user,
team=team,
organization=organization,
inputs=inputs,
**kwargs)
payload = self.payload(self.ds.credential_type, user=user, team=team, organization=organization, inputs=inputs, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
credential_type=CredentialType,
user=None,
team=None,
organization=None,
inputs=None,
**kwargs):
payload = self.create_payload(
credential_type=credential_type,
user=user,
team=team,
organization=organization,
inputs=inputs,
**kwargs)
return self.update_identity(
Credentials(
self.connection)).post(payload)
def create(self, credential_type=CredentialType, user=None, team=None, organization=None, inputs=None, **kwargs):
payload = self.create_payload(credential_type=credential_type, user=user, team=team, organization=organization, inputs=inputs, **kwargs)
return self.update_identity(Credentials(self.connection)).post(payload)
def test(self, data):
"""Test the credential endpoint."""
response = self.connection.post(urljoin(str(self.url), 'test/'), data)
exception = exception_from_status_code(response.status_code)
exc_str = "%s (%s) received" % (
http.responses[response.status_code], response.status_code
)
exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
if exception:
raise exception(exc_str, response.json())
elif response.status_code == http.FORBIDDEN:
@@ -343,11 +278,7 @@ class Credential(HasCopy, HasCreate, base.Base):
def expected_passwords_needed_to_start(self):
"""Return a list of expected passwords needed to start a job using this credential."""
passwords = []
for field in (
'password',
'become_password',
'ssh_key_unlock',
'vault_password'):
for field in ('password', 'become_password', 'ssh_key_unlock', 'vault_password'):
if getattr(self.inputs, field, None) == 'ASK':
if field == 'password':
passwords.append('ssh_password')
@@ -356,9 +287,7 @@ class Credential(HasCopy, HasCreate, base.Base):
return passwords
page.register_page([resources.credential,
(resources.credentials, 'post'),
(resources.credential_copy, 'post')], Credential)
page.register_page([resources.credential, (resources.credentials, 'post'), (resources.credential_copy, 'post')], Credential)
class Credentials(page.PageList, Credential):
@@ -366,9 +295,7 @@ class Credentials(page.PageList, Credential):
pass
page.register_page([resources.credentials,
resources.related_credentials],
Credentials)
page.register_page([resources.credentials, resources.related_credentials], Credentials)
class CredentialCopy(base.Base):

View File

@@ -46,14 +46,13 @@ class ExecutionEnvironment(HasCreate, HasCopy, base.Base):
return payload
page.register_page([resources.execution_environment,
(resources.execution_environments, 'post'),
(resources.organization_execution_environments, 'post')], ExecutionEnvironment)
page.register_page(
[resources.execution_environment, (resources.execution_environments, 'post'), (resources.organization_execution_environments, 'post')], ExecutionEnvironment
)
class ExecutionEnvironments(page.PageList, ExecutionEnvironment):
pass
page.register_page([resources.execution_environments,
resources.organization_execution_environments], ExecutionEnvironments)
page.register_page([resources.execution_environments, resources.organization_execution_environments], ExecutionEnvironments)

View File

@@ -7,7 +7,6 @@ from . import page
class InstanceGroup(HasCreate, base.Base):
def add_instance(self, instance):
with suppress(exc.NoContent):
self.related.instances.post(dict(id=instance.id))
@@ -17,8 +16,7 @@ class InstanceGroup(HasCreate, base.Base):
self.related.instances.post(dict(id=instance.id, disassociate=True))
def payload(self, **kwargs):
payload = PseudoNamespace(name=kwargs.get('name') or
'Instance Group - {}'.format(random_title()))
payload = PseudoNamespace(name=kwargs.get('name') or 'Instance Group - {}'.format(random_title()))
fields = ('policy_instance_percentage', 'policy_instance_minimum', 'policy_instance_list', 'is_container_group')
update_payload(payload, fields, kwargs)
@@ -35,8 +33,7 @@ class InstanceGroup(HasCreate, base.Base):
return self.update_identity(InstanceGroups(self.connection).post(payload))
page.register_page([resources.instance_group,
(resources.instance_groups, 'post')], InstanceGroup)
page.register_page([resources.instance_group, (resources.instance_groups, 'post')], InstanceGroup)
class InstanceGroups(page.PageList, InstanceGroup):
@@ -44,5 +41,4 @@ class InstanceGroups(page.PageList, InstanceGroup):
pass
page.register_page([resources.instance_groups,
resources.related_instance_groups], InstanceGroups)
page.register_page([resources.instance_groups, resources.related_instance_groups], InstanceGroups)

View File

@@ -16,5 +16,4 @@ class Instances(page.PageList, Instance):
pass
page.register_page([resources.instances,
resources.related_instances], Instances)
page.register_page([resources.instances, resources.related_instances], Instances)

View File

@@ -2,23 +2,8 @@ import logging
import json
import re
from awxkit.api.pages import (
Credential,
Organization,
Project,
UnifiedJob,
UnifiedJobTemplate
)
from awxkit.utils import (
filter_by_class,
random_title,
update_payload,
suppress,
not_provided,
PseudoNamespace,
poll_until,
random_utf8
)
from awxkit.api.pages import Credential, Organization, Project, UnifiedJob, UnifiedJobTemplate
from awxkit.utils import filter_by_class, random_title, update_payload, suppress, not_provided, PseudoNamespace, poll_until, random_utf8
from awxkit.api.mixins import DSAdapter, HasCreate, HasInstanceGroups, HasNotifications, HasVariables, HasCopy
from awxkit.api.resources import resources
import awxkit.exceptions as exc
@@ -68,56 +53,31 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Inventory - {}'.format(
random_title()),
name=kwargs.get('name') or 'Inventory - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id)
organization=organization.id,
)
optional_fields = (
'host_filter',
'insights_credential',
'kind',
'variables')
optional_fields = ('host_filter', 'insights_credential', 'kind', 'variables')
update_payload(payload, optional_fields, kwargs)
if 'variables' in payload and isinstance(payload.variables, dict):
payload.variables = json.dumps(payload.variables)
if 'insights_credential' in payload and isinstance(
payload.insights_credential, Credential):
if 'insights_credential' in payload and isinstance(payload.insights_credential, Credential):
payload.insights_credential = payload.insights_credential.id
return payload
def create_payload(
self,
name='',
description='',
organization=Organization,
**kwargs):
def create_payload(self, name='', description='', organization=Organization, **kwargs):
self.create_and_update_dependencies(organization)
payload = self.payload(
name=name,
description=description,
organization=self.ds.organization,
**kwargs)
payload = self.payload(name=name, description=description, organization=self.ds.organization, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
organization=Organization,
**kwargs):
payload = self.create_payload(
name=name,
description=description,
organization=organization,
**kwargs)
return self.update_identity(
Inventories(
self.connection).post(payload))
def create(self, name='', description='', organization=Organization, **kwargs):
payload = self.create_payload(name=name, description=description, organization=organization, **kwargs)
return self.update_identity(Inventories(self.connection).post(payload))
def add_host(self, host=None):
if host is None:
@@ -135,17 +95,16 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
self.get()
except exc.NotFound:
return True
poll_until(_wait, interval=1, timeout=60)
def update_inventory_sources(self, wait=False):
response = self.related.update_inventory_sources.post()
source_ids = [entry['inventory_source']
for entry in response if entry['status'] == 'started']
source_ids = [entry['inventory_source'] for entry in response if entry['status'] == 'started']
inv_updates = []
for source_id in source_ids:
inv_source = self.related.inventory_sources.get(
id=source_id).results.pop()
inv_source = self.related.inventory_sources.get(id=source_id).results.pop()
inv_updates.append(inv_source.related.current_job.get())
if wait:
@@ -154,9 +113,7 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
return inv_updates
page.register_page([resources.inventory,
(resources.inventories, 'post'),
(resources.inventory_copy, 'post')], Inventory)
page.register_page([resources.inventory, (resources.inventories, 'post'), (resources.inventory_copy, 'post')], Inventory)
class Inventories(page.PageList, Inventory):
@@ -164,8 +121,7 @@ class Inventories(page.PageList, Inventory):
pass
page.register_page([resources.inventories,
resources.related_inventories], Inventories)
page.register_page([resources.inventories, resources.related_inventories], Inventories)
class InventoryScript(HasCopy, HasCreate, base.Base):
@@ -174,77 +130,48 @@ class InventoryScript(HasCopy, HasCreate, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Inventory Script - {}'.format(
random_title()),
name=kwargs.get('name') or 'Inventory Script - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
script=kwargs.get('script') or self._generate_script())
script=kwargs.get('script') or self._generate_script(),
)
return payload
def create_payload(
self,
name='',
description='',
organization=Organization,
script='',
**kwargs):
def create_payload(self, name='', description='', organization=Organization, script='', **kwargs):
self.create_and_update_dependencies(organization)
payload = self.payload(
name=name,
description=description,
organization=self.ds.organization,
script=script,
**kwargs)
payload = self.payload(name=name, description=description, organization=self.ds.organization, script=script, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
organization=Organization,
script='',
**kwargs):
payload = self.create_payload(
name=name,
description=description,
organization=organization,
script=script,
**kwargs)
return self.update_identity(
InventoryScripts(
self.connection).post(payload))
def create(self, name='', description='', organization=Organization, script='', **kwargs):
payload = self.create_payload(name=name, description=description, organization=organization, script=script, **kwargs)
return self.update_identity(InventoryScripts(self.connection).post(payload))
def _generate_script(self):
script = '\n'.join([
'#!/usr/bin/env python',
'# -*- coding: utf-8 -*-',
'import json',
'inventory = dict()',
'inventory["{0}"] = dict()',
'inventory["{0}"]["hosts"] = list()',
'inventory["{0}"]["hosts"].append("{1}")',
'inventory["{0}"]["hosts"].append("{2}")',
'inventory["{0}"]["hosts"].append("{3}")',
'inventory["{0}"]["hosts"].append("{4}")',
'inventory["{0}"]["hosts"].append("{5}")',
'inventory["{0}"]["vars"] = dict(ansible_host="127.0.0.1", ansible_connection="local")',
'print(json.dumps(inventory))'
])
script = '\n'.join(
[
'#!/usr/bin/env python',
'# -*- coding: utf-8 -*-',
'import json',
'inventory = dict()',
'inventory["{0}"] = dict()',
'inventory["{0}"]["hosts"] = list()',
'inventory["{0}"]["hosts"].append("{1}")',
'inventory["{0}"]["hosts"].append("{2}")',
'inventory["{0}"]["hosts"].append("{3}")',
'inventory["{0}"]["hosts"].append("{4}")',
'inventory["{0}"]["hosts"].append("{5}")',
'inventory["{0}"]["vars"] = dict(ansible_host="127.0.0.1", ansible_connection="local")',
'print(json.dumps(inventory))',
]
)
group_name = re.sub(r"[\']", "", "group_{}".format(random_title(non_ascii=False)))
host_names = [
re.sub(
r"[\':]",
"",
"host_{}".format(
random_utf8())) for _ in range(5)]
host_names = [re.sub(r"[\':]", "", "host_{}".format(random_utf8())) for _ in range(5)]
return script.format(group_name, *host_names)
page.register_page([resources.inventory_script,
(resources.inventory_scripts, 'post'),
(resources.inventory_script_copy, 'post')], InventoryScript)
page.register_page([resources.inventory_script, (resources.inventory_scripts, 'post'), (resources.inventory_script_copy, 'post')], InventoryScript)
class InventoryScripts(page.PageList, InventoryScript):
@@ -272,11 +199,10 @@ class Group(HasCreate, HasVariables, base.Base):
def payload(self, inventory, credential=None, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Group{}'.format(
random_title(
non_ascii=False)),
name=kwargs.get('name') or 'Group{}'.format(random_title(non_ascii=False)),
description=kwargs.get('description') or random_title(10),
inventory=inventory.id)
inventory=inventory.id,
)
if credential:
payload.credential = credential.id
@@ -288,38 +214,19 @@ class Group(HasCreate, HasVariables, base.Base):
return payload
def create_payload(
self,
name='',
description='',
inventory=Inventory,
credential=None,
source_script=None,
**kwargs):
credential, source_script = filter_by_class(
(credential, Credential), (source_script, InventoryScript))
self.create_and_update_dependencies(
inventory, credential, source_script)
def create_payload(self, name='', description='', inventory=Inventory, credential=None, source_script=None, **kwargs):
credential, source_script = filter_by_class((credential, Credential), (source_script, InventoryScript))
self.create_and_update_dependencies(inventory, credential, source_script)
credential = self.ds.credential if credential else None
payload = self.payload(
inventory=self.ds.inventory,
credential=credential,
name=name,
description=description,
**kwargs)
payload = self.payload(inventory=self.ds.inventory, credential=credential, name=name, description=description, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(self, name='', description='', inventory=Inventory, **kwargs):
payload = self.create_payload(
name=name,
description=description,
inventory=inventory,
**kwargs)
payload = self.create_payload(name=name, description=description, inventory=inventory, **kwargs)
parent = kwargs.get('parent', None) # parent must be a Group instance
resource = parent.related.children if parent else Groups(
self.connection)
resource = parent.related.children if parent else Groups(self.connection)
return self.update_identity(resource.post(payload))
def add_host(self, host=None):
@@ -348,8 +255,7 @@ class Group(HasCreate, HasVariables, base.Base):
self.related.children.post(dict(id=group.id, disassociate=True))
page.register_page([resources.group,
(resources.groups, 'post')], Group)
page.register_page([resources.group, (resources.groups, 'post')], Group)
class Groups(page.PageList, Group):
@@ -357,12 +263,17 @@ class Groups(page.PageList, Group):
pass
page.register_page([resources.groups,
resources.host_groups,
resources.inventory_related_groups,
resources.inventory_related_root_groups,
resources.group_children,
resources.group_potential_children], Groups)
page.register_page(
[
resources.groups,
resources.host_groups,
resources.inventory_related_groups,
resources.inventory_related_root_groups,
resources.group_children,
resources.group_potential_children,
],
Groups,
)
class Host(HasCreate, HasVariables, base.Base):
@@ -372,11 +283,10 @@ class Host(HasCreate, HasVariables, base.Base):
def payload(self, inventory, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Host{}'.format(
random_title(
non_ascii=False)),
name=kwargs.get('name') or 'Host{}'.format(random_title(non_ascii=False)),
description=kwargs.get('description') or random_title(10),
inventory=inventory.id)
inventory=inventory.id,
)
optional_fields = ('enabled', 'instance_id')
@@ -385,9 +295,7 @@ class Host(HasCreate, HasVariables, base.Base):
variables = kwargs.get('variables', not_provided)
if variables is None:
variables = dict(
ansible_host='127.0.0.1',
ansible_connection='local')
variables = dict(ansible_host='127.0.0.1', ansible_connection='local')
if variables != not_provided:
if isinstance(variables, dict):
@@ -396,42 +304,18 @@ class Host(HasCreate, HasVariables, base.Base):
return payload
def create_payload(
self,
name='',
description='',
variables=None,
inventory=Inventory,
**kwargs):
self.create_and_update_dependencies(
*filter_by_class((inventory, Inventory)))
payload = self.payload(
inventory=self.ds.inventory,
name=name,
description=description,
variables=variables,
**kwargs)
def create_payload(self, name='', description='', variables=None, inventory=Inventory, **kwargs):
self.create_and_update_dependencies(*filter_by_class((inventory, Inventory)))
payload = self.payload(inventory=self.ds.inventory, name=name, description=description, variables=variables, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
variables=None,
inventory=Inventory,
**kwargs):
payload = self.create_payload(
name=name,
description=description,
variables=variables,
inventory=inventory,
**kwargs)
def create(self, name='', description='', variables=None, inventory=Inventory, **kwargs):
payload = self.create_payload(name=name, description=description, variables=variables, inventory=inventory, **kwargs)
return self.update_identity(Hosts(self.connection).post(payload))
page.register_page([resources.host,
(resources.hosts, 'post')], Host)
page.register_page([resources.host, (resources.hosts, 'post')], Host)
class Hosts(page.PageList, Host):
@@ -439,10 +323,7 @@ class Hosts(page.PageList, Host):
pass
page.register_page([resources.hosts,
resources.group_related_hosts,
resources.inventory_related_hosts,
resources.inventory_sources_related_hosts], Hosts)
page.register_page([resources.hosts, resources.group_related_hosts, resources.inventory_related_hosts, resources.inventory_sources_related_hosts], Hosts)
class FactVersion(base.Base):
@@ -454,7 +335,6 @@ page.register_page(resources.host_related_fact_version, FactVersion)
class FactVersions(page.PageList, FactVersion):
@property
def count(self):
return len(self.results)
@@ -478,20 +358,13 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
optional_dependencies = [Credential, InventoryScript, Project]
NATURAL_KEY = ('organization', 'name', 'inventory')
def payload(
self,
inventory,
source='custom',
credential=None,
source_script=None,
project=None,
**kwargs):
def payload(self, inventory, source='custom', credential=None, source_script=None, project=None, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'InventorySource - {}'.format(
random_title()),
name=kwargs.get('name') or 'InventorySource - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
inventory=inventory.id,
source=source)
source=source,
)
if credential:
payload.credential = credential.id
@@ -509,22 +382,16 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
'update_cache_timeout',
'update_on_launch',
'update_on_project_update',
'verbosity')
'verbosity',
)
update_payload(payload, optional_fields, kwargs)
return payload
def create_payload(
self,
name='',
description='',
source='custom',
inventory=Inventory,
credential=None,
source_script=InventoryScript,
project=None,
**kwargs):
self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs
):
if source != 'custom' and source_script == InventoryScript:
source_script = None
if source == 'scm':
@@ -532,12 +399,10 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
if project is None:
project = Project
inventory, credential, source_script, project = filter_by_class((inventory, Inventory),
(credential, Credential),
(source_script, InventoryScript),
(project, Project))
self.create_and_update_dependencies(
inventory, credential, source_script, project)
inventory, credential, source_script, project = filter_by_class(
(inventory, Inventory), (credential, Credential), (source_script, InventoryScript), (project, Project)
)
self.create_and_update_dependencies(inventory, credential, source_script, project)
if credential:
credential = self.ds.credential
@@ -554,20 +419,12 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
project=project,
name=name,
description=description,
**kwargs)
**kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
source='custom',
inventory=Inventory,
credential=None,
source_script=InventoryScript,
project=None,
**kwargs):
def create(self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs):
payload = self.create_payload(
name=name,
description=description,
@@ -576,10 +433,9 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
credential=credential,
source_script=source_script,
project=project,
**kwargs)
return self.update_identity(
InventorySources(
self.connection).post(payload))
**kwargs
)
return self.update_identity(InventorySources(self.connection).post(payload))
def update(self):
"""Update the inventory_source using related->update endpoint"""
@@ -587,45 +443,37 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
update_pg = self.get_related('update')
# assert can_update == True
assert update_pg.can_update, \
"The specified inventory_source (id:%s) is not able to update (can_update:%s)" % \
(self.id, update_pg.can_update)
assert update_pg.can_update, "The specified inventory_source (id:%s) is not able to update (can_update:%s)" % (self.id, update_pg.can_update)
# start the inventory_update
result = update_pg.post()
# assert JSON response
assert 'inventory_update' in result.json, \
"Unexpected JSON response when starting an inventory_update.\n%s" % \
json.dumps(result.json, indent=2)
assert 'inventory_update' in result.json, "Unexpected JSON response when starting an inventory_update.\n%s" % json.dumps(result.json, indent=2)
# locate and return the inventory_update
jobs_pg = self.related.inventory_updates.get(
id=result.json['inventory_update'])
assert jobs_pg.count == 1, \
"An inventory_update started (id:%s) but job not found in response at %s/inventory_updates/" % \
(result.json['inventory_update'], self.url)
jobs_pg = self.related.inventory_updates.get(id=result.json['inventory_update'])
assert jobs_pg.count == 1, "An inventory_update started (id:%s) but job not found in response at %s/inventory_updates/" % (
result.json['inventory_update'],
self.url,
)
return jobs_pg.results[0]
@property
def is_successful(self):
"""An inventory_source is considered successful when source != "" and super().is_successful ."""
return self.source != "" and super(
InventorySource, self).is_successful
return self.source != "" and super(InventorySource, self).is_successful
def add_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, associate=True))
self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, disassociate=True))
self.related.credentials.post(dict(id=credential.id, disassociate=True))
page.register_page([resources.inventory_source,
(resources.inventory_sources, 'post')], InventorySource)
page.register_page([resources.inventory_source, (resources.inventory_sources, 'post')], InventorySource)
class InventorySources(page.PageList, InventorySource):
@@ -633,9 +481,7 @@ class InventorySources(page.PageList, InventorySource):
pass
page.register_page([resources.inventory_sources,
resources.related_inventory_sources],
InventorySources)
page.register_page([resources.inventory_sources, resources.related_inventory_sources], InventorySources)
class InventorySourceGroups(page.PageList, Group):
@@ -643,9 +489,7 @@ class InventorySourceGroups(page.PageList, Group):
pass
page.register_page(
resources.inventory_sources_related_groups,
InventorySourceGroups)
page.register_page(resources.inventory_sources_related_groups, InventorySourceGroups)
class InventorySourceUpdate(base.Base):
@@ -653,9 +497,7 @@ class InventorySourceUpdate(base.Base):
pass
page.register_page([resources.inventory_sources_related_update,
resources.inventory_related_update_inventory_sources],
InventorySourceUpdate)
page.register_page([resources.inventory_sources_related_update, resources.inventory_related_update_inventory_sources], InventorySourceUpdate)
class InventoryUpdate(UnifiedJob):
@@ -671,10 +513,7 @@ class InventoryUpdates(page.PageList, InventoryUpdate):
pass
page.register_page([resources.inventory_updates,
resources.inventory_source_updates,
resources.project_update_scm_inventory_updates],
InventoryUpdates)
page.register_page([resources.inventory_updates, resources.inventory_source_updates, resources.project_update_scm_inventory_updates], InventoryUpdates)
class InventoryUpdateCancel(base.Base):

View File

@@ -1,13 +1,6 @@
import json
from awxkit.utils import (
filter_by_class,
not_provided,
random_title,
suppress,
update_payload,
set_payload_foreign_key_args,
PseudoNamespace)
from awxkit.utils import filter_by_class, not_provided, random_title, suppress, update_payload, set_payload_foreign_key_args, PseudoNamespace
from awxkit.api.pages import Credential, Inventory, Project, UnifiedJobTemplate
from awxkit.api.mixins import HasCreate, HasInstanceGroups, HasNotifications, HasSurvey, HasCopy, DSAdapter
from awxkit.api.resources import resources
@@ -16,13 +9,7 @@ from . import base
from . import page
class JobTemplate(
HasCopy,
HasCreate,
HasInstanceGroups,
HasNotifications,
HasSurvey,
UnifiedJobTemplate):
class JobTemplate(HasCopy, HasCreate, HasInstanceGroups, HasNotifications, HasSurvey, UnifiedJobTemplate):
optional_dependencies = [Inventory, Credential, Project]
NATURAL_KEY = ('organization', 'name')
@@ -38,16 +25,13 @@ class JobTemplate(
# return job
if result.json['type'] == 'job':
jobs_pg = self.get_related('jobs', id=result.json['job'])
assert jobs_pg.count == 1, \
"job_template launched (id:%s) but job not found in response at %s/jobs/" % \
(result.json['job'], self.url)
assert jobs_pg.count == 1, "job_template launched (id:%s) but job not found in response at %s/jobs/" % (result.json['job'], self.url)
return jobs_pg.results[0]
elif result.json['type'] == 'workflow_job':
slice_workflow_jobs = self.get_related(
'slice_workflow_jobs', id=result.json['id'])
assert slice_workflow_jobs.count == 1, (
"job_template launched sliced job (id:%s) but not found in related %s/slice_workflow_jobs/" %
(result.json['id'], self.url)
slice_workflow_jobs = self.get_related('slice_workflow_jobs', id=result.json['id'])
assert slice_workflow_jobs.count == 1, "job_template launched sliced job (id:%s) but not found in related %s/slice_workflow_jobs/" % (
result.json['id'],
self.url,
)
return slice_workflow_jobs.results[0]
else:
@@ -56,10 +40,7 @@ class JobTemplate(
def payload(self, job_type='run', playbook='ping.yml', **kwargs):
name = kwargs.get('name') or 'JobTemplate - {}'.format(random_title())
description = kwargs.get('description') or random_title(10)
payload = PseudoNamespace(
name=name,
description=description,
job_type=job_type)
payload = PseudoNamespace(name=name, description=description, job_type=job_type)
optional_fields = (
'ask_scm_branch_on_launch',
@@ -90,7 +71,8 @@ class JobTemplate(
'job_slice_count',
'webhook_service',
'webhook_credential',
'scm_branch')
'scm_branch',
)
update_payload(payload, optional_fields, kwargs)
@@ -113,94 +95,53 @@ class JobTemplate(
with suppress(exc.NoContent):
self.related.labels.post(label)
def create_payload(
self,
name='',
description='',
job_type='run',
playbook='ping.yml',
credential=Credential,
inventory=Inventory,
project=None,
**kwargs):
def create_payload(self, name='', description='', job_type='run', playbook='ping.yml', credential=Credential, inventory=Inventory, project=None, **kwargs):
if not project:
project = Project
if not inventory and not kwargs.get('ask_inventory_on_launch', False):
inventory = Inventory
self.create_and_update_dependencies(
*
filter_by_class(
(credential,
Credential),
(inventory,
Inventory),
(project,
Project)))
self.create_and_update_dependencies(*filter_by_class((credential, Credential), (inventory, Inventory), (project, Project)))
project = self.ds.project if project else None
inventory = self.ds.inventory if inventory else None
credential = self.ds.credential if credential else None
payload = self.payload(
name=name,
description=description,
job_type=job_type,
playbook=playbook,
credential=credential,
inventory=inventory,
project=project,
**kwargs)
name=name, description=description, job_type=job_type, playbook=playbook, credential=credential, inventory=inventory, project=project, **kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload, credential
def create(
self,
name='',
description='',
job_type='run',
playbook='ping.yml',
credential=Credential,
inventory=Inventory,
project=None,
**kwargs):
payload, credential = self.create_payload(name=name, description=description, job_type=job_type,
playbook=playbook, credential=credential, inventory=inventory,
project=project, **kwargs)
ret = self.update_identity(
JobTemplates(
self.connection).post(payload))
def create(self, name='', description='', job_type='run', playbook='ping.yml', credential=Credential, inventory=Inventory, project=None, **kwargs):
payload, credential = self.create_payload(
name=name, description=description, job_type=job_type, playbook=playbook, credential=credential, inventory=inventory, project=project, **kwargs
)
ret = self.update_identity(JobTemplates(self.connection).post(payload))
if credential:
with suppress(exc.NoContent):
self.related.credentials.post(dict(id=credential.id))
if 'vault_credential' in kwargs:
with suppress(exc.NoContent):
if not isinstance(kwargs['vault_credential'], int):
raise ValueError(
"Expected 'vault_credential' value to be an integer, the id of the desired vault credential")
self.related.credentials.post(
dict(id=kwargs['vault_credential']))
raise ValueError("Expected 'vault_credential' value to be an integer, the id of the desired vault credential")
self.related.credentials.post(dict(id=kwargs['vault_credential']))
return ret
def add_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, associate=True))
self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, disassociate=True))
self.related.credentials.post(dict(id=credential.id, disassociate=True))
def remove_all_credentials(self):
for cred in self.related.credentials.get().results:
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=cred.id, disassociate=True))
self.related.credentials.post(dict(id=cred.id, disassociate=True))
page.register_page([resources.job_template,
(resources.job_templates, 'post'),
(resources.job_template_copy, 'post')], JobTemplate)
page.register_page([resources.job_template, (resources.job_templates, 'post'), (resources.job_template_copy, 'post')], JobTemplate)
class JobTemplates(page.PageList, JobTemplate):
@@ -208,8 +149,7 @@ class JobTemplates(page.PageList, JobTemplate):
pass
page.register_page([resources.job_templates,
resources.related_job_templates], JobTemplates)
page.register_page([resources.job_templates, resources.related_job_templates], JobTemplates)
class JobTemplateCallback(base.Base):

View File

@@ -5,7 +5,6 @@ from . import page
class Job(UnifiedJob):
def relaunch(self, payload={}):
result = self.related.relaunch.post(payload)
return self.walk(result.endpoint)
@@ -19,9 +18,7 @@ class Jobs(page.PageList, Job):
pass
page.register_page([resources.jobs,
resources.job_template_jobs,
resources.system_job_template_jobs], Jobs)
page.register_page([resources.jobs, resources.job_template_jobs, resources.system_job_template_jobs], Jobs)
class JobCancel(UnifiedJob):
@@ -37,8 +34,7 @@ class JobEvent(base.Base):
pass
page.register_page([resources.job_event,
resources.job_job_event], JobEvent)
page.register_page([resources.job_event, resources.job_job_event], JobEvent)
class JobEvents(page.PageList, JobEvent):
@@ -46,10 +42,7 @@ class JobEvents(page.PageList, JobEvent):
pass
page.register_page([resources.job_events,
resources.job_job_events,
resources.job_event_children,
resources.group_related_job_events], JobEvents)
page.register_page([resources.job_events, resources.job_job_events, resources.job_event_children, resources.group_related_job_events], JobEvents)
class JobPlay(base.Base):
@@ -97,8 +90,7 @@ class JobHostSummaries(page.PageList, JobHostSummary):
pass
page.register_page([resources.job_host_summaries,
resources.group_related_job_host_summaries], JobHostSummaries)
page.register_page([resources.job_host_summaries, resources.group_related_job_host_summaries], JobHostSummaries)
class JobRelaunch(base.Base):

View File

@@ -19,43 +19,24 @@ class Label(HasCreate, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Label - {}'.format(
random_title()),
name=kwargs.get('name') or 'Label - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id)
organization=organization.id,
)
return payload
def create_payload(
self,
name='',
description='',
organization=Organization,
**kwargs):
def create_payload(self, name='', description='', organization=Organization, **kwargs):
self.create_and_update_dependencies(organization)
payload = self.payload(
organization=self.ds.organization,
name=name,
description=description,
**kwargs)
payload = self.payload(organization=self.ds.organization, name=name, description=description, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
organization=Organization,
**kwargs):
payload = self.create_payload(
name=name,
description=description,
organization=organization,
**kwargs)
def create(self, name='', description='', organization=Organization, **kwargs):
payload = self.create_payload(name=name, description=description, organization=organization, **kwargs)
return self.update_identity(Labels(self.connection).post(payload))
page.register_page([resources.label,
(resources.labels, 'post')], Label)
page.register_page([resources.label, (resources.labels, 'post')], Label)
class Labels(page.PageList, Label):
@@ -63,7 +44,4 @@ class Labels(page.PageList, Label):
pass
page.register_page([resources.labels,
resources.job_labels,
resources.job_template_labels,
resources.workflow_job_template_labels], Labels)
page.register_page([resources.labels, resources.job_labels, resources.job_template_labels, resources.workflow_job_template_labels], Labels)

View File

@@ -4,12 +4,9 @@ from . import page
class Metrics(base.Base):
def get(self, **query_parameters):
request = self.connection.get(self.endpoint, query_parameters,
headers={'Accept': 'application/json'})
request = self.connection.get(self.endpoint, query_parameters, headers={'Accept': 'application/json'})
return self.page_identity(request)
page.register_page([resources.metrics,
(resources.metrics, 'get')], Metrics)
page.register_page([resources.metrics, (resources.metrics, 'get')], Metrics)

View File

@@ -9,16 +9,7 @@ from . import page
job_results = ('any', 'error', 'success')
notification_types = (
'email',
'irc',
'pagerduty',
'slack',
'twilio',
'webhook',
'mattermost',
'grafana',
'rocketchat')
notification_types = ('email', 'irc', 'pagerduty', 'slack', 'twilio', 'webhook', 'mattermost', 'grafana', 'rocketchat')
class NotificationTemplate(HasCopy, HasCreate, base.Base):
@@ -28,18 +19,17 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def test(self):
"""Create test notification"""
assert 'test' in self.related, \
"No such related attribute 'test'"
assert 'test' in self.related, "No such related attribute 'test'"
# trigger test notification
notification_id = self.related.test.post().notification
# return notification page
notifications_pg = self.get_related(
'notifications', id=notification_id).wait_until_count(1)
assert notifications_pg.count == 1, \
"test notification triggered (id:%s) but notification not found in response at %s/notifications/" % \
(notification_id, self.url)
notifications_pg = self.get_related('notifications', id=notification_id).wait_until_count(1)
assert notifications_pg.count == 1, "test notification triggered (id:%s) but notification not found in response at %s/notifications/" % (
notification_id,
self.url,
)
return notifications_pg.results[0]
def silent_delete(self):
@@ -53,41 +43,25 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def payload(self, organization, notification_type='slack', messages=not_provided, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'NotificationTemplate ({0}) - {1}' .format(
notification_type,
random_title()),
name=kwargs.get('name') or 'NotificationTemplate ({0}) - {1}'.format(notification_type, random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
notification_type=notification_type)
notification_type=notification_type,
)
if messages != not_provided:
payload['messages'] = messages
notification_configuration = kwargs.get(
'notification_configuration', {})
notification_configuration = kwargs.get('notification_configuration', {})
payload.notification_configuration = notification_configuration
if payload.notification_configuration == {}:
services = config.credentials.notification_services
if notification_type == 'email':
fields = (
'host',
'username',
'password',
'port',
'use_ssl',
'use_tls',
'sender',
'recipients')
fields = ('host', 'username', 'password', 'port', 'use_ssl', 'use_tls', 'sender', 'recipients')
cred = services.email
elif notification_type == 'irc':
fields = (
'server',
'port',
'use_ssl',
'password',
'nickname',
'targets')
fields = ('server', 'port', 'use_ssl', 'password', 'nickname', 'targets')
cred = services.irc
elif notification_type == 'pagerduty':
fields = ('client_name', 'service_key', 'subdomain', 'token')
@@ -96,34 +70,22 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
fields = ('channels', 'token')
cred = services.slack
elif notification_type == 'twilio':
fields = (
'account_sid',
'account_token',
'from_number',
'to_numbers')
fields = ('account_sid', 'account_token', 'from_number', 'to_numbers')
cred = services.twilio
elif notification_type == 'webhook':
fields = ('url', 'headers')
cred = services.webhook
elif notification_type == 'mattermost':
fields = (
'mattermost_url',
'mattermost_username',
'mattermost_channel',
'mattermost_icon_url',
'mattermost_no_verify_ssl')
fields = ('mattermost_url', 'mattermost_username', 'mattermost_channel', 'mattermost_icon_url', 'mattermost_no_verify_ssl')
cred = services.mattermost
elif notification_type == 'grafana':
fields = ('grafana_url',
'grafana_key')
fields = ('grafana_url', 'grafana_key')
cred = services.grafana
elif notification_type == 'rocketchat':
fields = ('rocketchat_url',
'rocketchat_no_verify_ssl')
fields = ('rocketchat_url', 'rocketchat_no_verify_ssl')
cred = services.rocketchat
else:
raise ValueError(
'Unknown notification_type {0}'.format(notification_type))
raise ValueError('Unknown notification_type {0}'.format(notification_type))
for field in fields:
if field == 'bot_token':
@@ -136,47 +98,21 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
return payload
def create_payload(
self,
name='',
description='',
notification_type='slack',
organization=Organization,
messages=not_provided,
**kwargs):
def create_payload(self, name='', description='', notification_type='slack', organization=Organization, messages=not_provided, **kwargs):
if notification_type not in notification_types:
raise ValueError(
'Unsupported notification type "{0}". Please use one of {1}.' .format(
notification_type, notification_types))
raise ValueError('Unsupported notification type "{0}". Please use one of {1}.'.format(notification_type, notification_types))
self.create_and_update_dependencies(organization)
payload = self.payload(
organization=self.ds.organization,
notification_type=notification_type,
name=name,
description=description,
messages=messages,
**kwargs)
organization=self.ds.organization, notification_type=notification_type, name=name, description=description, messages=messages, **kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
notification_type='slack',
organization=Organization,
messages=not_provided,
**kwargs):
def create(self, name='', description='', notification_type='slack', organization=Organization, messages=not_provided, **kwargs):
payload = self.create_payload(
name=name,
description=description,
notification_type=notification_type,
organization=organization,
messages=messages,
**kwargs)
return self.update_identity(
NotificationTemplates(
self.connection).post(payload))
name=name, description=description, notification_type=notification_type, organization=organization, messages=messages, **kwargs
)
return self.update_identity(NotificationTemplates(self.connection).post(payload))
def associate(self, resource, job_result='any'):
"""Associates a NotificationTemplate with the provided resource"""
@@ -188,15 +124,11 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def _associate(self, resource, job_result='any', disassociate=False):
if job_result not in job_results:
raise ValueError(
'Unsupported job_result type "{0}". Please use one of {1}.' .format(
job_result, job_results))
raise ValueError('Unsupported job_result type "{0}". Please use one of {1}.'.format(job_result, job_results))
result_attr = 'notification_templates_{0}'.format(job_result)
if result_attr not in resource.related:
raise ValueError(
'Unsupported resource "{0}". Does not have a related {1} field.' .format(
resource, result_attr))
raise ValueError('Unsupported resource "{0}". Does not have a related {1} field.'.format(resource, result_attr))
payload = dict(id=self.id)
if disassociate:
@@ -206,14 +138,19 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
getattr(resource.related, result_attr).post(payload)
page.register_page([resources.notification_template,
(resources.notification_templates, 'post'),
(resources.notification_template_copy, 'post'),
resources.notification_template_any,
resources.notification_template_started,
resources.notification_template_error,
resources.notification_template_success,
resources.notification_template_approval], NotificationTemplate)
page.register_page(
[
resources.notification_template,
(resources.notification_templates, 'post'),
(resources.notification_template_copy, 'post'),
resources.notification_template_any,
resources.notification_template_started,
resources.notification_template_error,
resources.notification_template_success,
resources.notification_template_approval,
],
NotificationTemplate,
)
class NotificationTemplates(page.PageList, NotificationTemplate):
@@ -221,14 +158,18 @@ class NotificationTemplates(page.PageList, NotificationTemplate):
pass
page.register_page([resources.notification_templates,
resources.related_notification_templates,
resources.notification_templates_any,
resources.notification_templates_started,
resources.notification_templates_error,
resources.notification_templates_success,
resources.notification_templates_approvals],
NotificationTemplates)
page.register_page(
[
resources.notification_templates,
resources.related_notification_templates,
resources.notification_templates_any,
resources.notification_templates_started,
resources.notification_templates_error,
resources.notification_templates_success,
resources.notification_templates_approvals,
],
NotificationTemplates,
)
class NotificationTemplateCopy(base.Base):
@@ -244,6 +185,4 @@ class NotificationTemplateTest(base.Base):
pass
page.register_page(
resources.notification_template_test,
NotificationTemplateTest)
page.register_page(resources.notification_template_test, NotificationTemplateTest)

View File

@@ -6,10 +6,8 @@ from . import page
class Notification(HasStatus, base.Base):
def __str__(self):
items = ['id', 'notification_type', 'status', 'error', 'notifications_sent',
'subject', 'recipients']
items = ['id', 'notification_type', 'status', 'error', 'notifications_sent', 'subject', 'recipients']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
@@ -40,13 +38,10 @@ page.register_page(resources.notification, Notification)
class Notifications(page.PageList, Notification):
def wait_until_count(self, count, interval=10, timeout=60, **kw):
"""Poll notifications page until it is populated with `count` number of notifications."""
poll_until(lambda: getattr(self.get(), 'count') == count,
interval=interval, timeout=timeout, **kw)
poll_until(lambda: getattr(self.get(), 'count') == count, interval=interval, timeout=timeout, **kw)
return self
page.register_page([resources.notifications,
resources.related_notifications], Notifications)
page.register_page([resources.notifications, resources.related_notifications], Notifications)

View File

@@ -26,22 +26,27 @@ class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
if isinstance(credential, page.Page):
credential = credential.json
with suppress(exc.NoContent):
self.related.galaxy_credentials.post({
"id": credential.id,
})
self.related.galaxy_credentials.post(
{
"id": credential.id,
}
)
def remove_galaxy_credential(self, credential):
if isinstance(credential, page.Page):
credential = credential.json
with suppress(exc.NoContent):
self.related.galaxy_credentials.post({
"id": credential.id,
"disassociate": True,
})
self.related.galaxy_credentials.post(
{
"id": credential.id,
"disassociate": True,
}
)
def payload(self, **kwargs):
payload = PseudoNamespace(name=kwargs.get('name') or 'Organization - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10))
payload = PseudoNamespace(
name=kwargs.get('name') or 'Organization - {}'.format(random_title()), description=kwargs.get('description') or random_title(10)
)
payload = set_payload_foreign_key_args(payload, ('default_environment',), kwargs)
@@ -57,8 +62,7 @@ class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
return self.update_identity(Organizations(self.connection).post(payload))
page.register_page([resources.organization,
(resources.organizations, 'post')], Organization)
page.register_page([resources.organization, (resources.organizations, 'post')], Organization)
class Organizations(page.PageList, Organization):
@@ -66,6 +70,4 @@ class Organizations(page.PageList, Organization):
pass
page.register_page([resources.organizations,
resources.user_organizations,
resources.project_organizations], Organizations)
page.register_page([resources.organizations, resources.user_organizations, resources.project_organizations], Organizations)

View File

@@ -6,15 +6,7 @@ import re
from requests import Response
import http.client as http
from awxkit.utils import (
PseudoNamespace,
is_relative_endpoint,
are_same_endpoint,
super_dir_set,
suppress,
is_list_or_tuple,
to_str
)
from awxkit.utils import PseudoNamespace, is_relative_endpoint, are_same_endpoint, super_dir_set, suppress, is_list_or_tuple, to_str
from awxkit.api import utils
from awxkit.api.client import Connection
from awxkit.api.registry import URLRegistry
@@ -41,17 +33,11 @@ def is_license_invalid(response):
def is_license_exceeded(response):
if re.match(
r".*license range of.*instances has been exceeded.*",
response.text):
if re.match(r".*license range of.*instances has been exceeded.*", response.text):
return True
if re.match(
r".*License count of.*instances has been reached.*",
response.text):
if re.match(r".*License count of.*instances has been reached.*", response.text):
return True
if re.match(
r".*License count of.*instances has been exceeded.*",
response.text):
if re.match(r".*License count of.*instances has been exceeded.*", response.text):
return True
if re.match(r".*License has expired.*", response.text):
return True
@@ -67,6 +53,7 @@ def is_duplicate_error(response):
def register_page(urls, page_cls):
if not _page_registry.default:
from awxkit.api.pages import Base
_page_registry.setdefault(Base)
if not is_list_or_tuple(urls):
@@ -108,32 +95,23 @@ class Page(object):
if 'endpoint' in kw:
self.endpoint = kw['endpoint']
self.connection = connection or Connection(
config.base_url, kw.get(
'verify', not config.assume_untrusted))
self.connection = connection or Connection(config.base_url, kw.get('verify', not config.assume_untrusted))
self.r = kw.get('r', None)
self.json = kw.get(
'json', objectify_response_json(
self.r) if self.r else {})
self.json = kw.get('json', objectify_response_json(self.r) if self.r else {})
self.last_elapsed = kw.get('last_elapsed', None)
def __getattr__(self, name):
if 'json' in self.__dict__ and name in self.json:
value = self.json[name]
if not isinstance(
value,
TentativePage) and is_relative_endpoint(value):
if not isinstance(value, TentativePage) and is_relative_endpoint(value):
value = TentativePage(value, self.connection)
elif isinstance(value, dict):
for key, item in value.items():
if not isinstance(
item, TentativePage) and is_relative_endpoint(item):
if not isinstance(item, TentativePage) and is_relative_endpoint(item):
value[key] = TentativePage(item, self.connection)
return value
raise AttributeError(
"{!r} object has no attribute {!r}".format(
self.__class__.__name__, name))
raise AttributeError("{!r} object has no attribute {!r}".format(self.__class__.__name__, name))
def __setattr__(self, name, value):
if 'json' in self.__dict__ and name in self.json:
@@ -200,20 +178,15 @@ class Page(object):
text = response.text
if len(text) > 1024:
text = text[:1024] + '... <<< Truncated >>> ...'
log.debug(
"Unable to parse JSON response ({0.status_code}): {1} - '{2}'".format(response, e, text))
log.debug("Unable to parse JSON response ({0.status_code}): {1} - '{2}'".format(response, e, text))
exc_str = "%s (%s) received" % (
http.responses[response.status_code], response.status_code)
exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
exception = exception_from_status_code(response.status_code)
if exception:
raise exception(exc_str, data)
if response.status_code in (
http.OK,
http.CREATED,
http.ACCEPTED):
if response.status_code in (http.OK, http.CREATED, http.ACCEPTED):
# Not all JSON responses include a URL. Grab it from the request
# object, if needed.
@@ -232,13 +205,7 @@ class Page(object):
return self
registered_type = get_registered_page(request_path, request_method)
return registered_type(
self.connection,
endpoint=endpoint,
json=data,
last_elapsed=response.elapsed,
r=response,
ds=ds)
return registered_type(self.connection, endpoint=endpoint, json=data, last_elapsed=response.elapsed, r=response, ds=ds)
elif response.status_code == http.FORBIDDEN:
if is_license_invalid(response):
@@ -341,14 +308,16 @@ class Page(object):
return natural_key
_exception_map = {http.NO_CONTENT: exc.NoContent,
http.NOT_FOUND: exc.NotFound,
http.INTERNAL_SERVER_ERROR: exc.InternalServerError,
http.BAD_GATEWAY: exc.BadGateway,
http.METHOD_NOT_ALLOWED: exc.MethodNotAllowed,
http.UNAUTHORIZED: exc.Unauthorized,
http.PAYMENT_REQUIRED: exc.PaymentRequired,
http.CONFLICT: exc.Conflict}
_exception_map = {
http.NO_CONTENT: exc.NoContent,
http.NOT_FOUND: exc.NotFound,
http.INTERNAL_SERVER_ERROR: exc.InternalServerError,
http.BAD_GATEWAY: exc.BadGateway,
http.METHOD_NOT_ALLOWED: exc.MethodNotAllowed,
http.UNAUTHORIZED: exc.Unauthorized,
http.PAYMENT_REQUIRED: exc.PaymentRequired,
http.CONFLICT: exc.Conflict,
}
def exception_from_status_code(status_code):
@@ -380,12 +349,7 @@ class PageList(object):
registered_type = self.__item_class__
else:
registered_type = get_registered_page(endpoint)
items.append(
registered_type(
self.connection,
endpoint=endpoint,
json=item,
r=self.r))
items.append(registered_type(self.connection, endpoint=endpoint, json=item, r=self.r))
return items
def go_to_next(self):
@@ -407,7 +371,6 @@ class PageList(object):
class TentativePage(str):
def __new__(cls, endpoint, connection):
return super(TentativePage, cls).__new__(cls, to_str(endpoint))
@@ -416,10 +379,7 @@ class TentativePage(str):
self.connection = connection
def _create(self):
return get_registered_page(
self.endpoint)(
self.connection,
endpoint=self.endpoint)
return get_registered_page(self.endpoint)(self.connection, endpoint=self.endpoint)
def get(self, **params):
return self._create().get(**params)
@@ -436,21 +396,15 @@ class TentativePage(str):
page = None
# look up users by username not name
if 'users' in self:
assert query_parameters.get(
'username'), 'For this resource, you must call this method with a "username" to look up the object by'
assert query_parameters.get('username'), 'For this resource, you must call this method with a "username" to look up the object by'
page = self.get(username=query_parameters['username'])
else:
assert query_parameters.get(
'name'), 'For this resource, you must call this method with a "name" to look up the object by'
assert query_parameters.get('name'), 'For this resource, you must call this method with a "name" to look up the object by'
if query_parameters.get('organization'):
if isinstance(query_parameters.get('organization'), int):
page = self.get(
name=query_parameters['name'],
organization=query_parameters.get('organization'))
page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization'))
else:
page = self.get(
name=query_parameters['name'],
organization=query_parameters.get('organization').id)
page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization').id)
else:
page = self.get(name=query_parameters['name'])
if page and page.results:
@@ -476,13 +430,9 @@ class TentativePage(str):
if query_parameters.get('name'):
if query_parameters.get('organization'):
if isinstance(query_parameters.get('organization'), int):
page = self.get(
name=query_parameters['name'],
organization=query_parameters.get('organization'))
page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization'))
else:
page = self.get(
name=query_parameters['name'],
organization=query_parameters.get('organization').id)
page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization').id)
else:
page = self.get(name=query_parameters['name'])

View File

@@ -18,13 +18,11 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
def payload(self, organization, scm_type='git', **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Project - {}'.format(
random_title()),
name=kwargs.get('name') or 'Project - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
scm_type=scm_type,
scm_url=kwargs.get('scm_url') or config.project_urls.get(
scm_type,
''))
scm_url=kwargs.get('scm_url') or config.project_urls.get(scm_type, ''),
)
if organization is not None:
payload.organization = organization.id
@@ -40,43 +38,25 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
'scm_update_cache_timeout',
'scm_update_on_launch',
'scm_refspec',
'allow_override')
'allow_override',
)
update_payload(payload, fields, kwargs)
payload = set_payload_foreign_key_args(payload, ('execution_environment', 'default_environment'), kwargs)
return payload
def create_payload(
self,
name='',
description='',
scm_type='git',
scm_url='',
scm_branch='',
organization=Organization,
credential=None,
**kwargs):
def create_payload(self, name='', description='', scm_type='git', scm_url='', scm_branch='', organization=Organization, credential=None, **kwargs):
if credential:
if isinstance(credential, Credential):
if credential.ds.credential_type.namespace not in (
'scm', 'insights'):
if credential.ds.credential_type.namespace not in ('scm', 'insights'):
credential = None # ignore incompatible credential from HasCreate dependency injection
elif credential in (Credential,):
credential = (
Credential, dict(
credential_type=(
True, dict(
kind='scm'))))
credential = (Credential, dict(credential_type=(True, dict(kind='scm'))))
elif credential is True:
credential = (
Credential, dict(
credential_type=(
True, dict(
kind='scm'))))
credential = (Credential, dict(credential_type=(True, dict(kind='scm'))))
self.create_and_update_dependencies(
*filter_by_class((credential, Credential), (organization, Organization)))
self.create_and_update_dependencies(*filter_by_class((credential, Credential), (organization, Organization)))
credential = self.ds.credential if credential else None
organization = self.ds.organization if organization else None
@@ -89,20 +69,12 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
scm_url=scm_url,
scm_branch=scm_branch,
credential=credential,
**kwargs)
**kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
name='',
description='',
scm_type='git',
scm_url='',
scm_branch='',
organization=Organization,
credential=None,
**kwargs):
def create(self, name='', description='', scm_type='git', scm_url='', scm_branch='', organization=Organization, credential=None, **kwargs):
payload = self.create_payload(
name=name,
description=description,
@@ -111,7 +83,8 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
scm_branch=scm_branch,
organization=organization,
credential=credential,
**kwargs)
**kwargs
)
self.update_identity(Projects(self.connection).post(payload))
if kwargs.get('wait', True):
@@ -127,25 +100,20 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
update_pg = self.get_related('update')
# assert can_update == True
assert update_pg.can_update, \
"The specified project (id:%s) is not able to update (can_update:%s)" % \
(self.id, update_pg.can_update)
assert update_pg.can_update, "The specified project (id:%s) is not able to update (can_update:%s)" % (self.id, update_pg.can_update)
# start the update
result = update_pg.post()
# assert JSON response
assert 'project_update' in result.json, \
"Unexpected JSON response when starting an project_update.\n%s" % \
json.dumps(result.json, indent=2)
assert 'project_update' in result.json, "Unexpected JSON response when starting an project_update.\n%s" % json.dumps(result.json, indent=2)
# locate and return the specific update
jobs_pg = self.get_related(
'project_updates',
id=result.json['project_update'])
assert jobs_pg.count == 1, \
"An project_update started (id:%s) but job not found in response at %s/inventory_updates/" % \
(result.json['project_update'], self.url)
jobs_pg = self.get_related('project_updates', id=result.json['project_update'])
assert jobs_pg.count == 1, "An project_update started (id:%s) but job not found in response at %s/inventory_updates/" % (
result.json['project_update'],
self.url,
)
return jobs_pg.results[0]
@property
@@ -154,13 +122,10 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
0) scm_type != ""
1) unified_job_template.is_successful
"""
return self.scm_type != "" and \
super(Project, self).is_successful
return self.scm_type != "" and super(Project, self).is_successful
page.register_page([resources.project,
(resources.projects, 'post'),
(resources.project_copy, 'post')], Project)
page.register_page([resources.project, (resources.projects, 'post'), (resources.project_copy, 'post')], Project)
class Projects(page.PageList, Project):
@@ -168,8 +133,7 @@ class Projects(page.PageList, Project):
pass
page.register_page([resources.projects,
resources.related_projects], Projects)
page.register_page([resources.projects, resources.related_projects], Projects)
class ProjectUpdate(UnifiedJob):
@@ -185,8 +149,7 @@ class ProjectUpdates(page.PageList, ProjectUpdate):
pass
page.register_page([resources.project_updates,
resources.project_project_updates], ProjectUpdates)
page.register_page([resources.project_updates, resources.project_project_updates], ProjectUpdates)
class ProjectUpdateLaunch(base.Base):

View File

@@ -18,15 +18,11 @@ class Role(base.Base):
cache = page.PageCache()
natural_key = super(Role, self).get_natural_key(cache=cache)
related_objs = [
related for name, related in self.related.items()
if name not in ('users', 'teams')
]
related_objs = [related for name, related in self.related.items() if name not in ('users', 'teams')]
if related_objs:
related_endpoint = cache.get_page(related_objs[0])
if related_endpoint is None:
log.error("Unable to obtain content_object %s for role %s",
related_objs[0], self.endpoint)
log.error("Unable to obtain content_object %s for role %s", related_objs[0], self.endpoint)
return None
natural_key['content_object'] = related_endpoint.get_natural_key(cache=cache)
@@ -41,6 +37,4 @@ class Roles(page.PageList, Role):
pass
page.register_page([resources.roles,
resources.related_roles,
resources.related_object_roles], Roles)
page.register_page([resources.roles, resources.related_roles, resources.related_object_roles], Roles)

View File

@@ -11,12 +11,10 @@ class Schedule(UnifiedJob):
NATURAL_KEY = ('unified_job_template', 'name')
page.register_page([resources.schedule,
resources.related_schedule], Schedule)
page.register_page([resources.schedule, resources.related_schedule], Schedule)
class Schedules(page.PageList, Schedule):
def get_zoneinfo(self):
return SchedulesZoneInfo(self.connection).get()
@@ -33,8 +31,7 @@ class Schedules(page.PageList, Schedule):
self.related.credentials.post(dict(id=cred.id, disassociate=True))
page.register_page([resources.schedules,
resources.related_schedules], Schedules)
page.register_page([resources.schedules, resources.related_schedules], Schedules)
class SchedulesPreview(base.Base):
@@ -46,7 +43,6 @@ page.register_page(((resources.schedules_preview, 'post'),), SchedulesPreview)
class SchedulesZoneInfo(base.Base):
def __getitem__(self, idx):
return self.json[idx]

View File

@@ -8,27 +8,31 @@ class Setting(base.Base):
pass
page.register_page([resources.setting,
resources.settings_all,
resources.settings_authentication,
resources.settings_changed,
resources.settings_github,
resources.settings_github_org,
resources.settings_github_team,
resources.settings_google_oauth2,
resources.settings_jobs,
resources.settings_ldap,
resources.settings_radius,
resources.settings_saml,
resources.settings_system,
resources.settings_tacacsplus,
resources.settings_ui,
resources.settings_user,
resources.settings_user_defaults], Setting)
page.register_page(
[
resources.setting,
resources.settings_all,
resources.settings_authentication,
resources.settings_changed,
resources.settings_github,
resources.settings_github_org,
resources.settings_github_team,
resources.settings_google_oauth2,
resources.settings_jobs,
resources.settings_ldap,
resources.settings_radius,
resources.settings_saml,
resources.settings_system,
resources.settings_tacacsplus,
resources.settings_ui,
resources.settings_user,
resources.settings_user_defaults,
],
Setting,
)
class Settings(page.PageList, Setting):
def get_endpoint(self, endpoint):
"""Helper method used to navigate to a specific settings endpoint.
(Pdb) settings_pg.get_endpoint('all')

View File

@@ -3,7 +3,6 @@ from . import page
class Subscriptions(page.Page):
def get_possible_licenses(self, **kwargs):
return self.post(json=kwargs).json

View File

@@ -5,7 +5,6 @@ from awxkit.api.resources import resources
class SurveySpec(base.Base):
def get_variable_default(self, var):
for item in self.spec:
if item.get('variable') == var:
@@ -26,5 +25,4 @@ class SurveySpec(base.Base):
return required_vars
page.register_page([resources.job_template_survey_spec,
resources.workflow_job_template_survey_spec], SurveySpec)
page.register_page([resources.job_template_survey_spec, resources.workflow_job_template_survey_spec], SurveySpec)

View File

@@ -5,16 +5,13 @@ from . import page
class SystemJobTemplate(UnifiedJobTemplate, HasNotifications):
def launch(self, payload={}):
"""Launch the system_job_template using related->launch endpoint."""
result = self.related.launch.post(payload)
# return job
jobs_pg = self.get_related('jobs', id=result.json['system_job'])
assert jobs_pg.count == 1, \
"system_job_template launched (id:%s) but unable to find matching " \
"job at %s/jobs/" % (result.json['job'], self.url)
assert jobs_pg.count == 1, "system_job_template launched (id:%s) but unable to find matching " "job at %s/jobs/" % (result.json['job'], self.url)
return jobs_pg.results[0]

View File

@@ -20,9 +20,11 @@ class Team(HasCreate, base.Base):
self.related.users.post(user)
def payload(self, organization, **kwargs):
payload = PseudoNamespace(name=kwargs.get('name') or 'Team - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id)
payload = PseudoNamespace(
name=kwargs.get('name') or 'Team - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
)
return payload
def create_payload(self, name='', description='', organization=Organization, **kwargs):
@@ -36,8 +38,7 @@ class Team(HasCreate, base.Base):
return self.update_identity(Teams(self.connection).post(payload))
page.register_page([resources.team,
(resources.teams, 'post')], Team)
page.register_page([resources.team, (resources.teams, 'post')], Team)
class Teams(page.PageList, Team):
@@ -45,6 +46,4 @@ class Teams(page.PageList, Team):
pass
page.register_page([resources.teams,
resources.credential_owner_teams,
resources.related_teams], Teams)
page.register_page([resources.teams, resources.credential_owner_teams, resources.related_teams], Teams)

View File

@@ -26,38 +26,19 @@ class UnifiedJobTemplate(HasStatus, base.Base):
# formatting issue where result_stdout contained '%s'. This later caused
# a python traceback when attempting to display output from this
# method.
items = [
'id',
'name',
'status',
'source',
'last_update_failed',
'last_updated',
'result_traceback',
'job_explanation',
'job_args']
items = ['id', 'name', 'status', 'source', 'last_update_failed', 'last_updated', 'result_traceback', 'job_explanation', 'job_args']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
output = '<{0.__class__.__name__} {1}>'.format(self, ', '.join(info))
return output.replace('%', '%%')
def add_schedule(
self,
name='',
description='',
enabled=True,
rrule=None,
**kwargs):
def add_schedule(self, name='', description='', enabled=True, rrule=None, **kwargs):
if rrule is None:
rrule = "DTSTART:30180101T000000Z RRULE:FREQ=YEARLY;INTERVAL=1"
payload = dict(
name=name or "{0} Schedule {1}".format(
self.name,
random_title()),
description=description or random_title(10),
enabled=enabled,
rrule=str(rrule))
name=name or "{0} Schedule {1}".format(self.name, random_title()), description=description or random_title(10), enabled=enabled, rrule=str(rrule)
)
update_payload(payload, self.optional_schedule_fields, kwargs)
@@ -70,9 +51,7 @@ class UnifiedJobTemplate(HasStatus, base.Base):
2) not last_update_failed
3) last_updated
"""
return super(
UnifiedJobTemplate,
self).is_successful and not self.last_update_failed and self.last_updated is not None
return super(UnifiedJobTemplate, self).is_successful and not self.last_update_failed and self.last_updated is not None
page.register_page(resources.unified_job_template, UnifiedJobTemplate)

View File

@@ -21,8 +21,7 @@ class UnifiedJob(HasStatus, base.Base):
# NOTE: I use .replace('%', '%%') to workaround an odd string
# formatting issue where result_stdout contained '%s'. This later caused
# a python traceback when attempting to display output from this method.
items = ['id', 'name', 'status', 'failed', 'result_stdout', 'result_traceback',
'job_explanation', 'job_args']
items = ['id', 'name', 'status', 'failed', 'result_stdout', 'result_traceback', 'job_explanation', 'job_args']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
@@ -32,9 +31,7 @@ class UnifiedJob(HasStatus, base.Base):
@property
def result_stdout(self):
if 'result_stdout' not in self.json and 'stdout' in self.related:
return self.connection.get(
self.related.stdout, query_parameters=dict(format='txt_download')
).content.decode()
return self.connection.get(self.related.stdout, query_parameters=dict(format='txt_download')).content.decode()
return self.json.result_stdout.decode()
def assert_text_in_stdout(self, expected_text, replace_spaces=None, replace_newlines=' '):
@@ -55,9 +52,7 @@ class UnifiedJob(HasStatus, base.Base):
stdout = stdout.replace(' ', replace_spaces)
if expected_text not in stdout:
pretty_stdout = pformat(stdout)
raise AssertionError(
'Expected "{}", but it was not found in stdout. Full stdout:\n {}'.format(expected_text, pretty_stdout)
)
raise AssertionError('Expected "{}", but it was not found in stdout. Full stdout:\n {}'.format(expected_text, pretty_stdout))
@property
def is_successful(self):
@@ -103,7 +98,7 @@ class UnifiedJob(HasStatus, base.Base):
# Race condition where job finishes between can_cancel
# check and post.
if not any("not allowed" in field for field in e.msg.values()):
raise(e)
raise (e)
return self.get()
@property
@@ -114,6 +109,7 @@ class UnifiedJob(HasStatus, base.Base):
```assert dict(extra_var=extra_var_val) in unified_job.job_args```
If you need to ensure the job_args are of awx-provided format use raw unified_job.json.job_args.
"""
def attempt_yaml_load(arg):
try:
return yaml.safe_load(arg)
@@ -151,10 +147,7 @@ class UnifiedJob(HasStatus, base.Base):
if host_loc.startswith(expected_prefix):
return host_loc
raise RuntimeError(
'Could not find a controller private_data_dir for this job. '
'Searched for volume mount to {} inside of args {}'.format(
expected_prefix, job_args
)
'Could not find a controller private_data_dir for this job. ' 'Searched for volume mount to {} inside of args {}'.format(expected_prefix, job_args)
)
@@ -163,7 +156,4 @@ class UnifiedJobs(page.PageList, UnifiedJob):
pass
page.register_page([resources.unified_jobs,
resources.instance_related_jobs,
resources.instance_group_related_jobs,
resources.schedules_jobs], UnifiedJobs)
page.register_page([resources.unified_jobs, resources.instance_related_jobs, resources.instance_group_related_jobs, resources.schedules_jobs], UnifiedJobs)

View File

@@ -13,26 +13,13 @@ class User(HasCreate, base.Base):
def payload(self, **kwargs):
payload = PseudoNamespace(
username=kwargs.get('username') or 'User-{}'.format(
random_title(
non_ascii=False)),
username=kwargs.get('username') or 'User-{}'.format(random_title(non_ascii=False)),
password=kwargs.get('password') or config.credentials.default.password,
is_superuser=kwargs.get(
'is_superuser',
False),
is_system_auditor=kwargs.get(
'is_system_auditor',
False),
first_name=kwargs.get(
'first_name',
random_title()),
last_name=kwargs.get(
'last_name',
random_title()),
email=kwargs.get(
'email',
'{}@example.com'.format(random_title(5, non_ascii=False))
)
is_superuser=kwargs.get('is_superuser', False),
is_system_auditor=kwargs.get('is_system_auditor', False),
first_name=kwargs.get('first_name', random_title()),
last_name=kwargs.get('last_name', random_title()),
email=kwargs.get('email', '{}@example.com'.format(random_title(5, non_ascii=False))),
)
return payload
@@ -42,8 +29,7 @@ class User(HasCreate, base.Base):
return payload
def create(self, username='', password='', organization=None, **kwargs):
payload = self.create_payload(
username=username, password=password, **kwargs)
payload = self.create_payload(username=username, password=password, **kwargs)
self.password = payload.password
self.update_identity(Users(self.connection).post(payload))
@@ -54,8 +40,7 @@ class User(HasCreate, base.Base):
return self
page.register_page([resources.user,
(resources.users, 'post')], User)
page.register_page([resources.user, (resources.users, 'post')], User)
class Users(page.PageList, User):
@@ -63,11 +48,9 @@ class Users(page.PageList, User):
pass
page.register_page([resources.users,
resources.organization_admins,
resources.related_users,
resources.credential_owner_users,
resources.user_admin_organizations], Users)
page.register_page(
[resources.users, resources.organization_admins, resources.related_users, resources.credential_owner_users, resources.user_admin_organizations], Users
)
class Me(Users):

View File

@@ -5,7 +5,6 @@ from awxkit import exceptions
class WorkflowApproval(UnifiedJob):
def approve(self):
try:
self.related.approve.post()

View File

@@ -5,7 +5,6 @@ from . import page
class WorkflowJobNode(base.Base):
def wait_for_job(self, interval=5, timeout=60, **kw):
"""Waits until node's job exists"""
adjusted_timeout = timeout - seconds_since_date_string(self.created)
@@ -30,8 +29,13 @@ class WorkflowJobNodes(page.PageList, WorkflowJobNode):
pass
page.register_page([resources.workflow_job_nodes,
resources.workflow_job_workflow_nodes,
resources.workflow_job_node_always_nodes,
resources.workflow_job_node_failure_nodes,
resources.workflow_job_node_success_nodes], WorkflowJobNodes)
page.register_page(
[
resources.workflow_job_nodes,
resources.workflow_job_workflow_nodes,
resources.workflow_job_node_always_nodes,
resources.workflow_job_node_failure_nodes,
resources.workflow_job_node_success_nodes,
],
WorkflowJobNodes,
)

View File

@@ -15,12 +15,9 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
def payload(self, workflow_job_template, unified_job_template, **kwargs):
if not unified_job_template:
# May pass "None" to explicitly create an approval node
payload = PseudoNamespace(
workflow_job_template=workflow_job_template.id)
payload = PseudoNamespace(workflow_job_template=workflow_job_template.id)
else:
payload = PseudoNamespace(
workflow_job_template=workflow_job_template.id,
unified_job_template=unified_job_template.id)
payload = PseudoNamespace(workflow_job_template=workflow_job_template.id, unified_job_template=unified_job_template.id)
optional_fields = (
'diff_mode',
@@ -33,7 +30,8 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
'verbosity',
'extra_data',
'identifier',
'all_parents_must_converge')
'all_parents_must_converge',
)
update_payload(payload, optional_fields, kwargs)
@@ -42,45 +40,23 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
return payload
def create_payload(
self,
workflow_job_template=WorkflowJobTemplate,
unified_job_template=JobTemplate,
**kwargs):
def create_payload(self, workflow_job_template=WorkflowJobTemplate, unified_job_template=JobTemplate, **kwargs):
if not unified_job_template:
self.create_and_update_dependencies(workflow_job_template)
payload = self.payload(
workflow_job_template=self.ds.workflow_job_template,
unified_job_template=None,
**kwargs)
payload = self.payload(workflow_job_template=self.ds.workflow_job_template, unified_job_template=None, **kwargs)
else:
self.create_and_update_dependencies(
workflow_job_template, unified_job_template)
payload = self.payload(
workflow_job_template=self.ds.workflow_job_template,
unified_job_template=self.ds.unified_job_template,
**kwargs)
self.create_and_update_dependencies(workflow_job_template, unified_job_template)
payload = self.payload(workflow_job_template=self.ds.workflow_job_template, unified_job_template=self.ds.unified_job_template, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(
self,
workflow_job_template=WorkflowJobTemplate,
unified_job_template=JobTemplate,
**kwargs):
payload = self.create_payload(
workflow_job_template=workflow_job_template,
unified_job_template=unified_job_template,
**kwargs)
return self.update_identity(
WorkflowJobTemplateNodes(
self.connection).post(payload))
def create(self, workflow_job_template=WorkflowJobTemplate, unified_job_template=JobTemplate, **kwargs):
payload = self.create_payload(workflow_job_template=workflow_job_template, unified_job_template=unified_job_template, **kwargs)
return self.update_identity(WorkflowJobTemplateNodes(self.connection).post(payload))
def _add_node(self, endpoint, unified_job_template, **kwargs):
node = endpoint.post(
dict(unified_job_template=unified_job_template.id, **kwargs))
node.create_and_update_dependencies(
self.ds.workflow_job_template, unified_job_template)
node = endpoint.post(dict(unified_job_template=unified_job_template.id, **kwargs))
node.create_and_update_dependencies(self.ds.workflow_job_template, unified_job_template)
return node
def add_always_node(self, unified_job_template, **kwargs):
@@ -94,24 +70,18 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
def add_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, associate=True))
self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=credential.id, disassociate=True))
self.related.credentials.post(dict(id=credential.id, disassociate=True))
def remove_all_credentials(self):
for cred in self.related.credentials.get().results:
with suppress(exc.NoContent):
self.related.credentials.post(
dict(id=cred.id, disassociate=True))
self.related.credentials.post(dict(id=cred.id, disassociate=True))
def make_approval_node(
self,
**kwargs
):
def make_approval_node(self, **kwargs):
if 'name' not in kwargs:
kwargs['name'] = 'approval node {}'.format(random_title())
self.related.create_approval_template.post(kwargs)
@@ -122,10 +92,10 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
return candidates.results.pop()
page.register_page([resources.workflow_job_template_node,
(resources.workflow_job_template_nodes, 'post'),
(resources.workflow_job_template_workflow_nodes, 'post')],
WorkflowJobTemplateNode)
page.register_page(
[resources.workflow_job_template_node, (resources.workflow_job_template_nodes, 'post'), (resources.workflow_job_template_workflow_nodes, 'post')],
WorkflowJobTemplateNode,
)
class WorkflowJobTemplateNodes(page.PageList, WorkflowJobTemplateNode):
@@ -133,9 +103,13 @@ class WorkflowJobTemplateNodes(page.PageList, WorkflowJobTemplateNode):
pass
page.register_page([resources.workflow_job_template_nodes,
resources.workflow_job_template_workflow_nodes,
resources.workflow_job_template_node_always_nodes,
resources.workflow_job_template_node_failure_nodes,
resources.workflow_job_template_node_success_nodes],
WorkflowJobTemplateNodes)
page.register_page(
[
resources.workflow_job_template_nodes,
resources.workflow_job_template_workflow_nodes,
resources.workflow_job_template_node_always_nodes,
resources.workflow_job_template_node_failure_nodes,
resources.workflow_job_template_node_success_nodes,
],
WorkflowJobTemplateNodes,
)

View File

@@ -26,15 +26,14 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi
# return job
jobs_pg = self.related.workflow_jobs.get(id=result.workflow_job)
if jobs_pg.count != 1:
msg = "workflow_job_template launched (id:{}) but job not found in response at {}/workflow_jobs/".format(
result.json['workflow_job'], self.url
)
msg = "workflow_job_template launched (id:{}) but job not found in response at {}/workflow_jobs/".format(result.json['workflow_job'], self.url)
raise exc.UnexpectedAWXState(msg)
return jobs_pg.results[0]
def payload(self, **kwargs):
payload = PseudoNamespace(name=kwargs.get('name') or 'WorkflowJobTemplate - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10))
payload = PseudoNamespace(
name=kwargs.get('name') or 'WorkflowJobTemplate - {}'.format(random_title()), description=kwargs.get('description') or random_title(10)
)
optional_fields = (
"allow_simultaneous",
@@ -91,9 +90,9 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi
self.related.labels.post(label)
page.register_page([resources.workflow_job_template,
(resources.workflow_job_templates, 'post'),
(resources.workflow_job_template_copy, 'post')], WorkflowJobTemplate)
page.register_page(
[resources.workflow_job_template, (resources.workflow_job_templates, 'post'), (resources.workflow_job_template_copy, 'post')], WorkflowJobTemplate
)
class WorkflowJobTemplates(page.PageList, WorkflowJobTemplate):
@@ -101,8 +100,7 @@ class WorkflowJobTemplates(page.PageList, WorkflowJobTemplate):
pass
page.register_page([resources.workflow_job_templates,
resources.related_workflow_job_templates], WorkflowJobTemplates)
page.register_page([resources.workflow_job_templates, resources.related_workflow_job_templates], WorkflowJobTemplates)
class WorkflowJobTemplateLaunch(base.Base):

View File

@@ -4,7 +4,6 @@ from . import page
class WorkflowJob(UnifiedJob):
def __str__(self):
# TODO: Update after endpoint's fields are finished filling out
return super(UnifiedJob, self).__str__()
@@ -56,7 +55,4 @@ class WorkflowJobs(page.PageList, WorkflowJob):
pass
page.register_page([resources.workflow_jobs,
resources.workflow_job_template_jobs,
resources.job_template_slice_workflow_jobs],
WorkflowJobs)
page.register_page([resources.workflow_jobs, resources.workflow_job_template_jobs, resources.job_template_slice_workflow_jobs], WorkflowJobs)

View File

@@ -8,7 +8,6 @@ log = logging.getLogger(__name__)
class URLRegistry(object):
def __init__(self):
self.store = defaultdict(dict)
self.default = {}
@@ -81,8 +80,7 @@ class URLRegistry(object):
if method_pattern.pattern == not_provided:
exc_msg = '"{0.pattern}" already has methodless registration.'.format(url_pattern)
else:
exc_msg = ('"{0.pattern}" already has registered method "{1.pattern}"'
.format(url_pattern, method_pattern))
exc_msg = '"{0.pattern}" already has registered method "{1.pattern}"'.format(url_pattern, method_pattern)
raise TypeError(exc_msg)
self.store[url_pattern][method_pattern] = resource

View File

@@ -1,4 +1,3 @@
class Resources(object):
_activity = r'activity_stream/\d+/'

View File

@@ -15,12 +15,11 @@ def freeze(key):
def parse_description(desc):
options = {}
for line in desc[desc.index('POST'):].splitlines():
for line in desc[desc.index('POST') :].splitlines():
match = descRE.match(line)
if not match:
continue
options[match.group(1)] = {'type': match.group(2),
'required': match.group(3) == 'required'}
options[match.group(1)] = {'type': match.group(2), 'required': match.group(3) == 'required'}
return options
@@ -45,6 +44,5 @@ def get_post_fields(page, cache):
if 'POST' in options_page.json['actions']:
return options_page.json['actions']['POST']
else:
log.warning(
"Insufficient privileges on %s, inferring POST fields from description.", options_page.endpoint)
log.warning("Insufficient privileges on %s, inferring POST fields from description.", options_page.endpoint)
return parse_description(options_page.json['description'])