Add operation log API cmd to karborclient

Change-Id: I097ec3424b47939ff00f417bedb186305de39a62
blueprint: operation-log-api
This commit is contained in:
chenying
2017-07-18 15:42:05 +08:00
parent 05c98f7a20
commit cb0b8b0515
7 changed files with 437 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
# 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.
"""Data protection V1 operation_log action implementations"""
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.i18n import _
class ListOperationLogs(command.Lister):
_description = _("List operation_logs.")
log = logging.getLogger(__name__ + ".ListOperationLogs")
def get_parser(self, prog_name):
parser = super(ListOperationLogs, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Include all projects (admin only)'),
)
parser.add_argument(
'--status',
metavar='<status>',
help=_('Filter results by status'),
)
parser.add_argument(
'--marker',
metavar='<operation_log>',
help=_('The last operation_log ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-operation_logs>',
help=_('Maximum number of operation_logs to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc), "
"multiple keys and directions can be "
"specified separated by comma"),
)
parser.add_argument(
'--project',
metavar='<project>',
help=_('Filter results by a project(admin only)')
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
data_protection_client = self.app.client_manager.data_protection
all_projects = bool(parsed_args.project) or parsed_args.all_projects
search_opts = {
'all_tenants': all_projects,
'project_id': parsed_args.project,
'status': parsed_args.status,
}
data = data_protection_client.operation_logs.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Operation Type', 'Checkpoint id',
'Plan Id', 'Provider id', 'Restore Id',
'Scheduled Operation Id', 'Status',
'Started At', 'Ended At', 'Error Info',
'Extra Info']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowOperationLog(command.ShowOne):
_description = "Shows operation_log details"
def get_parser(self, prog_name):
parser = super(ShowOperationLog, self).get_parser(prog_name)
parser.add_argument(
'operation_log',
metavar="<operation_log>",
help=_('The UUID of the operation_log.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
operation_log = osc_utils.find_resource(client.operation_logs,
parsed_args.operation_log)
operation_log._info.pop("links", None)
return zip(*sorted(operation_log._info.items()))

View File

@@ -0,0 +1,124 @@
# 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 karborclient.osc.v1 import operation_logs as osc_operation_logs
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import operation_logs
OPERATIONLOG_INFO = {
"id": "22b82aa7-9179-4c71-bba2-caf5c0e68db7",
"project_id": "e486a2f49695423ca9c47e589b948108",
"operation_type": "protect",
"checkpoint_id": "dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"plan_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"provider_id": "23902b02-5666-4ee6-8dfe-962ac09c3994",
"restore_id": None,
"scheduled_operation_id": "23902b02-5666-4ee6-8dfe-962ac09c3991",
"started_at": "2015-08-27T09:50:58-05:00",
"ended_at": "2015-08-27T10:50:58-05:00",
"status": "protecting",
"error_info": "Could not access bank",
"extra_info": None
}
class TestOperationLogs(fakes.TestDataProtection):
def setUp(self):
super(TestOperationLogs, self).setUp()
self.operation_logs_mock = (
self.app.client_manager.data_protection.operation_logs)
self.operation_logs_mock.reset_mock()
class TestListOperationLogs(TestOperationLogs):
def setUp(self):
super(TestListOperationLogs, self).setUp()
self.operation_logs_mock.list.return_value = [
operation_logs.OperationLog(None, OPERATIONLOG_INFO)]
# Command to test
self.cmd = osc_operation_logs.ListOperationLogs(self.app, None)
def test_operation_logs_list(self):
arglist = ['--status', 'success']
verifylist = [('status', 'success')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Operation Type', 'Checkpoint id', 'Plan Id',
'Provider id', 'Restore Id', 'Scheduled Operation Id',
'Status', 'Started At', 'Ended At', 'Error Info',
'Extra Info'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [("22b82aa7-9179-4c71-bba2-caf5c0e68db7",
"protect",
"dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"23902b02-5666-4ee6-8dfe-962ac09c3994",
None,
"23902b02-5666-4ee6-8dfe-962ac09c3991",
"protecting",
"2015-08-27T09:50:58-05:00",
"2015-08-27T10:50:58-05:00",
"Could not access bank",
None)]
self.assertEqual(expected_data, list(data))
class TestShowOperationLog(TestOperationLogs):
def setUp(self):
super(TestShowOperationLog, self).setUp()
self.operation_logs_mock.get.return_value = (
operation_logs.OperationLog(None, OPERATIONLOG_INFO))
# Command to test
self.cmd = osc_operation_logs.ShowOperationLog(self.app, None)
def test_operation_log_show(self):
arglist = ['22b82aa7-9179-4c71-bba2-caf5c0e68db7']
verifylist = [('operation_log',
'22b82aa7-9179-4c71-bba2-caf5c0e68db7')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'checkpoint_id', 'ended_at', 'error_info', 'extra_info',
'id', 'operation_type', 'plan_id', 'project_id',
'provider_id', 'restore_id', 'scheduled_operation_id',
'started_at', 'status')
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(OPERATIONLOG_INFO['checkpoint_id'], data[0])
self.assertEqual(OPERATIONLOG_INFO['ended_at'], data[1])
self.assertEqual(OPERATIONLOG_INFO['error_info'], data[2])
self.assertEqual(OPERATIONLOG_INFO['extra_info'], data[3])
self.assertEqual(OPERATIONLOG_INFO['id'], data[4])
self.assertEqual(OPERATIONLOG_INFO['operation_type'], data[5])
self.assertEqual(OPERATIONLOG_INFO['plan_id'], data[6])
self.assertEqual(OPERATIONLOG_INFO['project_id'], data[7])
self.assertEqual(OPERATIONLOG_INFO['provider_id'], data[8])
self.assertEqual(OPERATIONLOG_INFO['restore_id'], data[9])
self.assertEqual(OPERATIONLOG_INFO['scheduled_operation_id'], data[10])
self.assertEqual(OPERATIONLOG_INFO['started_at'], data[11])
self.assertEqual(OPERATIONLOG_INFO['status'], data[12])

View File

@@ -0,0 +1,63 @@
# 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 mock
from karborclient.tests.unit import base
from karborclient.tests.unit.v1 import fakes
cs = fakes.FakeClient()
mock_request_return = ({}, {'operation_log': {}})
class OperationLogsTest(base.TestCaseShell):
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_operation_logs_with_marker_limit(self, mock_request):
mock_request.return_value = mock_request_return
cs.operation_logs.list(marker=1234, limit=2)
mock_request.assert_called_with(
'GET',
'/operation_logs?limit=2&marker=1234', headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_operation_logs_with_sort_key_dir(self, mock_request):
mock_request.return_value = mock_request_return
cs.operation_logs.list(sort_key='id', sort_dir='asc')
mock_request.assert_called_with(
'GET',
'/operation_logs?'
'sort_dir=asc&sort_key=id', headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_operation_logs_with_invalid_sort_key(self, mock_request):
self.assertRaises(ValueError,
cs.operation_logs.list,
sort_key='invalid', sort_dir='asc')
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_operation_log(self, mock_request):
mock_request.return_value = mock_request_return
cs.operation_logs.get('1')
mock_request.assert_called_with(
'GET',
'/operation_logs/1',
headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_operation_log_with_headers(self, mock_request):
mock_request.return_value = mock_request_return
cs.operation_logs.get('1', session_id='fake_session_id')
mock_request.assert_called_with(
'GET',
'/operation_logs/1',
headers={'X-Configuration-Session': 'fake_session_id'})

View File

@@ -14,6 +14,7 @@
from karborclient.common import http
from karborclient.v1 import checkpoints
from karborclient.v1 import operation_logs
from karborclient.v1 import plans
from karborclient.v1 import protectables
from karborclient.v1 import providers
@@ -42,3 +43,5 @@ class Client(object):
self.triggers = triggers.TriggerManager(self.http_client)
self.scheduled_operations = \
scheduled_operations.ScheduledOperationManager(self.http_client)
self.operation_logs = \
operation_logs.OperationLogManager(self.http_client)

View File

@@ -0,0 +1,44 @@
# 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 karborclient.common import base
class OperationLog(base.Resource):
def __repr__(self):
return "<OperationLog %s>" % self._info
class OperationLogManager(base.ManagerWithFind):
resource_class = OperationLog
def get(self, operation_log_id, session_id=None):
if session_id:
headers = {'X-Configuration-Session': session_id}
else:
headers = {}
url = "/operation_logs/{operation_log_id}".format(
operation_log_id=operation_log_id)
return self._get(url, response_key="operation_log", headers=headers)
def list(self, detailed=False, search_opts=None, marker=None, limit=None,
sort_key=None, sort_dir=None, sort=None):
"""Lists all operation_logs.
"""
resource_type = "operation_logs"
url = self._build_list_url(
resource_type, detailed=detailed,
search_opts=search_opts, marker=marker,
limit=limit, sort_key=sort_key,
sort_dir=sort_dir, sort=sort)
return self._list(url, 'operation_logs')

View File

@@ -1018,3 +1018,93 @@ def do_scheduledoperation_delete(cs, args):
if failure_count == len(args.scheduledoperation):
raise exceptions.CommandError("Unable to find and delete any of the "
"specified scheduled operation.")
@utils.arg('--all-tenants',
dest='all_tenants',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=0,
help='Shows details for all tenants. Admin only.')
@utils.arg('--all_tenants',
nargs='?',
type=int,
const=1,
help=argparse.SUPPRESS)
@utils.arg('--status',
metavar='<status>',
default=None,
help='Filters results by a status. Default=None.')
@utils.arg('--marker',
metavar='<marker>',
default=None,
help='Begin returning restores that appear later in the '
'operation_log list than that represented by this '
'operation_logs id. Default=None.')
@utils.arg('--limit',
metavar='<limit>',
default=None,
help='Maximum number of operation_logs to return. Default=None.')
@utils.arg('--sort_key',
metavar='<sort_key>',
default=None,
help=argparse.SUPPRESS)
@utils.arg('--sort_dir',
metavar='<sort_dir>',
default=None,
help=argparse.SUPPRESS)
@utils.arg('--sort',
metavar='<key>[:<direction>]',
default=None,
help=(('Comma-separated list of sort keys and directions in the '
'form of <key>[:<asc|desc>]. '
'Valid keys: %s. '
'Default=None.') % ', '.join(base.SORT_KEY_VALUES)))
@utils.arg('--tenant',
type=str,
dest='tenant',
nargs='?',
metavar='<tenant>',
help='Display information from single tenant (Admin only).')
def do_operationlog_list(cs, args):
"""Lists all operation_logs."""
all_tenants = 1 if args.tenant else \
int(os.environ.get("ALL_TENANTS", args.all_tenants))
search_opts = {
'all_tenants': all_tenants,
'project_id': args.tenant,
'status': args.status,
}
if args.sort and (args.sort_key or args.sort_dir):
raise exceptions.CommandError(
'The --sort_key and --sort_dir arguments are deprecated and are '
'not supported with --sort.')
operation_logs = cs.operation_logs.list(
search_opts=search_opts, marker=args.marker,
limit=args.limit, sort_key=args.sort_key,
sort_dir=args.sort_dir, sort=args.sort)
key_list = ['Id', 'Operation Type', 'Checkpoint id', 'Plan Id',
'Provider id', 'Restore Id', 'Scheduled Operation Id',
'Status', 'Started At', 'Ended At', 'Error Info', 'Extra Info']
if args.sort_key or args.sort_dir or args.sort:
sortby_index = None
else:
sortby_index = 0
utils.print_list(operation_logs, key_list, exclude_unavailable=True,
sortby_index=sortby_index)
@utils.arg('operation_log',
metavar='<operation_log>',
help='ID of operation_log.')
def do_operationlog_show(cs, args):
"""Shows operation_log details."""
operation_log = cs.operation_logs.get(args.operation_log)
utils.print_dict(operation_log.to_dict())

View File

@@ -61,6 +61,8 @@ openstack.data_protection.v1 =
data_protection_scheduledoperation_show = karborclient.osc.v1.scheduled_operations:ShowScheduledOperation
data_protection_scheduledoperation_create = karborclient.osc.v1.scheduled_operations:CreateScheduledOperation
data_protection_scheduledoperation_delete = karborclient.osc.v1.scheduled_operations:DeleteScheduledOperation
data_protection_operationlog_list = karborclient.osc.v1.operation_logs:ListOperationLogs
data_protection_operationlog_show = karborclient.osc.v1.operation_logs:ShowOperationLog
[compile_catalog]
directory = karborclient/locale