Add build data access to Drydock client
- Add CLI actions to output the build data for a node or a task - Add API methods to access the Drydock API to retrieve node or task build data Change-Id: I0ee01bd4b165b93c2bc0e3050554514ba40f152a
This commit is contained in:
parent
f57301fae9
commit
246775da42
4
Makefile
4
Makefile
@ -45,7 +45,7 @@ external_dep: requirements-host.txt
|
||||
|
||||
# Run unit and Postgres integration tests in coverage mode
|
||||
.PHONY: coverage_test
|
||||
coverage_test: build_drydock external_dep
|
||||
coverage_test: build_drydock
|
||||
tox -re cover
|
||||
|
||||
# Run just unit tests
|
||||
@ -101,7 +101,7 @@ helm-install:
|
||||
# Make targets intended for use by the primary targets above.
|
||||
|
||||
.PHONY: build_drydock
|
||||
build_drydock:
|
||||
build_drydock: external_dep
|
||||
ifeq ($(USE_PROXY), true)
|
||||
docker build --network host -t $(IMAGE) --label $(LABEL) -f images/drydock/Dockerfile \
|
||||
--build-arg http_proxy=$(PROXY) \
|
||||
|
@ -30,3 +30,22 @@ class NodeList(CliAction): # pylint: disable=too-few-public-methods
|
||||
|
||||
def invoke(self):
|
||||
return self.api_client.get_nodes()
|
||||
|
||||
|
||||
class NodeBuildData(CliAction):
|
||||
""" Action to print node build data."""
|
||||
|
||||
def __init__(self, api_client, nodename, latest):
|
||||
"""
|
||||
:param DrydockClient api_client: the api client used for invocation.
|
||||
:param str nodename: The name of the node to retrieve data for.
|
||||
:param bool latest: If only the latest build data should be retrieved.
|
||||
"""
|
||||
super().__init__(api_client)
|
||||
self.nodename = nodename
|
||||
self.latest = latest
|
||||
self.logger.debug('NodeBuildData action initialized')
|
||||
|
||||
def invoke(self):
|
||||
return self.api_client.get_node_build_data(
|
||||
self.nodename, latest=self.latest)
|
||||
|
@ -16,10 +16,12 @@
|
||||
"""
|
||||
import click
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from prettytable import PrettyTable
|
||||
|
||||
from drydock_provisioner.cli.node.actions import NodeList
|
||||
from drydock_provisioner.cli.node.actions import NodeBuildData
|
||||
|
||||
|
||||
@click.group()
|
||||
@ -54,3 +56,27 @@ def node_list(ctx, output='table'):
|
||||
click.echo(pt)
|
||||
elif output == 'json':
|
||||
click.echo(json.dumps(nodelist))
|
||||
|
||||
|
||||
@node.command(name='builddata')
|
||||
@click.option(
|
||||
'--latest/--no-latest',
|
||||
help='Retrieve only the latest data items.',
|
||||
default=True)
|
||||
@click.option(
|
||||
'--output', '-o', help='Output format: yaml|json', default='yaml')
|
||||
@click.argument('nodename')
|
||||
@click.pass_context
|
||||
def node_builddata(ctx, nodename, latest=True, output='yaml'):
|
||||
"""List build data for ``nodename``."""
|
||||
node_bd = NodeBuildData(ctx.obj['CLIENT'], nodename, latest).invoke()
|
||||
|
||||
if output == 'json':
|
||||
click.echo(json.dumps(node_bd))
|
||||
else:
|
||||
if output != 'yaml':
|
||||
click.echo(
|
||||
"Invalid output format {}, default to YAML.".format(output))
|
||||
click.echo(
|
||||
yaml.safe_dump(
|
||||
node_bd, allow_unicode=True, default_flow_style=False))
|
||||
|
@ -141,3 +141,18 @@ class TaskShow(CliAction): # pylint: disable=too-few-public-methods
|
||||
task = self.api_client.get_task(task_id=task_id)
|
||||
if task.status in [TaskStatus.Complete, TaskStatus.Terminated]:
|
||||
return task
|
||||
|
||||
|
||||
class TaskBuildData(CliAction):
|
||||
"""Action to retrieve task build data."""
|
||||
|
||||
def __init__(self, api_client, task_id):
|
||||
"""
|
||||
:param DrydockClient api_client: the api client instance used for invocation.
|
||||
:param str task_id: A UUID-like task_id
|
||||
"""
|
||||
super().__init__(api_client)
|
||||
self.task_id = task_id
|
||||
|
||||
def invoke(self):
|
||||
return self.api_client.get_task_build_data(self.task_id)
|
||||
|
@ -14,10 +14,12 @@
|
||||
"""Contains commands related to tasks against designs."""
|
||||
import click
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from drydock_provisioner.cli.task.actions import TaskList
|
||||
from drydock_provisioner.cli.task.actions import TaskShow
|
||||
from drydock_provisioner.cli.task.actions import TaskCreate
|
||||
from drydock_provisioner.cli.task.actions import TaskBuildData
|
||||
|
||||
|
||||
@click.group()
|
||||
@ -105,3 +107,26 @@ def task_show(ctx, task_id=None, block=False):
|
||||
|
||||
click.echo(
|
||||
json.dumps(TaskShow(ctx.obj['CLIENT'], task_id=task_id).invoke()))
|
||||
|
||||
|
||||
@task.command(name='builddata')
|
||||
@click.option('--task-id', '-t', help='The required task id')
|
||||
@click.option(
|
||||
'--output', '-o', help='The output format (yaml|json)', default='yaml')
|
||||
@click.pass_context
|
||||
def task_builddata(ctx, task_id=None, output='yaml'):
|
||||
"""Show builddata assoicated with ``task_id``."""
|
||||
if not task_id:
|
||||
ctx.fail('The task id must be specified by --task-id')
|
||||
|
||||
task_bd = TaskBuildData(ctx.obj['CLIENT'], task_id=task_id).invoke()
|
||||
|
||||
if output == 'json':
|
||||
click.echo(json.dumps(task_bd))
|
||||
else:
|
||||
if output != 'yaml':
|
||||
click.echo(
|
||||
'Invalid output format {}, defaulting to YAML.'.format(output))
|
||||
click.echo(
|
||||
yaml.safe_dump(
|
||||
task_bd, allow_unicode=True, default_flow_style=False))
|
||||
|
@ -711,10 +711,9 @@ class ConfigureNodeProvisioner(BaseMaasAction):
|
||||
self.task.failure()
|
||||
if repo_list.remove_unlisted:
|
||||
defined_repos = [x.get_id() for x in repo_list]
|
||||
to_delete = [r
|
||||
for r
|
||||
in current_repos
|
||||
if r.name not in defined_repos]
|
||||
to_delete = [
|
||||
r for r in current_repos if r.name not in defined_repos
|
||||
]
|
||||
for r in to_delete:
|
||||
if r.name not in self.DEFAULT_REPOS:
|
||||
r.delete()
|
||||
@ -745,11 +744,13 @@ class ConfigureNodeProvisioner(BaseMaasAction):
|
||||
model_fields['distributions'] = ','.join(repo_obj.distributions)
|
||||
if repo_obj.components:
|
||||
if repo_obj.get_id() in ConfigureNodeProvisioner.DEFAULT_REPOS:
|
||||
model_fields['disabled_components'] = ','.join(repo_obj.get_disabled_components())
|
||||
model_fields['disabled_components'] = ','.join(
|
||||
repo_obj.get_disabled_components())
|
||||
else:
|
||||
model_fields['components'] = ','.join(repo_obj.components)
|
||||
if repo_obj.get_disabled_subrepos():
|
||||
model_fields['disabled_pockets'] = ','.join(repo_obj.get_disabled_subrepos())
|
||||
model_fields['disabled_pockets'] = ','.join(
|
||||
repo_obj.get_disabled_subrepos())
|
||||
if repo_obj.arches:
|
||||
model_fields['arches'] = ','.join(repo_obj.arches)
|
||||
|
||||
|
@ -29,6 +29,31 @@ class DrydockClient(object):
|
||||
self.session = session
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def get_task_build_data(self, task_id):
|
||||
"""Get the build data associated with ``task_id``.
|
||||
|
||||
:param str task_id: A UUID-formatted task ID
|
||||
:return: A list of dictionaries resembling objects.builddata.BuildData
|
||||
"""
|
||||
endpoint = 'v1.0/tasks/{}/builddata'.format(task_id)
|
||||
|
||||
resp = self.session.get(endpoint)
|
||||
self._check_response(resp)
|
||||
return resp.json()
|
||||
|
||||
def get_node_build_data(self, nodename, latest=True):
|
||||
"""Get the build data associated with ``nodename``.
|
||||
|
||||
:param str nodename: Name of the node
|
||||
:param bool latest: Whether to request only the latest version of each data item
|
||||
:return: A list of dictionaries resembling objects.builddata.BuildData
|
||||
"""
|
||||
endpoint = 'v1.0/nodes/{}/builddata?latest={}'.format(nodename, latest)
|
||||
|
||||
resp = self.session.get(endpoint)
|
||||
self._check_response(resp)
|
||||
return resp.json()
|
||||
|
||||
def get_nodes(self):
|
||||
"""Get list of nodes in MaaS and their status."""
|
||||
endpoint = 'v1.0/nodes'
|
||||
|
@ -132,6 +132,7 @@ class Repository(base.DrydockObject):
|
||||
std = self.STANDARD_SUBREPOS.get(self.repo_type, ())
|
||||
return std - enabled
|
||||
|
||||
|
||||
@base.DrydockObjectRegistry.register
|
||||
class RepositoryList(base.DrydockObjectListBase, base.DrydockObject):
|
||||
|
||||
|
@ -11,11 +11,18 @@
|
||||
# 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.
|
||||
import yaml
|
||||
import pytest
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
import drydock_provisioner.drydock_client.session as dc_session
|
||||
import drydock_provisioner.drydock_client.client as dc_client
|
||||
|
||||
from drydock_provisioner.cli.task.actions import TaskCreate
|
||||
from drydock_provisioner.cli.task.actions import TaskBuildData
|
||||
|
||||
import drydock_provisioner.cli.commands as cli
|
||||
|
||||
def test_taskcli_blank_nodefilter():
|
||||
"""If no filter values are specified, node filter should be None."""
|
||||
@ -29,3 +36,55 @@ def test_taskcli_blank_nodefilter():
|
||||
dd_client, "http://foo.bar", action_name="deploy_nodes")
|
||||
|
||||
assert action.node_filter is None
|
||||
|
||||
def test_taskcli_builddata_action(mocker):
|
||||
"""Test the CLI task get build data routine."""
|
||||
task_id = "aaaa-bbbb-cccc-dddd"
|
||||
build_data = [{
|
||||
"node_name": "foo",
|
||||
"task_id": task_id,
|
||||
"collected_data": "1/1/2000",
|
||||
"generator": "test",
|
||||
"data_format": "text/plain",
|
||||
"data_element": "Hello World!",
|
||||
}]
|
||||
|
||||
api_client = mocker.MagicMock()
|
||||
api_client.get_task_build_data.return_value = build_data
|
||||
|
||||
bd_action = TaskBuildData(api_client, task_id)
|
||||
|
||||
assert bd_action.invoke() == build_data
|
||||
api_client.get_task_build_data.assert_called_with(task_id)
|
||||
|
||||
@pytest.mark.skip(reason='Working on mocking needed for click.testing')
|
||||
def test_taskcli_builddata_command(mocker):
|
||||
"""Test the CLI task get build data command."""
|
||||
task_id = "aaaa-bbbb-cccc-dddd"
|
||||
build_data = [{
|
||||
"node_name": "foo",
|
||||
"task_id": task_id,
|
||||
"collected_data": "1/1/2000",
|
||||
"generator": "test",
|
||||
"data_format": "text/plain",
|
||||
"data_element": "Hello World!",
|
||||
}]
|
||||
|
||||
api_client = mocker.MagicMock()
|
||||
api_client.get_task_build_data.return_value = build_data
|
||||
|
||||
mocker.patch('drydock_provisioner.cli.commands.DrydockClient', new=api_client)
|
||||
mocker.patch('drydock_provisioner.cli.commands.KeystoneClient')
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli.drydock, ['-u',
|
||||
'http://foo',
|
||||
'task',
|
||||
'builddata',
|
||||
'-t',
|
||||
task_id])
|
||||
|
||||
print(result.exc_info)
|
||||
api_client.get_task_build_data.assert_called_with(task_id)
|
||||
|
||||
assert yaml.safe_dump(build_data, allow_unicode=True, default_flow_style=False) in result.output
|
||||
|
Loading…
Reference in New Issue
Block a user