Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bb1cc2c7a | ||
|
|
b56f70e7fd | ||
|
|
cd7d456491 | ||
|
|
eb70a9d460 | ||
|
|
5eab398150 | ||
|
|
1a7243354e | ||
|
|
5ded9c1f19 | ||
|
|
2fc5904648 | ||
|
|
71e029c629 | ||
|
|
289370ab38 | ||
|
|
795fa730a8 | ||
|
|
3f91adc2d8 | ||
|
|
1b3b860565 | ||
|
|
d358465dcf | ||
|
|
b11e847b52 | ||
|
|
a82f43ae23 | ||
|
|
9936200bf8 | ||
|
|
75a7e6276f | ||
|
|
7b2a2e8c92 | ||
|
|
22d5a7ec38 | ||
|
|
6f2feab60d | ||
|
|
3233c39c52 | ||
|
|
7eb818c0ee | ||
|
|
b658068fa6 | ||
|
|
378a7a38f9 | ||
|
|
1f907c95b8 | ||
|
|
1bf9f74604 | ||
|
|
f898a169cb | ||
|
|
2800c5f1c2 | ||
|
|
2d1d7624e8 | ||
|
|
995fe11dbe | ||
|
|
956f0207e7 | ||
|
|
61a46a6752 | ||
|
|
0d9611ba9b | ||
|
|
e3f50485fd | ||
|
|
cd75dda7bb | ||
|
|
1924aba63a | ||
|
|
86b15d38b8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,6 +24,7 @@ pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
cover
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
||||
@@ -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
|
||||
|
||||
|
||||
166
doc/source/specs/karbor-support-in-python-openstackclient.rst
Normal file
166
doc/source/specs/karbor-support-in-python-openstackclient.rst
Normal 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
|
||||
@@ -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', '')
|
||||
|
||||
0
karborclient/osc/__init__.py
Normal file
0
karborclient/osc/__init__.py
Normal file
56
karborclient/osc/plugin.py
Normal file
56
karborclient/osc/plugin.py
Normal 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
|
||||
0
karborclient/osc/v1/__init__.py
Normal file
0
karborclient/osc/v1/__init__.py
Normal file
205
karborclient/osc/v1/checkpoints.py
Normal file
205
karborclient/osc/v1/checkpoints.py
Normal 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.")
|
||||
265
karborclient/osc/v1/plans.py
Normal file
265
karborclient/osc/v1/plans.py
Normal 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.")
|
||||
177
karborclient/osc/v1/protectables.py
Normal file
177
karborclient/osc/v1/protectables.py
Normal 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()))
|
||||
99
karborclient/osc/v1/providers.py
Normal file
99
karborclient/osc/v1/providers.py
Normal 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()))
|
||||
195
karborclient/osc/v1/restores.py
Normal file
195
karborclient/osc/v1/restores.py
Normal 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()))
|
||||
208
karborclient/osc/v1/scheduled_operations.py
Normal file
208
karborclient/osc/v1/scheduled_operations.py
Normal 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.")
|
||||
229
karborclient/osc/v1/triggers.py
Normal file
229
karborclient/osc/v1/triggers.py
Normal 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.")
|
||||
@@ -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
|
||||
|
||||
|
||||
0
karborclient/tests/unit/osc/__init__.py
Normal file
0
karborclient/tests/unit/osc/__init__.py
Normal file
1
karborclient/tests/unit/osc/v1/__init__.py
Normal file
1
karborclient/tests/unit/osc/v1/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__author__ = 'c00179918'
|
||||
29
karborclient/tests/unit/osc/v1/fakes.py
Normal file
29
karborclient/tests/unit/osc/v1/fakes.py
Normal 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()
|
||||
160
karborclient/tests/unit/osc/v1/test_checkpoints.py
Normal file
160
karborclient/tests/unit/osc/v1/test_checkpoints.py
Normal 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')
|
||||
180
karborclient/tests/unit/osc/v1/test_plans.py
Normal file
180
karborclient/tests/unit/osc/v1/test_plans.py
Normal 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])
|
||||
161
karborclient/tests/unit/osc/v1/test_protectables.py
Normal file
161
karborclient/tests/unit/osc/v1/test_protectables.py
Normal 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})
|
||||
141
karborclient/tests/unit/osc/v1/test_providers.py
Normal file
141
karborclient/tests/unit/osc/v1/test_providers.py
Normal 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])
|
||||
134
karborclient/tests/unit/osc/v1/test_restores.py
Normal file
134
karborclient/tests/unit/osc/v1/test_restores.py
Normal 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])
|
||||
167
karborclient/tests/unit/osc/v1/test_scheduledoperations.py
Normal file
167
karborclient/tests/unit/osc/v1/test_scheduledoperations.py
Normal 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])
|
||||
186
karborclient/tests/unit/osc/v1/test_triggers.py
Normal file
186
karborclient/tests/unit/osc/v1/test_triggers.py
Normal 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])
|
||||
@@ -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
146
karborclient/utils.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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
|
||||
|
||||
46
setup.cfg
46
setup.cfg
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
7
tox.ini
7
tox.ini
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user