From 4ead07c737329c0e29254e70453bff5694947f2d Mon Sep 17 00:00:00 2001 From: Bryan Strassner Date: Tue, 18 Jul 2017 14:32:10 -0500 Subject: [PATCH] Add task commands to CLI with basic functionality task create, task list, task show included with this commit. --- drydock_provisioner/cli/commands.py | 2 + drydock_provisioner/cli/design/actions.py | 4 +- drydock_provisioner/cli/part/commands.py | 8 +- drydock_provisioner/cli/task/actions.py | 84 ++++++++++++++++++++ drydock_provisioner/cli/task/commands.py | 81 +++++++++++++++++++ drydock_provisioner/drydock_client/client.py | 2 +- setup.py | 1 + 7 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 drydock_provisioner/cli/task/actions.py create mode 100644 drydock_provisioner/cli/task/commands.py diff --git a/drydock_provisioner/cli/commands.py b/drydock_provisioner/cli/commands.py index 75e4c625..85f79fbb 100644 --- a/drydock_provisioner/cli/commands.py +++ b/drydock_provisioner/cli/commands.py @@ -22,6 +22,7 @@ from drydock_provisioner.drydock_client.session import DrydockSession from drydock_provisioner.drydock_client.client import DrydockClient from .design import commands as design from .part import commands as part +from .task import commands as task @click.group() @click.option('--debug/--no-debug', @@ -75,3 +76,4 @@ def drydock(ctx, debug, token, url): drydock.add_command(design.design) drydock.add_command(part.part) +drydock.add_command(task.task) diff --git a/drydock_provisioner/cli/design/actions.py b/drydock_provisioner/cli/design/actions.py index 224b8d69..edf8e2d3 100644 --- a/drydock_provisioner/cli/design/actions.py +++ b/drydock_provisioner/cli/design/actions.py @@ -27,9 +27,11 @@ class DesignList(CliAction): # pylint: disable=too-few-public-methods class DesignCreate(CliAction): # pylint: disable=too-few-public-methods """ Action to create designs - :param string base_design: A UUID of the base design to model after """ def __init__(self, api_client, base_design=None): + """ + :param string base_design: A UUID of the base design to model after + """ super().__init__(api_client) self.logger.debug("DesignCreate action initialized with base_design=%s", base_design) self.base_design = base_design diff --git a/drydock_provisioner/cli/part/commands.py b/drydock_provisioner/cli/part/commands.py index 86d3251b..078c9ea2 100644 --- a/drydock_provisioner/cli/part/commands.py +++ b/drydock_provisioner/cli/part/commands.py @@ -74,8 +74,12 @@ def part_show(ctx, source, kind, key): """ if not kind: ctx.fail('The kind must be specified by --kind') - + if not key: ctx.fail('The key must be specified by --key') - click.echo(PartShow(ctx.obj['CLIENT'], design_id=ctx.obj['DESIGN_ID'], kind=kind, key=key, source=source).invoke()) + click.echo(PartShow(ctx.obj['CLIENT'], + design_id=ctx.obj['DESIGN_ID'], + kind=kind, + key=key, + source=source).invoke()) diff --git a/drydock_provisioner/cli/task/actions.py b/drydock_provisioner/cli/task/actions.py new file mode 100644 index 00000000..1fc3d437 --- /dev/null +++ b/drydock_provisioner/cli/task/actions.py @@ -0,0 +1,84 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" Actions related to task commands +""" + +from drydock_provisioner.cli.action import CliAction + +class TaskList(CliAction): # pylint: disable=too-few-public-methods + """ Action to list tasks + """ + def __init__(self, api_client): + """ + :param DrydockClient api_client: The api client used for invocation. + """ + super().__init__(api_client) + self.logger.debug('TaskList action initialized') + + def invoke(self): + return self.api_client.get_tasks() + +class TaskCreate(CliAction): # pylint: disable=too-few-public-methods + """ Action to create tasks against a design + """ + def __init__(self, api_client, design_id, action_name=None, node_names=None, rack_names=None, node_tags=None): + """ + node_filter : {node_names: [], rack_names:[], node-tags[]} + :param DrydockClient api_client: The api client used for invocation. + :param string design_id: The UUID of the design for which to create a task + :param string action_name: The name of the action being performed for this task + :param List node_names: The list of node names to restrict action application + :param List rack_names: The list of rack names to restrict action application + :param List node_tags: The list of node tags to restrict action application + """ + super().__init__(api_client) + self.design_id = design_id + self.action_name = action_name + self.logger.debug('TaskCreate action initialized for design=%s', design_id) + self.logger.debug('Action is %s', action_name) + if node_names is None: + node_names = [] + if rack_names is None: + rack_names = [] + if node_tags is None: + node_tags = [] + + self.logger.debug("Node names = %s", node_names) + self.logger.debug("Rack names = %s", rack_names) + self.logger.debug("Node tags = %s", node_tags) + + self.node_filter = {'node_names' : node_names, + 'rack_names' : rack_names, + 'node_tags' : node_tags + } + + def invoke(self): + return self.api_client.create_task(design_id=self.design_id, + task_action=self.action_name, + node_filter=self.node_filter) + +class TaskShow(CliAction): # pylint: disable=too-few-public-methods + """ Action to show a task's detial. + """ + def __init__(self, api_client, task_id): + """ + :param DrydockClient api_client: The api client used for invocation. + :param string task_id: the UUID of the task to retrieve + """ + super().__init__(api_client) + self.task_id = task_id + self.logger.debug('TaskShow action initialized for task_id=%s,', task_id) + + def invoke(self): + return self.api_client.get_task(task_id=self.task_id) diff --git a/drydock_provisioner/cli/task/commands.py b/drydock_provisioner/cli/task/commands.py new file mode 100644 index 00000000..c9ad25e9 --- /dev/null +++ b/drydock_provisioner/cli/task/commands.py @@ -0,0 +1,81 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" cli.task.commands + Contains commands related to tasks against designs +""" +import click + +from drydock_provisioner.cli.task.actions import TaskList +from drydock_provisioner.cli.task.actions import TaskShow +from drydock_provisioner.cli.task.actions import TaskCreate + +@click.group() +def task(): + """ Drydock task commands + """ + +@task.command(name='create') +@click.option('--design-id', + '-d', + help='The design id for this action') +@click.option('--action', + '-a', + help='The action to perform') +@click.option('--node-names', + '-n', + help='The nodes targeted by this action, comma separated') +@click.option('--rack-names', + '-r', + help='The racks targeted by this action, comma separated') +@click.option('--node-tags', + '-t', + help='The nodes by tag name targeted by this action, comma separated') +@click.pass_context +def task_create(ctx, design_id=None, action=None, node_names=None, rack_names=None, node_tags=None): + """ Create a task + """ + if not design_id: + ctx.fail('Error: Design id must be specified using --design-id') + + if not action: + ctx.fail('Error: Action must be specified using --action') + + click.echo(TaskCreate(ctx.obj['CLIENT'], + design_id=design_id, + action_name=action, + node_names=[x.strip() for x in node_names.split(',')] if node_names else [], + rack_names=[x.strip() for x in rack_names.split(',')] if rack_names else [], + node_tags=[x.strip() for x in node_tags.split(',')] if node_tags else [] + ).invoke()) + +@task.command(name='list') +@click.pass_context +def task_list(ctx): + """ List tasks. + """ + click.echo(TaskList(ctx.obj['CLIENT']).invoke()) + +@task.command(name='show') +@click.option('--task-id', + '-t', + help='The required task id') +@click.pass_context +def task_show(ctx, task_id=None): + """ show a task's details + """ + if not task_id: + ctx.fail('The task id must be specified by --task-id') + + click.echo(TaskShow(ctx.obj['CLIENT'], + task_id=task_id).invoke()) diff --git a/drydock_provisioner/drydock_client/client.py b/drydock_provisioner/drydock_client/client.py index 63481b67..3fe12f5f 100644 --- a/drydock_provisioner/drydock_client/client.py +++ b/drydock_provisioner/drydock_client/client.py @@ -186,7 +186,7 @@ class DrydockClient(object): resp = self.session.post(endpoint, data=task_dict) if resp.status_code == 201: - return resp.json().get('id') + return resp.json().get('task_id') elif resp.status_code == 400: raise errors.ClientError("Invalid inputs, received a %d: %s" % (resp.status_code, resp.text), code=resp.status_code) diff --git a/setup.py b/setup.py index 227af94e..3e1fa69a 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ setup(name='drydock_provisioner', 'drydock_provisioner.cli', 'drydock_provisioner.cli.design', 'drydock_provisioner.cli.part', + 'drydock_provisioner.cli.task', 'drydock_provisioner.drydock_client'], install_requires=[ 'PyYAML',