A new Shipyard site statuses API and CLI

A new Shipyard site statuses API and CLI supporting nodes
provisoning status and node power state. This API
can be further developed to support new status
requirements by expanding the filters option.

Change-Id: I620aefd82d4a17b616f3f253265605e519506257
This commit is contained in:
Smruti Soumitra Khuntia 2018-07-12 16:58:53 +05:30
parent 4d0a2759f9
commit f4c724fbf1
15 changed files with 869 additions and 5 deletions

View File

@ -18,13 +18,14 @@
Shipyard API
============
Logically, the API has three parts to handle the three areas of
functionality in Shipyard.
Logically, the API has several parts, each to handle each area of
Shipyard functionality:
1. Document Staging
2. Action Handling
3. Airflow Monitoring
4. Logs Retrieval
4. Site Statuses
5. Logs Retrieval
Standards used by the API
-------------------------
@ -965,6 +966,154 @@ Example
}
Site Statuses API
-----------------
Site Statuses API retrieves node provision status and/or node power state
for all nodes in the site.
/v1.0/site-statuses
~~~~~~~~~~~~~~~~~~~
GET /v1.0/site-statuses
^^^^^^^^^^^^^^^^^^^^^^^
Returns the dictionary with nodes provision status and nodes power state status
Query Parameters
''''''''''''''''
- filters=nodes-provision-status,machines-power-state
filters query parameter allows to specify one or more status types to return statuses
of those types. The filter value ``nodes-provision-status`` will fetch provisioning
statuses of all nodes in the site. The filter value ``machines-power-state`` will fetch
power states of all baremetal machines in the site. By omitting the filters
query parameter, statuses of all status types will be returned.
Responses
'''''''''
200 OK
If statuses are retrieved successfully.
400 Bad Request
If invalid filters option is given.
Example
'''''''
::
$ curl -X GET $URL/api/v1.0/site-statuses -H "X-Auth-Token:$TOKEN"
HTTP/1.1 200 OK
x-shipyard-req: 0804d13e-08fc-4e60-a819-3b7532cac4ec
content-type: application/json; charset=UTF-8
{
{
"nodes-provision-status": [
{
"hostname": "abc.xyz.com",
"status": "Ready"
},
{
"hostname": "def.xyz.com",
"status": "Ready"
}
],
"machines-power-state": [
{
"hostname": "abc.xyz.com",
"power_state": "On",
},
{
"hostname": "def.xyz.com",
"power_state": "On",
}
]
}
}
::
$ curl -X GET $URL/api/v1.0/site-statuses?filters=nodes-provision-status \
-H "X-Auth-Token:$TOKEN"
HTTP/1.1 200 OK
x-shipyard-req: 0804d13e-08fc-4e60-a819-3b7532cac4ec
content-type: application/json; charset=UTF-8
{
{
"nodes-provision-status": [
{
"hostname": "abc.xyz.com",
"status": "Ready"
},
{
"hostname": "def.xyz.com",
"status": "Ready"
}
]
}
}
::
$ curl -X GET $URL/api/v1.0/site-statuses?filters=machines-power-state \
-H "X-Auth-Token:$TOKEN"
HTTP/1.1 200 OK
x-shipyard-req: 0804d13e-08fc-4e60-a819-3b7532cac4ec
content-type: application/json; charset=UTF-8
{
{
"machines-power-state": [
{
"hostname": "abc.xyz.com",
"power_state": "On",
},
{
"hostname": "def.xyz.com",
"power_state": "On",
}
]
}
}
::
$ curl -X GET $URL/api/v1.0/site-statuses?filters=nodes-provision-status,machines-power-state \
-H "X-Auth-Token:$TOKEN"
HTTP/1.1 200 OK
x-shipyard-req: 0804d13e-08fc-4e60-a819-3b7532cac4ec
content-type: application/json; charset=UTF-8
{
{
"nodes-provision-status": [
{
"hostname": "abc.xyz.com",
"status": "Ready"
},
{
"hostname": "def.xyz.com",
"status": "Ready"
}
],
"machines-power-state": [
{
"hostname": "abc.xyz.com",
"power_state": "On",
},
{
"hostname": "def.xyz.com",
"power_state": "On",
}
]
}
}
Logs Retrieval API
------------------
This API allows users to query and view logs. Its usuage is currently limited

View File

@ -737,6 +737,77 @@ Sample
deploy_site__2017-11-27T20:34:33.000000 failed
update_site__2017-11-27T20:45:47.000000 running
get site-statuses
~~~~~~~~~~~~~~~~~
Retrieve the provisioning status of nodes and/or power states of the baremetal
machines in the site. If no option provided, retrieve records for both status types.
::
shipyard get site-statuses
[--status-type=<status-type>] (repeatable)
|
Example:
shipyard get site-statuses
shipyard get site-statuses --status-type=nodes-provision-status
shipyard get site-statuses --status-type=machines-power-state
shipyard get site-statuses --status-type=nodes-provision-status --status-type=machines-power-state
\--status-type=<status-type>
Retrieve provisioning statuses of all nodes for status-type
"nodes-provision-status" and retrieve power states of all baremetal
machines in the site for status-type "machines-power-state".
Sample
^^^^^^
::
$ shipyard get site-statuses
Nodes Provision Status:
Hostname Status
abc.xyz.com Ready
def.xyz.com Deploying
Machines Power State:
Hostname Power State
abc.xyz.com On
def.xyz.com On
::
$ shipyard get site-statuses --status-type=nodes-provision-status
Nodes Provision Status:
Hostname Status
abc.xyz.com Ready
def.xyz.com Deploying
::
$ shipyard get site-statuses --status-type=nodes-power-state
Machines Power State:
Hostname Power State
abc.xyz.com On
def.xyz.com On
::
$ shipyard get site-statuses --status-type=nodes-provision-status --status-type=nodes-power-state
Nodes Provision Status:
Hostname Status
abc.xyz.com Ready
def.xyz.com Deploying
Machines Power State:
Hostname Power State
abc.xyz.com On
def.xyz.com On
Logs Commands
-------------

View File

@ -59,3 +59,6 @@
# GET /api/v1.0/workflows/{id}
#"workflow_orchestrator:get_workflow": "rule:admin_required"
# Retrieve the status for node provision status
# GET /api/v1.0/site_statuses
#"workflow_orchestrator:get_site_statuses": "rule:admin_required"

View File

@ -40,6 +40,7 @@ from shipyard_airflow.control.health import HealthResource
from shipyard_airflow.control.middleware.auth import AuthMiddleware
from shipyard_airflow.control.middleware.context import ContextMiddleware
from shipyard_airflow.control.middleware.logging_mw import LoggingMiddleware
from shipyard_airflow.control.status.status_api import StatusResource
from shipyard_airflow.errors import (AppError, default_error_serializer,
default_exception_handler)
@ -77,6 +78,7 @@ def start_api():
('/renderedconfigdocs', RenderedConfigDocsResource()),
('/workflows', WorkflowResource()),
('/workflows/{workflow_id}', WorkflowIdResource()),
('/site_statuses', StatusResource()),
]
# Set up the 1.0 routes

View File

@ -0,0 +1,155 @@
# Copyright 2018 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.
"""
Status helper is a layer for status API, which interacts with
multiple components to fetch required status as per the filter
values.
"""
import logging
from drydock_provisioner import error as dderrors
import falcon
from oslo_config import cfg
import shipyard_airflow.control.service_clients as sc
from shipyard_airflow.errors import ApiError, AppError
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
NODE_PROVISION_STATUS = 'nodes-provision-status'
MACHINES_POWER_STATE = 'machines-power-state'
# This will be expanded with new status filters to support more status types.
valid_filters = [NODE_PROVISION_STATUS, MACHINES_POWER_STATE]
def get_machines_powerstate(drydock):
# Calls Drydock client to fetch nodes power state
try:
machines = drydock.get_nodes()
machines_ps = []
for machine in machines:
machines_ps.append(
{
'hostname': machine.get('hostname'),
'power_state': machine.get('power_state')
})
except dderrors.ClientError as ddex:
raise AppError(
title='Unable to retrieve nodes power-state',
description=(
'Drydock has responded unexpectedly: {}'.format(
ddex.response_message)),
status=falcon.HTTP_500,
retry=False, )
machines_powerstate = {'machines_powerstate': machines_ps}
return machines_powerstate
def get_nodes_provision_status(drydock):
# Calls Drydock client to fetch node provision status
try:
nodes = drydock.get_nodes()
nodes_status = []
for node in nodes:
nodes_status.append(
{
'hostname': node.get('hostname'),
'status': node.get('status_name')
})
except dderrors.ClientError as ddex:
raise AppError(
title='Unable to retrieve nodes status',
description=(
'Drydock has responded unexpectedly: '
'{}'.format(ddex.response_message)),
status=falcon.HTTP_500,
retry=False, )
machine_status = {'nodes_provision_status': nodes_status}
return machine_status
class StatusHelper(object):
"""
StatusHelper provides a layer to fetch statuses from respective
components based on filter values.
A new status_helper is intended to be used for each invocation..
"""
def __init__(self, context):
"""
Sets up this status helper with the supplied
request context
"""
# Instantiate the client for a component api interaction
self.drydock = None
self.ctx = context
def get_site_statuses(self, sts_filters=None):
"""
:param sts_filters: A list of filters representing statuses
that needs to be fetched
Returns dictionary of statuses
"""
# check for filters else set to all valid filters.
if sts_filters:
pass
else:
sts_filters = valid_filters
LOG.debug("Filters for status search are %s", sts_filters)
# check for valid status filters
for sts_filter in sts_filters:
if sts_filter not in valid_filters:
raise ApiError(
title='Not a valid status filter',
description='filter {} is not supported'.format(
sts_filter),
status=falcon.HTTP_400,
retry=False)
# get Drydock client
if not self.drydock:
self.drydock = sc.drydock_client()
statuses = {}
# iterate through filters to invoke required fun
for sts_filter in sts_filters:
call_func = self._switcher(sts_filter)
status = call_func(self.drydock)
statuses.update(status)
return statuses
def _switcher(self, fltr):
# Helper that returns mapped function name as per filter
status_func_switcher = {
NODE_PROVISION_STATUS: get_nodes_provision_status,
MACHINES_POWER_STATE: get_machines_powerstate,
}
call_func = status_func_switcher.get(fltr, lambda: None)
# return the function name from switcher dictionary
return call_func

View File

@ -0,0 +1,46 @@
# Copyright 2018 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.
import logging
import falcon
from shipyard_airflow import policy
from shipyard_airflow.control.helpers.status_helper import (
StatusHelper)
from shipyard_airflow.control.base import BaseResource
LOG = logging.getLogger(__name__)
# /api/v1.0/site_statuses
class StatusResource(BaseResource):
"""
The status resource handles the retrieval of Drydock provisioning
node status and power state
"""
@policy.ApiEnforcer('workflow_orchestrator:get_site_statuses')
def on_get(self, req, resp, **kwargs):
"""
Return site based statuses that has been invoked through shipyard.
:returns: a json array of site status entities
"""
status_filters = req.get_param(name='filters') or None
if status_filters:
fltrs = status_filters.split(',')
else:
fltrs = None
helper = StatusHelper(req.context)
resp.body = self.to_json(helper.get_site_statuses(fltrs))
resp.status = falcon.HTTP_200

View File

@ -174,6 +174,15 @@ class ShipyardPolicy(object):
'method': 'GET'
}]
),
policy.DocumentedRuleDefault(
'workflow_orchestrator:get_site_statuses',
RULE_ADMIN_REQUIRED,
'Retrieve the statuses for the site',
[{
'path': '/api/v1.0/site_statuses',
'method': 'GET'
}]
),
]
# Regions Policy

View File

@ -0,0 +1,36 @@
# Copyright 2018 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.
""" Tests for the status_api"""
import json
from unittest.mock import patch
from shipyard_airflow.control.helpers.status_helper import \
StatusHelper
from shipyard_airflow.control.base import ShipyardRequestContext
from tests.unit.control import common
CTX = ShipyardRequestContext()
class TestStatusResource():
@patch.object(StatusHelper, 'get_site_statuses',
common.str_responder)
def test_on_get(self, api_client):
"""Validate the on_get method returns 200 on success"""
result = api_client.simulate_get(
"/api/v1.0/site_statuses", headers=common.AUTH_HEADERS)
assert result.status_code == 200
assert result.text == json.dumps(common.str_responder(), default=str)
assert result.headers[
'content-type'] == 'application/json; charset=UTF-8'

View File

@ -0,0 +1,217 @@
# Copyright 2018 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.
from unittest import mock
from shipyard_airflow.control.base import ShipyardRequestContext
from shipyard_airflow.control.helpers.status_helper import (
StatusHelper)
import shipyard_airflow.control.helpers.status_helper as sh
CTX = ShipyardRequestContext()
MACH_STATUS_DICT = {
'nodes_provision_status': [
{
'hostname': 'abc.xyz.com',
'status': 'READY'
},
{
'hostname': 'def.xyz.com',
'status': 'DEPLOYING'
}
]
}
MACH_POWERSTATE_DICT = {
'machines_powerstate': [
{
'hostname': 'abc.xyz.com',
'power_state': 'ON'
},
{
'hostname': 'def.xyz.com',
'power_state': 'ON'
}
]
}
STATUS_LIST = [
{
'hostname': 'abc.xyz.com',
'status': 'READY'
},
{
'hostname': 'def.xyz.com',
'status': 'DEPLOYING'
}
]
MACH_PS_LIST = [
{
'hostname': 'abc.xyz.com',
'power_state': 'ON'
},
{
'hostname': 'def.xyz.com',
'power_state': 'ON'
}
]
ALL_STATUSES_DICT = {
'nodes_provision_status': [
{
'hostname': 'abc.xyz.com',
'status': 'READY'
},
{
'hostname': 'def.xyz.com',
'status': 'DEPLOYING'
}
],
'machines_powerstate': [
{
'hostname': 'abc.xyz.com',
'power_state': 'ON'
},
{
'hostname': 'def.xyz.com',
'power_state': 'ON'
}
]
}
NODE_LIST = [
{
'hostname': 'abc.xyz.com',
'memory': 12800,
'cpu_count': 32,
'status_name': 'READY',
'boot_mac': '08:00:27:76:c1:2c',
'power_state': 'ON',
'power_address': 'dummy',
'boot_ip': '1.2.3.4'
},
{
'hostname': 'def.xyz.com',
'memory': 12800,
'cpu_count': 32,
'status_name': 'DEPLOYING',
'boot_mac': '08:00:27:76:c1:2e',
'power_state': 'ON',
'power_address': 'dummy',
'boot_ip': '1.2.3.5'
}
]
NODE_PROVISION_STATUS = 'nodes-provision-status'
MACHINES_POWER_STATE = 'machines-power-state'
def test_construct_status_helper():
"""
Creates a status helper, tests that the context
is passed to the sub-helper
"""
helper = StatusHelper(CTX)
assert helper.ctx == CTX
@mock.patch(
'shipyard_airflow.control.helpers.status_helper.get_machines_powerstate',
return_value=MACH_POWERSTATE_DICT)
@mock.patch(
'shipyard_airflow.control.helpers.status_helper'
'.get_nodes_provision_status',
return_value=MACH_STATUS_DICT)
def test_get_site_statuses(patch1, patch2):
"""
Testing status according to filter values
"""
helper = StatusHelper(CTX)
helper.drydock = 'Dummy'
# test with filter for machine provision status
ret_mach_status = helper.get_site_statuses([NODE_PROVISION_STATUS])
prov_status_list = ret_mach_status.get('nodes_provision_status')
assert STATUS_LIST == sorted(prov_status_list, key=lambda x: x['hostname'])
# test with filter for machine power state
ret_mach_powerstate = helper.get_site_statuses([MACHINES_POWER_STATE])
mach_ps_list = ret_mach_powerstate.get('machines_powerstate')
assert MACH_PS_LIST == sorted(mach_ps_list, key=lambda x: x['hostname'])
# test without filters
ret_wo_filters = helper.get_site_statuses()
psl_wo = ret_wo_filters.get('nodes_provision_status')
assert STATUS_LIST == sorted(psl_wo, key=lambda x: x['hostname'])
mpl_wo = ret_wo_filters.get('machines_powerstate')
assert MACH_PS_LIST == sorted(mpl_wo, key=lambda x: x['hostname'])
# test with both filters
all_filters = [NODE_PROVISION_STATUS, MACHINES_POWER_STATE]
ret_all_statuses = helper.get_site_statuses(all_filters)
psl_with = ret_all_statuses.get('nodes_provision_status')
assert STATUS_LIST == sorted(psl_with, key=lambda x: x['hostname'])
mpl_with = ret_all_statuses.get('machines_powerstate')
assert MACH_PS_LIST == sorted(mpl_with, key=lambda x: x['hostname'])
@mock.patch("drydock_provisioner.drydock_client.client.DrydockClient")
def test_get_machines_powerstate(drydock):
"""
Tests the functionality of the get_machines_powerstate method
"""
drydock.get_nodes.return_value = NODE_LIST
mach_ps_dict = sh.get_machines_powerstate(drydock)
actual = mach_ps_dict.get('machines_powerstate')
expected = MACH_POWERSTATE_DICT.get('machines_powerstate')
assert actual == sorted(expected, key=lambda x: x['hostname'])
@mock.patch("drydock_provisioner.drydock_client.client.DrydockClient")
def test_get_nodes_provision_status(drydock):
"""
Tests the functionality of the get_nodes_provision_status method
"""
drydock.get_nodes.return_value = NODE_LIST
nodes_provision_status = sh.get_nodes_provision_status(drydock)
actual = nodes_provision_status.get('nodes_provision_status')
expected = MACH_STATUS_DICT.get('nodes_provision_status')
assert actual == sorted(expected, key=lambda x: x['hostname'])
def test__switcher():
"""
Tests the functionality of the _switcher() method
"""
helper = StatusHelper(CTX)
pns = "get_nodes_provision_status"
mps = "get_machines_powerstate"
actual_pns = helper._switcher(NODE_PROVISION_STATUS)
actual_mps = helper._switcher(MACHINES_POWER_STATE)
assert pns in str(actual_pns)
assert mps in str(actual_mps)

View File

@ -36,6 +36,7 @@ class ApiPaths(enum.Enum):
POST_CONTROL_ACTION = _BASE_URL + 'actions/{}/control/{}'
GET_WORKFLOWS = _BASE_URL + 'workflows'
GET_DAG_DETAIL = _BASE_URL + 'workflows/{}'
GET_SITE_STATUSES = _BASE_URL + 'site_statuses'
class ShipyardClient(BaseClient):
@ -246,3 +247,18 @@ class ShipyardClient(BaseClient):
url = ApiPaths.GET_DAG_DETAIL.value.format(self.get_endpoint(),
workflow_id)
return self.get_resp(url)
def get_site_statuses(self, fltrs=None):
"""
Get statuses for the site.
:param str filters: status-types for site statuses to retrieve.
:rtype: Response object
"""
if fltrs:
query_params = {'filters': fltrs}
else:
query_params = {}
url = ApiPaths.GET_SITE_STATUSES.value.format(
self.get_endpoint())
return self.get_resp(url, query_params)

View File

@ -260,3 +260,79 @@ def gen_sub_workflows(wf_list):
for wf in wf_list:
wfs.append(gen_workflow_details(wf))
return '\n\n'.join(wfs)
def gen_site_statuses(status_dict):
"""Generates site statuses table.
Assumes status_types as list of filters and status_dict a dictionary
with statuses lists
"""
formatted_output = ''
status_types = status_dict.keys()
for st in status_types:
call_func = _site_statuses_switcher(st)
op = call_func(status_dict)
formatted_output = "{}\n{}\n".format(formatted_output, op)
return formatted_output
def _gen_machines_powerstate_table(status_dict):
# Generates machines power states status table
machine_powerstate_table = format_utils.table_factory(
field_names=['Hostname', 'Power State'])
pwrstate_list = status_dict.get('machines_powerstate')
if pwrstate_list:
for pwrstate in pwrstate_list:
machine_powerstate_table.add_row(
[pwrstate.get('hostname'),
pwrstate.get('power_state')])
else:
machine_powerstate_table.add_row(['', ''])
return format_utils.table_get_string(table=machine_powerstate_table,
title="Machines Power State:",
vertical_char=' ')
def _gen_nodes_provision_status_table(status_dict):
# Generates nodes provision status table
nodes_status_table = format_utils.table_factory(
field_names=['Hostname', 'Status'])
prov_status_list = status_dict.get('nodes_provision_status')
if prov_status_list:
for status in prov_status_list:
nodes_status_table.add_row(
[status.get('hostname'),
status.get('status')])
else:
nodes_status_table.add_row(['', ''])
return format_utils.table_get_string(table=nodes_status_table,
title="Nodes Provision Status:",
vertical_char=' ')
def _site_statuses_switcher(status_type):
"""Maps status types with a callabe function to the format
output.
The dictionary will be updated with new functions
to map future supported status-types for "site-statuses"
"""
status_func_switcher = {
'nodes_provision_status': _gen_nodes_provision_status_table,
'machines_powerstate': _gen_machines_powerstate_table,
}
call_func = status_func_switcher.get(status_type, lambda: None)
return call_func

View File

@ -175,3 +175,38 @@ class GetWorkflows(CliAction):
resp_j = response.json()
wf_list = resp_j if resp_j else []
return cli_format_common.gen_workflow_table(wf_list)
class GetSiteStatuses(CliAction):
"""Action to get the site statuses"""
def __init__(self, ctx, fltr=None):
"""Sets parameters."""
super().__init__(ctx)
self.logger.debug("GetSiteStatuses action with filter %s "
"invoked", fltr)
self.fltr = fltr
def invoke(self):
"""Calls API Client and formats response from API Client"""
self.logger.debug("Calling API Client get_site_statuses")
return self.get_api_client().get_site_statuses(self.fltr)
# Handle 400 with default error handler for cli.
cli_handled_err_resp_codes = [400]
# Handle 200 responses using the cli_format_response_handler
cli_handled_succ_resp_codes = [200]
def cli_format_response_handler(self, response):
"""CLI output handler
:param response: a requests response object
:returns: a string representing a CLI appropriate response
Handles 200 responses
"""
resp_j = response.json()
status_dict = resp_j if resp_j else []
return cli_format_common.gen_site_statuses(status_dict)

View File

@ -21,6 +21,7 @@ from shipyard_client.cli.get.actions import GetConfigdocs
from shipyard_client.cli.get.actions import GetConfigdocsStatus
from shipyard_client.cli.get.actions import GetRenderedConfigdocs
from shipyard_client.cli.get.actions import GetWorkflows
from shipyard_client.cli.get.actions import GetSiteStatuses
from shipyard_client.cli.input_checks import check_reformat_versions
@ -63,8 +64,8 @@ EXAMPLE: shipyard get configdocs --colllection=design
"""
SHORT_DESC_CONFIGDOCS = ("Retrieve documents loaded into Shipyard, either "
"committed, last site action, successful site action "
"or from the Shipyard Buffer. Allows comparison "
"committed, last site action, successful site action"
" or from the Shipyard Buffer. Allows comparison "
"between 2 revisions using valid revision tags")
@ -229,3 +230,38 @@ def get_version(ctx, buffer, committed, last_site_action,
else:
return 'buffer'
DESC_STATUS = """
COMMAND: status
DESCRIPTION: Retrieve statuses of different status types for the site.
Supported status types are nodes-provision-status and machines-power-state. \n
Status type nodes-provision-status will fetch provisioning status for all nodes
and machines-power-state will fetch power state for all baremetal machines
in the site. Supports fetching statuses of multiple types. Without status-type
option, command fetches statuses of all status types. \n
FORMAT: shipyard get site-statuses [--status-type=<status-type>] (repeatable)\n
EXAMPLE: shipyard get status --status-type=nodes-provision-status \
--status-type=machines-power-state
"""
SHORT_DESC_STATUS = "Retrieve statuses for the site."
@get.command(
name='site-statuses',
help=DESC_STATUS,
short_help=SHORT_DESC_STATUS)
@click.option(
'--status-type',
'-s',
multiple=True,
help='Fetches statuses of specific status type.(repeatable) \n'
'Supported status types are: \n'
'nodes-provision-status \n'
'machines-power-state')
@click.pass_context
def get_site_statuses(ctx, status_type):
fltr = ",".join(status_type)
click.echo(GetSiteStatuses(ctx, fltr).invoke_and_return_resp())

View File

@ -234,3 +234,16 @@ def test_get_step_log(*args):
assert result['url'] == '{}/actions/{}/steps/{}/logs'.format(
shipyard_client.get_endpoint(), action_id, step_id)
assert params['try'] == try_number
@mock.patch.object(BaseClient, 'post_resp', replace_post_rep)
@mock.patch.object(BaseClient, 'get_resp', replace_get_resp)
@mock.patch.object(BaseClient, 'get_endpoint', replace_get_endpoint)
def test_get_site_statuses(*args):
shipyard_client = get_api_client()
fltrs = 'nodes-provision-status,machines-power-state'
result = shipyard_client.get_site_statuses(fltrs=fltrs)
params = result['params']
assert result['url'] == '{}/site_statuses'.format(
shipyard_client.get_endpoint())
assert params['filters'] == fltrs