Compare commits

..

41 Commits

Author SHA1 Message Date
OpenStack Proposal Bot
604007cbd3 Updated from global requirements
Change-Id: I21577f0c7cb5c622e13b13cd5e35781391d90319
2018-01-24 02:19:37 +00:00
OpenStack Proposal Bot
4a5fe7dbc9 Updated from global requirements
Change-Id: I724d191cf50842c274a86bb53841abe28597bc13
2017-12-21 00:43:00 +00:00
Zuul
2d1ac6e744 Merge "Fixed non-ascii in README.txt" 2017-12-19 08:27:56 +00:00
Zuul
f93ac0b598 Merge "Avoid tox_install.sh for constraints support" 2017-12-15 07:32:54 +00:00
Thomas Goirand
194195a97a Fixed non-ascii in README.txt
Running "LANG=C python setup.py install" just fails because of a single
non-ascii char in README.txt. This patch fixes that.

Change-Id: I6840da97cf41891f75afebe06d00e0d3e3849a3d
2017-12-07 22:37:04 +01:00
Zuul
9cf5a90b5c Merge "Updated from global requirements" 2017-12-05 08:11:10 +00:00
chenying
a4cec6cb18 Add OSC commands for quota classes API
Change-Id: I9286d53d307d7b7b58340102967fc30bf87252a7
Implements: blueprint support-quotas-in-karbor
2017-12-05 15:43:53 +08:00
OpenStack Proposal Bot
b8726f4bf5 Updated from global requirements
Change-Id: I68301a8bd7c988d507740f4f306642cc4d462d6b
2017-12-05 03:32:01 +00:00
Zuul
7e084ac5c4 Merge "Add OSC commands for quota API" 2017-12-04 08:27:17 +00:00
chenying
8cf80d3041 Add OSC commands for quota API
Change-Id: I3b7afc0df984b015fb7a8b5488d447c470bd2a12
Implements: blueprint support-quotas-in-karbor
2017-12-04 14:54:12 +08:00
Andreas Jaeger
0b41772728 Avoid tox_install.sh for constraints support
We do not need tox_install.sh, pip can handle constraints itself
and install the project correctly. Thus update tox.ini and remove
the now obsolete tools/tox_install.sh file.

This follows https://review.openstack.org/#/c/508061 to remove
tools/tox_install.sh.

Change-Id: I28ba0904389699268d4b256f76fbfa3a6792eda0
2017-12-02 17:01:28 +00:00
Zuul
a09807472d Merge "Add commands for quota class API" 2017-11-29 12:15:32 +00:00
Zuul
f84d1c9b20 Merge "Add commands for quota API" 2017-11-29 12:12:01 +00:00
OpenStack Proposal Bot
9d4a1612c5 Updated from global requirements
Change-Id: I05e64b5f5542a722c9ede204b6c1f4e771ff1b88
2017-11-16 11:24:03 +00:00
chenying
3a22b3fcdd Add commands for quota class API
Change-Id: Id1710f986d7ff1943fe3841cfd9837d2316419e0
Implements: blueprint support-quotas-in-karbor
2017-11-14 19:38:42 +08:00
chenying
6c11094dc6 Add commands for quota API
Change-Id: Iac73809780012509ffa4883def68e37c7dc98bd4
Implements: blueprint support-quotas-in-karbor
2017-11-14 16:44:29 +08:00
Zuul
478ebe71ed Merge "Add OSC plugin for the service management API" 2017-10-27 10:17:33 +00:00
Zuul
a4a5088ff0 Merge "Add commands for service management API" 2017-10-26 07:51:42 +00:00
Jiao Pengju
87058d3e24 Add OSC plugin for the service management API
Depends-On: I3e9a62327653ea1dc9b5807f50f250c739c1566d
Change-Id: I49825e80935b357a06b0074c024390cd2ed3a9b9
Implements: blueprint karbor-service-management
2017-10-26 11:18:44 +08:00
Jiao Pengju
aba2875905 Add commands for service management API
Change-Id: I3e9a62327653ea1dc9b5807f50f250c739c1566d
Implements: blueprint karbor-service-management
2017-10-25 19:49:12 +08:00
chenying
50927b5c38 Add OSC plugin for the verification API
Change-Id: I2fbd064a058c7dcaff6e223a3e2c87bb16cdbaa0
Implements: blueprint support-verify-the-checkpoint-api
2017-10-25 15:00:18 +08:00
chenying
f13c3faef4 Add commands for verification API
Change-Id: I6b7075220bce16af9e162f2e42feb10ffd073169
Implements: blueprint support-verify-the-checkpoint-api
2017-10-25 11:15:08 +08:00
Jenkins
dd2e3ff3a3 Merge "Updated from global requirements" 2017-09-21 06:50:26 +00:00
OpenStack Proposal Bot
346c241876 Updated from global requirements
Change-Id: Ie9b8946e704e2ef85e73ef6d6c91a9537f2d4b83
2017-09-21 03:49:23 +00:00
lihaijing
1bfd6d0ee4 Delete bash_completion in subcommand
There are two "completion" in the subcommand table: bash-completion
and bash_completion. but "bash_completion" is not in help information
and it is repeated with "bash-completion", so delete it.

Change-Id: I18da874681ea00c18d72e164dc55aeea9d40731d
Closes-Bug: #1670123
2017-08-23 14:55:29 +08:00
Jenkins
7a7f708282 Merge "Updated from global requirements" 2017-08-20 08:48:26 +00:00
Jenkins
ea42b2ab3e Merge "Fix OSC scheduledoperations commands formatting" 2017-08-20 08:41:20 +00:00
OpenStack Proposal Bot
996cc1625f Updated from global requirements
Change-Id: I6a1540e5bd7966f887dbb0f968676f7d9bef2f10
2017-08-18 04:51:27 +00:00
chenying
a7de5b981e Fix OSC scheduledoperations commands formatting
Change-Id: I7eacb653ed8549c453fdca6598447bd9b5f93cc8
2017-08-17 09:47:58 +08:00
chenying
982a8c361d Fix OSC restore commands formatting
Change-Id: Ia85065631eef1f3d96df007e240df04362f18754
2017-08-16 21:10:35 +08:00
Yuval Brik
855cbe5cbb Fix OSC provider show formatting
Change-Id: I5c2c8283dd367ccdbf268b6abc38899a565fe94b
2017-08-15 10:16:19 +03:00
Yuval Brik
41d93cdca0 Fix OSC protectable show formatting
Change-Id: I353a9e2938335e1de3d1b2902a780039e03e5b7c
2017-08-15 10:15:58 +03:00
Yuval Brik
888008676c Convert DOS newlines to Unix newlines
Change-Id: If05dce63dadecd89de58350173fc000055e63ca5
2017-08-15 10:15:33 +03:00
Yuval Brik
c14aea8079 Fix OSC protectable instance formatting
Change-Id: Ib06abd01682c7194547332582c64b4c7ae7f5a05
2017-08-15 10:14:50 +03:00
Yuval Brik
e1f7540bac Use a copy of global literal for each test
If global is used in OSC tests, it might be altered, and then the test
result depends on concurrency and order.
Use a copy of the global literal for deterministic results.

Change-Id: I177cc4345c5ed194f2c36d80acc53db113f814e4
2017-08-15 10:13:10 +03:00
Jenkins
6a53851650 Merge "Fix OSC checkpoint show and create formatting" 2017-08-14 10:27:11 +00:00
Jenkins
84e4ff3733 Merge "Checkpoint list: show only plan name and id" 2017-08-14 10:25:50 +00:00
Yuval Brik
e774b2178c Fix OSC plan show, update, and create formatting
Change-Id: I0b7377506e38bf31f45e5bb5ad75f423a344af62
2017-08-14 11:22:05 +03:00
Yuval Brik
e3d987a2ce Fix OSC checkpoint show and create formatting
Change-Id: Iecbda83be307c30a432cc40a448429dc81a1245e
2017-08-14 11:11:03 +03:00
Yuval Brik
9b77c76ed0 Checkpoint list: show only plan name and id
No need to list the entire plan for each checkpoint in the checkpoint
list commands. Show only the name and id.

Change-Id: I50a7e65e8fe810e43e762a4355c7976c9c4eca96
2017-08-14 11:09:30 +03:00
OpenStack Proposal Bot
aa5d56495f Updated from global requirements
Change-Id: I8d9b3a48335b7eeb71464de54ed8adca7dbdc8c6
2017-07-27 20:32:31 +00:00
45 changed files with 3597 additions and 1883 deletions

View File

@@ -22,7 +22,7 @@ Karbor
Karbor Mission Statement
* Formalize Application Data Protection in OpenStack (APIs, Services, Plugins, )
* Formalize Application Data Protection in OpenStack (APIs, Services, Plugins, ...)
* Be able to protect Any Resource in OpenStack(as well as their dependencies)
* Allow Diversity of vendor solutions, capabilities and implementations
without compromising usability

View File

@@ -1,56 +1,56 @@
# 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
from osc_lib import utils
LOG = logging.getLogger(__name__)
DEFAULT_DATA_PROTECTION_API_VERSION = '1'
API_VERSION_OPTION = 'os_data_protection_api_version'
API_NAME = 'data_protection'
API_VERSIONS = {
'1': 'karborclient.v1.client.Client',
}
def make_client(instance):
"""Returns a data protection service client"""
data_protection_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating data protection client: %s',
data_protection_client)
client = data_protection_client(
auth=instance.auth,
session=instance.session,
service_type="data-protect"
)
return client
def build_option_parser(parser):
"""Hook to add global options"""
parser.add_argument(
'--os-data-protection-api-version',
metavar='<data-protection-api-version>',
default=utils.env(
'OS_DATA_PROTECTION_API_VERSION',
default=DEFAULT_DATA_PROTECTION_API_VERSION),
help='Data protection API version, default=' +
DEFAULT_DATA_PROTECTION_API_VERSION +
' (Env: OS_DATA_PROTECTION_API_VERSION)')
return parser
# 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
from osc_lib import utils
LOG = logging.getLogger(__name__)
DEFAULT_DATA_PROTECTION_API_VERSION = '1'
API_VERSION_OPTION = 'os_data_protection_api_version'
API_NAME = 'data_protection'
API_VERSIONS = {
'1': 'karborclient.v1.client.Client',
}
def make_client(instance):
"""Returns a data protection service client"""
data_protection_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating data protection client: %s',
data_protection_client)
client = data_protection_client(
auth=instance.auth,
session=instance.session,
service_type="data-protect"
)
return client
def build_option_parser(parser):
"""Hook to add global options"""
parser.add_argument(
'--os-data-protection-api-version',
metavar='<data-protection-api-version>',
default=utils.env(
'OS_DATA_PROTECTION_API_VERSION',
default=DEFAULT_DATA_PROTECTION_API_VERSION),
help='Data protection API version, default=' +
DEFAULT_DATA_PROTECTION_API_VERSION +
' (Env: OS_DATA_PROTECTION_API_VERSION)')
return parser

View File

@@ -1,205 +1,221 @@
# 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 checkpoint 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.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
class ListCheckpoints(command.Lister):
_description = _("List checkpoints.")
log = logging.getLogger(__name__ + ".ListCheckpoints")
def get_parser(self, prog_name):
parser = super(ListCheckpoints, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('ID of provider.'),
)
parser.add_argument(
'--plan_id',
metavar='<plan_id>',
default=None,
help=_('Filters results by a plan ID. Default=None.'),
)
parser.add_argument(
'--start_date',
type=str,
metavar='<start_date>',
default=None,
help=_('Filters results by a start date("Y-m-d"). Default=None.'),
)
parser.add_argument(
'--end_date',
type=str,
metavar='<end_date>',
default=None,
help=_('Filters results by a end date("Y-m-d"). Default=None.'),
)
parser.add_argument(
'--project_id',
metavar='<project_id>',
default=None,
help=_('Filters results by a project ID. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<checkpoint>',
help=_('The last checkpoint ID of the previous page.'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-checkpoints>',
help=_('Maximum number of checkpoints 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"),
)
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
search_opts = {
'plan_id': parsed_args.plan_id,
'start_date': parsed_args.start_date,
'end_date': parsed_args.end_date,
'project_id': parsed_args.project_id,
}
data = data_protection_client.checkpoints.list(
provider_id=parsed_args.provider_id, search_opts=search_opts,
marker=parsed_args.marker, limit=parsed_args.limit,
sort=parsed_args.sort)
column_headers = ['Id', 'Project id', 'Status', 'Protection plan',
'Metadata', 'Created at']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowCheckpoint(command.ShowOne):
_description = "Shows checkpoint details"
def get_parser(self, prog_name):
parser = super(ShowCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar="<provider_id>",
help=_('Id of provider.')
)
parser.add_argument(
'checkpoint_id',
metavar="<checkpoint_id>",
help=_('Id of checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
checkpoint = client.checkpoints.get(parsed_args.provider_id,
parsed_args.checkpoint_id)
checkpoint._info.pop("links", None)
return zip(*sorted(checkpoint._info.items()))
class CreateCheckpoint(command.ShowOne):
_description = "Creates a checkpoint"
def get_parser(self, prog_name):
parser = super(CreateCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('ID of provider.')
)
parser.add_argument(
'plan_id',
metavar='<plan_id>',
help=_('ID of plan.')
)
parser.add_argument(
'--extra_info',
type=str,
nargs='*',
metavar='<key=value>',
default=None,
help=_('The extra info of a checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
checkpoint_extra_info = None
if parsed_args.extra_info is not None:
checkpoint_extra_info = utils.extract_extra_info(parsed_args)
checkpoint = client.checkpoints.create(parsed_args.provider_id,
parsed_args.plan_id,
checkpoint_extra_info)
checkpoint._info.pop("links", None)
return zip(*sorted(checkpoint._info.items()))
class DeleteCheckpoint(command.Command):
_description = "Delete checkpoint"
log = logging.getLogger(__name__ + ".DeleteCheckpoint")
def get_parser(self, prog_name):
parser = super(DeleteCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('Id of provider.')
)
parser.add_argument(
'checkpoint',
metavar='<checkpoint>',
nargs="+",
help=_('Id of checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for checkpoint_id in parsed_args.checkpoint:
try:
client.checkpoints.delete(parsed_args.provider_id,
checkpoint_id)
except exceptions.NotFound:
failure_count += 1
self.log.error(
"Failed to delete '{0}'; checkpoint not found".
format(checkpoint_id))
if failure_count == len(parsed_args.checkpoint):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified checkpoint.")
# 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 checkpoint action implementations"""
import json
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
def format_checkpoint(checkpoint_info):
if 'protection_plan' in checkpoint_info:
plan = checkpoint_info['protection_plan']
checkpoint_info['protection_plan'] = "Name: %s\nId: %s" % (
plan['name'], plan['id'])
if 'resource_graph' in checkpoint_info:
checkpoint_info['resource_graph'] = json.dumps(json.loads(
checkpoint_info['resource_graph']), indent=2, sort_keys=True)
checkpoint_info.pop("links", None)
class ListCheckpoints(command.Lister):
_description = _("List checkpoints.")
log = logging.getLogger(__name__ + ".ListCheckpoints")
def get_parser(self, prog_name):
parser = super(ListCheckpoints, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('ID of provider.'),
)
parser.add_argument(
'--plan_id',
metavar='<plan_id>',
default=None,
help=_('Filters results by a plan ID. Default=None.'),
)
parser.add_argument(
'--start_date',
type=str,
metavar='<start_date>',
default=None,
help=_('Filters results by a start date("Y-m-d"). Default=None.'),
)
parser.add_argument(
'--end_date',
type=str,
metavar='<end_date>',
default=None,
help=_('Filters results by a end date("Y-m-d"). Default=None.'),
)
parser.add_argument(
'--project_id',
metavar='<project_id>',
default=None,
help=_('Filters results by a project ID. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<checkpoint>',
help=_('The last checkpoint ID of the previous page.'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-checkpoints>',
help=_('Maximum number of checkpoints 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"),
)
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
search_opts = {
'plan_id': parsed_args.plan_id,
'start_date': parsed_args.start_date,
'end_date': parsed_args.end_date,
'project_id': parsed_args.project_id,
}
data = data_protection_client.checkpoints.list(
provider_id=parsed_args.provider_id, search_opts=search_opts,
marker=parsed_args.marker, limit=parsed_args.limit,
sort=parsed_args.sort)
column_headers = ['Id', 'Project id', 'Status', 'Protection plan',
'Metadata', 'Created at']
def plan_formatter(plan):
return "Name: %s\nId: %s" % (plan['name'],
plan['id'])
formatters = {"Protection plan": plan_formatter}
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers, formatters=formatters
) for s in data))
class ShowCheckpoint(command.ShowOne):
_description = "Shows checkpoint details"
def get_parser(self, prog_name):
parser = super(ShowCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar="<provider_id>",
help=_('Id of provider.')
)
parser.add_argument(
'checkpoint_id',
metavar="<checkpoint_id>",
help=_('Id of checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
checkpoint = client.checkpoints.get(parsed_args.provider_id,
parsed_args.checkpoint_id)
format_checkpoint(checkpoint._info)
return zip(*sorted(checkpoint._info.items()))
class CreateCheckpoint(command.ShowOne):
_description = "Creates a checkpoint"
def get_parser(self, prog_name):
parser = super(CreateCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('ID of provider.')
)
parser.add_argument(
'plan_id',
metavar='<plan_id>',
help=_('ID of plan.')
)
parser.add_argument(
'--extra_info',
type=str,
nargs='*',
metavar='<key=value>',
default=None,
help=_('The extra info of a checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
checkpoint_extra_info = None
if parsed_args.extra_info is not None:
checkpoint_extra_info = utils.extract_extra_info(parsed_args)
checkpoint = client.checkpoints.create(parsed_args.provider_id,
parsed_args.plan_id,
checkpoint_extra_info)
format_checkpoint(checkpoint._info)
return zip(*sorted(checkpoint._info.items()))
class DeleteCheckpoint(command.Command):
_description = "Delete checkpoint"
log = logging.getLogger(__name__ + ".DeleteCheckpoint")
def get_parser(self, prog_name):
parser = super(DeleteCheckpoint, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('Id of provider.')
)
parser.add_argument(
'checkpoint',
metavar='<checkpoint>',
nargs="+",
help=_('Id of checkpoint.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for checkpoint_id in parsed_args.checkpoint:
try:
client.checkpoints.delete(parsed_args.provider_id,
checkpoint_id)
except exceptions.NotFound:
failure_count += 1
self.log.error(
"Failed to delete '{0}'; checkpoint not found".
format(checkpoint_id))
if failure_count == len(parsed_args.checkpoint):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified checkpoint.")

View File

@@ -1,111 +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()))
# 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

@@ -1,265 +1,274 @@
# 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 plan action implementations"""
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
class ListPlans(command.Lister):
_description = _("List plans.")
log = logging.getLogger(__name__ + ".ListPlans")
def get_parser(self, prog_name):
parser = super(ListPlans, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Include all projects (admin only)'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filter results by plan name'),
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('Filter results by plan description'),
)
parser.add_argument(
'--status',
metavar='<status>',
help=_('Filter results by status'),
)
parser.add_argument(
'--marker',
metavar='<plan>',
help=_('The last plan ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-plans>',
help=_('Maximum number of plans to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), 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,
'name': parsed_args.name,
'description': parsed_args.description,
'status': parsed_args.status,
}
data = data_protection_client.plans.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Description', 'Provider id', 'Status']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowPlan(command.ShowOne):
_description = "Shows plan details"
def get_parser(self, prog_name):
parser = super(ShowPlan, self).get_parser(prog_name)
parser.add_argument(
'plan',
metavar="<plan>",
help=_('The UUID of the plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
plan = osc_utils.find_resource(client.plans, parsed_args.plan)
plan._info.pop("links", None)
return zip(*sorted(plan._info.items()))
class CreatePlan(command.ShowOne):
_description = "Creates a plan"
def get_parser(self, prog_name):
parser = super(CreatePlan, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the plan.')
)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('The UUID of the provider.')
)
parser.add_argument(
'resources',
metavar='<id=type=name=extra_info,id=type=name=extra_info>',
help=_('Resource in list must be a dict when creating'
' a plan. The keys of resource are id ,type, name and '
'extra_info. The extra_info field is optional.')
)
parser.add_argument(
'--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help=_('Plan parameters in json format.')
)
parser.add_argument(
'--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help=_('Plan parameters, may be specified multiple times. '
'resource_type: type of resource to apply parameters. '
'resource_id: limit the parameters to a specific resource. '
'Other keys and values: according to provider\'s protect '
'schema.')
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('The description of the plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
plan_resources = utils.extract_resources(parsed_args)
utils.check_resources(client, plan_resources)
plan_parameters = utils.extract_parameters(parsed_args)
plan = client.plans.create(parsed_args.name, parsed_args.provider_id,
plan_resources, plan_parameters,
description=parsed_args.description)
plan._info.pop("links", None)
return zip(*sorted(plan._info.items()))
class UpdatePlan(command.ShowOne):
_description = "Update a plan"
def get_parser(self, prog_name):
parser = super(UpdatePlan, self).get_parser(prog_name)
parser.add_argument(
"plan_id",
metavar="<PLAN ID>",
help=_("Id of plan to update.")
)
parser.add_argument(
"--name",
metavar="<name>",
help=_("A name to which the plan will be renamed.")
)
parser.add_argument(
"--resources",
metavar="<id=type=name,id=type=name>",
help=_("Resources to which the plan will be updated.")
)
parser.add_argument(
"--status",
metavar="<suspended|started>",
help=_("status to which the plan will be updated.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
data = {}
if parsed_args.name is not None:
data['name'] = parsed_args.name
if parsed_args.resources is not None:
plan_resources = utils.extract_resources(parsed_args)
data['resources'] = plan_resources
if parsed_args.status is not None:
data['status'] = parsed_args.status
try:
plan = osc_utils.find_resource(client.plans,
parsed_args.plan_id)
plan = client.plans.update(plan.id, data)
except exceptions.NotFound:
raise exceptions.CommandError(
"Plan %s not found" % parsed_args.plan_id)
else:
plan._info.pop("links", None)
return zip(*sorted(plan._info.items()))
class DeletePlan(command.Command):
_description = "Delete plan"
def get_parser(self, prog_name):
parser = super(DeletePlan, self).get_parser(prog_name)
parser.add_argument(
'plan',
metavar='<plan>',
nargs="+",
help=_('ID of plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for plan_id in parsed_args.plan:
try:
plan = osc_utils.find_resource(client.plans, plan_id)
client.plans.delete(plan.id)
except exceptions.NotFound:
failure_count += 1
print("Failed to delete '{0}'; plan not "
"found".format(plan_id))
if failure_count == len(parsed_args.plan):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified plan.")
# 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 plan action implementations"""
import json
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
def format_plan(plan_info):
for key in ('resources', 'parameters'):
if key not in plan_info:
continue
plan_info[key] = json.dumps(plan_info[key], indent=2, sort_keys=True)
plan_info.pop("links", None)
class ListPlans(command.Lister):
_description = _("List plans.")
log = logging.getLogger(__name__ + ".ListPlans")
def get_parser(self, prog_name):
parser = super(ListPlans, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Include all projects (admin only)'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filter results by plan name'),
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('Filter results by plan description'),
)
parser.add_argument(
'--status',
metavar='<status>',
help=_('Filter results by status'),
)
parser.add_argument(
'--marker',
metavar='<plan>',
help=_('The last plan ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-plans>',
help=_('Maximum number of plans to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), 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,
'name': parsed_args.name,
'description': parsed_args.description,
'status': parsed_args.status,
}
data = data_protection_client.plans.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Description', 'Provider id', 'Status']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowPlan(command.ShowOne):
_description = "Shows plan details"
def get_parser(self, prog_name):
parser = super(ShowPlan, self).get_parser(prog_name)
parser.add_argument(
'plan',
metavar="<plan>",
help=_('The UUID of the plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
plan = osc_utils.find_resource(client.plans, parsed_args.plan)
format_plan(plan._info)
return zip(*sorted(plan._info.items()))
class CreatePlan(command.ShowOne):
_description = "Creates a plan"
def get_parser(self, prog_name):
parser = super(CreatePlan, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the plan.')
)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('The UUID of the provider.')
)
parser.add_argument(
'resources',
metavar='<id=type=name=extra_info,id=type=name=extra_info>',
help=_('Resource in list must be a dict when creating'
' a plan. The keys of resource are id ,type, name and '
'extra_info. The extra_info field is optional.')
)
parser.add_argument(
'--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help=_('Plan parameters in json format.')
)
parser.add_argument(
'--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help=_('Plan parameters, may be specified multiple times. '
'resource_type: type of resource to apply parameters. '
'resource_id: limit the parameters to a specific resource. '
'Other keys and values: according to provider\'s protect '
'schema.')
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('The description of the plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
plan_resources = utils.extract_resources(parsed_args)
utils.check_resources(client, plan_resources)
plan_parameters = utils.extract_parameters(parsed_args)
plan = client.plans.create(parsed_args.name, parsed_args.provider_id,
plan_resources, plan_parameters,
description=parsed_args.description)
format_plan(plan._info)
return zip(*sorted(plan._info.items()))
class UpdatePlan(command.ShowOne):
_description = "Update a plan"
def get_parser(self, prog_name):
parser = super(UpdatePlan, self).get_parser(prog_name)
parser.add_argument(
"plan_id",
metavar="<PLAN ID>",
help=_("Id of plan to update.")
)
parser.add_argument(
"--name",
metavar="<name>",
help=_("A name to which the plan will be renamed.")
)
parser.add_argument(
"--resources",
metavar="<id=type=name,id=type=name>",
help=_("Resources to which the plan will be updated.")
)
parser.add_argument(
"--status",
metavar="<suspended|started>",
help=_("status to which the plan will be updated.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
data = {}
if parsed_args.name is not None:
data['name'] = parsed_args.name
if parsed_args.resources is not None:
plan_resources = utils.extract_resources(parsed_args)
data['resources'] = plan_resources
if parsed_args.status is not None:
data['status'] = parsed_args.status
try:
plan = osc_utils.find_resource(client.plans,
parsed_args.plan_id)
plan = client.plans.update(plan.id, data)
except exceptions.NotFound:
raise exceptions.CommandError(
"Plan %s not found" % parsed_args.plan_id)
else:
format_plan(plan._info)
return zip(*sorted(plan._info.items()))
class DeletePlan(command.Command):
_description = "Delete plan"
def get_parser(self, prog_name):
parser = super(DeletePlan, self).get_parser(prog_name)
parser.add_argument(
'plan',
metavar='<plan>',
nargs="+",
help=_('ID of plan.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for plan_id in parsed_args.plan:
try:
plan = osc_utils.find_resource(client.plans, plan_id)
client.plans.delete(plan.id)
except exceptions.NotFound:
failure_count += 1
print("Failed to delete '{0}'; plan not "
"found".format(plan_id))
if failure_count == len(parsed_args.plan):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified plan.")

View File

@@ -1,177 +1,196 @@
# 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 protectables 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 _
from karborclient import utils
class ListProtectables(command.Lister):
_description = _("List protectable types.")
log = logging.getLogger(__name__ + ".ListProtectables")
def get_parser(self, prog_name):
parser = super(ListProtectables, self).get_parser(prog_name)
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
data = data_protection_client.protectables.list()
column_headers = ['Protectable type']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowProtectable(command.ShowOne):
_description = "Shows protectable type details"
def get_parser(self, prog_name):
parser = super(ShowProtectable, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Protectable type.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
protectable = osc_utils.find_resource(client.protectables,
parsed_args.protectable_type)
protectable._info.pop("links", None)
return zip(*sorted(protectable._info.items()))
class ListProtectableInstances(command.Lister):
_description = _("List protectable instances.")
log = logging.getLogger(__name__ + ".ListProtectableInstances")
def get_parser(self, prog_name):
parser = super(ListProtectableInstances, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Type of protectable.')
)
parser.add_argument(
'--type',
metavar="<type>",
default=None,
help=_('Filters results by protectable type. Default=None.')
)
parser.add_argument(
'--marker',
metavar="<protectable_instance>",
default=None,
help=_('The last protectable instance ID of the previous page.')
)
parser.add_argument(
'--limit',
metavar="<num-protectable_instances>",
default=None,
help=_('Maximum number of protectable instances 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(
'--parameters',
type=str,
nargs='*',
metavar="<key=value>",
default=None,
help=_('List instances by parameters key and value pair. '
'Default=None.')
)
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
search_opts = {
'type': parsed_args.type,
'parameters': (utils.extract_instances_parameters(parsed_args)
if parsed_args.parameters else None),
}
data = data_protection_client.protectables.list_instances(
parsed_args.protectable_type, search_opts=search_opts,
marker=parsed_args.marker, limit=parsed_args.limit,
sort=parsed_args.sort)
column_headers = ['Id', 'Type', 'Name', 'Dependent resources',
'Extra info']
return (column_headers, data)
class ShowProtectableInstance(command.ShowOne):
_description = "Shows protectable instance details"
def get_parser(self, prog_name):
parser = super(ShowProtectableInstance, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Protectable type.')
)
parser.add_argument(
'protectable_id',
metavar="<protectable_id>",
help=_('Protectable instance id.')
)
parser.add_argument(
'--parameters',
type=str,
nargs='*',
metavar="<key=value>",
default=None,
help=_('Show a instance by parameters key and value pair. '
'Default=None.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
search_opts = {
'parameters': (utils.extract_instances_parameters(parsed_args)
if parsed_args.parameters else None),
}
instance = client.protectables.get_instance(
parsed_args.protectable_type,
parsed_args.protectable_id,
search_opts=search_opts)
instance._info.pop("links", None)
return zip(*sorted(instance._info.items()))
# 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 protectables action implementations"""
import functools
import json
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 _
from karborclient import utils
class ListProtectables(command.Lister):
_description = _("List protectable types.")
log = logging.getLogger(__name__ + ".ListProtectables")
def get_parser(self, prog_name):
parser = super(ListProtectables, self).get_parser(prog_name)
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
data = data_protection_client.protectables.list()
column_headers = ['Protectable type']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowProtectable(command.ShowOne):
_description = "Shows protectable type details"
def get_parser(self, prog_name):
parser = super(ShowProtectable, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Protectable type.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
protectable = osc_utils.find_resource(client.protectables,
parsed_args.protectable_type)
protectable._info.pop("links", None)
if 'dependent_types' in protectable._info:
protectable._info['dependent_types'] = "\n".join(
protectable._info['dependent_types'])
return zip(*sorted(protectable._info.items()))
class ListProtectableInstances(command.Lister):
_description = _("List protectable instances.")
log = logging.getLogger(__name__ + ".ListProtectableInstances")
def get_parser(self, prog_name):
parser = super(ListProtectableInstances, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Type of protectable.')
)
parser.add_argument(
'--type',
metavar="<type>",
default=None,
help=_('Filters results by protectable type. Default=None.')
)
parser.add_argument(
'--marker',
metavar="<protectable_instance>",
default=None,
help=_('The last protectable instance ID of the previous page.')
)
parser.add_argument(
'--limit',
metavar="<num-protectable_instances>",
default=None,
help=_('Maximum number of protectable instances 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(
'--parameters',
type=str,
nargs='*',
metavar="<key=value>",
default=None,
help=_('List instances by parameters key and value pair. '
'Default=None.')
)
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
search_opts = {
'type': parsed_args.type,
'parameters': (utils.extract_instances_parameters(parsed_args)
if parsed_args.parameters else None),
}
data = data_protection_client.protectables.list_instances(
parsed_args.protectable_type, search_opts=search_opts,
marker=parsed_args.marker, limit=parsed_args.limit,
sort=parsed_args.sort)
column_headers = ['Id', 'Type', 'Name', 'Dependent resources',
'Extra info']
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
formatters = {
"Extra info": json_dumps,
"Dependent resources": json_dumps,
}
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers, formatters=formatters,
) for s in data))
class ShowProtectableInstance(command.ShowOne):
_description = "Shows protectable instance details"
def get_parser(self, prog_name):
parser = super(ShowProtectableInstance, self).get_parser(prog_name)
parser.add_argument(
'protectable_type',
metavar="<protectable_type>",
help=_('Protectable type.')
)
parser.add_argument(
'protectable_id',
metavar="<protectable_id>",
help=_('Protectable instance id.')
)
parser.add_argument(
'--parameters',
type=str,
nargs='*',
metavar="<key=value>",
default=None,
help=_('Show a instance by parameters key and value pair. '
'Default=None.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
search_opts = {
'parameters': (utils.extract_instances_parameters(parsed_args)
if parsed_args.parameters else None),
}
instance = client.protectables.get_instance(
parsed_args.protectable_type,
parsed_args.protectable_id,
search_opts=search_opts)
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
instance._info.pop("links", None)
for key in ('extra_info', 'dependent_resources'):
if key not in instance._info:
continue
instance._info[key] = json_dumps(instance._info[key])
return zip(*sorted(instance._info.items()))

View File

@@ -1,99 +1,104 @@
# 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 provider 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 ListProviders(command.Lister):
_description = _("List providers.")
log = logging.getLogger(__name__ + ".ListProviders")
def get_parser(self, prog_name):
parser = super(ListProviders, self).get_parser(prog_name)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('Filters results by a description. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<provider>',
help=_('The last provider ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-providers>',
help=_('Maximum number of providers to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma"),
)
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
search_opts = {
'name': parsed_args.name,
'description': parsed_args.description,
}
data = data_protection_client.providers.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Description']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowProvider(command.ShowOne):
_description = "Shows provider details"
def get_parser(self, prog_name):
parser = super(ShowProvider, self).get_parser(prog_name)
parser.add_argument(
'provider',
metavar="<provider>",
help=_('The UUID of the provider.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
provider = osc_utils.find_resource(client.providers,
parsed_args.provider)
provider._info.pop("links", None)
return zip(*sorted(provider._info.items()))
# 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 provider action implementations"""
import functools
import json
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 ListProviders(command.Lister):
_description = _("List providers.")
log = logging.getLogger(__name__ + ".ListProviders")
def get_parser(self, prog_name):
parser = super(ListProviders, self).get_parser(prog_name)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('Filters results by a description. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<provider>',
help=_('The last provider ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-providers>',
help=_('Maximum number of providers to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma"),
)
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
search_opts = {
'name': parsed_args.name,
'description': parsed_args.description,
}
data = data_protection_client.providers.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Description']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowProvider(command.ShowOne):
_description = "Shows provider details"
def get_parser(self, prog_name):
parser = super(ShowProvider, self).get_parser(prog_name)
parser.add_argument(
'provider',
metavar="<provider>",
help=_('The UUID of the provider.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
provider = osc_utils.find_resource(client.providers,
parsed_args.provider)
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
provider._info.pop("links", None)
if 'extended_info_schema' in provider._info:
provider._info['extended_info_schema'] = json_dumps(
provider._info['extended_info_schema'])
return zip(*sorted(provider._info.items()))

View File

@@ -0,0 +1,75 @@
# 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 quota classes action implementations"""
from osc_lib.command import command
def quota_class_set_pretty_show(quota_classes):
"""Convert quotas class object to dict and display."""
new_quota_classes = []
for quota_k, quota_v in sorted(quota_classes.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
new_quota_classes.append((quota_k, quota_v))
return new_quota_classes
class ShowQuotaClasses(command.ShowOne):
_description = "Shows Quota classes."
def get_parser(self, prog_name):
parser = super(ShowQuotaClasses, self).get_parser(prog_name)
parser.add_argument(
'class_name',
metavar='<class_name>',
help='Name of quota class to list the quotas for.')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
result = client.quota_classes.get(parsed_args.class_name)
quota_classes = quota_class_set_pretty_show(result)
return zip(*sorted(quota_classes))
class UpdateQuotaClasses(command.ShowOne):
_description = "Update the quotas for a quota class (Admin only)."
def get_parser(self, prog_name):
parser = super(UpdateQuotaClasses, self).get_parser(prog_name)
parser.add_argument(
'class_name',
metavar='<class_name>',
help='Name of quota class to set the quotas for.')
parser.add_argument(
'--plans',
metavar='<plans>',
type=int,
default=None,
help='New value for the "plans" quota.')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
class_name = parsed_args.class_name
data = {
"plans": parsed_args.plans,
}
result = client.quota_classes.update(class_name, data)
quota_classes = quota_class_set_pretty_show(result)
return zip(*sorted(quota_classes))

View File

@@ -0,0 +1,108 @@
# 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 quotas action implementations"""
from osc_lib.command import command
def quota_set_pretty_show(quotas):
"""Convert quotas object to dict and display."""
new_quotas = []
for quota_k, quota_v in sorted(quotas.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
new_quotas.append((quota_k, quota_v))
return new_quotas
class ShowQuotas(command.ShowOne):
_description = "Shows Quotas"
def get_parser(self, prog_name):
parser = super(ShowQuotas, self).get_parser(prog_name)
parser.add_argument(
'--tenant',
metavar='<tenant>',
default=None,
help='ID of tenant to list the quotas for.')
parser.add_argument(
'--detail',
action='store_true',
help='Optional flag to indicate whether to show quota in detail. '
'Default false.')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
project_id = parsed_args.tenant or client.http_client.get_project_id()
kwargs = {
"project_id": project_id,
"detail": parsed_args.detail,
}
result = client.quotas.get(**kwargs)
quotas = quota_set_pretty_show(result)
return zip(*sorted(quotas))
class ShowDefaultQuotas(command.ShowOne):
_description = "Shows default Quotas"
def get_parser(self, prog_name):
parser = super(ShowDefaultQuotas, self).get_parser(prog_name)
parser.add_argument(
'--tenant',
metavar='<tenant>',
default=None,
help='ID of tenant to list the quotas for.')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
project_id = parsed_args.tenant or client.http_client.get_project_id()
result = client.quotas.defaults(project_id)
quotas = quota_set_pretty_show(result)
return zip(*sorted(quotas))
class UpdateQuotas(command.ShowOne):
_description = "Updates Quotas"
def get_parser(self, prog_name):
parser = super(UpdateQuotas, self).get_parser(prog_name)
parser.add_argument(
'tenant',
metavar='<tenant>',
help='ID of tenant to set the quotas for.')
parser.add_argument(
'--plans',
metavar='<plans>',
type=int,
default=None,
help='New value for the "plans" quota.')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
project_id = parsed_args.tenant
data = {
"plans": parsed_args.plans,
}
result = client.quotas.update(project_id, data)
quotas = quota_set_pretty_show(result)
return zip(*sorted(quotas))

View File

@@ -1,195 +1,212 @@
# 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 restore action implementations"""
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
class ListRestores(command.Lister):
_description = _("List restores.")
log = logging.getLogger(__name__ + ".ListRestores")
def get_parser(self, prog_name):
parser = super(ListRestores, 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='<restore>',
help=_('The last restore ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-restores>',
help=_('Maximum number of restores 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.restores.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Project id', 'Provider id', 'Checkpoint id',
'Restore target', 'Parameters', 'Status']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowRestore(command.ShowOne):
_description = "Shows restore details"
def get_parser(self, prog_name):
parser = super(ShowRestore, self).get_parser(prog_name)
parser.add_argument(
'restore',
metavar="<restore>",
help=_('The UUID of the restore.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
restore = osc_utils.find_resource(client.restores, parsed_args.restore)
restore._info.pop("links", None)
return zip(*sorted(restore._info.items()))
class CreateRestore(command.ShowOne):
_description = "Creates a restore"
def get_parser(self, prog_name):
parser = super(CreateRestore, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('The UUID of the provider.')
)
parser.add_argument(
'checkpoint_id',
metavar='<checkpoint_id>',
help=_('The UUID of the checkpoint.')
)
parser.add_argument(
'--restore_target',
metavar='<restore_target>',
help=_('The target of the restore operation.')
)
parser.add_argument(
'--restore_username',
metavar='<restore_username>',
default=None,
help=_('Username to restore target.')
)
parser.add_argument(
'--restore_password',
metavar='<restore_password>',
default=None,
help=_('Password to restore target.')
)
parser.add_argument(
'--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help=_('Restore parameters in json format.')
)
parser.add_argument(
'--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help=_("Restore parameters, may be specified multiple times. "
"resource_type: type of resource to apply parameters. "
"resource_id: limit the parameters to a specific resource. "
"Other keys and values: according to provider\'s "
"restore schema.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
if not uuidutils.is_uuid_like(parsed_args.checkpoint_id):
raise exceptions.CommandError(
"Invalid checkpoint id provided.")
restore_parameters = utils.extract_parameters(parsed_args)
restore_auth = None
if parsed_args.restore_target is not None:
if parsed_args.restore_username is None:
raise exceptions.CommandError(
"Must specify username for restore_target.")
if parsed_args.restore_password is None:
raise exceptions.CommandError(
"Must specify password for restore_target.")
restore_auth = {
'type': 'password',
'username': parsed_args.restore_username,
'password': parsed_args.restore_password,
}
restore = client.restores.create(parsed_args.provider_id,
parsed_args.checkpoint_id,
parsed_args.restore_target,
restore_parameters, restore_auth)
restore._info.pop("links", None)
return zip(*sorted(restore._info.items()))
# 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 restore action implementations"""
import functools
import json
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
def format_restore(restore_info):
for key in ('parameters', 'resources_status',
'resources_reason'):
if key not in restore_info:
continue
restore_info[key] = json.dumps(restore_info[key],
indent=2, sort_keys=True)
restore_info.pop("links", None)
class ListRestores(command.Lister):
_description = _("List restores.")
log = logging.getLogger(__name__ + ".ListRestores")
def get_parser(self, prog_name):
parser = super(ListRestores, 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='<restore>',
help=_('The last restore ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-restores>',
help=_('Maximum number of restores 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.restores.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Project id', 'Provider id', 'Checkpoint id',
'Restore target', 'Parameters', 'Status']
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
formatters = {
"Parameters": json_dumps,
}
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers, formatters=formatters,
) for s in data))
class ShowRestore(command.ShowOne):
_description = "Shows restore details"
def get_parser(self, prog_name):
parser = super(ShowRestore, self).get_parser(prog_name)
parser.add_argument(
'restore',
metavar="<restore>",
help=_('The UUID of the restore.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
restore = osc_utils.find_resource(client.restores, parsed_args.restore)
format_restore(restore._info)
return zip(*sorted(restore._info.items()))
class CreateRestore(command.ShowOne):
_description = "Creates a restore"
def get_parser(self, prog_name):
parser = super(CreateRestore, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('The UUID of the provider.')
)
parser.add_argument(
'checkpoint_id',
metavar='<checkpoint_id>',
help=_('The UUID of the checkpoint.')
)
parser.add_argument(
'--restore_target',
metavar='<restore_target>',
help=_('The target of the restore operation.')
)
parser.add_argument(
'--restore_username',
metavar='<restore_username>',
default=None,
help=_('Username to restore target.')
)
parser.add_argument(
'--restore_password',
metavar='<restore_password>',
default=None,
help=_('Password to restore target.')
)
parser.add_argument(
'--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help=_('Restore parameters in json format.')
)
parser.add_argument(
'--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help=_("Restore parameters, may be specified multiple times. "
"resource_type: type of resource to apply parameters. "
"resource_id: limit the parameters to a specific resource. "
"Other keys and values: according to provider\'s "
"restore schema.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
if not uuidutils.is_uuid_like(parsed_args.checkpoint_id):
raise exceptions.CommandError(
"Invalid checkpoint id provided.")
restore_parameters = utils.extract_parameters(parsed_args)
restore_auth = None
if parsed_args.restore_target is not None:
if parsed_args.restore_username is None:
raise exceptions.CommandError(
"Must specify username for restore_target.")
if parsed_args.restore_password is None:
raise exceptions.CommandError(
"Must specify password for restore_target.")
restore_auth = {
'type': 'password',
'username': parsed_args.restore_username,
'password': parsed_args.restore_password,
}
restore = client.restores.create(parsed_args.provider_id,
parsed_args.checkpoint_id,
parsed_args.restore_target,
restore_parameters, restore_auth)
format_restore(restore._info)
return zip(*sorted(restore._info.items()))

View File

@@ -1,208 +1,220 @@
# 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 scheduled_operations action implementations"""
import six
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
class ListScheduledOperations(command.Lister):
_description = _("List scheduled_operations.")
log = logging.getLogger(__name__ + ".ListScheduledOperations")
def get_parser(self, prog_name):
parser = super(ListScheduledOperations, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Shows details for all tenants. Admin only.'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--operation_type',
metavar='<operation_type>',
default=None,
help=_('Filters results by a type. Default=None.'),
)
parser.add_argument(
'--trigger_id',
metavar='<trigger_id>',
default=None,
help=_('Filters results by a trigger id. Default=None.'),
)
parser.add_argument(
'--operation_definition',
metavar='<operation_definition>',
default=None,
help=_('Filters results by a operation definition. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<scheduled_operations>',
help=_('The last scheduled_operations ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-scheduled_operations>',
help=_('Maximum number of scheduled_operations to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), 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,
'name': parsed_args.name,
'operation_type': parsed_args.operation_type,
'trigger_id': parsed_args.trigger_id,
'operation_definition': parsed_args.operation_definition,
}
data = data_protection_client.scheduled_operations.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'OperationType', 'TriggerId',
'OperationDefinition']
scheduled_operations = []
for s in data:
scheduled_operation = (s.id, s.name, s.operation_type,
s.trigger_id, s.operation_definition)
scheduled_operations.append(scheduled_operation)
return (column_headers, scheduled_operations)
class ShowScheduledOperation(command.ShowOne):
_description = "Shows scheduled_operation details"
def get_parser(self, prog_name):
parser = super(ShowScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'scheduledoperation',
metavar="<scheduledoperation>",
help=_('The UUID of the scheduledoperation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
so = osc_utils.find_resource(client.scheduled_operations,
parsed_args.scheduledoperation)
so._info.pop("links", None)
return zip(*sorted(six.iteritems(so._info)))
class CreateScheduledOperation(command.ShowOne):
_description = "Creates a scheduled operation"
def get_parser(self, prog_name):
parser = super(CreateScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the scheduled operation.')
)
parser.add_argument(
'operation_type',
metavar='<operation_type>',
help=_('Operation Type of scheduled operation.')
)
parser.add_argument(
'trigger_id',
metavar='<trigger_id>',
help=_('Trigger id of scheduled operation.')
)
parser.add_argument(
'operation_definition',
metavar='<key=value,key=value>',
help=_('Operation definition of scheduled operation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.trigger_id):
raise exceptions.CommandError(
"Invalid trigger id provided.")
so = client.scheduled_operations.create(
parsed_args.name, parsed_args.operation_type,
parsed_args.trigger_id, parsed_args.operation_definition)
so._info.pop("links", None)
return zip(*sorted(six.iteritems(so._info)))
class DeleteScheduledOperation(command.Command):
_description = "Delete scheduled operation"
def get_parser(self, prog_name):
parser = super(DeleteScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'scheduledoperation',
metavar='<scheduledoperation>',
nargs="+",
help=_('ID of scheduled operation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for so_id in parsed_args.scheduledoperation:
try:
so = osc_utils.find_resource(client.scheduled_operations,
so_id)
client.scheduled_operations.delete(so.id)
except exceptions.NotFound:
failure_count += 1
print("Failed to delete '%s'; scheduled operation "
"not found" % so_id)
if failure_count == len(parsed_args.scheduledoperation):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified scheduled operation.")
# 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 scheduled_operations action implementations"""
import functools
import json
import six
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
def format_scheduledoperation(scheduledoperation_info):
for key in ('operation_definition', ):
if key not in scheduledoperation_info:
continue
scheduledoperation_info[key] = json.dumps(scheduledoperation_info[key],
indent=2, sort_keys=True)
scheduledoperation_info.pop("links", None)
class ListScheduledOperations(command.Lister):
_description = _("List scheduled_operations.")
log = logging.getLogger(__name__ + ".ListScheduledOperations")
def get_parser(self, prog_name):
parser = super(ListScheduledOperations, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Shows details for all tenants. Admin only.'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--operation_type',
metavar='<operation_type>',
default=None,
help=_('Filters results by a type. Default=None.'),
)
parser.add_argument(
'--trigger_id',
metavar='<trigger_id>',
default=None,
help=_('Filters results by a trigger id. Default=None.'),
)
parser.add_argument(
'--operation_definition',
metavar='<operation_definition>',
default=None,
help=_('Filters results by a operation definition. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<scheduled_operations>',
help=_('The last scheduled_operations ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-scheduled_operations>',
help=_('Maximum number of scheduled_operations to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), 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,
'name': parsed_args.name,
'operation_type': parsed_args.operation_type,
'trigger_id': parsed_args.trigger_id,
'operation_definition': parsed_args.operation_definition,
}
data = data_protection_client.scheduled_operations.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Operation Type', 'Trigger Id',
'Operation Definition']
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
formatters = {
"Operation Definition": json_dumps,
}
return (column_headers,
list(osc_utils.get_item_properties(
s, column_headers, formatters=formatters,
) for s in data))
class ShowScheduledOperation(command.ShowOne):
_description = "Shows scheduled_operation details"
def get_parser(self, prog_name):
parser = super(ShowScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'scheduledoperation',
metavar="<scheduledoperation>",
help=_('The UUID of the scheduledoperation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
so = osc_utils.find_resource(client.scheduled_operations,
parsed_args.scheduledoperation)
format_scheduledoperation(so._info)
return zip(*sorted(six.iteritems(so._info)))
class CreateScheduledOperation(command.ShowOne):
_description = "Creates a scheduled operation"
def get_parser(self, prog_name):
parser = super(CreateScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the scheduled operation.')
)
parser.add_argument(
'operation_type',
metavar='<operation_type>',
help=_('Operation Type of scheduled operation.')
)
parser.add_argument(
'trigger_id',
metavar='<trigger_id>',
help=_('Trigger id of scheduled operation.')
)
parser.add_argument(
'operation_definition',
metavar='<key=value,key=value>',
help=_('Operation definition of scheduled operation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.trigger_id):
raise exceptions.CommandError(
"Invalid trigger id provided.")
so = client.scheduled_operations.create(
parsed_args.name, parsed_args.operation_type,
parsed_args.trigger_id, parsed_args.operation_definition)
format_scheduledoperation(so._info)
return zip(*sorted(six.iteritems(so._info)))
class DeleteScheduledOperation(command.Command):
_description = "Delete scheduled operation"
def get_parser(self, prog_name):
parser = super(DeleteScheduledOperation, self).get_parser(prog_name)
parser.add_argument(
'scheduledoperation',
metavar='<scheduledoperation>',
nargs="+",
help=_('ID of scheduled operation.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for so_id in parsed_args.scheduledoperation:
try:
so = osc_utils.find_resource(client.scheduled_operations,
so_id)
client.scheduled_operations.delete(so.id)
except exceptions.NotFound:
failure_count += 1
print("Failed to delete '%s'; scheduled operation "
"not found" % so_id)
if failure_count == len(parsed_args.scheduledoperation):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified scheduled operation.")

View File

@@ -0,0 +1,99 @@
# 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 os-services 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 ListServices(command.Lister):
_description = _("List services.")
log = logging.getLogger(__name__ + ".ListServices")
def get_parser(self, prog_name):
parser = super(ListServices, self).get_parser(prog_name)
parser.add_argument(
'--host',
metavar='<host>',
help=_('Filter results by host'),
)
parser.add_argument(
'--binary',
metavar='<binary>',
help=_('Filter results by binary'),
)
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
data = data_protection_client.services.list(
host=parsed_args.host,
binary=parsed_args.binary
)
column_headers = ["Id", "Binary", "Host", "Status", "State",
"Updated_at", "Disabled Reason"]
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class EnableService(command.ShowOne):
_description = _('Enable service')
def get_parser(self, prog_name):
parser = super(EnableService, self).get_parser(prog_name)
parser.add_argument(
'service_id',
metavar='<service_id>',
help=_('The ID of the service.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
service = client.services.enable(parsed_args.service_id)
return zip(*sorted(service._info.items()))
class DisableService(command.ShowOne):
_description = _('Disable service')
def get_parser(self, prog_name):
parser = super(DisableService, self).get_parser(prog_name)
parser.add_argument(
'service_id',
metavar='<service_id>',
help=_('The ID of the service.'),
)
parser.add_argument(
'--reason',
metavar='<reason>',
help=_('Reason for disabling the service.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if parsed_args.reason:
service = client.services.disable_log_reason(
parsed_args.service_id, parsed_args.reason)
else:
service = client.services.disable(parsed_args.service_id)
return zip(*sorted(service._info.items()))

View File

@@ -1,229 +1,229 @@
# 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 triggers 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.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
class ListTriggers(command.Lister):
_description = _("List triggers.")
log = logging.getLogger(__name__ + ".ListTriggers")
def get_parser(self, prog_name):
parser = super(ListTriggers, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Shows details for all tenants. Admin only.'),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--type',
metavar='<type>',
default=None,
help=_('Filters results by a type. Default=None.'),
)
parser.add_argument(
'--properties',
metavar='<properties>',
default=None,
help=_('Filters results by a properties. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<trigger>',
help=_('The last trigger ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-triggers>',
help=_('Maximum number of triggers to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma"),
)
parser.add_argument(
'--project',
metavar='<project>',
help=_('Display information from single tenant (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,
'name': parsed_args.name,
'type': parsed_args.type,
'properties': parsed_args.properties,
}
data = data_protection_client.triggers.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Type', 'Properties']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowTrigger(command.ShowOne):
_description = "Shows trigger details"
def get_parser(self, prog_name):
parser = super(ShowTrigger, self).get_parser(prog_name)
parser.add_argument(
'trigger',
metavar="<trigger>",
help=_('The UUID of the trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
trigger = osc_utils.find_resource(client.triggers, parsed_args.trigger)
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class CreateTrigger(command.ShowOne):
_description = "Creates a trigger"
def get_parser(self, prog_name):
parser = super(CreateTrigger, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the trigger.')
)
parser.add_argument(
'type',
metavar='<type>',
help=_('Type of trigger.')
)
parser.add_argument(
'properties',
metavar='<key=value,key=value>',
help=_('Properties of trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
trigger = client.triggers.create(parsed_args.name, parsed_args.type,
parsed_args.properties)
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class UpdateTrigger(command.ShowOne):
_description = "Update a trigger"
def get_parser(self, prog_name):
parser = super(UpdateTrigger, self).get_parser(prog_name)
parser.add_argument(
"trigger_id",
metavar="<TRIGGER ID>",
help=_("Id of trigger to update.")
)
parser.add_argument(
"--name",
metavar="<name>",
help=_("A name to which the trigger will be renamed.")
)
parser.add_argument(
"--properties",
metavar="<key=value,key=value>",
help=_("Properties of trigger which will be updated.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
data = {}
if parsed_args.name is not None:
data['name'] = parsed_args.name
if parsed_args.properties is not None:
trigger_properties = utils.extract_properties(parsed_args)
data['properties'] = trigger_properties
try:
trigger = osc_utils.find_resource(client.triggers,
parsed_args.trigger_id)
trigger = client.triggers.update(trigger.id, data)
except exceptions.NotFound:
raise exceptions.CommandError(
"Trigger %s not found" % parsed_args.trigger_id)
else:
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class DeleteTrigger(command.Command):
_description = "Delete trigger"
log = logging.getLogger(__name__ + ".DeleteTrigger")
def get_parser(self, prog_name):
parser = super(DeleteTrigger, self).get_parser(prog_name)
parser.add_argument(
'trigger',
metavar='<trigger>',
nargs="+",
help=_('ID of trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for trigger_id in parsed_args.trigger:
try:
trigger = osc_utils.find_resource(client.triggers, trigger_id)
client.triggers.delete(trigger.id)
except exceptions.NotFound:
failure_count += 1
self.log.error(
"Failed to delete '{0}'; trigger not found".
format(trigger_id))
if failure_count == len(parsed_args.trigger):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified trigger.")
# 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 triggers 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.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
class ListTriggers(command.Lister):
_description = _("List triggers.")
log = logging.getLogger(__name__ + ".ListTriggers")
def get_parser(self, prog_name):
parser = super(ListTriggers, self).get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Shows details for all tenants. Admin only.'),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_('Filters results by a name. Default=None.'),
)
parser.add_argument(
'--type',
metavar='<type>',
default=None,
help=_('Filters results by a type. Default=None.'),
)
parser.add_argument(
'--properties',
metavar='<properties>',
default=None,
help=_('Filters results by a properties. Default=None.'),
)
parser.add_argument(
'--marker',
metavar='<trigger>',
help=_('The last trigger ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-triggers>',
help=_('Maximum number of triggers to display'),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default=None,
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma"),
)
parser.add_argument(
'--project',
metavar='<project>',
help=_('Display information from single tenant (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,
'name': parsed_args.name,
'type': parsed_args.type,
'properties': parsed_args.properties,
}
data = data_protection_client.triggers.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Name', 'Type', 'Properties']
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers
) for s in data))
class ShowTrigger(command.ShowOne):
_description = "Shows trigger details"
def get_parser(self, prog_name):
parser = super(ShowTrigger, self).get_parser(prog_name)
parser.add_argument(
'trigger',
metavar="<trigger>",
help=_('The UUID of the trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
trigger = osc_utils.find_resource(client.triggers, parsed_args.trigger)
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class CreateTrigger(command.ShowOne):
_description = "Creates a trigger"
def get_parser(self, prog_name):
parser = super(CreateTrigger, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('The name of the trigger.')
)
parser.add_argument(
'type',
metavar='<type>',
help=_('Type of trigger.')
)
parser.add_argument(
'properties',
metavar='<key=value,key=value>',
help=_('Properties of trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
trigger = client.triggers.create(parsed_args.name, parsed_args.type,
parsed_args.properties)
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class UpdateTrigger(command.ShowOne):
_description = "Update a trigger"
def get_parser(self, prog_name):
parser = super(UpdateTrigger, self).get_parser(prog_name)
parser.add_argument(
"trigger_id",
metavar="<TRIGGER ID>",
help=_("Id of trigger to update.")
)
parser.add_argument(
"--name",
metavar="<name>",
help=_("A name to which the trigger will be renamed.")
)
parser.add_argument(
"--properties",
metavar="<key=value,key=value>",
help=_("Properties of trigger which will be updated.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
data = {}
if parsed_args.name is not None:
data['name'] = parsed_args.name
if parsed_args.properties is not None:
trigger_properties = utils.extract_properties(parsed_args)
data['properties'] = trigger_properties
try:
trigger = osc_utils.find_resource(client.triggers,
parsed_args.trigger_id)
trigger = client.triggers.update(trigger.id, data)
except exceptions.NotFound:
raise exceptions.CommandError(
"Trigger %s not found" % parsed_args.trigger_id)
else:
trigger._info.pop("links", None)
return zip(*sorted(trigger._info.items()))
class DeleteTrigger(command.Command):
_description = "Delete trigger"
log = logging.getLogger(__name__ + ".DeleteTrigger")
def get_parser(self, prog_name):
parser = super(DeleteTrigger, self).get_parser(prog_name)
parser.add_argument(
'trigger',
metavar='<trigger>',
nargs="+",
help=_('ID of trigger.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
failure_count = 0
for trigger_id in parsed_args.trigger:
try:
trigger = osc_utils.find_resource(client.triggers, trigger_id)
client.triggers.delete(trigger.id)
except exceptions.NotFound:
failure_count += 1
self.log.error(
"Failed to delete '{0}'; trigger not found".
format(trigger_id))
if failure_count == len(parsed_args.trigger):
raise exceptions.CommandError(
"Unable to find and delete any of the "
"specified trigger.")

View File

@@ -0,0 +1,182 @@
# 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 verification action implementations"""
import functools
import json
from oslo_utils import uuidutils
from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
from karborclient.common.apiclient import exceptions
from karborclient.i18n import _
from karborclient import utils
def format_verification(verification_info):
for key in ('parameters', 'resources_status',
'resources_reason'):
if key not in verification_info:
continue
verification_info[key] = json.dumps(verification_info[key],
indent=2, sort_keys=True)
verification_info.pop("links", None)
class ListVerifications(command.Lister):
_description = _("List verifications.")
log = logging.getLogger(__name__ + ".ListVerifications")
def get_parser(self, prog_name):
parser = super(ListVerifications, 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='<verification>',
help=_('The last verification ID of the previous page'),
)
parser.add_argument(
'--limit',
type=int,
metavar='<num-verifications>',
help=_('Maximum number of verifications 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.verifications.list(
search_opts=search_opts, marker=parsed_args.marker,
limit=parsed_args.limit, sort=parsed_args.sort)
column_headers = ['Id', 'Project id', 'Provider id', 'Checkpoint id',
'Parameters', 'Status']
json_dumps = functools.partial(json.dumps, indent=2, sort_keys=True)
formatters = {
"Parameters": json_dumps,
}
return (column_headers,
(osc_utils.get_item_properties(
s, column_headers, formatters=formatters,
) for s in data))
class ShowVerification(command.ShowOne):
_description = "Shows verification details"
def get_parser(self, prog_name):
parser = super(ShowVerification, self).get_parser(prog_name)
parser.add_argument(
'verification',
metavar="<verification>",
help=_('The UUID of the verification.')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
verification = osc_utils.find_resource(client.verifications,
parsed_args.verification)
format_verification(verification._info)
return zip(*sorted(verification._info.items()))
class CreateVerification(command.ShowOne):
_description = "Creates a verification"
def get_parser(self, prog_name):
parser = super(CreateVerification, self).get_parser(prog_name)
parser.add_argument(
'provider_id',
metavar='<provider_id>',
help=_('The UUID of the provider.')
)
parser.add_argument(
'checkpoint_id',
metavar='<checkpoint_id>',
help=_('The UUID of the checkpoint.')
)
parser.add_argument(
'--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help=_('Verification parameters in json format.')
)
parser.add_argument(
'--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help=_("Verification parameters, may be specified multiple times. "
"resource_type: type of resource to apply parameters. "
"resource_id: limit the parameters to a specific resource. "
"Other keys and values: according to provider\'s "
"verification schema.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.data_protection
if not uuidutils.is_uuid_like(parsed_args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
if not uuidutils.is_uuid_like(parsed_args.checkpoint_id):
raise exceptions.CommandError(
"Invalid checkpoint id provided.")
verification_parameters = utils.extract_parameters(parsed_args)
verification = client.verifications.create(parsed_args.provider_id,
parsed_args.checkpoint_id,
verification_parameters)
format_verification(verification._info)
return zip(*sorted(verification._info.items()))

View File

@@ -165,19 +165,8 @@ class KarborShell(object):
self._find_actions(subparsers, submodule)
self._find_actions(subparsers, self)
self._add_bash_completion_subparser(subparsers)
return parser
def _add_bash_completion_subparser(self, subparsers):
subparser = subparsers.add_parser(
'bash_completion',
add_help=False,
formatter_class=HelpFormatter
)
self.subcommands['bash_completion'] = subparser
subparser.set_defaults(func=self.do_bash_completion)
def _find_actions(self, subparsers, actions_module):
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
# I prefer to be hypen-separated instead of underscores.
@@ -425,7 +414,6 @@ class KarborShell(object):
options.add(option)
commands.remove('bash-completion')
commands.remove('bash_completion')
print(' '.join(commands | options))
@utils.arg('command', metavar='<subcommand>', nargs='?',

View File

@@ -1 +1 @@
__author__ = 'c00179918'
__author__ = 'c00179918'

View File

@@ -1,29 +1,29 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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 osc_lib.tests import utils
class TestDataProtection(utils.TestCommand):
def setUp(self):
super(TestDataProtection, self).setUp()
self.app.client_manager.data_protection = mock.Mock()
self.app.client_manager.network = mock.Mock()
self.app.client_manager.compute = mock.Mock()
self.app.client_manager.volume = mock.Mock()
# Copyright (c) 2015 Mirantis Inc.
#
# 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 osc_lib.tests import utils
class TestDataProtection(utils.TestCommand):
def setUp(self):
super(TestDataProtection, self).setUp()
self.app.client_manager.data_protection = mock.Mock()
self.app.client_manager.network = mock.Mock()
self.app.client_manager.compute = mock.Mock()
self.app.client_manager.volume = mock.Mock()

View File

@@ -11,6 +11,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import json
from karborclient.osc.v1 import checkpoints as osc_checkpoints
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import checkpoints
@@ -29,9 +32,11 @@ CHECKPOINT_INFO = {
"type": "OS::Glance::Image",
"name": "cirros-0.3.4-x86_64-uec"}]
},
"resource_graph": "[{'0x0': ['OS::Glance::Image', "
"'99777fdd-8a5b-45ab-ba2c-52420008103f', "
"'cirros-0.3.4-x86_64-uec']}, [[['0x0']]]]"
"resource_graph": json.dumps(
"[{'0x0': ['OS::Glance::Image', "
"'99777fdd-8a5b-45ab-ba2c-52420008103f', "
"'cirros-0.3.4-x86_64-uec']}, [[['0x0']]]]"
),
}
@@ -47,7 +52,7 @@ class TestListCheckpoints(TestCheckpoints):
def setUp(self):
super(TestListCheckpoints, self).setUp()
self.checkpoints_mock.list.return_value = [checkpoints.Checkpoint(
None, CHECKPOINT_INFO)]
None, copy.deepcopy(CHECKPOINT_INFO))]
# Command to test
self.cmd = osc_checkpoints.ListCheckpoints(self.app, None)
@@ -71,14 +76,9 @@ class TestListCheckpoints(TestCheckpoints):
"dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"e486a2f49695423ca9c47e589b948108",
"available",
{
"Name: %(name)s\nId: %(id)s" % {
"id": "3523a271-68aa-42f5-b9ba-56e5200a2ebb",
"name": "My application",
"provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"resources": [{
"id": "99777fdd-8a5b-45ab-ba2c-52420008103f",
"type": "OS::Glance::Image",
"name": "cirros-0.3.4-x86_64-uec"}]
},
'',
'')]
@@ -89,7 +89,7 @@ class TestCreateCheckpoint(TestCheckpoints):
def setUp(self):
super(TestCreateCheckpoint, self).setUp()
self.checkpoints_mock.create.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
None, copy.deepcopy(CHECKPOINT_INFO))
# Command to test
self.cmd = osc_checkpoints.CreateCheckpoint(self.app, None)
@@ -114,7 +114,7 @@ class TestShowCheckpoint(TestCheckpoints):
def setUp(self):
super(TestShowCheckpoint, self).setUp()
self.checkpoints_mock.get.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
None, copy.deepcopy(CHECKPOINT_INFO))
# Command to test
self.cmd = osc_checkpoints.ShowCheckpoint(self.app, None)
@@ -139,7 +139,7 @@ class TestDeleteCheckpoint(TestCheckpoints):
def setUp(self):
super(TestDeleteCheckpoint, self).setUp()
self.checkpoints_mock.get.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
None, copy.deepcopy(CHECKPOINT_INFO))
# Command to test
self.cmd = osc_checkpoints.DeleteCheckpoint(self.app, None)

View File

@@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
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
@@ -45,7 +47,8 @@ class TestListOperationLogs(TestOperationLogs):
def setUp(self):
super(TestListOperationLogs, self).setUp()
self.operation_logs_mock.list.return_value = [
operation_logs.OperationLog(None, OPERATIONLOG_INFO)]
operation_logs.OperationLog(None,
copy.deepcopy(OPERATIONLOG_INFO))]
# Command to test
self.cmd = osc_operation_logs.ListOperationLogs(self.app, None)
@@ -85,8 +88,9 @@ class TestListOperationLogs(TestOperationLogs):
class TestShowOperationLog(TestOperationLogs):
def setUp(self):
super(TestShowOperationLog, self).setUp()
self._oplog_info = copy.deepcopy(OPERATIONLOG_INFO)
self.operation_logs_mock.get.return_value = (
operation_logs.OperationLog(None, OPERATIONLOG_INFO))
operation_logs.OperationLog(None, self._oplog_info))
# Command to test
self.cmd = osc_operation_logs.ShowOperationLog(self.app, None)
@@ -109,16 +113,16 @@ class TestShowOperationLog(TestOperationLogs):
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])
self.assertEqual(self._oplog_info['checkpoint_id'], data[0])
self.assertEqual(self._oplog_info['ended_at'], data[1])
self.assertEqual(self._oplog_info['error_info'], data[2])
self.assertEqual(self._oplog_info['extra_info'], data[3])
self.assertEqual(self._oplog_info['id'], data[4])
self.assertEqual(self._oplog_info['operation_type'], data[5])
self.assertEqual(self._oplog_info['plan_id'], data[6])
self.assertEqual(self._oplog_info['project_id'], data[7])
self.assertEqual(self._oplog_info['provider_id'], data[8])
self.assertEqual(self._oplog_info['restore_id'], data[9])
self.assertEqual(self._oplog_info['scheduled_operation_id'], data[10])
self.assertEqual(self._oplog_info['started_at'], data[11])
self.assertEqual(self._oplog_info['status'], data[12])

View File

@@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from karborclient.osc.v1 import plans as osc_plans
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import plans
@@ -41,7 +43,7 @@ class TestListPlans(TestPlans):
def setUp(self):
super(TestListPlans, self).setUp()
self.plans_mock.list.return_value = [plans.Plan(
None, PLAN_INFO)]
None, copy.deepcopy(PLAN_INFO))]
# Command to test
self.cmd = osc_plans.ListPlans(self.app, None)
@@ -72,7 +74,7 @@ class TestCreatePlan(TestPlans):
def setUp(self):
super(TestCreatePlan, self).setUp()
self.plans_mock.create.return_value = plans.Plan(
None, PLAN_INFO)
None, copy.deepcopy(PLAN_INFO))
# Command to test
self.cmd = osc_plans.CreatePlan(self.app, None)
@@ -104,9 +106,9 @@ class TestUpdatePlan(TestPlans):
def setUp(self):
super(TestUpdatePlan, self).setUp()
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
None, copy.deepcopy(PLAN_INFO))
self.plans_mock.update.return_value = plans.Plan(
None, PLAN_INFO)
None, copy.deepcopy(PLAN_INFO))
# Command to test
self.cmd = osc_plans.UpdatePlan(self.app, None)
@@ -130,7 +132,7 @@ class TestDeletePlan(TestPlans):
def setUp(self):
super(TestDeletePlan, self).setUp()
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
None, copy.deepcopy(PLAN_INFO))
# Command to test
self.cmd = osc_plans.DeletePlan(self.app, None)
@@ -150,8 +152,9 @@ class TestDeletePlan(TestPlans):
class TestShowPlan(TestPlans):
def setUp(self):
super(TestShowPlan, self).setUp()
self._plan_info = copy.deepcopy(PLAN_INFO)
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
None, self._plan_info)
# Command to test
self.cmd = osc_plans.ShowPlan(self.app, None)
@@ -171,10 +174,10 @@ class TestShowPlan(TestPlans):
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(PLAN_INFO['description'], data[0])
self.assertEqual(PLAN_INFO['id'], data[1])
self.assertEqual(PLAN_INFO['name'], data[2])
self.assertEqual(PLAN_INFO['parameters'], data[3])
self.assertEqual(PLAN_INFO['provider_id'], data[4])
self.assertEqual(PLAN_INFO['resources'], data[5])
self.assertEqual(PLAN_INFO['status'], data[6])
self.assertEqual(self._plan_info['description'], data[0])
self.assertEqual(self._plan_info['id'], data[1])
self.assertEqual(self._plan_info['name'], data[2])
self.assertEqual(self._plan_info['parameters'], data[3])
self.assertEqual(self._plan_info['provider_id'], data[4])
self.assertEqual(self._plan_info['resources'], data[5])
self.assertEqual(self._plan_info['status'], data[6])

View File

@@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from karborclient.osc.v1 import protectables as osc_protectables
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import protectables
@@ -65,7 +67,7 @@ class TestListProtectables(TestProtectables):
def setUp(self):
super(TestListProtectables, self).setUp()
self.protectables_mock.list.return_value = [protectables.Protectable(
None, PROTECTABLE_LIST_INFO)]
None, copy.deepcopy(PROTECTABLE_LIST_INFO))]
# Command to test
self.cmd = osc_protectables.ListProtectables(self.app, None)
@@ -95,7 +97,7 @@ class TestShowProtectable(TestProtectables):
def setUp(self):
super(TestShowProtectable, self).setUp()
self.protectables_mock.get.return_value = protectables.Protectable(
None, PROTECTABLE_SHOW_INFO)
None, copy.deepcopy(PROTECTABLE_SHOW_INFO))
# Command to test
self.cmd = osc_protectables.ShowProtectable(self.app, None)
@@ -116,8 +118,8 @@ class TestListProtectableInstances(TestProtectables):
def setUp(self):
super(TestListProtectableInstances, self).setUp()
pm = self.protectables_mock
pm.list_instances.return_value = protectables.Instances(
None, PROTECTABLE_INSTANCE_LIST_INFO)
pm.list_instances.return_value = [protectables.Instances(
None, copy.deepcopy(PROTECTABLE_INSTANCE_LIST_INFO)), ]
# Command to test
self.cmd = osc_protectables.ListProtectableInstances(self.app, None)
@@ -141,7 +143,7 @@ class TestShowProtectableInstance(TestProtectables):
super(TestShowProtectableInstance, self).setUp()
pm = self.protectables_mock
pm.get_instance.return_value = protectables.Instances(
None, PROTECTABLE_INSTANCE_SHOW_INFO)
None, copy.deepcopy(PROTECTABLE_INSTANCE_SHOW_INFO))
# Command to test
self.cmd = osc_protectables.ShowProtectableInstance(self.app, None)

View File

@@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from karborclient.osc.v1 import providers as osc_providers
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import providers
@@ -87,7 +89,7 @@ class TestListProviders(TestProviders):
def setUp(self):
super(TestListProviders, self).setUp()
self.providers_mock.list.return_value = [providers.Provider(
None, PROVIDER_INFO)]
None, copy.deepcopy(PROVIDER_INFO))]
# Command to test
self.cmd = osc_providers.ListProviders(self.app, None)
@@ -115,8 +117,9 @@ class TestListProviders(TestProviders):
class TestShowProvider(TestProviders):
def setUp(self):
super(TestShowProvider, self).setUp()
self._provider_info = copy.deepcopy(PROVIDER_INFO)
self.providers_mock.get.return_value = providers.Provider(
None, PROVIDER_INFO)
None, self._provider_info)
# Command to test
self.cmd = osc_providers.ShowProvider(self.app, None)
@@ -135,7 +138,7 @@ class TestShowProvider(TestProviders):
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(PROVIDER_INFO['description'], data[0])
self.assertEqual(PROVIDER_INFO['extended_info_schema'], data[1])
self.assertEqual(PROVIDER_INFO['id'], data[2])
self.assertEqual(PROVIDER_INFO['name'], data[3])
self.assertEqual(self._provider_info['description'], data[0])
self.assertEqual(self._provider_info['extended_info_schema'], data[1])
self.assertEqual(self._provider_info['id'], data[2])
self.assertEqual(self._provider_info['name'], data[3])

View File

@@ -0,0 +1,77 @@
# 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 copy
from karborclient.osc.v1 import quota_classes as osc_quota_classes
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import quota_classes
QUOTA_CLASSES_INFO = {
"id": "default",
"plans": "40"
}
class TestQuotaClasses(fakes.TestDataProtection):
def setUp(self):
super(TestQuotaClasses, self).setUp()
self.quotas_mock = (
self.app.client_manager.data_protection.quota_classes)
self.quotas_mock.reset_mock()
class TestUpdateQuotaClasses(TestQuotaClasses):
def setUp(self):
super(TestUpdateQuotaClasses, self).setUp()
self.quotas_mock.update.return_value = quota_classes.QuotaClass(
None, copy.deepcopy(QUOTA_CLASSES_INFO))
self.cmd = osc_quota_classes.UpdateQuotaClasses(self.app, None)
def test_quota_classes_update(self):
arglist = ['--plans',
'40', 'default']
verifylist = [('plans', 40),
('class_name',
'default')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.quotas_mock.update.assert_called_once_with(
'default',
{'plans': 40})
class TestShowQuotaClasses(TestQuotaClasses):
def setUp(self):
super(TestShowQuotaClasses, self).setUp()
self._quota_info = copy.deepcopy(QUOTA_CLASSES_INFO)
self.quotas_mock.get.return_value = quota_classes.QuotaClass(
None, copy.deepcopy(QUOTA_CLASSES_INFO))
self.cmd = osc_quota_classes.ShowQuotaClasses(self.app, None)
def test_quota_classes_show(self):
arglist = ['default']
verifylist = [('class_name', 'default')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(('id', 'plans'), columns)
self.assertEqual(('default', '40'),
data)

View File

@@ -0,0 +1,76 @@
# 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 copy
from karborclient.osc.v1 import quotas as osc_quotas
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import quotas
QUOTA_INFO = {
"id": "73f74f90a1754bd7ad658afb3272323f",
"plans": "40"
}
class TestQuotas(fakes.TestDataProtection):
def setUp(self):
super(TestQuotas, self).setUp()
self.quotas_mock = self.app.client_manager.data_protection.quotas
self.quotas_mock.reset_mock()
class TestUpdateQuotas(TestQuotas):
def setUp(self):
super(TestUpdateQuotas, self).setUp()
self.quotas_mock.update.return_value = quotas.Quota(
None, copy.deepcopy(QUOTA_INFO))
self.cmd = osc_quotas.UpdateQuotas(self.app, None)
def test_quotas_update(self):
arglist = ['--plans',
'40', '73f74f90a1754bd7ad658afb3272323f']
verifylist = [('plans', 40),
('tenant',
'73f74f90a1754bd7ad658afb3272323f')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.quotas_mock.update.assert_called_once_with(
'73f74f90a1754bd7ad658afb3272323f',
{'plans': 40})
class TestShowQuotas(TestQuotas):
def setUp(self):
super(TestShowQuotas, self).setUp()
self._quota_info = copy.deepcopy(QUOTA_INFO)
self.quotas_mock.get.return_value = quotas.Quota(
None, copy.deepcopy(QUOTA_INFO))
self.cmd = osc_quotas.ShowQuotas(self.app, None)
def test_quota_show(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(('id', 'plans'), columns)
self.assertEqual(('73f74f90a1754bd7ad658afb3272323f', '40'),
data)

View File

@@ -11,6 +11,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import json
from karborclient.osc.v1 import restores as osc_restores
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import restores
@@ -41,7 +44,7 @@ class TestListRestores(TestRestores):
def setUp(self):
super(TestListRestores, self).setUp()
self.restores_mock.list.return_value = [restores.Restore(
None, RESTORE_INFO)]
None, copy.deepcopy(RESTORE_INFO))]
# Command to test
self.cmd = osc_restores.ListRestores(self.app, None)
@@ -66,7 +69,7 @@ class TestListRestores(TestRestores):
"cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"",
{},
json.dumps({}),
"success")]
self.assertEqual(expected_data, list(data))
@@ -75,7 +78,7 @@ class TestCreateRestore(TestRestores):
def setUp(self):
super(TestCreateRestore, self).setUp()
self.restores_mock.create.return_value = restores.Restore(
None, RESTORE_INFO)
None, copy.deepcopy(RESTORE_INFO))
# Command to test
self.cmd = osc_restores.CreateRestore(self.app, None)
@@ -100,8 +103,9 @@ class TestCreateRestore(TestRestores):
class TestShowRestore(TestRestores):
def setUp(self):
super(TestShowRestore, self).setUp()
self._restore_info = copy.deepcopy(RESTORE_INFO)
self.restores_mock.get.return_value = restores.Restore(
None, RESTORE_INFO)
None, self._restore_info)
# Command to test
self.cmd = osc_restores.ShowRestore(self.app, None)
@@ -122,13 +126,13 @@ class TestShowRestore(TestRestores):
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(RESTORE_INFO['checkpoint_id'], data[0])
self.assertEqual(RESTORE_INFO['id'], data[1])
self.assertEqual(RESTORE_INFO['parameters'], data[2])
self.assertEqual(RESTORE_INFO['project_id'], data[3])
self.assertEqual(RESTORE_INFO['provider_id'], data[4])
self.assertEqual(RESTORE_INFO['resources_reason'], data[5])
self.assertEqual(RESTORE_INFO['resources_status'], data[6])
self.assertEqual(RESTORE_INFO['restore_auth'], data[7])
self.assertEqual(RESTORE_INFO['restore_target'], data[8])
self.assertEqual(RESTORE_INFO['status'], data[9])
self.assertEqual(self._restore_info['checkpoint_id'], data[0])
self.assertEqual(self._restore_info['id'], data[1])
self.assertEqual(self._restore_info['parameters'], data[2])
self.assertEqual(self._restore_info['project_id'], data[3])
self.assertEqual(self._restore_info['provider_id'], data[4])
self.assertEqual(self._restore_info['resources_reason'], data[5])
self.assertEqual(self._restore_info['resources_status'], data[6])
self.assertEqual(self._restore_info['restore_auth'], data[7])
self.assertEqual(self._restore_info['restore_target'], data[8])
self.assertEqual(self._restore_info['status'], data[9])

View File

@@ -11,6 +11,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import json
from karborclient.osc.v1 import scheduled_operations as osc_so
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import scheduled_operations
@@ -25,7 +28,7 @@ SCHEDULEDOPERATION_INFO = {
"operation_definition": {
"provider_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa399",
"plan_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa398"
},
},
"enabled": 1
}
@@ -41,9 +44,10 @@ class TestScheduledOperations(fakes.TestDataProtection):
class TestListScheduledOperations(TestScheduledOperations):
def setUp(self):
super(TestListScheduledOperations, self).setUp()
self.so_mock.list.return_value = \
[scheduled_operations.ScheduledOperation(None,
SCHEDULEDOPERATION_INFO)]
self.so_mock.list.return_value = [
scheduled_operations.ScheduledOperation(
None, copy.deepcopy(SCHEDULEDOPERATION_INFO))
]
# Command to test
self.cmd = osc_so.ListScheduledOperations(self.app, None)
@@ -58,21 +62,23 @@ class TestListScheduledOperations(TestScheduledOperations):
# Check that columns are correct
expected_columns = (
['Id', 'Name', 'OperationType', 'TriggerId',
'OperationDefinition'])
['Id', 'Name', 'Operation Type', 'Trigger Id',
'Operation Definition'])
self.assertEqual(expected_columns, columns)
operation_definition = {
"provider_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa399",
"plan_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa398"
}
# Check that data is correct
expected_data = [("1a2c0c3d-f402-4cd8-b5db-82e85cb51fad",
"My scheduled operation",
"protect",
"23902b02-5666-4ee6-8dfe-962ac09c3995",
{
"provider_id":
"2a9ce1f3-cc1a-4516-9435-0ebb13caa399", # noqa
"plan_id":
"2a9ce1f3-cc1a-4516-9435-0ebb13caa398"
})]
json.dumps(operation_definition,
indent=2, sort_keys=True)
)]
self.assertEqual(expected_data, data)
@@ -80,7 +86,7 @@ class TestCreateScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestCreateScheduledOperation, self).setUp()
self.so_mock.create.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
ScheduledOperation(None, copy.deepcopy(SCHEDULEDOPERATION_INFO))
# Command to test
self.cmd = osc_so.CreateScheduledOperation(self.app, None)
@@ -114,7 +120,7 @@ class TestDeleteScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestDeleteScheduledOperation, self).setUp()
self.so_mock.get.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
ScheduledOperation(None, copy.deepcopy(SCHEDULEDOPERATION_INFO))
# Command to test
self.cmd = osc_so.DeleteScheduledOperation(self.app, None)
@@ -135,8 +141,9 @@ class TestDeleteScheduledOperation(TestScheduledOperations):
class TestShowScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestShowScheduledOperation, self).setUp()
self._schedop_info = copy.deepcopy(SCHEDULEDOPERATION_INFO)
self.so_mock.get.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
ScheduledOperation(None, self._schedop_info)
# Command to test
self.cmd = osc_so.ShowScheduledOperation(self.app, None)
@@ -157,11 +164,10 @@ class TestShowScheduledOperation(TestScheduledOperations):
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(SCHEDULEDOPERATION_INFO['description'], data[0])
self.assertEqual(SCHEDULEDOPERATION_INFO['enabled'], data[1])
self.assertEqual(SCHEDULEDOPERATION_INFO['id'], data[2])
self.assertEqual(SCHEDULEDOPERATION_INFO['name'], data[3])
self.assertEqual(SCHEDULEDOPERATION_INFO['operation_definition'],
data[4])
self.assertEqual(SCHEDULEDOPERATION_INFO['operation_type'], data[5])
self.assertEqual(SCHEDULEDOPERATION_INFO['trigger_id'], data[6])
self.assertEqual(self._schedop_info['description'], data[0])
self.assertEqual(self._schedop_info['enabled'], data[1])
self.assertEqual(self._schedop_info['id'], data[2])
self.assertEqual(self._schedop_info['name'], data[3])
self.assertEqual(self._schedop_info['operation_definition'], data[4])
self.assertEqual(self._schedop_info['operation_type'], data[5])
self.assertEqual(self._schedop_info['trigger_id'], data[6])

View File

@@ -0,0 +1,101 @@
# 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 copy
from karborclient.osc.v1 import services as osc_services
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import services
SERVICE_INFO = {
"status": "enabled",
"binary": "karbor-operationengine",
"state": "up",
"updated_at": "2017-10-25T07:06:58.000000",
"host": "fake_host",
"disabled_reason": None,
"id": 1
}
class TestServices(fakes.TestDataProtection):
def setUp(self):
super(TestServices, self).setUp()
self.services_mock = self.app.client_manager.data_protection.services
self.services_mock.reset_mock()
class TestListServices(TestServices):
def setUp(self):
super(TestListServices, self).setUp()
self.services_mock.list.return_value = [
services.Service(None, copy.deepcopy(SERVICE_INFO))]
self.cmd = osc_services.ListServices(self.app, None)
def test_services_list(self):
arg_list = ['--host', 'fake_host']
verify_list = [('host', 'fake_host')]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
columns, data = self.cmd.take_action(parsed_args)
expected_columns = (["Id", "Binary", "Host", "Status", "State",
"Updated_at", "Disabled Reason"])
self.assertEqual(expected_columns, columns)
expected_data = [(1,
"karbor-operationengine",
"fake_host",
"enabled",
"up",
"2017-10-25T07:06:58.000000",
None
)]
self.assertEqual(expected_data, list(data))
class TestEnableService(TestServices):
def setUp(self):
super(TestEnableService, self).setUp()
self.services_mock.enable.return_value = services.Service(
None, copy.deepcopy(SERVICE_INFO))
self.cmd = osc_services.EnableService(self.app, None)
def test_enable_service(self):
arg_list = ['1']
verify_list = [('service_id', '1')]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
self.cmd.take_action(parsed_args)
self.services_mock.enable.assert_called_once_with('1')
class TestDisableService(TestServices):
def setUp(self):
super(TestDisableService, self).setUp()
self.services_mock.disable.return_value = services.Service(
None, copy.deepcopy(SERVICE_INFO))
self.services_mock.disable_log_reason.return_value = services.Service(
None, copy.deepcopy(SERVICE_INFO))
self.cmd = osc_services.DisableService(self.app, None)
def test_disable_service(self):
arg_list = ['1']
verify_list = [('service_id', '1')]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
self.cmd.take_action(parsed_args)
self.services_mock.disable.assert_called_once_with('1')
def test_disable_service_with_reason(self):
arg_list = ['1', '--reason', 'fake_reason']
verify_list = [('service_id', '1'), ('reason', 'fake_reason')]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
self.cmd.take_action(parsed_args)
self.services_mock.disable_log_reason.assert_called_once_with(
'1', 'fake_reason')

View File

@@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from karborclient.osc.v1 import triggers as osc_triggers
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import triggers
@@ -41,7 +43,7 @@ class TestListTriggers(TestTriggers):
def setUp(self):
super(TestListTriggers, self).setUp()
self.triggers_mock.list.return_value = [triggers.Trigger(
None, TRIGGER_INFO)]
None, copy.deepcopy(TRIGGER_INFO))]
# Command to test
self.cmd = osc_triggers.ListTriggers(self.app, None)
@@ -75,7 +77,7 @@ class TestCreateTrigger(TestTriggers):
def setUp(self):
super(TestCreateTrigger, self).setUp()
self.triggers_mock.create.return_value = triggers.Trigger(
None, TRIGGER_INFO)
None, copy.deepcopy(TRIGGER_INFO))
# Command to test
self.cmd = osc_triggers.CreateTrigger(self.app, None)
@@ -114,9 +116,9 @@ class TestUpdateTrigger(TestTriggers):
def setUp(self):
super(TestUpdateTrigger, self).setUp()
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
None, copy.deepcopy(TRIGGER_INFO))
self.triggers_mock.update.return_value = triggers.Trigger(
None, TRIGGER_INFO)
None, copy.deepcopy(TRIGGER_INFO))
# Command to test
self.cmd = osc_triggers.UpdateTrigger(self.app, None)
@@ -140,7 +142,7 @@ class TestDeleteTrigger(TestTriggers):
def setUp(self):
super(TestDeleteTrigger, self).setUp()
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
None, copy.deepcopy(TRIGGER_INFO))
# Command to test
self.cmd = osc_triggers.DeleteTrigger(self.app, None)
@@ -160,8 +162,9 @@ class TestDeleteTrigger(TestTriggers):
class TestShowTrigger(TestTriggers):
def setUp(self):
super(TestShowTrigger, self).setUp()
self._trigger_info = copy.deepcopy(TRIGGER_INFO)
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
None, self._trigger_info)
# Command to test
self.cmd = osc_triggers.ShowTrigger(self.app, None)
@@ -180,7 +183,7 @@ class TestShowTrigger(TestTriggers):
self.assertEqual(expected_columns, columns)
# Check that data is correct
self.assertEqual(TRIGGER_INFO['id'], data[0])
self.assertEqual(TRIGGER_INFO['name'], data[1])
self.assertEqual(TRIGGER_INFO['properties'], data[2])
self.assertEqual(TRIGGER_INFO['type'], data[3])
self.assertEqual(self._trigger_info['id'], data[0])
self.assertEqual(self._trigger_info['name'], data[1])
self.assertEqual(self._trigger_info['properties'], data[2])
self.assertEqual(self._trigger_info['type'], data[3])

View File

@@ -0,0 +1,129 @@
# 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 copy
import json
from karborclient.osc.v1 import verifications as osc_verifications
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import verifications
VERIFICATION_INFO = {
"id": "22b82aa7-9179-4c71-bba2-caf5c0e68db7",
"project_id": "e486a2f49695423ca9c47e589b948108",
"provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"checkpoint_id": "dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"parameters": {},
"resources_status": {},
"resources_reason": {},
"status": "success"
}
class TestVerifications(fakes.TestDataProtection):
def setUp(self):
super(TestVerifications, self).setUp()
self.verifications_mock = (
self.app.client_manager.data_protection.verifications)
self.verifications_mock.reset_mock()
class TestListVerifications(TestVerifications):
def setUp(self):
super(TestListVerifications, self).setUp()
self.verifications_mock.list.return_value = (
[verifications.Verification(
None, copy.deepcopy(VERIFICATION_INFO))])
# Command to test
self.cmd = osc_verifications.ListVerifications(self.app, None)
def test_verifications_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)
expected_columns = (
['Id', 'Project id', 'Provider id', 'Checkpoint id',
'Parameters', 'Status'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [("22b82aa7-9179-4c71-bba2-caf5c0e68db7",
"e486a2f49695423ca9c47e589b948108",
"cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
json.dumps({}),
"success")]
self.assertEqual(expected_data, list(data))
class TestCreateVerification(TestVerifications):
def setUp(self):
super(TestCreateVerification, self).setUp()
self.verifications_mock.create.return_value = (
verifications.Verification(
None, copy.deepcopy(VERIFICATION_INFO)))
self.cmd = osc_verifications.CreateVerification(self.app, None)
def test_verification_create(self):
arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3']
verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'),
('checkpoint_id',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.verifications_mock.create.assert_called_once_with(
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3', {})
class TestShowVerification(TestVerifications):
def setUp(self):
super(TestShowVerification, self).setUp()
self._verification_info = copy.deepcopy(VERIFICATION_INFO)
self.verifications_mock.get.return_value = (
verifications.Verification(None, self._verification_info))
self.cmd = osc_verifications.ShowVerification(self.app, None)
def test_verification_show(self):
arglist = ['22b82aa7-9179-4c71-bba2-caf5c0e68db7']
verifylist = [('verification', '22b82aa7-9179-4c71-bba2-caf5c0e68db7')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
expected_columns = (
'checkpoint_id', 'id', 'parameters', 'project_id',
'provider_id', 'resources_reason', 'resources_status',
'status')
self.assertEqual(expected_columns, columns)
self.assertEqual(self._verification_info['checkpoint_id'], data[0])
self.assertEqual(self._verification_info['id'], data[1])
self.assertEqual(self._verification_info['parameters'], data[2])
self.assertEqual(self._verification_info['project_id'], data[3])
self.assertEqual(self._verification_info['provider_id'], data[4])
self.assertEqual(self._verification_info['resources_reason'], data[5])
self.assertEqual(self._verification_info['resources_status'], data[6])
self.assertEqual(self._verification_info['status'], data[7])

View File

@@ -0,0 +1,40 @@
# 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 = ({}, {'quota_class': {'plans': 50}})
class QuotaClassesTest(base.TestCaseShell):
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_quota_class_update(self, mock_request):
mock_request.return_value = mock_request_return
cs.quota_classes.update('default', {'plans': 50})
mock_request.assert_called_with(
'PUT',
'/quota_classes/default',
data={'quota_class': {'plans': 50}}, headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_quota_class(self, mock_request):
mock_request.return_value = mock_request_return
cs.quota_classes.get('default')
mock_request.assert_called_with(
'GET',
'/quota_classes/default',
headers={})

View File

@@ -0,0 +1,60 @@
# 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 = ({}, {'quota': {'plans': 50}})
class QuotasTest(base.TestCaseShell):
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_quota_update(self, mock_request):
mock_request.return_value = mock_request_return
cs.quotas.update(fakes.PROJECT_ID, {'plans': 50})
mock_request.assert_called_with(
'PUT',
'/quotas/{project_id}'.format(project_id=fakes.PROJECT_ID),
data={'quota': {'plans': 50}}, headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_quota(self, mock_request):
mock_request.return_value = mock_request_return
cs.quotas.get(fakes.PROJECT_ID, detail=False)
mock_request.assert_called_with(
'GET',
'/quotas/{project_id}'.format(project_id=fakes.PROJECT_ID),
headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_quota_with_detail(self, mock_request):
mock_request.return_value = mock_request_return
cs.quotas.get(fakes.PROJECT_ID, detail=True)
mock_request.assert_called_with(
'GET',
'/quotas/{project_id}/detail'.format(
project_id=fakes.PROJECT_ID),
headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_quota_with_default(self, mock_request):
mock_request.return_value = mock_request_return
cs.quotas.defaults(fakes.PROJECT_ID)
mock_request.assert_called_with(
'GET',
'/quotas/{project_id}/defaults'.format(
project_id=fakes.PROJECT_ID),
headers={})

View File

@@ -0,0 +1,104 @@
# 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 = ({}, {'service': {}})
class ServicesTest(base.TestCaseShell):
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_services(self, mock_request):
mock_request.return_value = mock_request_return
cs.services.list()
mock_request.assert_called_with(
'GET',
'/os-services',
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_services_with_host(self, mock_request):
mock_request.return_value = mock_request_return
cs.services.list(host='fake_host')
mock_request.assert_called_with(
'GET',
'/os-services?host=fake_host',
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_services_with_binary(self, mock_request):
mock_request.return_value = mock_request_return
cs.services.list(binary='fake_binary')
mock_request.assert_called_with(
'GET',
'/os-services?binary=fake_binary',
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_services_with_host_and_binary(self, mock_request):
mock_request.return_value = mock_request_return
cs.services.list(host='fake_host', binary='fake_binary')
mock_request.assert_called_with(
'GET',
'/os-services?binary=fake_binary&host=fake_host',
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_enable_service(self, mock_request):
mock_request.return_value = mock_request_return
body = {
'status': 'enabled'
}
cs.services.enable('1')
mock_request.assert_called_with(
'PUT',
'/os-services/1',
data=body,
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_disable_service(self, mock_request):
mock_request.return_value = mock_request_return
body = {
'status': 'disabled'
}
cs.services.disable('1')
mock_request.assert_called_with(
'PUT',
'/os-services/1',
data=body,
headers={}
)
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_disable_service_with_reason(self, mock_request):
mock_request.return_value = mock_request_return
body = {
'status': 'disabled',
'disabled_reason': 'fake_reason'
}
cs.services.disable_log_reason('1', 'fake_reason')
mock_request.assert_called_with(
'PUT',
'/os-services/1',
data=body,
headers={}
)

View File

@@ -0,0 +1,80 @@
# 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 = ({}, {'verification': {}})
class VerificationsTest(base.TestCaseShell):
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_verifications_with_marker_limit(self, mock_request):
mock_request.return_value = mock_request_return
cs.verifications.list(marker=1234, limit=2)
mock_request.assert_called_with(
'GET',
'/verifications?limit=2&marker=1234', headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_verifications_with_sort_key_dir(self, mock_request):
mock_request.return_value = mock_request_return
cs.verifications.list(sort_key='id', sort_dir='asc')
mock_request.assert_called_with(
'GET',
'/verifications?'
'sort_dir=asc&sort_key=id', headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_list_verifications_with_invalid_sort_key(self, mock_request):
self.assertRaises(ValueError,
cs.verifications.list,
sort_key='invalid', sort_dir='asc')
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_create_verification(self, mock_request):
mock_request.return_value = mock_request_return
cs.verifications.create('586cc6ce-e286-40bd-b2b5-dd32694d9944',
'2220f8b1-975d-4621-a872-fa9afb43cb6c',
'{}')
mock_request.assert_called_with(
'POST',
'/verifications',
data={
'verification':
{
'checkpoint_id': '2220f8b1-975d-4621-a872-fa9afb43cb6c',
'parameters': '{}',
'provider_id': '586cc6ce-e286-40bd-b2b5-dd32694d9944'
}}, headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_verification(self, mock_request):
mock_request.return_value = mock_request_return
cs.verifications.get('1')
mock_request.assert_called_with(
'GET',
'/verifications/1',
headers={})
@mock.patch('karborclient.common.http.HTTPClient.json_request')
def test_show_verification_with_headers(self, mock_request):
mock_request.return_value = mock_request_return
cs.verifications.get('1', session_id='fake_session_id')
mock_request.assert_called_with(
'GET',
'/verifications/1',
headers={'X-Configuration-Session': 'fake_session_id'})

View File

@@ -1,146 +1,146 @@
# 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 oslo_serialization import jsonutils
from oslo_utils import uuidutils
from karborclient.common.apiclient import exceptions
def extract_resources(args):
resources = []
for data in args.resources.split(','):
if '=' in data and len(data.split('=')) in [3, 4]:
resource = dict(zip(['id', 'type', 'name', 'extra_info'],
data.split('=')))
if resource.get('extra_info'):
resource['extra_info'] = jsonutils.loads(
resource.get('extra_info'))
else:
raise exceptions.CommandError(
"Unable to parse parameter resources. "
"The keys of resource are id , type, name and "
"extra_info. The extra_info field is optional.")
resources.append(resource)
return resources
def check_resources(cs, resources):
# check the resource whether it is available
for resource in resources:
try:
instance = cs.protectables.get_instance(
resource["type"], resource["id"])
except exceptions.NotFound:
raise exceptions.CommandError(
"The resource: %s can not be found." % resource["id"])
else:
if instance is None:
raise exceptions.CommandError(
"The resource: %s is invalid." % resource["id"])
def extract_parameters(args):
if all((args.parameters, args.parameters_json)):
raise exceptions.CommandError(
"Must provide parameters or parameters-json, not both")
if not any((args.parameters, args.parameters_json)):
return {}
if args.parameters_json:
return jsonutils.loads(args.parameters_json)
parameters = {}
for resource_params in args.parameters:
resource_type = None
resource_id = None
parameter = {}
for param_kv in resource_params.split(','):
try:
key, value = param_kv.split('=')
except Exception:
raise exceptions.CommandError(
'parameters must be in the form: key1=val1,key2=val2,...'
)
if key == "resource_type":
resource_type = value
elif key == "resource_id":
if not uuidutils.is_uuid_like(value):
raise exceptions.CommandError('resource_id must be a uuid')
resource_id = value
else:
parameter[key] = value
if resource_type is None:
raise exceptions.CommandError(
'Must specify resource_type for parameters'
)
if resource_id is None:
resource_key = resource_type
else:
resource_key = "%s#%s" % (resource_type, resource_id)
parameters[resource_key] = parameter
return parameters
def extract_instances_parameters(args):
parameters = {}
for parameter in args.parameters:
if '=' in parameter:
(key, value) = parameter.split('=', 1)
else:
key = parameter
value = None
parameters[key] = value
return parameters
def extract_extra_info(args):
checkpoint_extra_info = {}
for data in args.extra_info:
# unset doesn't require a val, so we have the if/else
if '=' in data:
(key, value) = data.split('=', 1)
else:
key = data
value = None
checkpoint_extra_info[key] = value
return checkpoint_extra_info
def extract_properties(args):
properties = {}
if args.properties is None:
return properties
for data in args.properties.split(','):
if '=' in data:
(resource_key, resource_value) = data.split('=', 1)
else:
raise exceptions.CommandError(
"Unable to parse parameter properties.")
properties[resource_key] = resource_value
return properties
def extract_operation_definition(args):
operation_definition = {}
for data in args.operation_definition.split(','):
if '=' in data:
(resource_key, resource_value) = data.split('=', 1)
else:
raise exceptions.CommandError(
"Unable to parse parameter operation_definition.")
operation_definition[resource_key] = resource_value
return operation_definition
# 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 oslo_serialization import jsonutils
from oslo_utils import uuidutils
from karborclient.common.apiclient import exceptions
def extract_resources(args):
resources = []
for data in args.resources.split(','):
if '=' in data and len(data.split('=')) in [3, 4]:
resource = dict(zip(['id', 'type', 'name', 'extra_info'],
data.split('=')))
if resource.get('extra_info'):
resource['extra_info'] = jsonutils.loads(
resource.get('extra_info'))
else:
raise exceptions.CommandError(
"Unable to parse parameter resources. "
"The keys of resource are id , type, name and "
"extra_info. The extra_info field is optional.")
resources.append(resource)
return resources
def check_resources(cs, resources):
# check the resource whether it is available
for resource in resources:
try:
instance = cs.protectables.get_instance(
resource["type"], resource["id"])
except exceptions.NotFound:
raise exceptions.CommandError(
"The resource: %s can not be found." % resource["id"])
else:
if instance is None:
raise exceptions.CommandError(
"The resource: %s is invalid." % resource["id"])
def extract_parameters(args):
if all((args.parameters, args.parameters_json)):
raise exceptions.CommandError(
"Must provide parameters or parameters-json, not both")
if not any((args.parameters, args.parameters_json)):
return {}
if args.parameters_json:
return jsonutils.loads(args.parameters_json)
parameters = {}
for resource_params in args.parameters:
resource_type = None
resource_id = None
parameter = {}
for param_kv in resource_params.split(','):
try:
key, value = param_kv.split('=')
except Exception:
raise exceptions.CommandError(
'parameters must be in the form: key1=val1,key2=val2,...'
)
if key == "resource_type":
resource_type = value
elif key == "resource_id":
if not uuidutils.is_uuid_like(value):
raise exceptions.CommandError('resource_id must be a uuid')
resource_id = value
else:
parameter[key] = value
if resource_type is None:
raise exceptions.CommandError(
'Must specify resource_type for parameters'
)
if resource_id is None:
resource_key = resource_type
else:
resource_key = "%s#%s" % (resource_type, resource_id)
parameters[resource_key] = parameter
return parameters
def extract_instances_parameters(args):
parameters = {}
for parameter in args.parameters:
if '=' in parameter:
(key, value) = parameter.split('=', 1)
else:
key = parameter
value = None
parameters[key] = value
return parameters
def extract_extra_info(args):
checkpoint_extra_info = {}
for data in args.extra_info:
# unset doesn't require a val, so we have the if/else
if '=' in data:
(key, value) = data.split('=', 1)
else:
key = data
value = None
checkpoint_extra_info[key] = value
return checkpoint_extra_info
def extract_properties(args):
properties = {}
if args.properties is None:
return properties
for data in args.properties.split(','):
if '=' in data:
(resource_key, resource_value) = data.split('=', 1)
else:
raise exceptions.CommandError(
"Unable to parse parameter properties.")
properties[resource_key] = resource_value
return properties
def extract_operation_definition(args):
operation_definition = {}
for data in args.operation_definition.split(','):
if '=' in data:
(resource_key, resource_value) = data.split('=', 1)
else:
raise exceptions.CommandError(
"Unable to parse parameter operation_definition.")
operation_definition[resource_key] = resource_value
return operation_definition

View File

@@ -18,9 +18,13 @@ from karborclient.v1 import operation_logs
from karborclient.v1 import plans
from karborclient.v1 import protectables
from karborclient.v1 import providers
from karborclient.v1 import quota_classes
from karborclient.v1 import quotas
from karborclient.v1 import restores
from karborclient.v1 import scheduled_operations
from karborclient.v1 import services
from karborclient.v1 import triggers
from karborclient.v1 import verifications
class Client(object):
@@ -45,3 +49,8 @@ class Client(object):
scheduled_operations.ScheduledOperationManager(self.http_client)
self.operation_logs = \
operation_logs.OperationLogManager(self.http_client)
self.verifications = verifications.VerificationManager(
self.http_client)
self.services = services.ServiceManager(self.http_client)
self.quotas = quotas.QuotaManager(self.http_client)
self.quota_classes = quota_classes.QuotaClassManager(self.http_client)

View File

@@ -0,0 +1,42 @@
# 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 QuotaClass(base.Resource):
def __repr__(self):
return "<QuotaClass %s>" % self._info
class QuotaClassManager(base.ManagerWithFind):
resource_class = QuotaClass
def list(self):
pass
def update(self, class_name, data):
body = {"quota_class": data}
return self._update('/quota_classes/{class_name}'
.format(class_name=class_name),
body, "quota_class")
def get(self, class_name, session_id=None):
if session_id:
headers = {'X-Configuration-Session': session_id}
else:
headers = {}
url = "/quota_classes/{class_name}".format(
class_name=class_name)
return self._get(url, response_key="quota_class", headers=headers)

55
karborclient/v1/quotas.py Normal file
View File

@@ -0,0 +1,55 @@
# 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 Quota(base.Resource):
def __repr__(self):
return "<Quota %s>" % self._info
class QuotaManager(base.ManagerWithFind):
resource_class = Quota
def list(self):
pass
def update(self, project_id, data):
body = {"quota": data}
return self._update('/quotas/{project_id}'
.format(project_id=project_id),
body, "quota")
def get(self, project_id, detail, session_id=None):
if session_id:
headers = {'X-Configuration-Session': session_id}
else:
headers = {}
base_url = "/quotas/{project_id}".format(
project_id=project_id)
if detail:
url = base_url + '/detail'
else:
url = base_url
return self._get(url, response_key="quota", headers=headers)
def defaults(self, project_id, session_id=None):
if session_id:
headers = {'X-Configuration-Session': session_id}
else:
headers = {}
url = "/quotas/{project_id}/defaults".format(
project_id=project_id)
return self._get(url, response_key="quota", headers=headers)

View File

@@ -0,0 +1,64 @@
# 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 Service(base.Resource):
def __repr__(self):
return "<Service %s>" % self._info
class ServiceManager(base.ManagerWithFind):
resource_class = Service
def enable(self, service_id):
"""Enable the service specified by the service ID
:param service_id: The ID of the service to enable.
"""
body = {
'status': 'enabled'
}
return self._update('/os-services/%s' % service_id, body, "service")
def disable(self, service_id):
"""Disable the service specified by the service ID.
:param service_id: The ID of the service to disable.
"""
body = {
'status': 'disabled'
}
return self._update('/os-services/%s' % service_id, body, "service")
def disable_log_reason(self, service_id, reason):
"""Disable the service with a reason.
:param service_id: The ID of the service to disable.
:param reason: The reason for disabling a service.
"""
body = {
'status': 'disabled',
'disabled_reason': reason
}
return self._update("/os-services/%s" % service_id, body, "service")
def list(self, host=None, binary=None):
"""Lists all services."""
search_opts = {
'host': host,
'binary': binary
}
resource_type = "os-services"
url = self._build_list_url(resource_type, search_opts=search_opts)
return self._list(url, 'services')

View File

@@ -365,6 +365,140 @@ def do_restore_show(cs, args):
utils.print_dict(restore.to_dict(), dict_format_list=dict_format_list)
@utils.arg('provider_id',
metavar='<provider_id>',
help='Provider id.')
@utils.arg('checkpoint_id',
metavar='<checkpoint_id>',
help='Checkpoint id.')
@utils.arg('--parameters-json',
type=str,
dest='parameters_json',
metavar='<parameters>',
default=None,
help='Verification parameters in json format.')
@utils.arg('--parameters',
action='append',
metavar='resource_type=<type>[,resource_id=<id>,key=val,...]',
default=[],
help='Verification parameters, may be specified multiple times. '
'resource_type: type of resource to apply parameters. '
'resource_id: limit the parameters to a specific resource. '
'Other keys and values: according to provider\'s schema.'
)
def do_verification_create(cs, args):
"""Creates a verification."""
if not uuidutils.is_uuid_like(args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
if not uuidutils.is_uuid_like(args.checkpoint_id):
raise exceptions.CommandError(
"Invalid checkpoint id provided.")
verification_parameters = arg_utils.extract_parameters(args)
verification = cs.verifications.create(args.provider_id,
args.checkpoint_id,
verification_parameters)
dict_format_list = {"parameters"}
utils.print_dict(verification.to_dict(), dict_format_list=dict_format_list)
@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 verifications that appear later in the'
'list than that represented by this verification id. '
'Default=None.')
@utils.arg('--limit',
metavar='<limit>',
default=None,
help='Maximum number of verifications 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_verification_list(cs, args):
"""Lists all verifications."""
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 '
'not supported with --sort.')
verifications = cs.verifications.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', 'Project id', 'Provider id', 'Checkpoint id',
'Parameters', 'Status']
if args.sort_key or args.sort_dir or args.sort:
sortby_index = None
else:
sortby_index = 0
formatters = {"Parameters": lambda obj: json.dumps(
obj.parameters, indent=2, sort_keys=True)}
utils.print_list(verifications, key_list, exclude_unavailable=True,
sortby_index=sortby_index, formatters=formatters)
@utils.arg('verification',
metavar='<verification>',
help='ID of verification.')
def do_verification_show(cs, args):
"""Shows verification details."""
verification = cs.verifications.get(args.verification)
dict_format_list = {"parameters"}
utils.print_dict(verification.to_dict(),
dict_format_list=dict_format_list)
def do_protectable_list(cs, args):
"""Lists all protectable types."""
@@ -670,8 +804,11 @@ def do_checkpoint_list(cs, args):
sortby_index = None
else:
sortby_index = 0
formatters = {"Protection plan": lambda obj: json.dumps(
obj.protection_plan, indent=2, sort_keys=True)}
def plan_formatter(obj):
return "Name: %s\nId: %s" % (obj.protection_plan['name'],
obj.protection_plan['id'])
formatters = {"Protection plan": plan_formatter}
utils.print_list(checkpoints, key_list, exclude_unavailable=True,
sortby_index=sortby_index, formatters=formatters)
@@ -1108,3 +1245,142 @@ 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())
@utils.arg('--host',
metavar='<hostname>',
default=None,
help='Name of host.')
@utils.arg('--binary',
metavar='<binary>',
default=None,
help='Service binary.')
def do_service_list(cs, args):
"""Show a list of all running services. Filter by host & binary."""
result = cs.services.list(host=args.host, binary=args.binary)
columns = ["Id", "Binary", "Host", "Status", "State",
"Updated_at", "Disabled Reason"]
utils.print_list(result, columns)
@utils.arg('service_id',
metavar='<service_id>',
help='ID of the service.')
def do_service_enable(cs, args):
"""Enable the service."""
result = cs.services.enable(args.service_id)
utils.print_list([result], ["Id", "Binary", "Host", "Status", "State",
"Updated_at", "Disabled Reason"])
@utils.arg('service_id',
metavar='<service_id>',
help='ID of the service.')
@utils.arg('--reason',
metavar='<reason>',
help='Reason for disabling the service.')
def do_service_disable(cs, args):
"""Disable the service"""
if args.reason:
result = cs.services.disable_log_reason(args.service_id, args.reason)
else:
result = cs.services.disable(args.service_id)
utils.print_list([result], ["Id", "Binary", "Host", "Status", "State",
"Updated_at", "Disabled Reason"])
@utils.arg(
'--tenant',
metavar='<tenant>',
default=None,
help='ID of tenant to list the quotas for.')
@utils.arg(
'--detail',
action='store_true',
help='Optional flag to indicate whether to show quota in detail. '
'Default false.')
def do_quota_show(cs, args):
"""List the quotas for a tenant."""
project_id = args.tenant or cs.http_client.get_project_id()
kwargs = {
"project_id": project_id,
"detail": args.detail,
}
result = cs.quotas.get(**kwargs)
_quota_set_pretty_show(result)
def _quota_set_pretty_show(quotas):
"""Convert quotas object to dict and display."""
new_quotas = {}
for quota_k, quota_v in sorted(quotas.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
new_quotas[quota_k] = quota_v
utils.print_dict(new_quotas)
@utils.arg(
'tenant',
metavar='<tenant>',
help='ID of tenant to set the quotas for.')
@utils.arg(
'--plans',
metavar='<plans>',
type=int,
default=None,
help='New value for the "plans" quota.')
def do_quota_update(cs, args):
"""Update the quotas for a project (Admin only)."""
project_id = args.tenant
data = {
"plans": args.plans,
}
result = cs.quotas.update(project_id, data)
_quota_set_pretty_show(result)
@utils.arg(
'--tenant',
metavar='<tenant>',
default=None,
help='ID of tenant to list the quotas for.')
def do_quota_defaults(cs, args):
"""List the default quotas for a tenant."""
project_id = args.tenant or cs.http_client.get_project_id()
result = cs.quotas.defaults(project_id)
_quota_set_pretty_show(result)
@utils.arg(
'class_name',
metavar='<class_name>',
help='Name of quota class to list the quotas for.')
def do_quota_class_show(cs, args):
"""List the quotas for a quota class."""
result = cs.quota_classes.get(args.class_name)
_quota_set_pretty_show(result)
@utils.arg(
'class_name',
metavar='<class_name>',
help='Name of quota class to set the quotas for.')
@utils.arg(
'--plans',
metavar='<plans>',
type=int,
default=None,
help='New value for the "plans" quota.')
def do_quota_class_update(cs, args):
"""Update the quotas for a quota class (Admin only)."""
class_name = args.class_name
data = {
"plans": args.plans,
}
result = cs.quota_classes.update(class_name, data)
_quota_set_pretty_show(result)

View File

@@ -0,0 +1,65 @@
# 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 Verification(base.Resource):
def __repr__(self):
return "<Verification %s>" % self._info
class VerificationManager(base.ManagerWithFind):
resource_class = Verification
def create(self, provider_id, checkpoint_id, parameters):
body = {
'verification': {
'provider_id': provider_id,
'checkpoint_id': checkpoint_id,
'parameters': parameters,
}
}
url = "/verifications"
return self._create(url, body, 'verification')
def get(self, verification_id, session_id=None):
if session_id:
headers = {'X-Configuration-Session': session_id}
else:
headers = {}
url = "/verifications/{verification_id}".format(
verification_id=verification_id)
return self._get(url, response_key="verification", headers=headers)
def list(self, detailed=False, search_opts=None, marker=None, limit=None,
sort_key=None, sort_dir=None, sort=None):
"""Lists all verifications.
:param detailed: Whether to return detailed verification info.
:param search_opts: Search options to filter out verifications.
:param marker: Begin returning verifications that appear later in the
list than that represented by this verification id.
:param limit: Maximum number of verifications to return.
:param sort_key: Key to be sorted;
:param sort_dir: Sort direction, should be 'desc' or 'asc';
:param sort: Sort information
:rtype: list of :class:`Verification`
"""
resource_type = "verifications"
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, 'verifications')

View File

@@ -3,12 +3,12 @@
# process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
PrettyTable<0.8,>=0.7.1 # BSD
keystoneauth1>=3.0.1 # Apache-2.0
keystoneauth1>=3.3.0 # Apache-2.0
requests>=2.14.2 # Apache-2.0
simplejson>=2.2.0 # MIT
simplejson>=3.5.1 # MIT
Babel!=2.4.0,>=2.3.4 # BSD
six>=1.9.0 # MIT
osc-lib>=1.7.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
six>=1.10.0 # MIT
osc-lib>=1.8.0 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0

View File

@@ -63,6 +63,17 @@ openstack.data_protection.v1 =
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
data_protection_verification_list = karborclient.osc.v1.verifications:ListVerifications
data_protection_verification_show = karborclient.osc.v1.verifications:ShowVerification
data_protection_verification_create = karborclient.osc.v1.verifications:CreateVerification
data_protection_service_list = karborclient.osc.v1.services:ListServices
data_protection_service_enable = karborclient.osc.v1.services:EnableService
data_protection_service_disable = karborclient.osc.v1.services:DisableService
data_protection_quotas_show = karborclient.osc.v1.quotas:ShowQuotas
data_protection_quotas_default = karborclient.osc.v1.quotas:ShowDefaultQuotas
data_protection_quotas_update = karborclient.osc.v1.quotas:UpdateQuotas
data_protection_quotaclass_show = karborclient.osc.v1.quota_classes:ShowQuotaClasses
data_protection_quotaclass_update = karborclient.osc.v1.quota_classes:UpdateQuotaClasses
[compile_catalog]
directory = karborclient/locale

View File

@@ -5,13 +5,13 @@
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
python-subunit>=1.0.0 # Apache-2.0/BSD
docutils>=0.11 # OSI-Approved Open Source, Public Domain
sphinx>=1.6.2 # BSD
openstackdocstheme>=1.11.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0
requests-mock>=1.1 # Apache-2.0
sphinx!=1.6.6,>=1.6.2 # BSD
openstackdocstheme>=1.17.0 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
python-openstackclient>=3.12.0 # Apache-2.0
requests-mock>=1.1.0 # Apache-2.0
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
testtools>=2.2.0 # MIT

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Client constraint file contains this client version pin that is in conflict
# with installing the client from source. We should remove the version pin in
# the constraints file before applying it for from-source installation.
CONSTRAINTS_FILE="$1"
shift 1
set -e
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
# published to logs.openstack.org for easy debugging.
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
fi
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
pip install -c"$localfile" openstack-requirements
# This is the main purpose of the script: Allow local installation of
# the current repo. It is listed in constraints file and thus any
# install will be constrained and we need to unconstrain it.
edit-constraints "$localfile" -- "$CLIENT_NAME"
pip install -c"$localfile" -U "$@"
exit $?

View File

@@ -5,13 +5,13 @@ skipsdist = True
[testenv]
usedevelop = True
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
install_command = pip install {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
BRANCH_NAME=master
CLIENT_NAME=python-karborclient
PYTHONWARNINGS=default::DeprecationWarning
deps = -r{toxinidir}/requirements.txt
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py test --slowest --testr-args='{posargs}'