Compare commits

..

38 Commits

Author SHA1 Message Date
Jenkins
7bb1cc2c7a Merge "Remove unused None from dict.get()" 2017-07-13 17:53:54 +00:00
Jenkins
b56f70e7fd Merge "Replace six.iteritems() with .items()" 2017-07-13 12:13:25 +00:00
sudhir_agarwal
cd7d456491 Remove unused None from dict.get()
Since the default value is None when can't get a key from a dict,
So there is no need to use dict.get('key', None).

Change-Id: I9f2ea40e13a63992170149936ae4e80b8969c023
2017-07-05 19:09:03 +05:30
sudhir_agarwal
eb70a9d460 Replace six.iteritems() with .items()
1.As mentioned in [1], we should avoid using six.iteritems to achieve
iterators. We can use dict.items instead, as it will return iterators
in PY3 as well. And dict.items/keys will more readable.
2.In py2, the performance about list should be negligible, see the
link [2].
[1] https://wiki.openstack.org/wiki/Python3
[2] http://lists.openstack.org/pipermail/openstack-dev/2015-June/066391.html

Change-Id: Ib752ad4c3aed525c4bea9dbd5710172ddbaf193b
2017-07-05 18:26:01 +05:30
Jeremy Liu
5eab398150 Add OpenStackClient plugin for scheduledoperations
This patch adds data protection support to the python-openstackclient
through a plugin for scheduledoperations.

Co-Authored-By: Spencer Yu <yushb@gohighsec.com>
Change-Id: Id2712e3a081e0fd2b69cad093d1a6b26b644e967
Partially-Implements: blueprint karbor-support-python-openstackclient
2017-07-05 17:29:00 +08:00
Jenkins
1a7243354e Merge "Add OpenStackClient plugin for checkpoint" 2017-07-05 09:20:48 +00:00
Jenkins
5ded9c1f19 Merge "Drop MANIFEST.in - it's not needed by pbr" 2017-07-05 09:14:23 +00:00
yushangbin
2fc5904648 Add OpenStackClient plugin for checkpoint
This patch adds dataprotection support to the python-openstackclient
through a plugin for checkpoint.

Partially-Implements: blueprint karbor-support-python-openstackclient
Change-Id: I5c4c927c63ac39538d5565a4167f5836e7127170
2017-07-05 16:52:41 +08:00
Jenkins
71e029c629 Merge "Add OpenStackClient plugin for triggers" 2017-07-05 08:43:06 +00:00
yushangbin
289370ab38 Add OpenStackClient plugin for triggers
This patch adds data protection support to the python-openstackclient
through a plugin for trigger.

Change-Id: I1a19340f47cbb1713ee8da1b238ea18ea5f3bc92
Partially-Implements: blueprint karbor-support-python-openstackclient
2017-07-05 16:02:49 +08:00
Jeremy Liu
795fa730a8 Fix bug in plan deletion when using osc plugin for karbor
Change-Id: Idb5620506dd800428407031eddd525e68b93f414
2017-07-05 15:58:43 +08:00
liyanhang
3f91adc2d8 Drop MANIFEST.in - it's not needed by pbr
This patch removes `MANIFEST.in` file as pbr generates a
sensible manifest from git files and some standard files
and it removes the need for an explicit `MANIFEST.in` file.

Change-Id: I9b3d5b6ca56766389b0a8c69325bbd4f391112b5
2017-07-05 14:14:17 +08:00
yushangbin
1b3b860565 Add OpenStackClient plugin for protectable
This patch adds dataprotection support to the python-openstackclient
through a plugin for protectable.

Partially-Implements: blueprint karbor-support-python-openstackclient
Change-Id: I088919edd7287602baaa9176b3af9d2995e5c116
2017-07-04 21:47:19 +08:00
Jenkins
d358465dcf Merge "Add OpenStackClient plugin for provider" 2017-07-04 11:01:57 +00:00
Jeremy Liu
b11e847b52 Add OpenStackClient plugin for provider
This patch adds data protection support to the python-openstackclient
through a plugin for provider.

Co-Authored-By: Spencer Yu <yushb@gohighsec.com>
Change-Id: I54d731f29c7f27c3b020a0de741d6efe0b062c8b
Partially-Implements: blueprint karbor-support-python-openstackclient
2017-07-04 07:10:30 +00:00
Jeremy Liu
a82f43ae23 Add unit tests for showing a plan/restore
Change-Id: Ibc1e578c9de6bec026411cfc34ea4a6dc83b43f7
2017-07-04 15:03:58 +08:00
Jeremy Liu
9936200bf8 Update spec to match what we've registered in openstackclient
Change-Id: Idacccaa8d61b47311691456a6f2fdf152b244e66
2017-07-04 10:09:40 +08:00
Jenkins
75a7e6276f Merge "Enable coverage report in console output" 2017-07-02 08:37:55 +00:00
OpenStack Proposal Bot
7b2a2e8c92 Updated from global requirements
Change-Id: I502d11c6165b197aace610d192f2028d3005cec9
2017-06-29 21:03:48 +00:00
Kiran_totad
22d5a7ec38 Remove old pep8 ignores that are no longer necessary
Change-Id: Ie2595cd59dd48c58b6afb8852f5743212ca61848
2017-06-29 13:34:35 +05:30
Kiran_totad
6f2feab60d switch to openstackdocstheme
Change-Id: Ie5a86285db70f6b5be237e9c4c84a26b60bd13be
2017-06-29 06:23:52 +00:00
Jenkins
3233c39c52 Merge "Fix help message for plan command." 2017-06-29 03:20:33 +00:00
yushangbin
7eb818c0ee Add OpenStackClient plugin for restore
This patch adds dataprotection support to the python-openstackclient
through a plugin for restore.

Partially-Implements: blueprint karbor-support-python-openstackclient
Change-Id: I48aa68af1dbf199b6de3a648abdf977b6201bf48
2017-06-28 15:05:48 +08:00
Jenkins
b658068fa6 Merge "Updated from global requirements" 2017-06-28 06:34:59 +00:00
Jenkins
378a7a38f9 Merge "Fix a bug for plan delete test." 2017-06-28 06:34:30 +00:00
OpenStack Proposal Bot
1f907c95b8 Updated from global requirements
Change-Id: I49feca2eb4cf9ddd5455003aa3f5d2de22d098df
2017-06-27 12:21:33 +00:00
yushangbin
1bf9f74604 Fix help message for plan command.
Change-Id: I54d96690b45f4bed630892f1f4181540fa48849b
2017-06-27 18:04:18 +08:00
Jeremy Liu
f898a169cb Use 'project' instead of 'tenant' when switching to openstackclient command
We tend to use 'project' rather than 'tenant' when switching to openstackclient
command, such as:

  openstack role add --user <user> --project <project> <role>

Change-Id: I661f1d7ca3ea229ce03376b43867af260943de23
2017-06-27 17:50:36 +08:00
yushangbin
2800c5f1c2 Fix a bug for plan delete test.
Change-Id: Ie8527c2f7d70d54e5ea9ee09e6eeb7515c625437
2017-06-27 14:24:57 +08:00
Yuval Brik
2d1d7624e8 Enable translation of python-karborclient
Change-Id: I39bed8fbf905ba5cdbfdaadc4b7db863aa38924f
2017-06-23 10:25:36 +03:00
chenying
995fe11dbe Add plan commands for OpenStackClinet plugin in karbor
Change-Id: I8e10fc1775cd07271ff5651e104b6b8b26743be3
Partially-Implements: karbor-support-python-openstackclient
2017-06-22 15:12:29 +08:00
OpenStack Proposal Bot
956f0207e7 Updated from global requirements
Change-Id: I124d8200bc0d57416395ff14a1b4ffeeaee90c0f
2017-06-14 16:43:21 +00:00
Jenkins
61a46a6752 Merge "Add OpenStackClient plugin and plan list" 2017-06-14 13:11:13 +00:00
chenying
0d9611ba9b Add OpenStackClient plugin and plan list
This patch adds dataprotection support to the python-openstackclient
through a plugin.

The support can be demonstrated through the implementation of
the karbor command plan-list which is now:
    openstack dataprotection plan list

Partially-Implements: karbor-support-python-openstackclient
Change-Id: I4dfac08fd2b04f9ac254d3aa8fdadc3a1691de0a
2017-06-13 18:58:42 +08:00
chenying
e3f50485fd Spec for openstack client support
This spec is intent on implementing karbor commands in the
python-karborclient repository as python-openstackclient plugins.

Change-Id: I06eea28a922997586e670fe33bf82a6da72cb4a2
Partially-Implements: karbor-support-python-openstackclient
2017-06-05 23:21:23 +08:00
Jenkins
cd75dda7bb Merge "Optimize the link address" 2017-06-01 02:18:21 +00:00
Jeremy Liu
1924aba63a Enable coverage report in console output
This will output coverage rate of every module in console.

Change-Id: If9e24293ae8b4d7293721c2fa49562462d84ce8a
2017-05-18 21:23:00 +08:00
M V P Nitesh
86b15d38b8 Optimize the link address
Use https instead of http to ensure the safety

Change-Id: Iebd272cac1691bbb89a72cc5f02b4667e85fbc8d
2017-04-11 12:41:46 +05:30
34 changed files with 2990 additions and 164 deletions

1
.gitignore vendored
View File

@@ -24,6 +24,7 @@ pip-log.txt
# Unit test / coverage reports
.coverage
cover
.tox
nosetests.xml
.testrepository

View File

@@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@@ -23,7 +23,7 @@ sys.path.insert(0, os.path.abspath('../..'))
extensions = [
'sphinx.ext.autodoc',
#'sphinx.ext.intersphinx',
'oslosphinx'
'openstackdocstheme'
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
@@ -55,9 +55,15 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
# html_theme = '_theme'
html_theme = 'openstackdocs'
# html_static_path = ['static']
# openstackdocstheme options
repository_name = 'openstack/python-karborclient'
bug_project = 'python-karborclient'
bug_tag = ''
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project

View File

@@ -0,0 +1,166 @@
..
This work is licensed under a Creative Commons Attribution 3.0 Unported
License.
http://creativecommons.org/licenses/by/3.0/legalcode
========================================
Karbor support in python-openstackclient
========================================
Implement a new set of karbor commands as python-openstackclient plugins.
Launchpad Blueprint:
https://blueprints.launchpad.net/python-karborclient/+spec/karbor-support-python-openstackclient
Problem Description
===================
python-openstackclient is becoming the default command line client for many
OpenStack projects. Karbor would benefit from implementing all of its client
commands as a single python-openstackclient plugin implemented in the
python-karborclient repository.
Proposed Change
===============
The intent of this spec is to identify the commands to be implemented and
establish conventions for command and argument names. This spec is not
intended to be a full and correct specification of command and argument names.
The details can be left to the code reviews for the commands themselves.
The following conventions will be adopted for command names:
* As the ``OpenStackClient`` convention, the command name shall always take
the following form:
.. code-block:: bash
openstack [<global-options>] <object-1> <action> [<object-2>] \
[command-arguments]
As a example:
The following ``karbor`` commands about plan will be implemented for ``openstack``
initially suggesting these command names:
.. code-block:: bash
karbor plan-create <name> <provider_id> <resources>
openstack data protection plan create <name> <provider_id> <resources>
karbor plan-delete <plan>
openstack data protection plan delete <plan>
karbor plan-list
openstack data protection plan list
karbor plan-show <plan>
openstack data protection plan show <plan>
karbor plan-update <name> <resources> <status>
openstack data protection plan update <name> <resources> <status>
Configuration
-------------
None
Database
--------
None
Public API
----------
None
Public API Security
-------------------
None
Python API
----------
None
CLI (python-karborclient)
------------------------
A new directory named osc will be created under /karborclient/osc
for the ``OpenStackClient`` plugin and the commands mentioned above.
Internal API
------------
None
Guest Agent
-----------
None
Alternatives
------------
None
Dashboard Impact (UX)
=====================
None
Implementation
==============
Assignee(s)
-----------
Primary assignee:
chenying
Milestones
----------
Work Items
----------
CLI commands as stated above.
Unit tests
Upgrade Implications
====================
None
Dependencies
============
python-openstackclient
osc-lib
Testing
=======
Unit tests will be located in: /karborclient/tests/unit/osc/
Documentation Impact
====================
OpenStack Client adoption list will be updated to include python-karborclient.
References
==========
http://docs.openstack.org/developer/python-openstackclient/commands.html
Appendix
========
None

View File

@@ -43,7 +43,7 @@ def env(*vars, **kwargs):
returns the default defined in kwargs.
"""
for v in vars:
value = os.environ.get(v, None)
value = os.environ.get(v)
if value:
return value
return kwargs.get('default', '')

View File

View File

@@ -0,0 +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

View File

View File

@@ -0,0 +1,205 @@
# 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.")

View File

@@ -0,0 +1,265 @@
# 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.")

View File

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

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 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()))

View File

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

View File

@@ -0,0 +1,208 @@
# 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.")

View File

@@ -0,0 +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.")

View File

@@ -44,11 +44,11 @@ class TestResponse(requests.Response):
self._text = None
super(TestResponse, self)
if isinstance(data, dict):
self.status_code = data.get('status_code', None)
self.headers = data.get('headers', None)
self.status_code = data.get('status_code')
self.headers = data.get('headers')
self.reason = data.get('reason', '')
# Fake the text attribute to streamline Response creation
self._text = data.get('text', None)
self._text = data.get('text')
else:
self.status_code = data

View File

View File

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

View File

@@ -0,0 +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()

View File

@@ -0,0 +1,160 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import checkpoints as osc_checkpoints
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import checkpoints
CHECKPOINT_INFO = {
"id": "dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"project_id": "e486a2f49695423ca9c47e589b948108",
"status": "available",
"protection_plan": {
"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"}]
},
"resource_graph": "[{'0x0': ['OS::Glance::Image', "
"'99777fdd-8a5b-45ab-ba2c-52420008103f', "
"'cirros-0.3.4-x86_64-uec']}, [[['0x0']]]]"
}
class TestCheckpoints(fakes.TestDataProtection):
def setUp(self):
super(TestCheckpoints, self).setUp()
cm = self.app.client_manager
self.checkpoints_mock = cm.data_protection.checkpoints
self.checkpoints_mock.reset_mock()
class TestListCheckpoints(TestCheckpoints):
def setUp(self):
super(TestListCheckpoints, self).setUp()
self.checkpoints_mock.list.return_value = [checkpoints.Checkpoint(
None, CHECKPOINT_INFO)]
# Command to test
self.cmd = osc_checkpoints.ListCheckpoints(self.app, None)
def test_checkpoints_list(self):
arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9']
verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Project id', 'Status', 'Protection plan', 'Metadata',
'Created at'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [(
"dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"e486a2f49695423ca9c47e589b948108",
"available",
{
"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"}]
},
'',
'')]
self.assertEqual(expected_data, list(data))
class TestCreateCheckpoint(TestCheckpoints):
def setUp(self):
super(TestCreateCheckpoint, self).setUp()
self.checkpoints_mock.create.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
# Command to test
self.cmd = osc_checkpoints.CreateCheckpoint(self.app, None)
def test_checkpoint_create(self):
arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'3523a271-68aa-42f5-b9ba-56e5200a2ebb']
verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'),
('plan_id', '3523a271-68aa-42f5-b9ba-56e5200a2ebb')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.checkpoints_mock.create.assert_called_once_with(
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'3523a271-68aa-42f5-b9ba-56e5200a2ebb',
None)
class TestShowCheckpoint(TestCheckpoints):
def setUp(self):
super(TestShowCheckpoint, self).setUp()
self.checkpoints_mock.get.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
# Command to test
self.cmd = osc_checkpoints.ShowCheckpoint(self.app, None)
def test_checkpoint_show(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)
# Check that correct arguments were passed
self.checkpoints_mock.get.assert_called_once_with(
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3')
class TestDeleteCheckpoint(TestCheckpoints):
def setUp(self):
super(TestDeleteCheckpoint, self).setUp()
self.checkpoints_mock.get.return_value = checkpoints.Checkpoint(
None, CHECKPOINT_INFO)
# Command to test
self.cmd = osc_checkpoints.DeleteCheckpoint(self.app, None)
def test_checkpoint_delete(self):
arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3']
verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'),
('checkpoint',
['dcb20606-ad71-40a3-80e4-ef0fafdad0c3'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.checkpoints_mock.delete.assert_called_once_with(
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3')

View File

@@ -0,0 +1,180 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import plans as osc_plans
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import plans
PLAN_INFO = {
"status": "suspended",
"provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"description": "",
"parameters": {},
"id": "204c825e-eb2f-4609-95ab-70b3caa43ac8",
"resources": [{
'type': 'OS::Cinder::Volume',
'id': '71bfe64a-e0b9-4a91-9e15-a7fc9ab31b14',
'name': 'testsinglevolume'}],
"name": "OS Volume protection plan."
}
class TestPlans(fakes.TestDataProtection):
def setUp(self):
super(TestPlans, self).setUp()
self.plans_mock = self.app.client_manager.data_protection.plans
self.plans_mock.reset_mock()
class TestListPlans(TestPlans):
def setUp(self):
super(TestListPlans, self).setUp()
self.plans_mock.list.return_value = [plans.Plan(
None, PLAN_INFO)]
# Command to test
self.cmd = osc_plans.ListPlans(self.app, None)
def test_plans_list(self):
arglist = ['--status', 'suspended']
verifylist = [('status', 'suspended')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Name', 'Description', 'Provider id', 'Status'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [("204c825e-eb2f-4609-95ab-70b3caa43ac8",
"OS Volume protection plan.",
"",
"cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"suspended")]
self.assertEqual(expected_data, list(data))
class TestCreatePlan(TestPlans):
def setUp(self):
super(TestCreatePlan, self).setUp()
self.plans_mock.create.return_value = plans.Plan(
None, PLAN_INFO)
# Command to test
self.cmd = osc_plans.CreatePlan(self.app, None)
def test_plan_create(self):
arglist = ['OS Volume protection plan.',
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
"'71bfe64a-e0b9-4a91-9e15-a7fc9ab31b14'="
"'OS::Cinder::Volume'='testsinglevolume'"]
verifylist = [('name', 'OS Volume protection plan.'),
('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'),
('resources', "'71bfe64a-e0b9-4a91-9e15-a7fc9ab31b14'="
"'OS::Cinder::Volume'='testsinglevolume'")]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.plans_mock.create.assert_called_once_with(
'OS Volume protection plan.',
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
[{'id': "'71bfe64a-e0b9-4a91-9e15-a7fc9ab31b14'",
'type': "'OS::Cinder::Volume'",
'name': "'testsinglevolume'"}],
{}, description=None)
class TestUpdatePlan(TestPlans):
def setUp(self):
super(TestUpdatePlan, self).setUp()
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
self.plans_mock.update.return_value = plans.Plan(
None, PLAN_INFO)
# Command to test
self.cmd = osc_plans.UpdatePlan(self.app, None)
def test_plan_update(self):
arglist = ['204c825e-eb2f-4609-95ab-70b3caa43ac8',
'--status', 'started']
verifylist = [('plan_id', '204c825e-eb2f-4609-95ab-70b3caa43ac8'),
('status', 'started')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.plans_mock.update.assert_called_once_with(
'204c825e-eb2f-4609-95ab-70b3caa43ac8',
{'status': 'started'})
class TestDeletePlan(TestPlans):
def setUp(self):
super(TestDeletePlan, self).setUp()
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
# Command to test
self.cmd = osc_plans.DeletePlan(self.app, None)
def test_plan_delete(self):
arglist = ['204c825e-eb2f-4609-95ab-70b3caa43ac8']
verifylist = [('plan', ['204c825e-eb2f-4609-95ab-70b3caa43ac8'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.plans_mock.delete.assert_called_once_with(
'204c825e-eb2f-4609-95ab-70b3caa43ac8')
class TestShowPlan(TestPlans):
def setUp(self):
super(TestShowPlan, self).setUp()
self.plans_mock.get.return_value = plans.Plan(
None, PLAN_INFO)
# Command to test
self.cmd = osc_plans.ShowPlan(self.app, None)
def test_plan_show(self):
arglist = ['204c825e-eb2f-4609-95ab-70b3caa43ac8']
verifylist = [('plan', '204c825e-eb2f-4609-95ab-70b3caa43ac8')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'description', 'id', 'name', 'parameters', 'provider_id',
'resources', 'status')
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])

View File

@@ -0,0 +1,161 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import protectables as osc_protectables
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import protectables
PROTECTABLE_LIST_INFO = {
"protectable_type": [
"OS::Keystone::Project",
"OS::Cinder::Volume",
"OS::Glance::Image",
"OS::Nova::Server"
]
}
PROTECTABLE_SHOW_INFO = {
"name": "OS::Nova::Server",
"dependent_types": [
"OS::Cinder::Volume",
"OS::Glance::Image"
]
}
PROTECTABLE_INSTANCE_LIST_INFO = {
"id": "25336116-f38e-4c22-81ad-e9b7bd71ba51",
"type": "OS::Cinder::Volume",
"name": "System volume",
"extra_info": {
"availability_zone": "az1"
}
}
PROTECTABLE_INSTANCE_SHOW_INFO = {
"id": "cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01",
"type": "OS::Nova::Server",
"name": "My VM",
"dependent_resources": [{
"id": "99777fdd-8a5b-45ab-ba2c-52420008103f",
"type": "OS::Glance::Image",
"name": "cirros-0.3.4-x86_64-uec"}]
}
class TestProtectables(fakes.TestDataProtection):
def setUp(self):
super(TestProtectables, self).setUp()
cm = self.app.client_manager
self.protectables_mock = cm.data_protection.protectables
self.protectables_mock.reset_mock()
class TestListProtectables(TestProtectables):
def setUp(self):
super(TestListProtectables, self).setUp()
self.protectables_mock.list.return_value = [protectables.Protectable(
None, PROTECTABLE_LIST_INFO)]
# Command to test
self.cmd = osc_protectables.ListProtectables(self.app, None)
def test_protectables_list(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Protectable type'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [(['OS::Keystone::Project',
'OS::Cinder::Volume',
'OS::Glance::Image',
'OS::Nova::Server'],)]
self.assertEqual(expected_data, list(data))
class TestShowProtectable(TestProtectables):
def setUp(self):
super(TestShowProtectable, self).setUp()
self.protectables_mock.get.return_value = protectables.Protectable(
None, PROTECTABLE_SHOW_INFO)
# Command to test
self.cmd = osc_protectables.ShowProtectable(self.app, None)
def test_protectable_show(self):
arglist = ['OS::Nova::Server']
verifylist = [('protectable_type', 'OS::Nova::Server')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.protectables_mock.get.assert_called_once_with(
'OS::Nova::Server')
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)
# Command to test
self.cmd = osc_protectables.ListProtectableInstances(self.app, None)
def test_protectable_instances_list(self):
arglist = ['OS::Cinder::Volume']
verifylist = [('protectable_type', 'OS::Cinder::Volume')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.protectables_mock.list_instances.assert_called_once_with(
'OS::Cinder::Volume', limit=None, marker=None,
search_opts={'type': None, 'parameters': None},
sort=None)
class TestShowProtectableInstance(TestProtectables):
def setUp(self):
super(TestShowProtectableInstance, self).setUp()
pm = self.protectables_mock
pm.get_instance.return_value = protectables.Instances(
None, PROTECTABLE_INSTANCE_SHOW_INFO)
# Command to test
self.cmd = osc_protectables.ShowProtectableInstance(self.app, None)
def test_protectable_instance_show(self):
arglist = ['OS::Nova::Server', 'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01']
verifylist = [('protectable_type', 'OS::Nova::Server'),
('protectable_id',
'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.protectables_mock.get_instance.assert_called_once_with(
'OS::Nova::Server', 'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01',
search_opts={'parameters': None})

View File

@@ -0,0 +1,141 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import providers as osc_providers
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import providers
PROVIDER_INFO = {
"id": "2220f8b1-975d-4621-a872-fa9afb43cb6c",
"name": "OS Infra Provider",
"description": "provider description",
"extended_info_schema": {
"options_schema": {
"OS::Cinder::Volume": {
"required": [
"backup_mode"
],
"type": "object",
"properties": {
"backup_mode": {
"default": "auto",
"enum": [
"full",
"incremental",
"auto"
],
"type": "string",
"description": "The backup mode.",
"title": "Backup Mode"
}
},
"title": "Cinder Protection Options"
}
},
"saved_info_schema": {
"OS::Cinder::Volume": {
"required": [
"name"
],
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name for this backup.",
"title": "Name"
}
},
"title": "Cinder Protection Saved Info"
}
},
"restore_schema": {
"OS::Cinder::Volume": {
"type": "object",
"properties": {
"restore_name": {
"type": "string",
"description": "The name of the restored volume.",
"title": "Restore Name"
}
},
"title": "Cinder Protection Restore"
}
}
}
}
class TestProviders(fakes.TestDataProtection):
def setUp(self):
super(TestProviders, self).setUp()
self.providers_mock = self.app.client_manager.data_protection.providers
self.providers_mock.reset_mock()
class TestListProviders(TestProviders):
def setUp(self):
super(TestListProviders, self).setUp()
self.providers_mock.list.return_value = [providers.Provider(
None, PROVIDER_INFO)]
# Command to test
self.cmd = osc_providers.ListProviders(self.app, None)
def test_providers_list(self):
arglist = ['--name', 'OS Infra Provider']
verifylist = [('name', 'OS Infra Provider')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Name', 'Description'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [("2220f8b1-975d-4621-a872-fa9afb43cb6c",
"OS Infra Provider",
"provider description")]
self.assertEqual(expected_data, list(data))
class TestShowProvider(TestProviders):
def setUp(self):
super(TestShowProvider, self).setUp()
self.providers_mock.get.return_value = providers.Provider(
None, PROVIDER_INFO)
# Command to test
self.cmd = osc_providers.ShowProvider(self.app, None)
def test_provider_show(self):
arglist = ['2220f8b1-975d-4621-a872-fa9afb43cb6c']
verifylist = [('provider', '2220f8b1-975d-4621-a872-fa9afb43cb6c')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'description', 'extended_info_schema', 'id', 'name')
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])

View File

@@ -0,0 +1,134 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import restores as osc_restores
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import restores
RESTORE_INFO = {
"id": "22b82aa7-9179-4c71-bba2-caf5c0e68db7",
"project_id": "e486a2f49695423ca9c47e589b948108",
"provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9",
"checkpoint_id": "dcb20606-ad71-40a3-80e4-ef0fafdad0c3",
"restore_target": "",
"parameters": {},
"restore_auth": {},
"resources_status": {},
"resources_reason": {},
"status": "success"
}
class TestRestores(fakes.TestDataProtection):
def setUp(self):
super(TestRestores, self).setUp()
self.restores_mock = self.app.client_manager.data_protection.restores
self.restores_mock.reset_mock()
class TestListRestores(TestRestores):
def setUp(self):
super(TestListRestores, self).setUp()
self.restores_mock.list.return_value = [restores.Restore(
None, RESTORE_INFO)]
# Command to test
self.cmd = osc_restores.ListRestores(self.app, None)
def test_restores_list(self):
arglist = ['--status', 'success']
verifylist = [('status', 'success')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Project id', 'Provider id', 'Checkpoint id',
'Restore target', '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",
"",
{},
"success")]
self.assertEqual(expected_data, list(data))
class TestCreateRestore(TestRestores):
def setUp(self):
super(TestCreateRestore, self).setUp()
self.restores_mock.create.return_value = restores.Restore(
None, RESTORE_INFO)
# Command to test
self.cmd = osc_restores.CreateRestore(self.app, None)
def test_restore_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)
# Check that correct arguments were passed
self.restores_mock.create.assert_called_once_with(
'cf56bd3e-97a7-4078-b6d5-f36246333fd9',
'dcb20606-ad71-40a3-80e4-ef0fafdad0c3',
None, {}, None)
class TestShowRestore(TestRestores):
def setUp(self):
super(TestShowRestore, self).setUp()
self.restores_mock.get.return_value = restores.Restore(
None, RESTORE_INFO)
# Command to test
self.cmd = osc_restores.ShowRestore(self.app, None)
def test_restore_show(self):
arglist = ['22b82aa7-9179-4c71-bba2-caf5c0e68db7']
verifylist = [('restore', '22b82aa7-9179-4c71-bba2-caf5c0e68db7')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'checkpoint_id', 'id', 'parameters', 'project_id',
'provider_id', 'resources_reason', 'resources_status',
'restore_auth', 'restore_target', 'status')
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])

View File

@@ -0,0 +1,167 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import scheduled_operations as osc_so
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import scheduled_operations
SCHEDULEDOPERATION_INFO = {
"id": "1a2c0c3d-f402-4cd8-b5db-82e85cb51fad",
"name": "My scheduled operation",
"description": "It will run everyday",
"operation_type": "protect",
"trigger_id": "23902b02-5666-4ee6-8dfe-962ac09c3995",
"operation_definition": {
"provider_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa399",
"plan_id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa398"
},
"enabled": 1
}
class TestScheduledOperations(fakes.TestDataProtection):
def setUp(self):
super(TestScheduledOperations, self).setUp()
self.so_mock = self.app.client_manager.data_protection.\
scheduled_operations
self.so_mock.reset_mock()
class TestListScheduledOperations(TestScheduledOperations):
def setUp(self):
super(TestListScheduledOperations, self).setUp()
self.so_mock.list.return_value = \
[scheduled_operations.ScheduledOperation(None,
SCHEDULEDOPERATION_INFO)]
# Command to test
self.cmd = osc_so.ListScheduledOperations(self.app, None)
def test_scheduled_operations_list(self):
arglist = ['--name', 'My scheduled operation']
verifylist = [('name', 'My scheduled operation')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Name', 'OperationType', 'TriggerId',
'OperationDefinition'])
self.assertEqual(expected_columns, columns)
# 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"
})]
self.assertEqual(expected_data, data)
class TestCreateScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestCreateScheduledOperation, self).setUp()
self.so_mock.create.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
# Command to test
self.cmd = osc_so.CreateScheduledOperation(self.app, None)
def test_scheduled_operation_create(self):
arglist = ['My scheduled operation',
'protect',
"23902b02-5666-4ee6-8dfe-962ac09c3995",
"'provider_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa399,"
"plan_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa398'"]
verifylist = [('name', 'My scheduled operation'),
('operation_type', 'protect'),
('trigger_id', "23902b02-5666-4ee6-8dfe-962ac09c3995"),
('operation_definition',
"'provider_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa399,"
"plan_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa398'")]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.so_mock.create.assert_called_once_with(
'My scheduled operation',
'protect',
'23902b02-5666-4ee6-8dfe-962ac09c3995',
"'provider_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa399,"
"plan_id=2a9ce1f3-cc1a-4516-9435-0ebb13caa398'")
class TestDeleteScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestDeleteScheduledOperation, self).setUp()
self.so_mock.get.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
# Command to test
self.cmd = osc_so.DeleteScheduledOperation(self.app, None)
def test_scheduled_operation_delete(self):
arglist = ['1a2c0c3d-f402-4cd8-b5db-82e85cb51fad']
verifylist = [('scheduledoperation',
['1a2c0c3d-f402-4cd8-b5db-82e85cb51fad'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.so_mock.delete.assert_called_once_with(
'1a2c0c3d-f402-4cd8-b5db-82e85cb51fad')
class TestShowScheduledOperation(TestScheduledOperations):
def setUp(self):
super(TestShowScheduledOperation, self).setUp()
self.so_mock.get.return_value = scheduled_operations.\
ScheduledOperation(None, SCHEDULEDOPERATION_INFO)
# Command to test
self.cmd = osc_so.ShowScheduledOperation(self.app, None)
def test_scheduled_operation_show(self):
arglist = ['1a2c0c3d-f402-4cd8-b5db-82e85cb51fad']
verifylist = [('scheduledoperation',
'1a2c0c3d-f402-4cd8-b5db-82e85cb51fad')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'description', 'enabled', 'id', 'name', 'operation_definition',
'operation_type', 'trigger_id')
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])

View File

@@ -0,0 +1,186 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from karborclient.osc.v1 import triggers as osc_triggers
from karborclient.tests.unit.osc.v1 import fakes
from karborclient.v1 import triggers
TRIGGER_INFO = {
"id": "2a9ce1f3-cc1a-4516-9435-0ebb13caa398",
"name": "My backup trigger",
"type": "time",
"properties": {
"format": "calendar",
"pattern": "BEGIN:VEVENT\\nRRULE:FREQ=HOURLY;INTERVAL=1;\\nEND:VEVENT",
"start_time": "2015-12-17T08:30:00",
"end_time": "2016-03-17T08:30:00",
"window": "3600"
}
}
class TestTriggers(fakes.TestDataProtection):
def setUp(self):
super(TestTriggers, self).setUp()
self.triggers_mock = self.app.client_manager.data_protection.triggers
self.triggers_mock.reset_mock()
class TestListTriggers(TestTriggers):
def setUp(self):
super(TestListTriggers, self).setUp()
self.triggers_mock.list.return_value = [triggers.Trigger(
None, TRIGGER_INFO)]
# Command to test
self.cmd = osc_triggers.ListTriggers(self.app, None)
def test_triggers_list(self):
arglist = ['--name', 'My backup trigger']
verifylist = [('name', 'My backup trigger')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
['Id', 'Name', 'Type', 'Properties'])
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [("2a9ce1f3-cc1a-4516-9435-0ebb13caa398",
"My backup trigger",
"time",
{"format": "calendar",
"pattern": "BEGIN:VEVENT\\nRRULE:FREQ=HOURLY;INTERVAL=1;\\nEND:VEVENT", # noqa
"start_time": "2015-12-17T08:30:00",
"end_time": "2016-03-17T08:30:00",
"window": "3600"})]
self.assertEqual(expected_data, list(data))
class TestCreateTrigger(TestTriggers):
def setUp(self):
super(TestCreateTrigger, self).setUp()
self.triggers_mock.create.return_value = triggers.Trigger(
None, TRIGGER_INFO)
# Command to test
self.cmd = osc_triggers.CreateTrigger(self.app, None)
def test_trigger_create(self):
arglist = ['My backup trigger',
'time',
"'format'='calendar',"
"'pattern'='BEGIN:VEVENT\\nRRULE:FREQ=HOURLY;INTERVAL=1;\\nEND:VEVENT'," # noqa
"'start_time'='2015-12-17T08:30:00',"
"'end_time'='2016-03-17T08:30:00',"
"'window'='3600'"]
verifylist = [('name', 'My backup trigger'),
('type', 'time'),
('properties', "'format'='calendar',"
"'pattern'='BEGIN:VEVENT\\nRRULE:FREQ=HOURLY;INTERVAL=1;\\nEND:VEVENT'," # noqa
"'start_time'='2015-12-17T08:30:00',"
"'end_time'='2016-03-17T08:30:00',"
"'window'='3600'")]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.triggers_mock.create.assert_called_once_with(
'My backup trigger',
'time',
"'format'='calendar',"
"'pattern'='BEGIN:VEVENT\\nRRULE:FREQ=HOURLY;INTERVAL=1;\\nEND:VEVENT'," # noqa
"'start_time'='2015-12-17T08:30:00',"
"'end_time'='2016-03-17T08:30:00',"
"'window'='3600'")
class TestUpdateTrigger(TestTriggers):
def setUp(self):
super(TestUpdateTrigger, self).setUp()
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
self.triggers_mock.update.return_value = triggers.Trigger(
None, TRIGGER_INFO)
# Command to test
self.cmd = osc_triggers.UpdateTrigger(self.app, None)
def test_trigger_update(self):
arglist = ['2a9ce1f3-cc1a-4516-9435-0ebb13caa398',
'--name', 'My backup trigger']
verifylist = [('trigger_id', '2a9ce1f3-cc1a-4516-9435-0ebb13caa398'),
('name', 'My backup trigger')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.triggers_mock.update.assert_called_once_with(
'2a9ce1f3-cc1a-4516-9435-0ebb13caa398',
{'name': 'My backup trigger'})
class TestDeleteTrigger(TestTriggers):
def setUp(self):
super(TestDeleteTrigger, self).setUp()
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
# Command to test
self.cmd = osc_triggers.DeleteTrigger(self.app, None)
def test_trigger_delete(self):
arglist = ['2a9ce1f3-cc1a-4516-9435-0ebb13caa398']
verifylist = [('trigger', ['2a9ce1f3-cc1a-4516-9435-0ebb13caa398'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.triggers_mock.delete.assert_called_once_with(
'2a9ce1f3-cc1a-4516-9435-0ebb13caa398')
class TestShowTrigger(TestTriggers):
def setUp(self):
super(TestShowTrigger, self).setUp()
self.triggers_mock.get.return_value = triggers.Trigger(
None, TRIGGER_INFO)
# Command to test
self.cmd = osc_triggers.ShowTrigger(self.app, None)
def test_trigger_show(self):
arglist = ['2a9ce1f3-cc1a-4516-9435-0ebb13caa398']
verifylist = [('trigger', '2a9ce1f3-cc1a-4516-9435-0ebb13caa398')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = (
'id', 'name', 'properties', 'type')
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])

View File

@@ -82,7 +82,7 @@ class FakeHTTPClient(base_client.HTTPClient):
(method, url, callback))
# Note the call
self.callstack.append((method, url, kwargs.get('body', None)))
self.callstack.append((method, url, kwargs.get('body')))
status, headers, body = getattr(self, callback)(**kwargs)
# add fake request-id header
headers['x-openstack-request-id'] = REQUEST_ID

146
karborclient/utils.py Normal file
View File

@@ -0,0 +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

View File

@@ -25,7 +25,7 @@ class Checkpoint(base.Resource):
return
plan = self.protection_plan
if plan is not None:
provider_id = plan.get("provider_id", None)
provider_id = plan.get("provider_id")
new = self.manager.get(provider_id, self.id)
if new:
self._add_details(new._info)

View File

@@ -15,12 +15,12 @@ import json
import os
from datetime import datetime
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from karborclient.common.apiclient import exceptions
from karborclient.common import base
from karborclient.common import utils
from karborclient import utils as arg_utils
@utils.arg('--all-tenants',
@@ -145,9 +145,9 @@ def do_plan_create(cs, args):
if not uuidutils.is_uuid_like(args.provider_id):
raise exceptions.CommandError(
"Invalid provider id provided.")
plan_resources = _extract_resources(args)
_check_resources(cs, plan_resources)
plan_parameters = _extract_parameters(args)
plan_resources = arg_utils.extract_resources(args)
arg_utils.check_resources(cs, plan_resources)
plan_parameters = arg_utils.extract_parameters(args)
plan = cs.plans.create(args.name, args.provider_id, plan_resources,
plan_parameters, description=args.description)
dict_format_list = {"resources", "parameters"}
@@ -198,7 +198,7 @@ def do_plan_update(cs, args):
if args.name is not None:
data['name'] = args.name
if args.resources is not None:
plan_resources = _extract_resources(args)
plan_resources = arg_utils.extract_resources(args)
data['resources'] = plan_resources
if args.status is not None:
data['status'] = args.status
@@ -211,39 +211,6 @@ def do_plan_update(cs, args):
utils.print_dict(plan.to_dict())
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"])
@utils.arg('provider_id',
metavar='<provider_id>',
help='Provider id.')
@@ -286,7 +253,7 @@ def do_restore_create(cs, args):
raise exceptions.CommandError(
"Invalid checkpoint id provided.")
restore_parameters = _extract_parameters(args)
restore_parameters = arg_utils.extract_parameters(args)
restore_auth = None
if args.restore_target is not None:
if args.restore_username is None:
@@ -307,48 +274,6 @@ def do_restore_create(cs, args):
utils.print_dict(restore.to_dict(), dict_format_list=dict_format_list)
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
@utils.arg('--all-tenants',
dest='all_tenants',
metavar='<0|1>',
@@ -475,7 +400,7 @@ def do_protectable_show(cs, args):
def do_protectable_show_instance(cs, args):
"""Shows instance details."""
search_opts = {
'parameters': (_extract_instances_parameters(args)
'parameters': (arg_utils.extract_instances_parameters(args)
if args.parameters else None),
}
instance = cs.protectables.get_instance(args.protectable_type,
@@ -529,7 +454,7 @@ def do_protectable_list_instances(cs, args):
search_opts = {
'type': args.type,
'parameters': (_extract_instances_parameters(args)
'parameters': (arg_utils.extract_instances_parameters(args)
if args.parameters else None),
}
@@ -557,19 +482,6 @@ def do_protectable_list_instances(cs, args):
sortby_index=sortby_index, formatters=formatters)
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
@utils.arg('provider_id',
metavar='<provider_id>',
help='Id of provider.')
@@ -657,7 +569,7 @@ def do_checkpoint_create(cs, args):
checkpoint_extra_info = None
if args.extra_info is not None:
checkpoint_extra_info = _extract_extra_info(args)
checkpoint_extra_info = arg_utils.extract_extra_info(args)
checkpoint = cs.checkpoints.create(args.provider_id, args.plan_id,
checkpoint_extra_info)
dict_format_list = {"protection_plan"}
@@ -666,20 +578,6 @@ def do_checkpoint_create(cs, args):
json_format_list=json_format_list)
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
@utils.arg('provider_id',
metavar='<provider_id>',
help='ID of provider.')
@@ -919,27 +817,12 @@ def do_trigger_list(cs, args):
help='Properties of trigger.')
def do_trigger_create(cs, args):
"""Creates a trigger."""
trigger_properties = _extract_properties(args)
trigger_properties = arg_utils.extract_properties(args)
trigger = cs.triggers.create(args.name, args.type, trigger_properties)
dict_format_list = {"properties"}
utils.print_dict(trigger.to_dict(), dict_format_list=dict_format_list)
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
@utils.arg("trigger_id", metavar="<TRIGGER ID>",
help="Id of trigger to update.")
@utils.arg("--name", metavar="<name>",
@@ -949,7 +832,7 @@ def _extract_properties(args):
def do_trigger_update(cs, args):
"""Update a trigger."""
trigger_info = {}
trigger_properties = _extract_properties(args)
trigger_properties = arg_utils.extract_properties(args)
trigger_info['name'] = args.name
trigger_info['properties'] = trigger_properties
trigger = cs.triggers.update(args.trigger_id, trigger_info)
@@ -1095,7 +978,7 @@ def do_scheduledoperation_list(cs, args):
help='Operation definition of scheduled operation.')
def do_scheduledoperation_create(cs, args):
"""Creates a scheduled operation."""
operation_definition = _extract_operation_definition(args)
operation_definition = arg_utils.extract_operation_definition(args)
scheduledoperation = cs.scheduled_operations.create(args.name,
args.operation_type,
args.trigger_id,
@@ -1105,19 +988,6 @@ def do_scheduledoperation_create(cs, args):
dict_format_list=dict_format_list)
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
@utils.arg('scheduledoperation',
metavar='<scheduledoperation>',
help='ID of scheduled operation.')

View File

@@ -3,11 +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>=2.20.0 # Apache-2.0
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
keystoneauth1>=2.21.0 # Apache-2.0
requests>=2.14.2 # Apache-2.0
simplejson>=2.2.0 # MIT
Babel!=2.4.0,>=2.3.4 # BSD
six>=1.9.0 # MIT
osc-lib>=1.5.1 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0

View File

@@ -30,6 +30,52 @@ packages =
console_scripts =
karbor = karborclient.shell:main
openstack.cli.extension =
data_protection = karborclient.osc.plugin
openstack.data_protection.v1 =
data_protection_plan_list = karborclient.osc.v1.plans:ListPlans
data_protection_plan_show = karborclient.osc.v1.plans:ShowPlan
data_protection_plan_create = karborclient.osc.v1.plans:CreatePlan
data_protection_plan_update = karborclient.osc.v1.plans:UpdatePlan
data_protection_plan_delete = karborclient.osc.v1.plans:DeletePlan
data_protection_restore_list = karborclient.osc.v1.restores:ListRestores
data_protection_restore_show = karborclient.osc.v1.restores:ShowRestore
data_protection_restore_create = karborclient.osc.v1.restores:CreateRestore
data_protection_provider_list = karborclient.osc.v1.providers:ListProviders
data_protection_provider_show = karborclient.osc.v1.providers:ShowProvider
data_protection_protectable_list = karborclient.osc.v1.protectables.ListProtectables
data_protection_protectable_show = karborclient.osc.v1.protectables.ShowProtectable
data_protection_protectable_instance_list = karborclient.osc.v1.protectables.ListProtectableInstances
data_protection_protectable_instance_show = karborclient.osc.v1.protectables.ShowProtectableInstance
data_protection_trigger_list = karborclient.osc.v1.triggers:ListTriggers
data_protection_trigger_show = karborclient.osc.v1.triggers:ShowTrigger
data_protection_trigger_create = karborclient.osc.v1.triggers:CreateTrigger
data_protection_trigger_update = karborclient.osc.v1.triggers:UpdateTrigger
data_protection_trigger_delete = karborclient.osc.v1.triggers:DeleteTrigger
data_protection_checkpoint_list = karborclient.osc.v1.checkpoints:ListCheckpoints
data_protection_checkpoint_show = karborclient.osc.v1.checkpoints:ShowCheckpoint
data_protection_checkpoint_create = karborclient.osc.v1.checkpoints:CreateCheckpoint
data_protection_checkpoint_delete = karborclient.osc.v1.checkpoints:DeleteCheckpoint
data_protection_scheduledoperation_list = karborclient.osc.v1.scheduled_operations:ListScheduledOperations
data_protection_scheduledoperation_show = karborclient.osc.v1.scheduled_operations:ShowScheduledOperation
data_protection_scheduledoperation_create = karborclient.osc.v1.scheduled_operations:CreateScheduledOperation
data_protection_scheduledoperation_delete = karborclient.osc.v1.scheduled_operations:DeleteScheduledOperation
[compile_catalog]
directory = karborclient/locale
domain = karborclient
[update_catalog]
domain = karborclient
output_dir = karborclient/locale
input_file = karborclient/locale/karborclient.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = karborclient/locale/karborclient.pot
[build_sphinx]
source-dir = doc/source
build-dir = doc/build

View File

@@ -7,9 +7,11 @@ 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
docutils>=0.11 # OSI-Approved Open Source, Public Domain
sphinx!=1.6.1,>=1.5.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
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
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT

View File

@@ -27,7 +27,9 @@ setenv =
OS_TEST_PATH = ./karborclient/tests/functional
passenv = OS_*
[testenv:cover]
commands = python setup.py test --coverage --testr-args='{posargs}'
commands =
python setup.py test --coverage --testr-args='{posargs}'
coverage report
[testenv:docs]
commands = python setup.py build_sphinx
@@ -36,9 +38,8 @@ commands = python setup.py build_sphinx
commands = oslo_debug_helper -t karborclient/tests {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
ignore = E123,E125
ignore =
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools