mirror of
https://github.com/ZwareBear/awx.git
synced 2026-03-20 07:43:35 -05:00
awxkit cli support
fixes for awx cli
This commit is contained in:
committed by
Elijah DeLee
parent
861ba8a727
commit
34834252ff
@@ -1971,7 +1971,8 @@ class BulkHostCreateSerializer(serializers.Serializer):
|
|||||||
inventory = serializers.PrimaryKeyRelatedField(
|
inventory = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=Inventory.objects.all(), required=True, write_only=True, help_text=_('Primary Key ID of inventory to add hosts to.')
|
queryset=Inventory.objects.all(), required=True, write_only=True, help_text=_('Primary Key ID of inventory to add hosts to.')
|
||||||
)
|
)
|
||||||
hosts = serializers.ListField(child=BulkHostSerializer(), allow_empty=False, max_length=1000, write_only=True, help_text=_('Hosts to be created.'))
|
hosts_help_text = _('List of hosts to be created, JSON. e.g. [{"name": "example.com"}, {"name": "127.0.0.1"}]')
|
||||||
|
hosts = serializers.ListField(child=BulkHostSerializer(), allow_empty=False, max_length=1000, write_only=True, help_text=hosts_help_text)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('inventory', 'hosts')
|
fields = ('inventory', 'hosts')
|
||||||
@@ -4582,8 +4583,9 @@ class BulkJobNodeSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class BulkJobLaunchSerializer(BaseSerializer):
|
class BulkJobLaunchSerializer(BaseSerializer):
|
||||||
name = serializers.CharField(max_length=512, write_only=True, required=False) # limited by max name of jobs
|
name = serializers.CharField(default='Bulk Job Launch', max_length=512, write_only=True, required=False, allow_blank=True) # limited by max name of jobs
|
||||||
jobs = BulkJobNodeSerializer(many=True, allow_empty=False, write_only=True, max_length=1000)
|
job_node_help_text = _('List of jobs to be launched, JSON. e.g. [{"unified_job_template": 7}, {"unified_job_template": 10}]')
|
||||||
|
jobs = BulkJobNodeSerializer(many=True, allow_empty=False, write_only=True, max_length=1000, help_text=job_node_help_text)
|
||||||
description = serializers.CharField(write_only=True, required=False, allow_blank=False)
|
description = serializers.CharField(write_only=True, required=False, allow_blank=False)
|
||||||
extra_vars = serializers.CharField(write_only=True, required=False, allow_blank=False)
|
extra_vars = serializers.CharField(write_only=True, required=False, allow_blank=False)
|
||||||
organization = serializers.PrimaryKeyRelatedField(
|
organization = serializers.PrimaryKeyRelatedField(
|
||||||
@@ -4673,11 +4675,11 @@ class BulkJobLaunchSerializer(BaseSerializer):
|
|||||||
job_node_data = validated_data.pop('jobs')
|
job_node_data = validated_data.pop('jobs')
|
||||||
# FIXME: Need to set organization on the WorkflowJob in order for users to be able to see it --
|
# FIXME: Need to set organization on the WorkflowJob in order for users to be able to see it --
|
||||||
# normally their permission is sourced from the underlying WorkflowJobTemplate
|
# normally their permission is sourced from the underlying WorkflowJobTemplate
|
||||||
# maybe we need to add Organization to WorkflowJob
|
# maybe we need to add Organization to WorkflowJobd
|
||||||
if 'name' not in validated_data:
|
wfj_limit = validated_data.pop('limit', None)
|
||||||
validated_data['name'] = 'Bulk Job Launch'
|
|
||||||
|
|
||||||
wfj = WorkflowJob.objects.create(**validated_data, is_bulk_job=True)
|
wfj = WorkflowJob.objects.create(**validated_data, is_bulk_job=True)
|
||||||
|
if wfj_limit:
|
||||||
|
wfj.limit = wfj_limit
|
||||||
nodes = []
|
nodes = []
|
||||||
node_m2m_objects = {}
|
node_m2m_objects = {}
|
||||||
node_m2m_object_types_to_through_model = {
|
node_m2m_object_types_to_through_model = {
|
||||||
|
|||||||
@@ -16,9 +16,25 @@ from awx.api import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BulkJobLaunchView(GenericAPIView):
|
class BulkView(APIView):
|
||||||
_ignore_model_permissions = True
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
renderer_classes = [
|
||||||
|
renderers.BrowsableAPIRenderer,
|
||||||
|
JSONRenderer,
|
||||||
|
]
|
||||||
|
allowed_methods = ['GET', 'OPTIONS']
|
||||||
|
|
||||||
|
def get(self, request, format=None):
|
||||||
|
'''List top level resources'''
|
||||||
|
data = OrderedDict()
|
||||||
|
data['host_create'] = reverse('api:bulk_host_create', request=request)
|
||||||
|
data['job_launch'] = reverse('api:bulk_job_launch', request=request)
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
|
class BulkJobLaunchView(GenericAPIView):
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
model = UnifiedJob
|
||||||
serializer_class = serializers.BulkJobLaunchSerializer
|
serializer_class = serializers.BulkJobLaunchSerializer
|
||||||
allowed_methods = ['GET', 'POST', 'OPTIONS']
|
allowed_methods = ['GET', 'POST', 'OPTIONS']
|
||||||
|
|
||||||
@@ -35,26 +51,9 @@ class BulkJobLaunchView(GenericAPIView):
|
|||||||
return Response(bulkjob_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(bulkjob_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class BulkView(APIView):
|
|
||||||
_ignore_model_permissions = True
|
|
||||||
permission_classes = [IsAuthenticated]
|
|
||||||
renderer_classes = [
|
|
||||||
renderers.BrowsableAPIRenderer,
|
|
||||||
JSONRenderer,
|
|
||||||
]
|
|
||||||
allowed_methods = ['GET', 'OPTIONS']
|
|
||||||
|
|
||||||
def get(self, request, format=None):
|
|
||||||
'''List top level resources'''
|
|
||||||
data = OrderedDict()
|
|
||||||
data['bulk_host_create'] = reverse('api:bulk_host_create', request=request)
|
|
||||||
data['bulk_job_launch'] = reverse('api:bulk_job_launch', request=request)
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkHostCreateView(GenericAPIView):
|
class BulkHostCreateView(GenericAPIView):
|
||||||
_ignore_model_permissions = True
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
model = Host
|
||||||
serializer_class = serializers.BulkHostCreateSerializer
|
serializer_class = serializers.BulkHostCreateSerializer
|
||||||
allowed_methods = ['GET', 'POST', 'OPTIONS']
|
allowed_methods = ['GET', 'POST', 'OPTIONS']
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Order matters
|
# Order matters
|
||||||
from .page import * # NOQA
|
from .page import * # NOQA
|
||||||
from .base import * # NOQA
|
from .base import * # NOQA
|
||||||
|
from .bulk import * # NOQA
|
||||||
from .access_list import * # NOQA
|
from .access_list import * # NOQA
|
||||||
from .api import * # NOQA
|
from .api import * # NOQA
|
||||||
from .authtoken import * # NOQA
|
from .authtoken import * # NOQA
|
||||||
|
|||||||
12
awxkit/awxkit/api/pages/bulk.py
Normal file
12
awxkit/awxkit/api/pages/bulk.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from awxkit.api.resources import resources
|
||||||
|
from . import base
|
||||||
|
from . import page
|
||||||
|
|
||||||
|
|
||||||
|
class Bulk(base.Base):
|
||||||
|
def get(self, **query_parameters):
|
||||||
|
request = self.connection.get(self.endpoint, query_parameters, headers={'Accept': 'application/json'})
|
||||||
|
return self.page_identity(request)
|
||||||
|
|
||||||
|
|
||||||
|
page.register_page([resources.bulk, (resources.bulk, 'get')], Bulk)
|
||||||
@@ -13,6 +13,7 @@ class Resources(object):
|
|||||||
_applications = 'applications/'
|
_applications = 'applications/'
|
||||||
_auth = 'auth/'
|
_auth = 'auth/'
|
||||||
_authtoken = 'authtoken/'
|
_authtoken = 'authtoken/'
|
||||||
|
_bulk = 'bulk/'
|
||||||
_config = 'config/'
|
_config = 'config/'
|
||||||
_config_attach = 'config/attach/'
|
_config_attach = 'config/attach/'
|
||||||
_credential = r'credentials/\d+/'
|
_credential = r'credentials/\d+/'
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ class CustomAction(metaclass=CustomActionRegistryMeta):
|
|||||||
|
|
||||||
|
|
||||||
class Launchable(object):
|
class Launchable(object):
|
||||||
|
@property
|
||||||
|
def options_endpoint(self):
|
||||||
|
return self.page.endpoint + '1/{}/'.format(self.action)
|
||||||
|
|
||||||
def add_arguments(self, parser, resource_options_parser, with_pk=True):
|
def add_arguments(self, parser, resource_options_parser, with_pk=True):
|
||||||
from .options import pk_or_name
|
from .options import pk_or_name
|
||||||
|
|
||||||
@@ -53,7 +57,7 @@ class Launchable(object):
|
|||||||
parser.choices[self.action].add_argument('--action-timeout', type=int, help='If set with --monitor or --wait, time out waiting on job completion.')
|
parser.choices[self.action].add_argument('--action-timeout', type=int, help='If set with --monitor or --wait, time out waiting on job completion.')
|
||||||
parser.choices[self.action].add_argument('--wait', action='store_true', help='If set, waits until the launched job finishes.')
|
parser.choices[self.action].add_argument('--wait', action='store_true', help='If set, waits until the launched job finishes.')
|
||||||
|
|
||||||
launch_time_options = self.page.connection.options(self.page.endpoint + '1/{}/'.format(self.action))
|
launch_time_options = self.page.connection.options(self.options_endpoint)
|
||||||
if launch_time_options.ok:
|
if launch_time_options.ok:
|
||||||
launch_time_options = launch_time_options.json()['actions']['POST']
|
launch_time_options = launch_time_options.json()['actions']['POST']
|
||||||
resource_options_parser.options['LAUNCH'] = launch_time_options
|
resource_options_parser.options['LAUNCH'] = launch_time_options
|
||||||
@@ -90,6 +94,48 @@ class JobTemplateLaunch(Launchable, CustomAction):
|
|||||||
resource = 'job_templates'
|
resource = 'job_templates'
|
||||||
|
|
||||||
|
|
||||||
|
class BulkJobLaunch(Launchable, CustomAction):
|
||||||
|
action = 'job_launch'
|
||||||
|
resource = 'bulk'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options_endpoint(self):
|
||||||
|
return self.page.endpoint + '{}/'.format(self.action)
|
||||||
|
|
||||||
|
def add_arguments(self, parser, resource_options_parser):
|
||||||
|
Launchable.add_arguments(self, parser, resource_options_parser, with_pk=False)
|
||||||
|
|
||||||
|
def perform(self, **kwargs):
|
||||||
|
monitor_kwargs = {
|
||||||
|
'monitor': kwargs.pop('monitor', False),
|
||||||
|
'wait': kwargs.pop('wait', False),
|
||||||
|
'action_timeout': kwargs.pop('action_timeout', False),
|
||||||
|
}
|
||||||
|
response = self.page.get().job_launch.post(kwargs)
|
||||||
|
self.monitor(response, **monitor_kwargs)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class BulkHostCreate(CustomAction):
|
||||||
|
action = 'host_create'
|
||||||
|
resource = 'bulk'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options_endpoint(self):
|
||||||
|
return self.page.endpoint + '{}/'.format(self.action)
|
||||||
|
|
||||||
|
def add_arguments(self, parser, resource_options_parser):
|
||||||
|
options = self.page.connection.options(self.options_endpoint)
|
||||||
|
if options.ok:
|
||||||
|
options = options.json()['actions']['POST']
|
||||||
|
resource_options_parser.options['HOSTCREATEPOST'] = options
|
||||||
|
resource_options_parser.build_query_arguments(self.action, 'HOSTCREATEPOST')
|
||||||
|
|
||||||
|
def perform(self, **kwargs):
|
||||||
|
response = self.page.get().host_create.post(kwargs)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdate(Launchable, CustomAction):
|
class ProjectUpdate(Launchable, CustomAction):
|
||||||
action = 'update'
|
action = 'update'
|
||||||
resource = 'projects'
|
resource = 'projects'
|
||||||
|
|||||||
@@ -163,7 +163,10 @@ class ResourceOptionsParser(object):
|
|||||||
if method == 'list' and param.get('filterable') is False:
|
if method == 'list' and param.get('filterable') is False:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def json_or_yaml(v):
|
def list_of_json_or_yaml(v):
|
||||||
|
return json_or_yaml(v, expected_type=list)
|
||||||
|
|
||||||
|
def json_or_yaml(v, expected_type=dict):
|
||||||
if v.startswith('@'):
|
if v.startswith('@'):
|
||||||
v = open(os.path.expanduser(v[1:])).read()
|
v = open(os.path.expanduser(v[1:])).read()
|
||||||
try:
|
try:
|
||||||
@@ -174,9 +177,10 @@ class ResourceOptionsParser(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise argparse.ArgumentTypeError("{} is not valid JSON or YAML".format(v))
|
raise argparse.ArgumentTypeError("{} is not valid JSON or YAML".format(v))
|
||||||
|
|
||||||
if not isinstance(parsed, dict):
|
if not isinstance(parsed, expected_type):
|
||||||
raise argparse.ArgumentTypeError("{} is not valid JSON or YAML".format(v))
|
raise argparse.ArgumentTypeError("{} is not valid JSON or YAML".format(v))
|
||||||
|
|
||||||
|
if expected_type is dict:
|
||||||
for k, v in parsed.items():
|
for k, v in parsed.items():
|
||||||
# add support for file reading at top-level JSON keys
|
# add support for file reading at top-level JSON keys
|
||||||
# (to make things like SSH key data easier to work with)
|
# (to make things like SSH key data easier to work with)
|
||||||
@@ -258,6 +262,19 @@ class ResourceOptionsParser(object):
|
|||||||
if k == 'extra_vars':
|
if k == 'extra_vars':
|
||||||
args.append('-e')
|
args.append('-e')
|
||||||
|
|
||||||
|
# special handling for bulk endpoints
|
||||||
|
if self.resource == 'bulk':
|
||||||
|
if method == "host_create":
|
||||||
|
if k == "inventory":
|
||||||
|
kwargs['required'] = required = True
|
||||||
|
if k == 'hosts':
|
||||||
|
kwargs['type'] = list_of_json_or_yaml
|
||||||
|
kwargs['required'] = required = True
|
||||||
|
if method == "job_launch":
|
||||||
|
if k == 'jobs':
|
||||||
|
kwargs['type'] = list_of_json_or_yaml
|
||||||
|
kwargs['required'] = required = True
|
||||||
|
|
||||||
if required:
|
if required:
|
||||||
if required_group is None:
|
if required_group is None:
|
||||||
required_group = self.parser.choices[method].add_argument_group('required arguments')
|
required_group = self.parser.choices[method].add_argument_group('required arguments')
|
||||||
|
|||||||
Reference in New Issue
Block a user