Retire Packaging Deb project repos

This commit is part of a series to retire the Packaging Deb
project. Step 2 is to remove all content from the project
repos, replacing it with a README notification where to find
ongoing work, and how to recover the repo if needed at some
future point (as in
https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project).

Change-Id: Icd02d91c0d525420c34df2f78666f88e02d24f17
This commit is contained in:
Tony Breeds 2017-09-12 16:01:51 -06:00
parent 757bb793a8
commit dc4d398282
208 changed files with 14 additions and 30226 deletions

19
.gitignore vendored
View File

@ -1,19 +0,0 @@
*.pyc
*.swp
.coverage
.idea
.testrepository
.tox/*
.venv
cover/*
build/*
etc/manilaclient/manilaclient.conf*
subunit.log
python_manilaclient.egg-info
setuptools_git*.egg
setuptools_git*.egg
AUTHORS
ChangeLog
doc/source/manila_cli_output.rst.inc
# Files created by releasenotes build
releasenotes/build

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/python-manilaclient.git

View File

@ -1,9 +0,0 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./manilaclient/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=([^\.]*\.)*

View File

@ -1,16 +0,0 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps documented at:
https://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack
should be submitted for review via the Gerrit tool, following
the workflow documented at:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/python-manilaclient

115
HACKING
View File

@ -1,115 +0,0 @@
Manila Style Commandments
=========================
Step 1: Read https://www.python.org/dev/peps/pep-0008/
Step 2: Read https://www.python.org/dev/peps/pep-0008/ again
Step 3: Read on
Imports
-------
- thou shalt not import objects, only modules
- thou shalt not import more than one module per line
- thou shalt not make relative imports
- thou shalt organize your imports according to the following template
::
# vim: tabstop=4 shiftwidth=4 softtabstop=4
{{stdlib imports in human alphabetical order}}
\n
{{manila imports in human alphabetical order}}
\n
\n
{{begin your code}}
General
-------
- thou shalt put two newlines twixt toplevel code (funcs, classes, etc)
- thou shalt put one newline twixt methods in classes and anywhere else
- thou shalt not write "except:", use "except Exception:" at the very least
- thou shalt include your name with TODOs as in "TODO(termie)"
- thou shalt not name anything the same name as a builtin or reserved word
- thou shalt not violate causality in our time cone, or else
Human Alphabetical Order Examples
---------------------------------
::
import httplib
import logging
import random
import StringIO
import time
import unittest
from manila import flags
from manila import test
from manila.auth import users
from manila.endpoint import api
from manila.endpoint import cloud
Docstrings
----------
"""A one line docstring looks like this and ends in a period."""
"""A multiline docstring has a one-line summary, less than 80 characters.
Then a new paragraph after a newline that explains in more detail any
general information about the function, class or method. Example usages
are also great to have here if it is a complex class for function. After
you have finished your descriptions add an extra newline and close the
quotations.
When writing the docstring for a class, an extra line should be placed
after the closing quotations. For more in-depth explanations for these
decisions see https://www.python.org/dev/peps/pep-0257/
If you are going to describe parameters and return values, use Sphinx, the
appropriate syntax is as follows.
:param foo: the foo parameter
:param bar: the bar parameter
:returns: description of the return value
"""
Text encoding
----------
- All text within python code should be of type 'unicode'.
WRONG:
>>> s = 'foo'
>>> s
'foo'
>>> type(s)
<type 'str'>
RIGHT:
>>> u = u'foo'
>>> u
u'foo'
>>> type(u)
<type 'unicode'>
- Transitions between internal unicode and external strings should always
be immediately and explicitly encoded or decoded.
- All external text that is not explicitly encoded (database storage,
commandline arguments, etc.) should be presumed to be encoded as utf-8.
WRONG:
mystring = infile.readline()
myreturnstring = do_some_magic_with(mystring)
outfile.write(myreturnstring)
RIGHT:
mystring = infile.readline()
mytext = s.decode('utf-8')
returntext = do_some_magic_with(mytext)
returnstring = returntext.encode('utf-8')
outfile.write(returnstring)

208
LICENSE
View File

@ -1,208 +0,0 @@
Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1)
Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1)
All rights reserved.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
--- License for python-manilaclient versions prior to 2.1 ---
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of this project nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

14
README Normal file
View File

@ -0,0 +1,14 @@
This project is no longer maintained.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
For ongoing work on maintaining OpenStack packages in the Debian
distribution, please see the Debian OpenStack packaging team at
https://wiki.debian.org/OpenStack/.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,107 +0,0 @@
========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/badges/python-manilaclient.svg
:target: https://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
Python bindings to the OpenStack Manila API
===========================================
.. image:: https://img.shields.io/pypi/v/python-manilaclient.svg
:target: https://pypi.python.org/pypi/python-manilaclient/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/dm/python-manilaclient.svg
:target: https://pypi.python.org/pypi/python-manilaclient/
:alt: Downloads
This is a client for the OpenStack Manila API. There's a Python API (the
``manilaclient`` module), and a command-line script (``manila``). Each
implements 100% of the OpenStack Manila API.
See the `OpenStack CLI guide`_ for information on how to use the ``manila``
command-line tool. You may also want to look at the
`OpenStack API documentation`_.
.. _OpenStack CLI Guide: https://docs.openstack.org/python-openstackclient/latest/cli/
.. _OpenStack API documentation: https://docs.openstack.org/api/
The project is hosted on `Launchpad`_, where bugs can be filed. The code is
hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github
pull requests.
.. _Github: https://github.com/openstack/python-manilaclient
.. _Launchpad: https://launchpad.net/python-manilaclient
.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow
This code is a fork of `Cinderclient`_ of Grizzly release and then it was
developed separately. Cinderclient code is a fork of
`Jacobian's python-cloudservers`__ If you need API support for the Rackspace
API solely or the BSD license, you should use that repository.
python-manilaclient is licensed under the Apache License like the rest of
OpenStack.
.. _Cinderclient: https://github.com/openstack/python-cinderclient
__ https://github.com/jacobian/python-cloudservers
.. contents:: Contents:
:local:
Command-line API
----------------
Installing this package gets you a shell command, ``manila``, that you
can use to interact with any Rackspace compatible API (including OpenStack).
You'll need to provide your OpenStack username and password. You can do this
with the ``--os-username``, ``--os-password`` and ``--os-tenant-name``
params, but it's easier to just set them as environment variables::
export OS_USERNAME=foouser
export OS_PASSWORD=barpass
export OS_TENANT_NAME=fooproject
You will also need to define the authentication url either with param
``--os-auth-url`` or as an environment variable::
export OS_AUTH_URL=http://example.com:5000/v2.0/
Since Keystone can return multiple regions in the Service Catalog, you
can specify the one you want with ``--os-region-name`` (or
``export OS_REGION_NAME``). It defaults to the first in the list returned.
You'll find complete documentation on the shell by running
``manila help``, see ``manila help COMMAND`` for help on a specific command.
Python API
----------
There's also a complete Python API, but it has not yet been documented.
Quick-start using keystone::
# use v2.0 auth with http://example.com:5000/v2.0/
>>> from manilaclient.v1 import client
>>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="share")
>>> nt.shares.list()
[...]
* License: Apache License, Version 2.0
* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - release management
* `Blueprints`_ - feature specifications
* `Bugs`_ - issue tracking
* `Source`_
* `How to Contribute`_
.. _PyPi: https://pypi.python.org/pypi/python-manilaclient
.. _Online Documentation: https://docs.openstack.org/python-manilaclient/latest/
.. _Launchpad project: https://launchpad.net/python-manilaclient
.. _Blueprints: https://blueprints.launchpad.net/python-manilaclient
.. _Bugs: https://bugs.launchpad.net/python-manilaclient
.. _Source: https://git.openstack.org/cgit/openstack/python-manilaclient
.. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html

View File

@ -1,138 +0,0 @@
#!/bin/bash -xe
#
# 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.
# This script is executed inside post_test_hook function in devstack gate.
export MANILACLIENT_DIR="$BASE/new/python-manilaclient"
export MANILACLIENT_CONF="$MANILACLIENT_DIR/etc/manilaclient/manilaclient.conf"
# Go to the manilaclient dir
cd $MANILACLIENT_DIR
# Give permissions
sudo chown -R jenkins:stack .
# Create manilaclient config file
touch $MANILACLIENT_CONF
# Import functions from devstack
source $BASE/new/devstack/functions
env | grep OS_
# Set options to config client.
source $BASE/new/devstack/openrc demo demo
env | grep OS_
export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME}
iniset $MANILACLIENT_CONF DEFAULT username $OS_USERNAME
iniset $MANILACLIENT_CONF DEFAULT tenant_name $OS_TENANT_NAME
iniset $MANILACLIENT_CONF DEFAULT password $OS_PASSWORD
iniset $MANILACLIENT_CONF DEFAULT auth_url $OS_AUTH_URL
iniset $MANILACLIENT_CONF DEFAULT project_domain_name $OS_PROJECT_DOMAIN_NAME
iniset $MANILACLIENT_CONF DEFAULT user_domain_name $OS_USER_DOMAIN_NAME
iniset $MANILACLIENT_CONF DEFAULT project_domain_id $OS_PROJECT_DOMAIN_ID
iniset $MANILACLIENT_CONF DEFAULT user_domain_id $OS_USER_DOMAIN_ID
source $BASE/new/devstack/openrc admin demo
env | grep OS_
export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME}
iniset $MANILACLIENT_CONF DEFAULT admin_username $OS_USERNAME
iniset $MANILACLIENT_CONF DEFAULT admin_tenant_name $OS_TENANT_NAME
iniset $MANILACLIENT_CONF DEFAULT admin_password $OS_PASSWORD
iniset $MANILACLIENT_CONF DEFAULT admin_auth_url $OS_AUTH_URL
iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_name $OS_PROJECT_DOMAIN_NAME
iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_name $OS_USER_DOMAIN_NAME
iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_id $OS_PROJECT_DOMAIN_ID
iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_id $OS_USER_DOMAIN_ID
# Suppress errors in cleanup of resources
SUPPRESS_ERRORS=${SUPPRESS_ERRORS_IN_CLEANUP:-True}
iniset $MANILACLIENT_CONF DEFAULT suppress_errors_in_cleanup $SUPPRESS_ERRORS
# Set access type usage specific to dummy driver that we are using in CI
iniset $MANILACLIENT_CONF DEFAULT access_types_mapping "nfs:ip,cifs:user"
# Dummy driver is capable of running share migration tests
iniset $MANILACLIENT_CONF DEFAULT run_migration_tests "True"
# Running mountable snapshot tests in dummy driver
iniset $MANILACLIENT_CONF DEFAULT run_mount_snapshot_tests "True"
# Create share network and use it for functional tests if required
USE_SHARE_NETWORK=$(trueorfalse True USE_SHARE_NETWORK)
if [[ ${USE_SHARE_NETWORK} = True ]]; then
SHARE_NETWORK_NAME=${SHARE_NETWORK_NAME:-ci}
DEFAULT_NEUTRON_NET=$(neutron net-show private -c id -f value)
DEFAULT_NEUTRON_SUBNET=$(neutron subnet-show private-subnet -c id -f value)
NEUTRON_NET=${NEUTRON_NET:-$DEFAULT_NEUTRON_NET}
NEUTRON_SUBNET=${NEUTRON_SUBNET:-$DEFAULT_NEUTRON_SUBNET}
manila share-network-create \
--name $SHARE_NETWORK_NAME \
--neutron-net $NEUTRON_NET \
--neutron-subnet $NEUTRON_SUBNET
iniset $MANILACLIENT_CONF DEFAULT share_network $SHARE_NETWORK_NAME
iniset $MANILACLIENT_CONF DEFAULT admin_share_network $SHARE_NETWORK_NAME
fi
# Set share type if required
if [[ "$SHARE_TYPE" ]]; then
iniset $MANILACLIENT_CONF DEFAULT share_type $SHARE_TYPE
fi
# let us control if we die or not
set +o errexit
CONCURRENCY=${CONCURRENCY:-8}
# Run functional tests
sudo -H -u jenkins tox -e functional -v -- --concurrency=$CONCURRENCY
EXIT_CODE=$?
if [ -d ".testrepository" ] ; then
if [ -f ".testrepository/0.2" ] ; then
cp .testrepository/0.2 ./subunit_log.txt
elif [ -f ".testrepository/0" ] ; then
.tox/functional/bin/subunit-1to2 < .testrepository/0 > ./subunit_log.txt
fi
/usr/os-testr-env/bin/subunit2html ./subunit_log.txt testr_results.html
SUBUNIT_SIZE=$(du -k ./subunit_log.txt | awk '{print $1}')
gzip -9 ./subunit_log.txt
gzip -9 ./testr_results.html
sudo mv testr_results.html.gz $WORKSPACE/logs
sudo mv subunit_log.txt.gz $WORKSPACE/logs
sudo cp -R .tox $WORKSPACE
sudo cp -R .testrepository $WORKSPACE
if [[ "$SUBUNIT_SIZE" -gt 50000 ]]; then
echo
echo "sub_unit.log was greater than 50 MB of uncompressed data!"
echo "Something is causing tests for this project to log significant amounts of data."
echo "This may be writers to python logging, stdout, or stderr."
echo "Failing this test as a result."
echo
exit 1
fi
rancount=$(.tox/functional/bin/testr last | sed -ne 's/Ran \([0-9]\+\).*tests in.*/\1/p')
if [ -z "$rancount" ] || [ "$rancount" -eq "0" ] ; then
echo
echo "Zero tests were run. At least one test should have been run."
echo "Failing this test as a result."
echo
exit 1
fi
fi
return $EXIT_CODE

View File

@ -1,28 +0,0 @@
#!/bin/bash -xe
#
# 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.
# This script is executed inside pre_test_hook function in devstack gate.
# Run manila's pre_test_hook.sh script first
source $BASE/new/manila/contrib/ci/pre_test_hook.sh True dummy multibackend
localconf=$BASE/new/devstack/local.conf
echo "[[local|localrc]]" >> $localconf
# Set big quota for share networks to avoid limit exceedances
echo "MANILA_OPTGROUP_DEFAULT_quota_share_networks=50" >> $localconf
# Enable and use only v3 of Identity API
echo "IDENTITY_API_VERSION=3" >> $localconf
echo "ENABLE_IDENTITY_V2=False" >> $localconf

1
doc/.gitignore vendored
View File

@ -1 +0,0 @@
build/

View File

@ -1,90 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXSOURCE = source
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE)
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-manilaclient.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-manilaclient.qhc"
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View File

@ -1,26 +0,0 @@
The :mod:`manilaclient` Python API
==================================
.. module:: manilaclient
:synopsis: A client for the OpenStack Manila API.
.. currentmodule:: manilaclient
Usage
=====
In order to use the Python API directly, you must first obtain an auth
token and identify which endpoint you wish to speak to. Once you have
done so, you can use the API like so::
>>> from manilaclient import client
>>> manila = client.Client('1', $OS_USER_NAME, $OS_PASSWORD, $OS_TENANT_NAME, $OS_AUTH_URL)
>>> manila.shares.list()
[]
>>> share = manila.shares.create(share_proto="nfs", size=1, share_network_id="some_share_network_id")
>>> share.id
ce06d0a8-5c1b-4e2c-81d2-39eca6bbfb70
>>> manila.shares.list()
[<Share: ce06d0a8-5c1b-4e2c-81d2-39eca6bbfb70>]
>>>share.delete

View File

@ -1,71 +0,0 @@
# python-manilaclient documentation build configuration file
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
sys.path.insert(0, ROOT)
# path to local extensions
sys.path.append(os.path.abspath('exts'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'openstackdocstheme', 'clidoc']
# openstackdocstheme options
repository_name = 'openstack/python-manilaclient'
bug_project = 'python-manilaclient'
bug_tag = ''
# Must set this variable to include year, month, day, hours, and minutes.
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'python-manilaclient'
copyright = u'Rackspace, based on work by Jacob Kaplan-Moss'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2.6'
# The full version, including alpha/beta/rc tags.
release = '2.6.10'
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'python-manilaclientdoc'
html_theme = 'openstackdocs'

View File

@ -1,41 +0,0 @@
# Copyright 2014 SUSE Linux GmbH
#
# 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 os
import sys
from cStringIO import StringIO
def _get_cli_output():
stdout_org = sys.stdout
sys.stdout = output = StringIO()
from manilaclient.shell import OpenStackManilaShell
shell = OpenStackManilaShell()
shell.main(None)
sys.stdout = stdout_org
output.seek(0)
return map(lambda x: " %s" % x, output)
def builder_inited(app):
# generate the missing rst files
with open(os.path.join(app.env.srcdir, "manila_cli_output.rst.inc"), "w") as f:
f.write("``manila help``::\n\n")
f.write("\n".join(_get_cli_output()))
f.write("\n")
def setup(app):
app.connect('builder-inited', builder_inited)

View File

@ -1,78 +0,0 @@
Python bindings to the OpenStack Manila API
===========================================
This is a client for OpenStack Manila API. There's :doc:`a Python API
<api>` (the :mod:`manilaclient` module), and a :doc:`command-line script
<shell>` (installed as :program:`manila`). Each implements the entire
OpenStack Manila API.
You'll need credentials for an OpenStack cloud that implements the
Manila API in order to use the manila client.
Contents:
.. toctree::
:maxdepth: 2
shell
api
Contributing
============
Code is hosted at `git.openstack.org`_. Submit bugs to the
python-manilaclient project on `Launchpad`_. Submit code to the
openstack/python-manilaclient project using `Gerrit`_.
.. _git.openstack.org: https://git.openstack.org/cgit/openstack/python-manilaclient
.. _Launchpad: https://launchpad.net/python-manilaclient
.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow
Testing
-------
Manilaclient has two types of tests - 'unit' and 'functional'.
The preferred way to run tests is using ``tox``.
See `Consistent Testing Interface`_ for more details.
.. _Consistent Testing Interface: https://git.openstack.org/cgit/openstack/governance/tree/reference/project-testing-interface.rst
Functional tests
----------------
Functional CLI tests require several things to be able to run:
* Deployed and working manila service.
* Configured config file.
Config file is used to get information like 'auth_url', 'username',
'tenant_name' and 'password'.
To get config sample need to run following 'tox' job:
.. code-block:: console
$ tox -e genconfig
This will create file 'etc/manilaclient/manilaclient.conf.sample' with all
available config opts.
Then rename it removing ".sample" and set values for opts there. After it,
tests can be run using following tox job:
.. code-block:: console
$ tox -e functional
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Release Notes
=============
No releases.

View File

@ -1,51 +0,0 @@
The :program:`manila` shell utility
=========================================
.. program:: manila
.. highlight:: bash
The :program:`manila` shell utility interacts with the OpenStack Manila API
from the command line. It supports the entirety of the OpenStack Manila API.
You'll need to provide :program:`manila` with your OpenStack username and API
key. You can do this with the `--os-username`, `--os-password` and
`--os-tenant-name` options, but it's easier to just set them as environment
variables by setting two environment variables:
.. envvar:: OS_USERNAME or MANILA_USERNAME
Your OpenStack Manila username.
.. envvar:: OS_PASSWORD or MANILA_PASSWORD
Your password.
.. envvar:: OS_TENANT_NAME or MANILA_PROJECT_ID
Project for work.
.. envvar:: OS_AUTH_URL or MANILA_URL
The OpenStack API server URL.
.. envvar:: OS_SHARE_API_VERSION
The OpenStack Shared Filesystems API version.
For example, in Bash you'd use::
export OS_USERNAME=foo
export OS_PASSWORD=bar
export OS_TENANT_NAME=foobarproject
export OS_AUTH_URL=http://...
export OS_SHARE_API_VERSION=1
From there, all shell commands take the form::
manila <command> [arguments...]
Run :program:`manila help` to get a full list of all possible commands,
and run :program:`manila help <command>` to get detailed help for that
command.
.. include:: manila_cli_output.rst.inc

View File

@ -1,4 +0,0 @@
To generate the sample manilaclient.conf file, run the following
command from the top level of the manilaclient directory:
tox -egenconfig

View File

@ -1,3 +0,0 @@
[DEFAULT]
output_file = etc/manilaclient/manilaclient.conf.sample
namespace = manilaclient.config

View File

@ -1,35 +0,0 @@
# Copyright 2012 OpenStack Foundation
# Copyright 2015 Chuck Fouts
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
__all__ = ['__version__']
import pbr.version
from manilaclient import api_versions
version_info = pbr.version.VersionInfo('python-manilaclient')
# We have a circular import problem when we first run python setup.py sdist
# It's harmless, so deflect it.
try:
__version__ = version_info.version_string()
except AttributeError:
__version__ = None
API_MAX_VERSION = api_versions.APIVersion(api_versions.MAX_VERSION)
API_MIN_VERSION = api_versions.APIVersion(api_versions.MIN_VERSION)
API_DEPRECATED_VERSION = api_versions.APIVersion(
api_versions.DEPRECATED_VERSION)

View File

@ -1,406 +0,0 @@
# Copyright 2015 Chuck Fouts
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import functools
import logging
import re
import warnings
import manilaclient
from manilaclient.common._i18n import _
from manilaclient.common import cliutils
from manilaclient.common import constants
from manilaclient import exceptions
from manilaclient import utils
LOG = logging.getLogger(__name__)
MAX_VERSION = '2.33'
MIN_VERSION = '2.0'
DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {}
class APIVersion(object):
"""Top level object to support Manila API Versioning.
This class represents an API Version with convenience
methods for manipulation and comparison of version
numbers that we need to do to implement microversions.
"""
TYPE_ERROR_MSG = _("'%(other)s' should be an instance of '%(cls)s'")
def __init__(self, version_str=None):
"""Create an API version object."""
self.ver_major = 0
self.ver_minor = 0
if version_str is not None:
match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)$", version_str)
if match:
self.ver_major = int(match.group(1))
self.ver_minor = int(match.group(2))
else:
msg = _("Invalid format of client version '%s'. "
"Expected format 'X.Y', where X is a major part and Y "
"is a minor part of version.") % version_str
raise exceptions.UnsupportedVersion(msg)
def __str__(self):
"""Debug/Logging representation of object."""
return ("API Version Major: %s, Minor: %s"
% (self.ver_major, self.ver_minor))
def __repr__(self):
if self.is_null():
return "<APIVersion: null>"
else:
return "<APIVersion: %s>" % self.get_string()
def __lt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) <
(other.ver_major, other.ver_minor))
def __eq__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) ==
(other.ver_major, other.ver_minor))
def __gt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) >
(other.ver_major, other.ver_minor))
def __le__(self, other):
return self < other or self == other
def __ne__(self, other):
return not self.__eq__(other)
def __ge__(self, other):
return self > other or self == other
def is_null(self):
return self.ver_major == 0 and self.ver_minor == 0
def is_latest(self):
return self == manilaclient.API_MAX_VERSION
def matches(self, min_version, max_version):
"""Determines if version is within a range.
Returns whether the version object represents a version
greater than or equal to the minimum version and less than
or equal to the maximum version.
:param min_version: Minimum acceptable version.
:param max_version: Maximum acceptable version.
:returns: boolean
If min_version is null then there is no minimum limit.
If max_version is null then there is no maximum limit.
If self is null then raise ValueError
"""
if self.is_null():
raise ValueError(_("Null APIVersion doesn't support 'matches'."))
if max_version.is_null() and min_version.is_null():
return True
elif max_version.is_null():
return min_version <= self
elif min_version.is_null():
return self <= max_version
else:
return min_version <= self <= max_version
def get_string(self):
"""String representation of an APIVersion object."""
if self.is_null():
raise ValueError(
_("Null APIVersion cannot be converted to string."))
return "%s.%s" % (self.ver_major, self.ver_minor)
def get_major_version(self):
return "%s" % self.ver_major
class VersionedMethod(object):
def __init__(self, name, start_version, end_version, func):
"""Versioning information for a single method
:param name: Name of the method
:param start_version: Minimum acceptable version
:param end_version: Maximum acceptable_version
:param func: Method to call
Minimum and maximums are inclusive
"""
self.name = name
self.start_version = start_version
self.end_version = end_version
self.func = func
def __str__(self):
return ("Version Method %s: min: %s, max: %s"
% (self.name, self.start_version, self.end_version))
def __repr__(self):
return "<VersionedMethod %s>" % self.name
def check_version_supported(api_version):
"""Returns True if the API version is supported.
:warn Sends warning if version is not supported.
"""
if (check_version_matches_min_max(api_version) or
check_version_deprecated(api_version)):
return True
return False
def check_version_matches_min_max(api_version):
"""Returns True if the API version is within the supported range."""
if (not api_version.matches(
manilaclient.API_MIN_VERSION,
manilaclient.API_MAX_VERSION)):
msg = _("Invalid client version '%(version)s'. "
"Current version range is '%(min)s' through "
" '%(max)s'") % {
"version": api_version.get_string(),
"min": manilaclient.API_MIN_VERSION.get_string(),
"max": manilaclient.API_MAX_VERSION.get_string()}
warnings.warn(msg)
return False
return True
def check_version_deprecated(api_version):
"""Returns True if API version is deprecated."""
if api_version == manilaclient.API_DEPRECATED_VERSION:
msg = _("Client version '%(version)s' is deprecated.") % {
"version": api_version.get_string()}
warnings.warn(msg)
return True
return False
def get_api_version(version_string):
"""Returns checked APIVersion object."""
version_string = str(version_string)
api_version = APIVersion(version_string)
check_version_supported(api_version)
return api_version
def _get_server_version_range(client):
"""Obtain version range from server."""
response = client.services.server_api_version('')
server_version = None
for resource in response:
if hasattr(resource, 'version'):
if resource.status == "CURRENT":
server_version = resource
break
if not hasattr(server_version, 'version') or not server_version.version:
return APIVersion(), APIVersion()
min_version = APIVersion(server_version.min_version)
max_version = APIVersion(server_version.version)
return min_version, max_version
def discover_version(client, requested_version):
"""Discovers the most recent version for client and API.
Checks 'requested_version' and returns the most recent version
supported by both the API and the client. If there is not a supported
version then an UnsupportedVersion exception is thrown.
:param client: client object
:param requested_version: requested version represented by APIVersion obj
:returns: APIVersion
"""
server_start_version, server_end_version = _get_server_version_range(
client)
valid_version = requested_version
if server_start_version.is_null() and server_end_version.is_null():
msg = ("Server does not support microversions. Changing server "
"version to %(min_version)s.")
LOG.debug(msg, {"min_version": DEPRECATED_VERSION})
valid_version = APIVersion(DEPRECATED_VERSION)
else:
valid_version = _validate_requested_version(
requested_version,
server_start_version,
server_end_version)
_validate_server_version(server_start_version, server_end_version)
return valid_version
def _validate_requested_version(requested_version,
server_start_version,
server_end_version):
"""Validates the requested version.
Checks 'requested_version' is within the min/max range supported by the
server. If 'requested_version' is not within range then attempts to
downgrade to 'server_end_version'. Otherwise an UnsupportedVersion
exception is thrown.
:param requested_version: requestedversion represented by APIVersion obj
:param server_start_version: APIVersion object representing server min
:param server_end_version: APIVersion object representing server max
"""
valid_version = requested_version
if not requested_version.matches(server_start_version, server_end_version):
if server_end_version <= requested_version:
if (manilaclient.API_MIN_VERSION <= server_end_version and
server_end_version <= manilaclient.API_MAX_VERSION):
msg = _("Requested version %(requested_version)s is "
"not supported. Downgrading requested version "
"to %(server_end_version)s.")
LOG.debug(msg, {
"requested_version": requested_version,
"server_end_version": server_end_version})
valid_version = server_end_version
else:
raise exceptions.UnsupportedVersion(
_("The specified version isn't supported by server. The valid "
"version range is '%(min)s' to '%(max)s'") % {
"min": server_start_version.get_string(),
"max": server_end_version.get_string()})
return valid_version
def _validate_server_version(server_start_version, server_end_version):
"""Validates the server version.
Checks that the 'server_end_version' is greater than the minimum version
supported by the client. Then checks that the 'server_start_version' is
less than the maximum version supported by the client.
:param server_start_version:
:param server_end_version:
:return:
"""
if manilaclient.API_MIN_VERSION > server_end_version:
raise exceptions.UnsupportedVersion(
_("Server's version is too old. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'.") % {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string()})
elif manilaclient.API_MAX_VERSION < server_start_version:
raise exceptions.UnsupportedVersion(
_("Server's version is too new. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'.") % {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string()})
def add_versioned_method(versioned_method):
_VERSIONED_METHOD_MAP.setdefault(versioned_method.name, [])
_VERSIONED_METHOD_MAP[versioned_method.name].append(versioned_method)
def get_versioned_methods(func_name, api_version=None):
versioned_methods = _VERSIONED_METHOD_MAP.get(func_name, [])
if api_version and not api_version.is_null():
return [m for m in versioned_methods
if api_version.matches(m.start_version, m.end_version)]
return versioned_methods
def experimental_api(f):
"""Adds to HTTP Header to indicate this is an experimental API call."""
@functools.wraps(f)
def _wrapper(*args, **kwargs):
client = args[0]
if (isinstance(client, manilaclient.v2.client.Client) or
hasattr(client, 'client')):
dh = client.client.default_headers
dh[constants.EXPERIMENTAL_HTTP_HEADER] = 'true'
return f(*args, **kwargs)
return _wrapper
def wraps(start_version, end_version=MAX_VERSION):
"""Annotation used to return the correct method based on requested version.
Creates a VersionedMethod based on data from the method using the 'wraps'
annotation. The VersionedMethod is stored in the _VERSIONED_METHOD_MAP.
Also, adds a 'substitution' method that is used to look up the latest
method matching the start_version.
:param start_version: String obj representing first supported version.
:param end_version: String obj representing last supported version.
"""
start_version = APIVersion(start_version)
end_version = APIVersion(end_version)
def decor(func):
func.versioned = True
name = utils.get_function_name(func)
versioned_method = VersionedMethod(name, start_version,
end_version, func)
add_versioned_method(versioned_method)
@functools.wraps(func)
def substitution(obj, *args, **kwargs):
methods = get_versioned_methods(name, obj.api_version)
if not methods:
raise exceptions.UnsupportedVersion(
_("API version '%(version)s' is not supported on "
"'%(method)s' method.") % {
"version": obj.api_version.get_string(),
"method": name,
})
method = max(methods, key=lambda f: f.start_version)
return method.func(obj, *args, **kwargs)
if hasattr(func, 'arguments'):
for cli_args, cli_kwargs in func.arguments:
cliutils.add_arg(substitution, *cli_args, **cli_kwargs)
return substitution
return decor

View File

@ -1,222 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Base utilities to build API operation managers and objects on top of.
"""
import contextlib
import hashlib
import os
from six.moves.urllib import parse
from manilaclient.common import cliutils
from manilaclient import exceptions
from manilaclient import utils
# Python 2.4 compat
try:
all
except NameError:
def all(iterable):
return True not in (not x for x in iterable)
class Manager(utils.HookableMixin):
"""Manager for CRUD operations.
Managers interact with a particular type of API (shares, snapshots,
etc.) and provide CRUD operations for them.
"""
resource_class = None
def __init__(self, api):
self.api = api
self.client = api.client
@property
def api_version(self):
return self.api.api_version
def _list(self, url, response_key, obj_class=None, body=None):
resp = None
if body:
resp, body = self.api.client.post(url, body=body)
else:
resp, body = self.api.client.get(url)
if obj_class is None:
obj_class = self.resource_class
data = body[response_key]
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
# unlike other services which just return the list...
if isinstance(data, dict):
try:
data = data['values']
except KeyError:
pass
with self.completion_cache('human_id', obj_class, mode="w"):
with self.completion_cache('uuid', obj_class, mode="w"):
return [obj_class(self, res, loaded=True)
for res in data if res]
@contextlib.contextmanager
def completion_cache(self, cache_type, obj_class, mode):
"""Bash autocompletion items storage.
The completion cache store items that can be used for bash
autocompletion, like UUIDs or human-friendly IDs.
A resource listing will clear and repopulate the cache.
A resource create will append to the cache.
Delete is not handled because listings are assumed to be performed
often enough to keep the cache reasonably up-to-date.
"""
base_dir = cliutils.env('manilaclient_UUID_CACHE_DIR',
'MANILACLIENT_UUID_CACHE_DIR',
default="~/.manilaclient")
# NOTE(sirp): Keep separate UUID caches for each username + endpoint
# pair
username = cliutils.env('OS_USERNAME', 'MANILA_USERNAME')
url = cliutils.env('OS_URL', 'MANILA_URL')
uniqifier = hashlib.md5(username.encode('utf-8') +
url.encode('utf-8')).hexdigest()
cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier))
try:
os.makedirs(cache_dir, 0o755)
except OSError:
# NOTE(kiall): This is typically either permission denied while
# attempting to create the directory, or the directory
# already exists. Either way, don't fail.
pass
resource = obj_class.__name__.lower()
filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-'))
path = os.path.join(cache_dir, filename)
cache_attr = "_%s_cache" % cache_type
try:
setattr(self, cache_attr, open(path, mode))
except IOError:
# NOTE(kiall): This is typically a permission denied while
# attempting to write the cache file.
pass
try:
yield
finally:
cache = getattr(self, cache_attr, None)
if cache:
cache.close()
delattr(self, cache_attr)
def write_to_completion_cache(self, cache_type, val):
cache = getattr(self, "_%s_cache" % cache_type, None)
if cache:
cache.write("%s\n" % val)
def _get(self, url, response_key=None):
resp, body = self.api.client.get(url)
if response_key:
return self.resource_class(self, body[response_key], loaded=True)
else:
return self.resource_class(self, body, loaded=True)
def _get_with_base_url(self, url, response_key=None):
resp, body = self.api.client.get_with_base_url(url)
if response_key:
return [self.resource_class(self, res, loaded=True)
for res in body[response_key] if res]
else:
return self.resource_class(self, body, loaded=True)
def _create(self, url, body, response_key, return_raw=False, **kwargs):
self.run_hooks('modify_body_for_create', body, **kwargs)
resp, body = self.api.client.post(url, body=body)
if return_raw:
return body[response_key]
with self.completion_cache('human_id', self.resource_class, mode="a"):
with self.completion_cache('uuid', self.resource_class, mode="a"):
return self.resource_class(self, body[response_key])
def _delete(self, url):
resp, body = self.api.client.delete(url)
def _update(self, url, body, response_key=None, **kwargs):
self.run_hooks('modify_body_for_update', body, **kwargs)
resp, body = self.api.client.put(url, body=body)
if body:
if response_key:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
def _build_query_string(self, search_opts):
q_string = parse.urlencode(
sorted([(k, v) for (k, v) in search_opts.items() if v]))
return "?%s" % q_string if q_string else q_string
class ManagerWithFind(Manager):
"""Like a `Manager`, but with additional `find()`/`findall()` methods."""
def find(self, **kwargs):
"""Find a single item with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
matches = self.findall(**kwargs)
num_matches = len(matches)
if num_matches == 0:
msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
raise exceptions.NotFound(404, msg)
elif num_matches > 1:
raise exceptions.NoUniqueMatch
else:
return matches[0]
def findall(self, **kwargs):
"""Find all items with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
found = []
searches = list(kwargs.items())
for obj in self.list():
try:
if all(getattr(obj, attr) == value
for (attr, value) in searches):
found.append(obj)
except AttributeError:
continue
return found
def list(self):
raise NotImplementedError

View File

@ -1,69 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Piston Cloud Computing, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
OpenStack Client interface. Handles the REST calls and responses.
"""
from oslo_utils import importutils
from manilaclient import api_versions
from manilaclient import exceptions
def get_client_class(version):
version_map = {
'1': 'manilaclient.v1.client.Client',
'2': 'manilaclient.v2.client.Client',
}
try:
client_path = version_map[str(version)]
except (KeyError, ValueError):
msg = "Invalid client version '%s'. must be one of: %s" % (
(version, ', '.join(version_map)))
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
def Client(client_version, *args, **kwargs):
def _convert_to_api_version(version):
"""Convert version to an APIVersion object unless it already is one."""
if hasattr(version, 'get_major_version'):
api_version = version
else:
if version in ('1', '1.0'):
api_version = api_versions.APIVersion(
api_versions.DEPRECATED_VERSION)
elif version == '2':
api_version = api_versions.APIVersion(api_versions.MIN_VERSION)
else:
api_version = api_versions.APIVersion(version)
return api_version
api_version = _convert_to_api_version(client_version)
client_class = get_client_class(api_version.get_major_version())
# Make sure the kwarg api_version is set with an APIVersion object.
# 1st choice is to use the incoming kwarg. 2nd choice is the positional.
kwargs['api_version'] = _convert_to_api_version(
kwargs.get('api_version', api_version))
return client_class(*args, **kwargs)

View File

@ -1,30 +0,0 @@
# 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.
"""oslo.i18n integration module.
See https://docs.openstack.org/oslo.i18n/latest/user/usage.html
"""
import oslo_i18n
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
# application name when this module is synced into the separate
# repository. It is OK to have more than one translation function
# using the same domain, since there will still only be one message
# catalog.
_translators = oslo_i18n.TranslatorFactory(domain='manilaclient')
# The primary translation function using the well-known name "_"
_ = _translators.primary

View File

@ -1,217 +0,0 @@
# Copyright 2013 OpenStack Foundation
# Copyright 2013 Spanish National Research Council.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# E0202: An attribute inherited from %s hide this method
# pylint: disable=E0202
import abc
import argparse
import os
import six
from stevedore import extension
from manilaclient.common.apiclient import exceptions
from manilaclient.common import constants
_discovered_plugins = {}
def discover_auth_systems():
"""Discover the available auth-systems.
This won't take into account the old style auth-systems.
"""
global _discovered_plugins
_discovered_plugins = {}
def add_plugin(ext):
_discovered_plugins[ext.name] = ext.plugin
mgr = extension.ExtensionManager(constants.EXTENSION_PLUGIN_NAMESPACE)
mgr.map(add_plugin)
def load_auth_system_opts(parser):
"""Load options needed by the available auth-systems into a parser.
This function will try to populate the parser with options from the
available plugins.
"""
group = parser.add_argument_group("Common auth options")
BaseAuthPlugin.add_common_opts(group)
for name, auth_plugin in _discovered_plugins.items():
group = parser.add_argument_group(
"Auth-system '%s' options" % name,
conflict_handler="resolve")
auth_plugin.add_opts(group)
def load_plugin(auth_system):
try:
plugin_class = _discovered_plugins[auth_system]
except KeyError:
raise exceptions.AuthSystemNotFound(auth_system)
return plugin_class(auth_system=auth_system)
def load_plugin_from_args(args):
"""Load required plugin and populate it with options.
Try to guess auth system if it is not specified. Systems are tried in
alphabetical order.
:type args: argparse.Namespace
:raises: AuthPluginOptionsMissing
"""
auth_system = args.os_auth_system
if auth_system:
plugin = load_plugin(auth_system)
plugin.parse_opts(args)
plugin.sufficient_options()
return plugin
for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)):
plugin_class = _discovered_plugins[plugin_auth_system]
plugin = plugin_class()
plugin.parse_opts(args)
try:
plugin.sufficient_options()
except exceptions.AuthPluginOptionsMissing:
continue
return plugin
raise exceptions.AuthPluginOptionsMissing(["auth_system"])
@six.add_metaclass(abc.ABCMeta)
class BaseAuthPlugin(object):
"""Base class for authentication plugins.
An authentication plugin needs to override at least the authenticate
method to be a valid plugin.
"""
auth_system = None
opt_names = []
common_opt_names = [
"auth_system",
"username",
"password",
"tenant_name",
"token",
"auth_url",
]
def __init__(self, auth_system=None, **kwargs):
self.auth_system = auth_system or self.auth_system
self.opts = dict((name, kwargs.get(name))
for name in self.opt_names)
@staticmethod
def _parser_add_opt(parser, opt):
"""Add an option to parser in two variants.
:param opt: option name (with underscores)
"""
dashed_opt = opt.replace("_", "-")
env_var = "OS_%s" % opt.upper()
arg_default = os.environ.get(env_var, "")
arg_help = "Defaults to env[%s]." % env_var
parser.add_argument(
"--os-%s" % dashed_opt,
metavar="<%s>" % dashed_opt,
default=arg_default,
help=arg_help)
parser.add_argument(
"--os_%s" % opt,
metavar="<%s>" % dashed_opt,
help=argparse.SUPPRESS)
@classmethod
def add_opts(cls, parser):
"""Populate the parser with the options for this plugin."""
for opt in cls.opt_names:
# use `BaseAuthPlugin.common_opt_names` since it is never
# changed in child classes
if opt not in BaseAuthPlugin.common_opt_names:
cls._parser_add_opt(parser, opt)
@classmethod
def add_common_opts(cls, parser):
"""Add options that are common for several plugins."""
for opt in cls.common_opt_names:
cls._parser_add_opt(parser, opt)
@staticmethod
def get_opt(opt_name, args):
"""Return option name and value.
:param opt_name: name of the option, e.g., "username"
:param args: parsed arguments
"""
return (opt_name, getattr(args, "os_%s" % opt_name, None))
def parse_opts(self, args):
"""Parse the actual auth-system options if any.
This method is expected to populate the attribute `self.opts` with a
dict containing the options and values needed to make authentication.
"""
self.opts.update(dict(self.get_opt(opt_name, args)
for opt_name in self.opt_names))
def authenticate(self, http_client):
"""Authenticate using plugin defined method.
The method usually analyses `self.opts` and performs
a request to authentication server.
:param http_client: client object that needs authentication
:type http_client: HTTPClient
:raises: AuthorizationFailure
"""
self.sufficient_options()
self._do_authenticate(http_client)
@abc.abstractmethod
def _do_authenticate(self, http_client):
"""Protected method for authentication."""
def sufficient_options(self):
"""Check if all required options are present.
:raises: AuthPluginOptionsMissing
"""
missing = [opt
for opt in self.opt_names
if not self.opts.get(opt)]
if missing:
raise exceptions.AuthPluginOptionsMissing(missing)
@abc.abstractmethod
def token_and_endpoint(self, endpoint_type, service_type):
"""Return token and endpoint.
:param service_type: Service type of the endpoint
:type service_type: string
:param endpoint_type: Type of endpoint.
Possible values: public or publicURL,
internal or internalURL,
admin or adminURL
:type endpoint_type: string
:returns: tuple of token and endpoint strings
:raises: EndpointException
"""

View File

@ -1,518 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2012 Grid Dynamics
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Base utilities to build API operation managers and objects on top of.
"""
# E1102: %s is not callable
# pylint: disable=E1102
import abc
import copy
from oslo_utils import strutils
import six
from six.moves.urllib import parse
from manilaclient.common._i18n import _
from manilaclient.common.apiclient import exceptions
def getid(obj):
"""Return id if argument is a Resource.
Abstracts the common pattern of allowing both an object or an object's ID
(UUID) as a parameter when dealing with relationships.
"""
try:
if obj.uuid:
return obj.uuid
except AttributeError:
pass
try:
return obj.id
except AttributeError:
return obj
# TODO(aababilov): call run_hooks() in HookableMixin's child classes
class HookableMixin(object):
"""Mixin so classes can register and run hooks."""
_hooks_map = {}
@classmethod
def add_hook(cls, hook_type, hook_func):
"""Add a new hook of specified type.
:param cls: class that registers hooks
:param hook_type: hook type, e.g., '__pre_parse_args__'
:param hook_func: hook function
"""
if hook_type not in cls._hooks_map:
cls._hooks_map[hook_type] = []
cls._hooks_map[hook_type].append(hook_func)
@classmethod
def run_hooks(cls, hook_type, *args, **kwargs):
"""Run all hooks of specified type.
:param cls: class that registers hooks
:param hook_type: hook type, e.g., '__pre_parse_args__'
:param args: args to be passed to every hook function
:param kwargs: kwargs to be passed to every hook function
"""
hook_funcs = cls._hooks_map.get(hook_type) or []
for hook_func in hook_funcs:
hook_func(*args, **kwargs)
class BaseManager(HookableMixin):
"""Basic manager type providing common operations.
Managers interact with a particular type of API (servers, flavors, images,
etc.) and provide CRUD operations for them.
"""
resource_class = None
def __init__(self, client):
"""Initializes BaseManager with `client`.
:param client: instance of BaseClient descendant for HTTP requests
"""
super(BaseManager, self).__init__()
self.client = client
def _list(self, url, response_key=None, obj_class=None, json=None):
"""List the collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
:param obj_class: class for constructing the returned objects
(self.resource_class will be used by default)
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
"""
if json:
body = self.client.post(url, json=json).json()
else:
body = self.client.get(url).json()
if obj_class is None:
obj_class = self.resource_class
data = body[response_key] if response_key is not None else body
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
# unlike other services which just return the list...
try:
data = data['values']
except (KeyError, TypeError):
pass
return [obj_class(self, res, loaded=True) for res in data if res]
def _get(self, url, response_key=None):
"""Get an object from collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'. If response_key is None - all response body
will be used.
"""
body = self.client.get(url).json()
data = body[response_key] if response_key is not None else body
return self.resource_class(self, data, loaded=True)
def _head(self, url):
"""Retrieve request headers for an object.
:param url: a partial URL, e.g., '/servers'
"""
resp = self.client.head(url)
return resp.status_code == 204
def _post(self, url, json, response_key=None, return_raw=False):
"""Create an object.
:param url: a partial URL, e.g., '/servers'
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'. If response_key is None - all response body
will be used.
:param return_raw: flag to force returning raw JSON instead of
Python object of self.resource_class
"""
body = self.client.post(url, json=json).json()
data = body[response_key] if response_key is not None else body
if return_raw:
return data
return self.resource_class(self, data)
def _put(self, url, json=None, response_key=None):
"""Update an object with PUT method.
:param url: a partial URL, e.g., '/servers'
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
"""
resp = self.client.put(url, json=json)
# PUT requests may not return a body
if resp.content:
body = resp.json()
if response_key is not None:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
def _patch(self, url, json=None, response_key=None):
"""Update an object with PATCH method.
:param url: a partial URL, e.g., '/servers'
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
"""
body = self.client.patch(url, json=json).json()
if response_key is not None:
return self.resource_class(self, body[response_key])
else:
return self.resource_class(self, body)
def _delete(self, url):
"""Delete an object.
:param url: a partial URL, e.g., '/servers/my-server'
"""
return self.client.delete(url)
@six.add_metaclass(abc.ABCMeta)
class ManagerWithFind(BaseManager):
"""Manager with additional `find()`/`findall()` methods."""
@abc.abstractmethod
def list(self):
pass
def find(self, **kwargs):
"""Find a single item with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
matches = self.findall(**kwargs)
num_matches = len(matches)
if num_matches == 0:
msg = _("No %(name)s matching %(args)s.") % {
'name': self.resource_class.__name__,
'args': kwargs
}
raise exceptions.NotFound(msg)
elif num_matches > 1:
raise exceptions.NoUniqueMatch()
else:
return matches[0]
def findall(self, **kwargs):
"""Find all items with attributes matching ``**kwargs``.
This isn't very efficient: it loads the entire list then filters on
the Python side.
"""
found = []
searches = kwargs.items()
for obj in self.list():
try:
if all(getattr(obj, attr) == value
for (attr, value) in searches):
found.append(obj)
except AttributeError:
continue
return found
class CrudManager(BaseManager):
"""Base manager class for manipulating entities.
Children of this class are expected to define a `collection_key` and `key`.
- `collection_key`: Usually a plural noun by convention (e.g. `entities`);
used to refer collections in both URL's (e.g. `/v3/entities`) and JSON
objects containing a list of member resources (e.g. `{'entities': [{},
{}, {}]}`).
- `key`: Usually a singular noun by convention (e.g. `entity`); used to
refer to an individual member of the collection.
"""
collection_key = None
key = None
def build_url(self, base_url=None, **kwargs):
"""Builds a resource URL for the given kwargs.
Given an example collection where `collection_key = 'entities'` and
`key = 'entity'`, the following URL's could be generated.
By default, the URL will represent a collection of entities, e.g.::
/entities
If kwargs contains an `entity_id`, then the URL will represent a
specific member, e.g.::
/entities/{entity_id}
:param base_url: if provided, the generated URL will be appended to it
"""
url = base_url if base_url is not None else ''
url += '/%s' % self.collection_key
# do we have a specific entity?
entity_id = kwargs.get('%s_id' % self.key)
if entity_id is not None:
url += '/%s' % entity_id
return url
def _filter_kwargs(self, kwargs):
"""Drop null values and handle ids."""
for key, ref in kwargs.copy().items():
if ref is None:
kwargs.pop(key)
else:
if isinstance(ref, Resource):
kwargs.pop(key)
kwargs['%s_id' % key] = getid(ref)
return kwargs
def create(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._post(
self.build_url(**kwargs),
{self.key: kwargs},
self.key)
def get(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._get(
self.build_url(**kwargs),
self.key)
def head(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._head(self.build_url(**kwargs))
def list(self, base_url=None, **kwargs):
"""List the collection.
:param base_url: if provided, the generated URL will be appended to it
"""
kwargs = self._filter_kwargs(kwargs)
return self._list(
'%(base_url)s%(query)s' % {
'base_url': self.build_url(base_url=base_url, **kwargs),
'query': '?%s' % parse.urlencode(kwargs) if kwargs else '',
},
self.collection_key)
def put(self, base_url=None, **kwargs):
"""Update an element.
:param base_url: if provided, the generated URL will be appended to it
"""
kwargs = self._filter_kwargs(kwargs)
return self._put(self.build_url(base_url=base_url, **kwargs))
def update(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
params = kwargs.copy()
params.pop('%s_id' % self.key)
return self._patch(
self.build_url(**kwargs),
{self.key: params},
self.key)
def delete(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._delete(
self.build_url(**kwargs))
def find(self, base_url=None, **kwargs):
"""Find a single item with attributes matching ``**kwargs``.
:param base_url: if provided, the generated URL will be appended to it
"""
kwargs = self._filter_kwargs(kwargs)
rl = self._list(
'%(base_url)s%(query)s' % {
'base_url': self.build_url(base_url=base_url, **kwargs),
'query': '?%s' % parse.urlencode(kwargs) if kwargs else '',
},
self.collection_key)
num = len(rl)
if num == 0:
msg = _("No %(name)s matching %(args)s.") % {
'name': self.resource_class.__name__,
'args': kwargs
}
raise exceptions.NotFound(404, msg)
elif num > 1:
raise exceptions.NoUniqueMatch
else:
return rl[0]
class Extension(HookableMixin):
"""Extension descriptor."""
SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__')
manager_class = None
def __init__(self, name, module):
super(Extension, self).__init__()
self.name = name
self.module = module
self._parse_extension_module()
def _parse_extension_module(self):
self.manager_class = None
for attr_name, attr_value in self.module.__dict__.items():
if attr_name in self.SUPPORTED_HOOKS:
self.add_hook(attr_name, attr_value)
else:
try:
if issubclass(attr_value, BaseManager):
self.manager_class = attr_value
except TypeError:
pass
def __repr__(self):
return "<Extension '%s'>" % self.name
class Resource(object):
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
"""
HUMAN_ID = False
NAME_ATTR = 'name'
def __init__(self, manager, info, loaded=False):
"""Populate and bind to a manager.
:param manager: BaseManager object
:param info: dictionary representing resource attributes
:param loaded: prevent lazy-loading if set to True
"""
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def __repr__(self):
reprkeys = sorted(k
for k in self.__dict__.keys()
if k[0] != '_' and k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
@property
def human_id(self):
"""Human-readable ID which can be used for bash completion."""
if self.HUMAN_ID:
name = getattr(self, self.NAME_ATTR, None)
if name is not None:
return strutils.to_slug(name)
return None
def _add_details(self, info):
for (k, v) in info.items():
try:
setattr(self, k, v)
self._info[k] = v
except AttributeError:
# In this case we already defined the attribute on the class
pass
def __getattr__(self, k):
if k not in self.__dict__:
# NOTE(bcwaldon): disallow lazy-loading if already loaded once
if not self.is_loaded():
self.get()
return self.__getattr__(k)
raise AttributeError(k)
else:
return self.__dict__[k]
def get(self):
"""Support for lazy loading details.
Some clients, such as novaclient have the option to lazy load the
details, details which can be loaded with this function.
"""
# set_loaded() first ... so if we have to bail, we know we tried.
self.set_loaded(True)
if not hasattr(self.manager, 'get'):
return
new = self.manager.get(self.id)
if new:
self._add_details(new._info)
def __eq__(self, other):
if not isinstance(other, Resource):
return NotImplemented
# two resources of different types are not equal
if not isinstance(other, self.__class__):
return False
if hasattr(self, 'id') and hasattr(other, 'id'):
return self.id == other.id
return self._info == other._info
def __ne__(self, other):
return not self == other
def is_loaded(self):
return self._loaded
def set_loaded(self, val):
self._loaded = val
def to_dict(self):
return copy.deepcopy(self._info)

View File

@ -1,367 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Piston Cloud Computing, Inc.
# Copyright 2013 Alessio Ababilov
# Copyright 2013 Grid Dynamics
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
OpenStack Client interface. Handles the REST calls and responses.
"""
# E0202: An attribute inherited from %s hide this method
# pylint: disable=E0202
import logging
import time
try:
import simplejson as json
except ImportError:
import json
from oslo_utils import importutils
import requests
from manilaclient.common._i18n import _
from manilaclient.common.apiclient import exceptions
_logger = logging.getLogger(__name__)
class HTTPClient(object):
"""This client handles sending HTTP requests to OpenStack servers.
Features:
- share authentication information between several clients to different
services (e.g., for compute and image clients);
- reissue authentication request for expired tokens;
- encode/decode JSON bodies;
- raise exceptions on HTTP errors;
- pluggable authentication;
- store authentication information in a keyring;
- store time spent for requests;
- register clients for particular services, so one can use
`http_client.identity` or `http_client.compute`;
- log requests and responses in a format that is easy to copy-and-paste
into terminal and send the same request with curl.
"""
user_agent = "manilaclient.common.apiclient"
def __init__(self,
auth_plugin,
region_name=None,
endpoint_type="publicURL",
original_ip=None,
verify=True,
cert=None,
timeout=None,
timings=False,
keyring_saver=None,
debug=False,
user_agent=None,
http=None):
self.auth_plugin = auth_plugin
self.endpoint_type = endpoint_type
self.region_name = region_name
self.original_ip = original_ip
self.timeout = timeout
self.verify = verify
self.cert = cert
self.keyring_saver = keyring_saver
self.debug = debug
self.user_agent = user_agent or self.user_agent
self.times = [] # [("item", starttime, endtime), ...]
self.timings = timings
# requests within the same session can reuse TCP connections from pool
self.http = http or requests.Session()
self.cached_token = None
def _http_log_req(self, method, url, kwargs):
if not self.debug:
return
string_parts = [
"curl -i",
"-X '%s'" % method,
"'%s'" % url,
]
for element in kwargs['headers']:
header = "-H '%s: %s'" % (element, kwargs['headers'][element])
string_parts.append(header)
_logger.debug("REQ: %s", " ".join(string_parts))
if 'data' in kwargs:
_logger.debug("REQ BODY: %s\n", (kwargs['data']))
def _http_log_resp(self, resp):
if not self.debug:
return
_logger.debug(
"RESP: [%s] %s\n",
resp.status_code,
resp.headers)
if resp._content_consumed:
_logger.debug(
"RESP BODY: %s\n",
resp.text)
def serialize(self, kwargs):
if kwargs.get('json') is not None:
kwargs['headers']['Content-Type'] = 'application/json'
kwargs['data'] = json.dumps(kwargs['json'])
try:
del kwargs['json']
except KeyError:
pass
def get_timings(self):
return self.times
def reset_timings(self):
self.times = []
def request(self, method, url, **kwargs):
"""Send an http request with the specified characteristics.
Wrapper around `requests.Session.request` to handle tasks such as
setting headers, JSON encoding/decoding, and error handling.
:param method: method of HTTP request
:param url: URL of HTTP request
:param kwargs: any other parameter that can be passed to
requests.Session.request (such as `headers`) or `json`
that will be encoded as JSON and used as `data` argument
"""
kwargs.setdefault("headers", {})
kwargs["headers"]["User-Agent"] = self.user_agent
if self.original_ip:
kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
self.original_ip, self.user_agent)
if self.timeout is not None:
kwargs.setdefault("timeout", self.timeout)
kwargs.setdefault("verify", self.verify)
if self.cert is not None:
kwargs.setdefault("cert", self.cert)
self.serialize(kwargs)
self._http_log_req(method, url, kwargs)
if self.timings:
start_time = time.time()
resp = self.http.request(method, url, **kwargs)
if self.timings:
self.times.append(("%s %s" % (method, url),
start_time, time.time()))
self._http_log_resp(resp)
if resp.status_code >= 400:
_logger.debug(
"Request returned failure status: %s",
resp.status_code)
raise exceptions.from_response(resp, method, url)
return resp
@staticmethod
def concat_url(endpoint, url):
"""Concatenate endpoint and final URL.
E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to
"http://keystone/v2.0/tokens".
:param endpoint: the base URL
:param url: the final URL
"""
return "%s/%s" % (endpoint.rstrip("/"), url.strip("/"))
def client_request(self, client, method, url, **kwargs):
"""Send an http request using `client`'s endpoint and specified `url`.
If request was rejected as unauthorized (possibly because the token is
expired), issue one authorization attempt and send the request once
again.
:param client: instance of BaseClient descendant
:param method: method of HTTP request
:param url: URL of HTTP request
:param kwargs: any other parameter that can be passed to
`HTTPClient.request`
"""
filter_args = {
"endpoint_type": client.endpoint_type or self.endpoint_type,
"service_type": client.service_type,
}
token, endpoint = (self.cached_token, client.cached_endpoint)
just_authenticated = False
if not (token and endpoint):
try:
token, endpoint = self.auth_plugin.token_and_endpoint(
**filter_args)
except exceptions.EndpointException:
pass
if not (token and endpoint):
self.authenticate()
just_authenticated = True
token, endpoint = self.auth_plugin.token_and_endpoint(
**filter_args)
if not (token and endpoint):
raise exceptions.AuthorizationFailure(
_("Cannot find endpoint or token for request"))
old_token_endpoint = (token, endpoint)
kwargs.setdefault("headers", {})["X-Auth-Token"] = token
self.cached_token = token
client.cached_endpoint = endpoint
# Perform the request once. If we get Unauthorized, then it
# might be because the auth token expired, so try to
# re-authenticate and try again. If it still fails, bail.
try:
return self.request(
method, self.concat_url(endpoint, url), **kwargs)
except exceptions.Unauthorized as unauth_ex:
if just_authenticated:
raise
self.cached_token = None
client.cached_endpoint = None
if self.auth_plugin.opts.get('token'):
self.auth_plugin.opts['token'] = None
if self.auth_plugin.opts.get('endpoint'):
self.auth_plugin.opts['endpoint'] = None
self.authenticate()
try:
token, endpoint = self.auth_plugin.token_and_endpoint(
**filter_args)
except exceptions.EndpointException:
raise unauth_ex
if (not (token and endpoint) or
old_token_endpoint == (token, endpoint)):
raise unauth_ex
self.cached_token = token
client.cached_endpoint = endpoint
kwargs["headers"]["X-Auth-Token"] = token
return self.request(
method, self.concat_url(endpoint, url), **kwargs)
def add_client(self, base_client_instance):
"""Add a new instance of :class:`BaseClient` descendant.
`self` will store a reference to `base_client_instance`.
Example:
>>> def test_clients():
... from keystoneclient.auth import keystone
... from manilaclient.common.apiclient import client
... auth = keystone.KeystoneAuthPlugin(
... username="user", password="pass", tenant_name="tenant",
... auth_url="http://auth:5000/v2.0")
... openstack_client = client.HTTPClient(auth)
... # create nova client
... from novaclient.v1_1 import client
... client.Client(openstack_client)
... # create keystone client
... from keystoneclient.v2_0 import client
... client.Client(openstack_client)
... # use them
... openstack_client.identity.tenants.list()
... openstack_client.compute.servers.list()
"""
service_type = base_client_instance.service_type
if service_type and not hasattr(self, service_type):
setattr(self, service_type, base_client_instance)
def authenticate(self):
self.auth_plugin.authenticate(self)
# Store the authentication results in the keyring for later requests
if self.keyring_saver:
self.keyring_saver.save(self)
class BaseClient(object):
"""Top-level object to access the OpenStack API.
This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient`
will handle a bunch of issues such as authentication.
"""
service_type = None
endpoint_type = None # "publicURL" will be used
cached_endpoint = None
def __init__(self, http_client, extensions=None):
self.http_client = http_client
http_client.add_client(self)
# Add in any extensions...
if extensions:
for extension in extensions:
if extension.manager_class:
setattr(self, extension.name,
extension.manager_class(self))
def client_request(self, method, url, **kwargs):
return self.http_client.client_request(
self, method, url, **kwargs)
def head(self, url, **kwargs):
return self.client_request("HEAD", url, **kwargs)
def get(self, url, **kwargs):
return self.client_request("GET", url, **kwargs)
def post(self, url, **kwargs):
return self.client_request("POST", url, **kwargs)
def put(self, url, **kwargs):
return self.client_request("PUT", url, **kwargs)
def delete(self, url, **kwargs):
return self.client_request("DELETE", url, **kwargs)
def patch(self, url, **kwargs):
return self.client_request("PATCH", url, **kwargs)
@staticmethod
def get_class(api_name, version, version_map):
"""Returns the client class for the requested API version
:param api_name: the name of the API, e.g. 'compute', 'image', etc
:param version: the requested API version
:param version_map: a dict of client classes keyed by version
:rtype: a client class for the requested API version
"""
try:
client_path = version_map[str(version)]
except (KeyError, ValueError):
msg = _("Invalid %(api_name)s client version '%(version)s'. "
"Must be one of: %(version_map)s") % {
'api_name': api_name,
'version': version,
'version_map': ', '.join(version_map.keys())}
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)

View File

@ -1,477 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 Nebula, Inc.
# Copyright 2013 Alessio Ababilov
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Exception definitions.
"""
########################################################################
#
# THIS MODULE IS DEPRECATED
#
# Please refer to
# https://etherpad.openstack.org/p/kilo-oslo-library-proposals for
# the discussion leading to this deprecation.
#
# We recommend checking out the python-openstacksdk project
# (https://launchpad.net/python-openstacksdk) instead.
#
########################################################################
import inspect
import sys
import six
from manilaclient.common._i18n import _
class ClientException(Exception):
"""The base exception class for all exceptions this library raises."""
pass
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
class UnsupportedVersion(ClientException):
"""User is trying to use an unsupported version of the API."""
pass
class CommandError(ClientException):
"""Error in CLI tool."""
pass
class AuthorizationFailure(ClientException):
"""Cannot authorize API client."""
pass
class ConnectionError(ClientException):
"""Cannot connect to API service."""
pass
class ConnectionRefused(ConnectionError):
"""Connection refused while trying to connect to API service."""
pass
class AuthPluginOptionsMissing(AuthorizationFailure):
"""Auth plugin misses some options."""
def __init__(self, opt_names):
super(AuthPluginOptionsMissing, self).__init__(
_("Authentication failed. Missing options: %s") %
", ".join(opt_names))
self.opt_names = opt_names
class AuthSystemNotFound(AuthorizationFailure):
"""User has specified an AuthSystem that is not installed."""
def __init__(self, auth_system):
super(AuthSystemNotFound, self).__init__(
_("AuthSystemNotFound: %r") % auth_system)
self.auth_system = auth_system
class NoUniqueMatch(ClientException):
"""Multiple entities found instead of one."""
pass
class EndpointException(ClientException):
"""Something is rotten in Service Catalog."""
pass
class EndpointNotFound(EndpointException):
"""Could not find requested endpoint in Service Catalog."""
pass
class AmbiguousEndpoints(EndpointException):
"""Found more than one matching endpoint in Service Catalog."""
def __init__(self, endpoints=None):
super(AmbiguousEndpoints, self).__init__(
_("AmbiguousEndpoints: %r") % endpoints)
self.endpoints = endpoints
class HttpError(ClientException):
"""The base exception class for all HTTP exceptions."""
http_status = 0
message = _("HTTP Error")
def __init__(self, message=None, details=None,
response=None, request_id=None,
url=None, method=None, http_status=None):
self.http_status = http_status or self.http_status
self.message = message or self.message
self.details = details
self.request_id = request_id
self.response = response
self.url = url
self.method = method
formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
if request_id:
formatted_string += " (Request-ID: %s)" % request_id
super(HttpError, self).__init__(formatted_string)
class HTTPRedirection(HttpError):
"""HTTP Redirection."""
message = _("HTTP Redirection")
class HTTPClientError(HttpError):
"""Client-side HTTP error.
Exception for cases in which the client seems to have erred.
"""
message = _("HTTP Client Error")
class HttpServerError(HttpError):
"""Server-side HTTP error.
Exception for cases in which the server is aware that it has
erred or is incapable of performing the request.
"""
message = _("HTTP Server Error")
class MultipleChoices(HTTPRedirection):
"""HTTP 300 - Multiple Choices.
Indicates multiple options for the resource that the client may follow.
"""
http_status = 300
message = _("Multiple Choices")
class BadRequest(HTTPClientError):
"""HTTP 400 - Bad Request.
The request cannot be fulfilled due to bad syntax.
"""
http_status = 400
message = _("Bad Request")
class Unauthorized(HTTPClientError):
"""HTTP 401 - Unauthorized.
Similar to 403 Forbidden, but specifically for use when authentication
is required and has failed or has not yet been provided.
"""
http_status = 401
message = _("Unauthorized")
class PaymentRequired(HTTPClientError):
"""HTTP 402 - Payment Required.
Reserved for future use.
"""
http_status = 402
message = _("Payment Required")
class Forbidden(HTTPClientError):
"""HTTP 403 - Forbidden.
The request was a valid request, but the server is refusing to respond
to it.
"""
http_status = 403
message = _("Forbidden")
class NotFound(HTTPClientError):
"""HTTP 404 - Not Found.
The requested resource could not be found but may be available again
in the future.
"""
http_status = 404
message = _("Not Found")
class MethodNotAllowed(HTTPClientError):
"""HTTP 405 - Method Not Allowed.
A request was made of a resource using a request method not supported
by that resource.
"""
http_status = 405
message = _("Method Not Allowed")
class NotAcceptable(HTTPClientError):
"""HTTP 406 - Not Acceptable.
The requested resource is only capable of generating content not
acceptable according to the Accept headers sent in the request.
"""
http_status = 406
message = _("Not Acceptable")
class ProxyAuthenticationRequired(HTTPClientError):
"""HTTP 407 - Proxy Authentication Required.
The client must first authenticate itself with the proxy.
"""
http_status = 407
message = _("Proxy Authentication Required")
class RequestTimeout(HTTPClientError):
"""HTTP 408 - Request Timeout.
The server timed out waiting for the request.
"""
http_status = 408
message = _("Request Timeout")
class Conflict(HTTPClientError):
"""HTTP 409 - Conflict.
Indicates that the request could not be processed because of conflict
in the request, such as an edit conflict.
"""
http_status = 409
message = _("Conflict")
class Gone(HTTPClientError):
"""HTTP 410 - Gone.
Indicates that the resource requested is no longer available and will
not be available again.
"""
http_status = 410
message = _("Gone")
class LengthRequired(HTTPClientError):
"""HTTP 411 - Length Required.
The request did not specify the length of its content, which is
required by the requested resource.
"""
http_status = 411
message = _("Length Required")
class PreconditionFailed(HTTPClientError):
"""HTTP 412 - Precondition Failed.
The server does not meet one of the preconditions that the requester
put on the request.
"""
http_status = 412
message = _("Precondition Failed")
class RequestEntityTooLarge(HTTPClientError):
"""HTTP 413 - Request Entity Too Large.
The request is larger than the server is willing or able to process.
"""
http_status = 413
message = _("Request Entity Too Large")
def __init__(self, *args, **kwargs):
try:
self.retry_after = int(kwargs.pop('retry_after'))
except (KeyError, ValueError):
self.retry_after = 0
super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
class RequestUriTooLong(HTTPClientError):
"""HTTP 414 - Request-URI Too Long.
The URI provided was too long for the server to process.
"""
http_status = 414
message = _("Request-URI Too Long")
class UnsupportedMediaType(HTTPClientError):
"""HTTP 415 - Unsupported Media Type.
The request entity has a media type which the server or resource does
not support.
"""
http_status = 415
message = _("Unsupported Media Type")
class RequestedRangeNotSatisfiable(HTTPClientError):
"""HTTP 416 - Requested Range Not Satisfiable.
The client has asked for a portion of the file, but the server cannot
supply that portion.
"""
http_status = 416
message = _("Requested Range Not Satisfiable")
class ExpectationFailed(HTTPClientError):
"""HTTP 417 - Expectation Failed.
The server cannot meet the requirements of the Expect request-header field.
"""
http_status = 417
message = _("Expectation Failed")
class UnprocessableEntity(HTTPClientError):
"""HTTP 422 - Unprocessable Entity.
The request was well-formed but was unable to be followed due to semantic
errors.
"""
http_status = 422
message = _("Unprocessable Entity")
class InternalServerError(HttpServerError):
"""HTTP 500 - Internal Server Error.
A generic error message, given when no more specific message is suitable.
"""
http_status = 500
message = _("Internal Server Error")
# NotImplemented is a python keyword.
class HttpNotImplemented(HttpServerError):
"""HTTP 501 - Not Implemented.
The server either does not recognize the request method, or it lacks
the ability to fulfill the request.
"""
http_status = 501
message = _("Not Implemented")
class BadGateway(HttpServerError):
"""HTTP 502 - Bad Gateway.
The server was acting as a gateway or proxy and received an invalid
response from the upstream server.
"""
http_status = 502
message = _("Bad Gateway")
class ServiceUnavailable(HttpServerError):
"""HTTP 503 - Service Unavailable.
The server is currently unavailable.
"""
http_status = 503
message = _("Service Unavailable")
class GatewayTimeout(HttpServerError):
"""HTTP 504 - Gateway Timeout.
The server was acting as a gateway or proxy and did not receive a timely
response from the upstream server.
"""
http_status = 504
message = _("Gateway Timeout")
class HttpVersionNotSupported(HttpServerError):
"""HTTP 505 - HttpVersion Not Supported.
The server does not support the HTTP protocol version used in the request.
"""
http_status = 505
message = _("HTTP Version Not Supported")
# _code_map contains all the classes that have http_status attribute.
_code_map = dict(
(getattr(obj, 'http_status', None), obj)
for name, obj in vars(sys.modules[__name__]).items()
if inspect.isclass(obj) and getattr(obj, 'http_status', False)
)
def from_response(response, method, url):
"""Returns an instance of :class:`HttpError` or subclass based on response.
:param response: instance of `requests.Response` class
:param method: HTTP method used for request
:param url: URL used for request
"""
req_id = response.headers.get("x-openstack-request-id")
# NOTE(hdd) true for older versions of nova and cinder
if not req_id:
req_id = response.headers.get("x-compute-request-id")
kwargs = {
"http_status": response.status_code,
"response": response,
"method": method,
"url": url,
"request_id": req_id,
}
if "retry-after" in response.headers:
kwargs["retry_after"] = response.headers["retry-after"]
content_type = response.headers.get("Content-Type", "")
if content_type.startswith("application/json"):
try:
body = response.json()
except ValueError:
pass
else:
if isinstance(body, dict):
error = body.get(list(body)[0])
if isinstance(error, dict):
kwargs["message"] = (error.get("message") or
error.get("faultstring"))
kwargs["details"] = (error.get("details") or
six.text_type(body))
elif content_type.startswith("text/"):
kwargs["details"] = response.text
try:
cls = _code_map[response.status_code]
except KeyError:
if 500 <= response.status_code < 600:
cls = HttpServerError
elif 400 <= response.status_code < 500:
cls = HTTPClientError
else:
cls = HttpError
return cls(**kwargs)

View File

@ -1,174 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
A fake server that "responds" to API methods with pre-canned responses.
All of these responses come from the spec, so if for some reason the spec's
wrong the tests might raise AssertionError. I've indicated in comments the
places where actual behavior differs from the spec.
"""
# W0102: Dangerous default value %s as argument
# pylint: disable=W0102
import json
import requests
import six
from six.moves.urllib import parse
from manilaclient.common.apiclient import client
def assert_has_keys(dct, required=None, optional=None):
required = required or []
optional = optional or []
for k in required:
try:
assert k in dct
except AssertionError:
extra_keys = set(dct.keys()).difference(set(required + optional))
raise AssertionError("found unexpected keys: %s" %
list(extra_keys))
class TestResponse(requests.Response):
"""Wrap requests.Response and provide a convenient initialization."""
def __init__(self, data):
super(TestResponse, self).__init__()
self._content_consumed = True
if isinstance(data, dict):
self.status_code = data.get('status_code', 200)
# Fake the text attribute to streamline Response creation
text = data.get('text', "")
if isinstance(text, (dict, list)):
self._content = json.dumps(text)
default_headers = {
"Content-Type": "application/json",
}
else:
self._content = text
default_headers = {}
if six.PY3 and isinstance(self._content, six.string_types):
self._content = self._content.encode('utf-8', 'strict')
self.headers = data.get('headers') or default_headers
else:
self.status_code = data
def __eq__(self, other):
return (self.status_code == other.status_code and
self.headers == other.headers and
self._content == other._content)
class FakeHTTPClient(client.HTTPClient):
def __init__(self, *args, **kwargs):
self.callstack = []
self.fixtures = kwargs.pop("fixtures", None) or {}
if not args and "auth_plugin" not in kwargs:
args = (None, )
super(FakeHTTPClient, self).__init__(*args, **kwargs)
def assert_called(self, method, url, body=None, pos=-1):
"""Assert than an API method was just called."""
expected = (method, url)
called = self.callstack[pos][0:2]
assert self.callstack, \
"Expected %s %s but no calls were made." % expected
assert expected == called, 'Expected %s %s; got %s %s' % \
(expected + called)
if body is not None:
if self.callstack[pos][3] != body:
raise AssertionError('%r != %r' %
(self.callstack[pos][3], body))
def assert_called_anytime(self, method, url, body=None,
clear_callstack=True):
"""Assert than an API method was called anytime in the test."""
expected = (method, url)
assert self.callstack, \
"Expected %s %s but no calls were made." % expected
found = False
entry = None
for entry in self.callstack:
if expected == entry[0:2]:
found = True
break
assert found, 'Expected %s %s; got %s' % \
(method, url, self.callstack)
if body is not None:
assert entry[3] == body, "%s != %s" % (entry[3], body)
if clear_callstack:
self.callstack = []
def clear_callstack(self):
self.callstack = []
def authenticate(self):
pass
def client_request(self, client, method, url, **kwargs):
# Check that certain things are called correctly
if method in ["GET", "DELETE"]:
assert "json" not in kwargs
# Note the call
self.callstack.append(
(method,
url,
kwargs.get("headers") or {},
kwargs.get("json") or kwargs.get("data")))
try:
fixture = self.fixtures[url][method]
except KeyError:
pass
else:
return TestResponse({"headers": fixture[0],
"text": fixture[1]})
# Call the method
args = parse.parse_qsl(parse.urlparse(url)[4])
kwargs.update(args)
munged_url = url.rsplit('?', 1)[0]
munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
munged_url = munged_url.replace('-', '_')
callback = "%s_%s" % (method.lower(), munged_url)
if not hasattr(self, callback):
raise AssertionError('Called unknown API method: %s %s, '
'expected fakes method name: %s' %
(method, url, callback))
resp = getattr(self, callback)(**kwargs)
if len(resp) == 3:
status, headers, body = resp
else:
status, body = resp
headers = {}
return TestResponse({
"status_code": status,
"text": body,
"headers": headers,
})

View File

@ -1,83 +0,0 @@
#
# 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_utils import encodeutils
from oslo_utils import uuidutils
import six
from manilaclient.common._i18n import _
from manilaclient.common.apiclient import exceptions
def find_resource(manager, name_or_id, **find_args):
"""Look for resource in a given manager.
Used as a helper for the _find_* methods.
Example:
.. code-block:: python
def _find_hypervisor(cs, hypervisor):
#Get a hypervisor by name or ID.
return cliutils.find_resource(cs.hypervisors, hypervisor)
"""
# first try to get entity as integer id
try:
return manager.get(int(name_or_id))
except (TypeError, ValueError, exceptions.NotFound):
pass
# now try to get entity as uuid
try:
if six.PY2:
tmp_id = encodeutils.safe_encode(name_or_id)
else:
tmp_id = encodeutils.safe_decode(name_or_id)
if uuidutils.is_uuid_like(tmp_id):
return manager.get(tmp_id)
except (TypeError, ValueError, exceptions.NotFound):
pass
# for str id which is not uuid
if getattr(manager, 'is_alphanum_id_allowed', False):
try:
return manager.get(name_or_id)
except exceptions.NotFound:
pass
try:
try:
return manager.find(human_id=name_or_id, **find_args)
except exceptions.NotFound:
pass
# finally try to find entity by name
try:
resource = getattr(manager, 'resource_class', None)
name_attr = resource.NAME_ATTR if resource else 'name'
kwargs = {name_attr: name_or_id}
kwargs.update(find_args)
return manager.find(**kwargs)
except exceptions.NotFound:
msg = _("No %(name)s with a name or "
"ID of '%(name_or_id)s' exists.") % \
{"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
raise exceptions.CommandError(msg)
except exceptions.NoUniqueMatch:
msg = _("Multiple %(name)s matches found for "
"'%(name_or_id)s', use an ID to be more specific.") % \
{"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
raise exceptions.CommandError(msg)

View File

@ -1,271 +0,0 @@
# Copyright 2012 Red Hat, 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.
# W0603: Using the global statement
# W0621: Redefining name %s from outer scope
# pylint: disable=W0603,W0621
from __future__ import print_function
import getpass
import inspect
import os
import sys
import textwrap
from oslo_utils import encodeutils
from oslo_utils import strutils
import prettytable
import six
from six import moves
from manilaclient.common._i18n import _
class MissingArgs(Exception):
"""Supplied arguments are not sufficient for calling a function."""
def __init__(self, missing):
self.missing = missing
msg = _("Missing arguments: %s") % ", ".join(missing)
super(MissingArgs, self).__init__(msg)
def validate_args(fn, *args, **kwargs):
"""Check that the supplied args are sufficient for calling a function.
>>> validate_args(lambda a: None)
Traceback (most recent call last):
...
MissingArgs: Missing argument(s): a
>>> validate_args(lambda a, b, c, d: None, 0, c=1)
Traceback (most recent call last):
...
MissingArgs: Missing argument(s): b, d
:param fn: the function to check
:param arg: the positional arguments supplied
:param kwargs: the keyword arguments supplied
"""
argspec = inspect.getargspec(fn)
num_defaults = len(argspec.defaults or [])
required_args = argspec.args[:len(argspec.args) - num_defaults]
def isbound(method):
return getattr(method, '__self__', None) is not None
if isbound(fn):
required_args.pop(0)
missing = [arg for arg in required_args if arg not in kwargs]
missing = missing[len(args):]
if missing:
raise MissingArgs(missing)
def arg(*args, **kwargs):
"""Decorator for CLI args.
Example:
>>> @arg("name", help="Name of the new entity")
... def entity_create(args):
... pass
"""
def _decorator(func):
add_arg(func, *args, **kwargs)
return func
return _decorator
def env(*args, **kwargs):
"""Returns the first environment variable set.
If all are empty, defaults to '' or keyword arg `default`.
"""
for arg in args:
value = os.environ.get(arg)
if value:
return value
return kwargs.get('default', '')
def add_arg(func, *args, **kwargs):
"""Bind CLI arguments to a shell.py `do_foo` function."""
if not hasattr(func, 'arguments'):
func.arguments = []
# NOTE(sirp): avoid dups that can occur when the module is shared across
# tests.
if (args, kwargs) not in func.arguments:
# Because of the semantics of decorator composition if we just append
# to the options list positional options will appear to be backwards.
func.arguments.insert(0, (args, kwargs))
def unauthenticated(func):
"""Adds 'unauthenticated' attribute to decorated function.
Usage:
>>> @unauthenticated
... def mymethod(f):
... pass
"""
func.unauthenticated = True
return func
def isunauthenticated(func):
"""Checks if the function does not require authentication.
Mark such functions with the `@unauthenticated` decorator.
:returns: bool
"""
return getattr(func, 'unauthenticated', False)
def print_list(objs, fields, formatters=None, sortby_index=0,
mixed_case_fields=None, field_labels=None):
"""Print a list or objects as a table, one row per object.
:param objs: iterable of :class:`Resource`
:param fields: attributes that correspond to columns, in order
:param formatters: `dict` of callables for field formatting
:param sortby_index: index of the field for sorting table rows
:param mixed_case_fields: fields corresponding to object attributes that
have mixed case names (e.g., 'serverId')
:param field_labels: Labels to use in the heading of the table, default to
fields.
"""
formatters = formatters or {}
mixed_case_fields = mixed_case_fields or []
field_labels = field_labels or fields
if len(field_labels) != len(fields):
raise ValueError(_("Field labels list %(labels)s has different number "
"of elements than fields list %(fields)s"),
{'labels': field_labels, 'fields': fields})
if sortby_index is None:
kwargs = {}
else:
kwargs = {'sortby': field_labels[sortby_index]}
pt = prettytable.PrettyTable(field_labels)
pt.align = 'l'
for o in objs:
row = []
for field in fields:
if field in formatters:
row.append(formatters[field](o))
else:
if field in mixed_case_fields:
field_name = field.replace(' ', '_')
else:
field_name = field.lower().replace(' ', '_')
data = getattr(o, field_name, '')
row.append(data)
pt.add_row(row)
if six.PY3:
print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode())
else:
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
def print_dict(dct, dict_property="Property", wrap=0):
"""Print a `dict` as a table of two columns.
:param dct: `dict` to print
:param dict_property: name of the first column
:param wrap: wrapping for the second column
"""
pt = prettytable.PrettyTable([dict_property, 'Value'])
pt.align = 'l'
for k, v in dct.items():
# convert dict to str to check length
if isinstance(v, dict):
v = six.text_type(v)
if wrap > 0:
v = textwrap.fill(six.text_type(v), wrap)
# if value has a newline, add in multiple rows
# e.g. fault with stacktrace
if v and isinstance(v, six.string_types) and r'\n' in v:
lines = v.strip().split(r'\n')
col1 = k
for line in lines:
pt.add_row([col1, line])
col1 = ''
else:
pt.add_row([k, v])
if six.PY3:
print(encodeutils.safe_encode(pt.get_string()).decode())
else:
print(encodeutils.safe_encode(pt.get_string()))
def get_password(max_password_prompts=3):
"""Read password from TTY."""
verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD"))
pw = None
if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
# Check for Ctrl-D
try:
for __ in moves.range(max_password_prompts):
pw1 = getpass.getpass("OS Password: ")
if verify:
pw2 = getpass.getpass("Please verify: ")
else:
pw2 = pw1
if pw1 == pw2 and pw1:
pw = pw1
break
except EOFError:
pass
return pw
def service_type(stype):
"""Adds 'service_type' attribute to decorated function.
Usage:
.. code-block:: python
@service_type('volume')
def mymethod(f):
...
"""
def inner(f):
f.service_type = stype
return f
return inner
def get_service_type(f):
"""Retrieves service type from function."""
return getattr(f, 'service_type', None)
def pretty_choice_list(l):
return ', '.join("'%s'" % i for i in l)
def exit(msg=''):
if msg:
print(msg, file=sys.stderr)
sys.exit(1)

View File

@ -1,83 +0,0 @@
# Copyright 2014 Mirantis, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Common constants that can be used all over the manilaclient."""
# These are used for providing desired sorting params with list requests
SORT_DIR_VALUES = ('asc', 'desc')
SHARE_SORT_KEY_VALUES = (
'id', 'status', 'size', 'host', 'share_proto',
'availability_zone',
'user_id', 'project_id',
'created_at', 'updated_at',
'display_name', 'name',
'share_type_id', 'share_type',
'share_network_id', 'share_network',
'snapshot_id', 'snapshot',
)
SNAPSHOT_SORT_KEY_VALUES = (
'id',
'status',
'size',
'share_id',
'user_id',
'project_id',
'progress',
'name',
'display_name',
)
SHARE_GROUP_SORT_KEY_VALUES = (
'id',
'name',
'status',
'host',
'user_id',
'project_id',
'created_at',
'availability_zone',
'share_network',
'share_network_id',
'share_group_type',
'share_group_type_id',
'source_share_group_snapshot_id',
)
SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES = (
'id',
'name',
'status',
'host',
'user_id',
'project_id',
'created_at',
'share_group_id',
)
TASK_STATE_MIGRATION_SUCCESS = 'migration_success'
TASK_STATE_MIGRATION_ERROR = 'migration_error'
TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled'
TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE = 'migration_driver_phase1_done'
TASK_STATE_DATA_COPYING_COMPLETED = 'data_copying_completed'
EXPERIMENTAL_HTTP_HEADER = 'X-OpenStack-Manila-API-Experimental'
V1_SERVICE_TYPE = 'share'
V2_SERVICE_TYPE = 'sharev2'
SERVICE_TYPES = {'1': V1_SERVICE_TYPE, '2': V2_SERVICE_TYPE}
EXTENSION_PLUGIN_NAMESPACE = 'manilaclient.common.apiclient.auth'

View File

@ -1,202 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2011 Piston Cloud Computing, Inc.
# Copyright 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 copy
import logging
from oslo_serialization import jsonutils
from oslo_utils import strutils
import requests
import six
from manilaclient import exceptions
try:
from eventlet import sleep
except ImportError:
from time import sleep # noqa
class HTTPClient(object):
"""HTTP Client class used by multiple clients.
The imported Requests module caches and reuses objects with the same
destination. To avoid the problem of sending duplicate requests it is
necessary that the Requests module is only imported once during client
execution. This class is shared by multiple client versions so that the
client can be changed to another version during execution.
"""
API_VERSION_HEADER = "X-Openstack-Manila-Api-Version"
def __init__(self, endpoint_url, token, user_agent, api_version,
insecure=False, cacert=None, timeout=None, retries=None,
http_log_debug=False):
self.endpoint_url = endpoint_url
self.base_url = self._get_base_url(self.endpoint_url)
self.retries = int(retries or 0)
self.http_log_debug = http_log_debug
self.request_options = self._set_request_options(
insecure, cacert, timeout)
self.default_headers = {
'X-Auth-Token': token,
self.API_VERSION_HEADER: api_version.get_string(),
'User-Agent': user_agent,
'Accept': 'application/json',
}
self._add_log_handlers(http_log_debug)
def _add_log_handlers(self, http_log_debug):
self._logger = logging.getLogger(__name__)
# check that handler hasn't already been added
if http_log_debug and not self._logger.handlers:
ch = logging.StreamHandler()
ch._name = 'http_client_handler'
self._logger.setLevel(logging.DEBUG)
self._logger.addHandler(ch)
if hasattr(requests, 'logging'):
rql = requests.logging.getLogger(requests.__name__)
rql.addHandler(ch)
def _get_base_url(self, url):
"""Truncates url and returns transport, address, and port number."""
base_url = '/'.join(url.split('/')[:3]) + '/'
return base_url
def _set_request_options(self, insecure, cacert, timeout=None):
options = {'verify': True}
if insecure:
options['verify'] = False
elif cacert:
options['verify'] = cacert
if timeout:
options['timeout'] = timeout
return options
def request(self, url, method, **kwargs):
headers = copy.deepcopy(self.default_headers)
headers.update(kwargs.get('headers', {}))
options = copy.deepcopy(self.request_options)
if 'body' in kwargs:
headers['Content-Type'] = 'application/json'
options['data'] = jsonutils.dumps(kwargs['body'])
self.log_request(method, url, headers, options.get('data', None))
resp = requests.request(method, url, headers=headers, **options)
self.log_response(resp)
body = None
if resp.text:
try:
body = jsonutils.loads(resp.text)
except ValueError:
pass
if resp.status_code >= 400:
raise exceptions.from_response(resp, method, url)
return resp, body
def _cs_request(self, url, method, **kwargs):
return self._cs_request_with_retries(
self.endpoint_url + url,
method,
**kwargs)
def _cs_request_base_url(self, url, method, **kwargs):
return self._cs_request_with_retries(
self.base_url + url,
method,
**kwargs)
def _cs_request_with_retries(self, url, method, **kwargs):
attempts = 0
timeout = 1
while True:
attempts += 1
try:
resp, body = self.request(url, method, **kwargs)
return resp, body
except (exceptions.BadRequest,
requests.exceptions.RequestException,
exceptions.ClientException) as e:
if attempts > self.retries:
raise
self._logger.debug("Request error: %s", six.text_type(e))
self._logger.debug(
"Failed attempt(%(current)s of %(total)s), "
" retrying in %(sec)s seconds", {
'current': attempts,
'total': self.retries,
'sec': timeout
})
sleep(timeout)
timeout *= 2
def get_with_base_url(self, url, **kwargs):
return self._cs_request_base_url(url, 'GET', **kwargs)
def get(self, url, **kwargs):
return self._cs_request(url, 'GET', **kwargs)
def post(self, url, **kwargs):
return self._cs_request(url, 'POST', **kwargs)
def put(self, url, **kwargs):
return self._cs_request(url, 'PUT', **kwargs)
def delete(self, url, **kwargs):
return self._cs_request(url, 'DELETE', **kwargs)
def log_request(self, method, url, headers, data=None):
if not self.http_log_debug:
return
string_parts = ['curl -i', ' -X %s' % method, ' %s' % url]
for element in headers:
header = ' -H "%s: %s"' % (element, headers[element])
string_parts.append(header)
if data:
if "password" in data:
data = strutils.mask_password(data)
string_parts.append(" -d '%s'" % data)
self._logger.debug("\nREQ: %s\n", "".join(string_parts))
def log_response(self, resp):
if not self.http_log_debug:
return
self._logger.debug(
"RESP: [%(code)s] %(headers)s\nRESP BODY: %(body)s\n", {
'code': resp.status_code,
'headers': resp.headers,
'body': resp.text
})

View File

@ -1,216 +0,0 @@
# Copyright 2014 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import os
from oslo_config import cfg
import oslo_log._options as log_options
from manilaclient import api_versions
# 1. Define opts
# "auth_opts" are used by functional tests that are located in
# directory "%project_root%/manilaclient/tests/functional"
auth_opts = [
# Options for user with 'member' role.
cfg.StrOpt("username",
help="This should be the username of a user WITHOUT "
"administrative privileges."),
cfg.StrOpt("tenant_name",
help="The non-administrative user's tenant name."),
cfg.StrOpt("password",
help="The non-administrative user's password."),
cfg.StrOpt("auth_url",
help="URL for where to find the OpenStack Identity public "
"API endpoint."),
cfg.StrOpt("project_domain_name",
help=("Project domain Name of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("project_domain_id",
help=("Project domain ID of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("user_domain_name",
help=("User domain Name of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("user_domain_id",
help=("User domain ID of user with 'member' role "
"as specified for auth v3.")),
# Options for user with 'admin' role.
cfg.StrOpt("admin_username",
help="This should be the username of a user WITH "
"administrative privileges."),
cfg.StrOpt("admin_tenant_name",
help="The administrative user's tenant name."),
cfg.StrOpt("admin_password",
help="The administrative user's password."),
cfg.StrOpt("admin_auth_url",
help="URL for where to find the OpenStack Identity admin "
"API endpoint."),
cfg.StrOpt("admin_project_domain_name",
help=("Project domain Name of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_project_domain_id",
help=("Project domain ID of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_user_domain_name",
help=("User domain Name of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_user_domain_id",
help=("User domain ID of user with 'admin' role "
"as specified for auth v3.")),
# Other auth options
cfg.BoolOpt("insecure",
default=False,
help="Disable SSL certificate verification."),
]
base_opts = [
cfg.StrOpt("manila_exec_dir",
default=os.environ.get(
'OS_MANILA_EXEC_DIR',
os.path.join(os.path.abspath('.'), '.tox/functional/bin')),
help="The path to manilaclient to be executed."),
cfg.BoolOpt("suppress_errors_in_cleanup",
default=True,
help="Whether to suppress errors with clean up operation "
"or not."),
]
share_opts = [
cfg.StrOpt("min_api_microversion",
default="1.0",
help="The minimum API microversion is configured to be the "
"value of the minimum microversion supported by "
"Manilaclient functional tests. Defaults to 1.0."),
cfg.StrOpt("max_api_microversion",
default=api_versions.MAX_VERSION,
help="The maximum API microversion is configured to be the "
"value of the latest microversion supported by "
"Manilaclient functional tests. Defaults to "
"manilaclient's max supported API microversion."),
cfg.StrOpt("share_network",
help="Share network Name or ID, that will be used for shares. "
"Some backend drivers require a share network for share "
"creation."),
cfg.StrOpt("admin_share_network",
help="Share network Name or ID, that will be used for shares "
"in admin tenant."),
cfg.StrOpt("share_type",
help="Share type Name or ID, that will be used with share "
"creation scheduling. Optional."),
cfg.ListOpt("enable_protocols",
default=["nfs", "cifs"],
help="List of all enabled protocols. The first protocol in "
"the list will be used as the default protocol."),
cfg.IntOpt("build_interval",
default=3,
help="Time in seconds between share availability checks."),
cfg.IntOpt("build_timeout",
default=500,
help="Timeout in seconds to wait for a share to become "
"available."),
cfg.DictOpt('access_types_mapping',
default={'nfs': 'ip', 'cifs': 'ip'},
help="Dict contains access types mapping to share "
"protocol. It will be used to create access rules "
"for shares. Format: '<protocol>: <type1> <type2>',..."
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs."),
cfg.DictOpt('access_levels_mapping',
default={'nfs': 'rw ro', 'cifs': 'rw'},
help="Dict contains access levels mapping to share "
"protocol. It will be used to create access rules for "
"shares. Format: '<protocol>: <level1> <level2>',... "
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs."),
cfg.StrOpt("username_for_user_rules",
default="stack",
help="Username, that will be used in share access tests for "
"user type of access."),
cfg.BoolOpt("run_snapshot_tests",
default=True,
help="Defines whether to run tests that use share snapshots "
"or not. Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_share_servers_tests",
default=True,
help="Defines whether to run tests that use share servers "
"or not. Disable this feature if used driver doesn't "
"support it or when autodeletion of share servers "
"is enabled."),
cfg.BoolOpt("run_migration_tests",
default=False,
help="Defines whether to run share migration tests or not. "
"Disable this feature if there is no more than one "
"storage pool being tested or if used driver does not "
"support it."),
cfg.BoolOpt("run_mount_snapshot_tests",
default=False,
help="Defines whether to run mountable snapshots tests or "
"not. Disable this feature if used driver doesn't "
"support it."),
]
# 2. Generate config
PROJECT_NAME = 'manilaclient'
DEFAULT_CONFIG_FILE = (
os.environ.get('OS_%s_CONFIG_FILE' % PROJECT_NAME.upper()) or
'%s.conf' % PROJECT_NAME)
DEFAULT_CONFIG_DIR = (
os.environ.get('OS_%s_CONFIG_DIR' % PROJECT_NAME.upper()) or
os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
"etc/manilaclient")
)
DEFAULT_CONFIG_PATH = os.path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE)
FAILOVER_CONFIG_PATH = '/etc/%(pn)s/%(cn)s' % {
'pn': PROJECT_NAME, 'cn': DEFAULT_CONFIG_FILE}
CONFIG_FILES = []
if os.path.isfile(DEFAULT_CONFIG_PATH):
CONFIG_FILES.append(DEFAULT_CONFIG_PATH)
if os.path.isfile(FAILOVER_CONFIG_PATH):
CONFIG_FILES.append(FAILOVER_CONFIG_PATH)
CONF = cfg.CONF
if CONFIG_FILES:
CONF([], project=PROJECT_NAME, default_config_files=CONFIG_FILES)
else:
CONF([], project=PROJECT_NAME)
# 3. Register opts
CONF.register_opts(auth_opts)
CONF.register_opts(base_opts)
CONF.register_opts(share_opts)
# 4. Define list_opts for config sample generator
def list_opts():
"""Return a list of oslo_config options available in Manilaclient."""
opts = [
(None, copy.deepcopy(auth_opts)),
(None, copy.deepcopy(base_opts)),
(None, copy.deepcopy(share_opts)),
]
opts.extend(log_options.list_opts())
return opts

View File

@ -1,40 +0,0 @@
# Copyright 2010 Jacob Kaplan-Moss
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Exception definitions.
"""
from manilaclient.common.apiclient.exceptions import * # noqa
class NoTokenLookupException(ClientException):
"""No support for looking up endpoints.
This form of authentication does not support looking up
endpoints from an existing token.
"""
pass
class VersionNotFoundForAPIMethod(Exception):
msg_fmt = "API version '%(vers)s' is not supported on '%(method)s' method."
def __init__(self, version, method):
self.version = version
self.method = method
def __str__(self):
return self.msg_fmt % {"vers": self.version, "method": self.method}

View File

@ -1,39 +0,0 @@
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from manilaclient import base
from manilaclient import utils
class Extension(utils.HookableMixin):
"""Extension descriptor."""
SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__')
def __init__(self, name, module):
self.name = name
self.module = module
self._parse_extension_module()
def _parse_extension_module(self):
self.manager_class = None
for attr_name, attr_value in list(self.module.__dict__.items()):
if attr_name in self.SUPPORTED_HOOKS:
self.add_hook(attr_name, attr_value)
elif utils.safe_issubclass(attr_value, base.Manager):
self.manager_class = attr_value
def __repr__(self):
return "<Extension '%s'>" % self.name

View File

@ -1,698 +0,0 @@
# Copyright 2011 OpenStack Foundation
# Copyright 2014 Mirantis, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Command-line interface to the OpenStack Manila API.
"""
from __future__ import print_function
import argparse
import glob
import imp
import itertools
import logging
import os
import pkgutil
import sys
from oslo_utils import encodeutils
import six
from manilaclient import api_versions
from manilaclient import client
from manilaclient.common import cliutils
from manilaclient.common import constants
from manilaclient import exceptions as exc
import manilaclient.extension
from manilaclient.v2 import shell as shell_v2
DEFAULT_OS_SHARE_API_VERSION = api_versions.MAX_VERSION
DEFAULT_MANILA_ENDPOINT_TYPE = 'publicURL'
V1_MAJOR_VERSION = '1'
V2_MAJOR_VERSION = '2'
logger = logging.getLogger(__name__)
class AllowOnlyOneAliasAtATimeAction(argparse.Action):
"""Allows only one alias of argument to be used at a time."""
def __call__(self, parser, namespace, values, option_string=None):
# NOTE(vponomaryov): this method is redefinition of
# argparse.Action.__call__ interface
if not hasattr(self, 'calls'):
self.calls = {}
if self.dest not in self.calls:
self.calls[self.dest] = set()
local_values = sorted(values) if isinstance(values, list) else values
self.calls[self.dest].add(six.text_type(local_values))
if len(self.calls[self.dest]) == 1:
setattr(namespace, self.dest, local_values)
else:
msg = "Only one alias is allowed at a time."
raise argparse.ArgumentError(self, msg)
class ManilaClientArgumentParser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
super(ManilaClientArgumentParser, self).__init__(*args, **kwargs)
# NOTE(vponomaryov): Register additional action to be used by arguments
# with multiple aliases.
self.register('action', 'single_alias', AllowOnlyOneAliasAtATimeAction)
def error(self, message):
"""error(message: string)
Prints a usage message incorporating the message to stderr and
exits.
"""
self.print_usage(sys.stderr)
# FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value
choose_from = ' (choose from'
progparts = self.prog.partition(' ')
self.exit(2, "error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'"
" for more information.\n" %
{'errmsg': message.split(choose_from)[0],
'mainp': progparts[0],
'subp': progparts[2]})
def _get_option_tuples(self, option_string):
"""Avoid ambiguity in argument abbreviation.
Manilaclient uses aliases for command parameters and this method
is used for avoiding parameter ambiguity alert.
"""
option_tuples = super(
ManilaClientArgumentParser, self)._get_option_tuples(option_string)
if len(option_tuples) > 1:
opt_strings_list = []
opts = []
for opt in option_tuples:
if opt[0].option_strings not in opt_strings_list:
opt_strings_list.append(opt[0].option_strings)
opts.append(opt)
return opts
return option_tuples
class OpenStackManilaShell(object):
def get_base_parser(self):
parser = ManilaClientArgumentParser(
prog='manila',
description=__doc__.strip(),
epilog='See "manila help COMMAND" '
'for help on a specific command.',
add_help=False,
formatter_class=OpenStackHelpFormatter,
)
# Global arguments
parser.add_argument('-h', '--help',
action='store_true',
help=argparse.SUPPRESS)
parser.add_argument('--version',
action='version',
version=manilaclient.__version__)
parser.add_argument('-d', '--debug',
action='store_true',
default=cliutils.env('manilaclient_DEBUG',
'MANILACLIENT_DEBUG',
default=False),
help="Print debugging output.")
parser.add_argument('--os-cache',
default=cliutils.env('OS_CACHE', default=False),
action='store_true',
help='Use the auth token cache. '
'Defaults to env[OS_CACHE].')
parser.add_argument('--os-reset-cache',
default=False,
action='store_true',
help='Delete cached password and auth token.')
parser.add_argument('--os-user-id',
metavar='<auth-user-id>',
default=cliutils.env('OS_USER_ID'),
help=('Defaults to env [OS_USER_ID].'))
parser.add_argument('--os_user_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-username',
metavar='<auth-user-name>',
default=cliutils.env('OS_USERNAME',
'MANILA_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument('--os_username',
help=argparse.SUPPRESS)
parser.add_argument('--os-password',
metavar='<auth-password>',
default=cliutils.env('OS_PASSWORD',
'MANILA_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument('--os_password',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-name',
metavar='<auth-tenant-name>',
default=cliutils.env('OS_TENANT_NAME',
'MANILA_PROJECT_ID'),
help='Defaults to env[OS_TENANT_NAME].')
parser.add_argument('--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-name',
metavar='<auth-project-name>',
default=cliutils.env('OS_PROJECT_NAME'),
help=('Another way to specify tenant name. '
'This option is mutually exclusive with '
'--os-tenant-name. '
'Defaults to env[OS_PROJECT_NAME].'))
parser.add_argument('--os_project_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-id',
metavar='<auth-tenant-id>',
default=cliutils.env('OS_TENANT_ID',
'MANILA_TENANT_ID'),
help='Defaults to env[OS_TENANT_ID].')
parser.add_argument('--os_tenant_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-id',
metavar='<auth-project-id>',
default=cliutils.env('OS_PROJECT_ID'),
help=('Another way to specify tenant ID. '
'This option is mutually exclusive with '
'--os-tenant-id. '
'Defaults to env[OS_PROJECT_ID].'))
parser.add_argument('--os_project_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-id',
metavar='<auth-user-domain-id>',
default=cliutils.env('OS_USER_DOMAIN_ID'),
help=('OpenStack user domain ID. '
'Defaults to env[OS_USER_DOMAIN_ID].'))
parser.add_argument('--os_user_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-name',
metavar='<auth-user-domain-name>',
default=cliutils.env('OS_USER_DOMAIN_NAME'),
help=('OpenStack user domain name. '
'Defaults to env[OS_USER_DOMAIN_NAME].'))
parser.add_argument('--os_user_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-id',
metavar='<auth-project-domain-id>',
default=cliutils.env('OS_PROJECT_DOMAIN_ID'),
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
parser.add_argument('--os_project_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-name',
metavar='<auth-project-domain-name>',
default=cliutils.env('OS_PROJECT_DOMAIN_NAME'),
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
parser.add_argument('--os_project_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-url',
metavar='<auth-url>',
default=cliutils.env('OS_AUTH_URL',
'MANILA_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument('--os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument('--os-region-name',
metavar='<region-name>',
default=cliutils.env('OS_REGION_NAME',
'MANILA_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME].')
parser.add_argument('--os_region_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-token',
metavar='<token>',
default=cliutils.env('OS_TOKEN'),
help='Defaults to env[OS_TOKEN].')
parser.add_argument('--os_token',
help=argparse.SUPPRESS)
parser.add_argument('--bypass-url',
metavar='<bypass-url>',
default=cliutils.env('OS_MANILA_BYPASS_URL',
'MANILACLIENT_BYPASS_URL'),
help=("Use this API endpoint instead of the "
"Service Catalog. Defaults to "
"env[OS_MANILA_BYPASS_URL]."))
parser.add_argument('--bypass_url',
help=argparse.SUPPRESS)
parser.add_argument('--service-type',
metavar='<service-type>',
help='Defaults to compute for most actions.')
parser.add_argument('--service_type',
help=argparse.SUPPRESS)
parser.add_argument('--service-name',
metavar='<service-name>',
default=cliutils.env('OS_MANILA_SERVICE_NAME',
'MANILA_SERVICE_NAME'),
help='Defaults to env[OS_MANILA_SERVICE_NAME].')
parser.add_argument('--service_name',
help=argparse.SUPPRESS)
parser.add_argument('--share-service-name',
metavar='<share-service-name>',
default=cliutils.env(
'OS_MANILA_SHARE_SERVICE_NAME',
'MANILA_share_service_name'),
help='Defaults to env'
'[OS_MANILA_SHARE_SERVICE_NAME].')
parser.add_argument('--share_service_name',
help=argparse.SUPPRESS)
parser.add_argument('--endpoint-type',
metavar='<endpoint-type>',
default=cliutils.env(
'OS_MANILA_ENDPOINT_TYPE',
'MANILA_ENDPOINT_TYPE',
default=DEFAULT_MANILA_ENDPOINT_TYPE),
help='Defaults to env[OS_MANILA_ENDPOINT_TYPE] or '
+ DEFAULT_MANILA_ENDPOINT_TYPE + '.')
parser.add_argument('--endpoint_type',
help=argparse.SUPPRESS)
parser.add_argument('--os-share-api-version',
metavar='<share-api-ver>',
default=cliutils.env(
'OS_SHARE_API_VERSION',
default=DEFAULT_OS_SHARE_API_VERSION),
help='Accepts 1.x to override default '
'to env[OS_SHARE_API_VERSION].')
parser.add_argument('--os_share_api_version',
help=argparse.SUPPRESS)
parser.add_argument('--os-cacert',
metavar='<ca-certificate>',
default=cliutils.env('OS_CACERT', default=None),
help='Specify a CA bundle file to use in '
'verifying a TLS (https) server certificate. '
'Defaults to env[OS_CACERT].')
parser.add_argument('--insecure',
default=cliutils.env('manilaclient_INSECURE',
'MANILACLIENT_INSECURE',
default=False),
action='store_true',
help=argparse.SUPPRESS)
parser.add_argument('--retries',
metavar='<retries>',
type=int,
default=0,
help='Number of retries.')
parser.add_argument('--os-cert',
metavar='<certificate>',
default=cliutils.env('OS_CERT'),
help='Defaults to env[OS_CERT].')
parser.add_argument('--os_cert',
help=argparse.SUPPRESS)
return parser
def get_subcommand_parser(self, version):
parser = self.get_base_parser()
self.subcommands = {}
subparsers = parser.add_subparsers(metavar='<subcommand>')
try:
actions_module = {
V2_MAJOR_VERSION: shell_v2,
}[version]
except KeyError:
actions_module = shell_v2
self._find_actions(subparsers, actions_module)
self._find_actions(subparsers, self)
for extension in self.extensions:
self._find_actions(subparsers, extension.module)
self._add_bash_completion_subparser(subparsers)
return parser
def _discover_extensions(self, api_version):
extensions = []
for name, module in itertools.chain(
self._discover_via_python_path(),
self._discover_via_contrib_path(api_version)):
extension = manilaclient.extension.Extension(name, module)
extensions.append(extension)
return extensions
def _discover_via_python_path(self):
for (module_loader, name, ispkg) in pkgutil.iter_modules():
if name.endswith('python_manilaclient_ext'):
if not hasattr(module_loader, 'load_module'):
# Python 2.6 compat: actually get an ImpImporter obj
module_loader = module_loader.find_module(name)
module = module_loader.load_module(name)
yield name, module
def _discover_via_contrib_path(self, api_version):
module_path = os.path.dirname(os.path.abspath(__file__))
version_str = 'v' + api_version.get_major_version()
ext_path = os.path.join(module_path, version_str, 'contrib')
ext_glob = os.path.join(ext_path, "*.py")
for ext_path in glob.iglob(ext_glob):
name = os.path.basename(ext_path)[:-3]
if name == "__init__":
continue
module = imp.load_source(name, ext_path)
yield name, module
def _add_bash_completion_subparser(self, subparsers):
subparser = subparsers.add_parser(
'bash_completion',
add_help=False,
formatter_class=OpenStackHelpFormatter)
self.subcommands['bash_completion'] = subparser
subparser.set_defaults(func=self.do_bash_completion)
def _find_actions(self, subparsers, actions_module):
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
# I prefer to be hypen-separated instead of underscores.
command = attr[3:].replace('_', '-')
callback = getattr(actions_module, attr)
desc = callback.__doc__ or ''
help = desc.strip()
arguments = getattr(callback, 'arguments', [])
subparser = subparsers.add_parser(
command,
help=help,
description=desc,
add_help=False,
formatter_class=OpenStackHelpFormatter)
subparser.add_argument('-h', '--help',
action='help',
help=argparse.SUPPRESS,)
self.subcommands[command] = subparser
for (args, kwargs) in arguments:
subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback)
def setup_debugging(self, debug):
if not debug:
return
streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
logging.basicConfig(level=logging.DEBUG, format=streamformat)
logging.getLogger('requests.packages.urllib3.connectionpool'
).setLevel(logging.WARNING)
logging.getLogger('keystoneclient.session').setLevel(logging.WARNING)
def _build_subcommands_and_extensions(self,
os_api_version,
argv,
options):
self.extensions = self._discover_extensions(os_api_version)
self._run_extension_hooks('__pre_parse_args__')
self.parser = self.get_subcommand_parser(
os_api_version.get_major_version())
if options.help or not argv:
self.parser.print_help()
return False
args = self.parser.parse_args(argv)
self._run_extension_hooks('__post_parse_args__', args)
return args
def main(self, argv):
# Parse args once to find version and debug settings
parser = self.get_base_parser()
(options, args) = parser.parse_known_args(argv)
self.setup_debugging(options.debug)
os_api_version = self._validate_input_api_version(options)
# build available subcommands based on version
args = self._build_subcommands_and_extensions(os_api_version,
argv,
options)
if not args:
return 0
# Short-circuit and deal with help right away.
if args.func == self.do_help:
self.do_help(args)
return 0
elif args.func == self.do_bash_completion:
self.do_bash_completion(args)
return 0
os_service_type = args.service_type
os_endpoint_type = args.endpoint_type
client_args = dict(
username=args.os_username,
password=args.os_password,
project_name=args.os_project_name or args.os_tenant_name,
auth_url=args.os_auth_url,
insecure=args.insecure,
region_name=args.os_region_name,
tenant_id=args.os_project_id or args.os_tenant_id,
endpoint_type=DEFAULT_MANILA_ENDPOINT_TYPE,
extensions=self.extensions,
service_type=constants.V1_SERVICE_TYPE,
service_name=args.service_name,
retries=options.retries,
http_log_debug=args.debug,
cacert=args.os_cacert,
use_keyring=args.os_cache,
force_new_token=args.os_reset_cache,
user_id=args.os_user_id,
user_domain_id=args.os_user_domain_id,
user_domain_name=args.os_user_domain_name,
project_domain_id=args.os_project_domain_id,
project_domain_name=args.os_project_domain_name,
cert=args.os_cert,
input_auth_token=args.os_token,
service_catalog_url=args.bypass_url,
)
# Handle deprecated parameters
if args.share_service_name:
client_args['share_service_name'] = args.share_service_name
self._validate_required_options(
args.os_tenant_name, args.os_tenant_id,
args.os_project_name, args.os_project_id,
args.os_token, args.bypass_url,
client_args['auth_url'])
# This client is needed to discover the server api version.
temp_client = client.Client(manilaclient.API_MAX_VERSION,
**client_args)
self.cs, discovered_version = self._discover_client(temp_client,
os_api_version,
os_endpoint_type,
os_service_type,
client_args)
args = self._build_subcommands_and_extensions(discovered_version,
argv,
options)
args.func(self.cs, args)
def _discover_client(self,
current_client,
os_api_version,
os_endpoint_type,
os_service_type,
client_args):
if os_api_version == manilaclient.API_DEPRECATED_VERSION:
discovered_version = manilaclient.API_DEPRECATED_VERSION
os_service_type = constants.V1_SERVICE_TYPE
else:
discovered_version = api_versions.discover_version(
current_client,
os_api_version
)
if not os_endpoint_type:
os_endpoint_type = DEFAULT_MANILA_ENDPOINT_TYPE
if not os_service_type:
os_service_type = self._discover_service_type(discovered_version)
if (discovered_version != manilaclient.API_MAX_VERSION or
os_service_type != constants.V1_SERVICE_TYPE or
os_endpoint_type != DEFAULT_MANILA_ENDPOINT_TYPE):
client_args['version'] = discovered_version
client_args['service_type'] = os_service_type
client_args['endpoint_type'] = os_endpoint_type
return (client.Client(discovered_version, **client_args),
discovered_version)
else:
return current_client, discovered_version
def _discover_service_type(self, discovered_version):
major_version = discovered_version.get_major_version()
service_type = constants.SERVICE_TYPES[major_version]
return service_type
def _validate_input_api_version(self, options):
if not options.os_share_api_version:
api_version = manilaclient.API_MAX_VERSION
else:
api_version = api_versions.get_api_version(
options.os_share_api_version)
return api_version
def _validate_required_options(self, tenant_name, tenant_id,
project_name, project_id,
token, service_catalog_url, auth_url):
if token and not service_catalog_url:
raise exc.CommandError(
"bypass_url missing: When specifying a token the bypass_url "
"must be set via --bypass-url or env[OS_MANILA_BYPASS_URL]")
if service_catalog_url and not token:
raise exc.CommandError(
"Token missing: When specifying a bypass_url a token must be "
"set via --os-token or env[OS_TOKEN]")
if token and service_catalog_url:
return
if not (tenant_name or tenant_id or project_name or project_id):
raise exc.CommandError(
"You must provide a tenant_name, tenant_id, "
"project_id or project_name (with "
"project_domain_name or project_domain_id) via "
"--os-tenant-name or env[OS_TENANT_NAME], "
"--os-tenant-id or env[OS_TENANT_ID], "
"--os-project-id or env[OS_PROJECT_ID], "
"--os-project-name or env[OS_PROJECT_NAME], "
"--os-project-domain-id or env[OS_PROJECT_DOMAIN_ID] and "
"--os-project-domain-name or env[OS_PROJECT_DOMAIN_NAME]."
)
if not auth_url:
raise exc.CommandError(
"You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]")
def _run_extension_hooks(self, hook_type, *args, **kwargs):
"""Run hooks for all registered extensions."""
for extension in self.extensions:
extension.run_hooks(hook_type, *args, **kwargs)
def do_bash_completion(self, args):
"""Print arguments for bash_completion.
Prints all of the commands and options to stdout so that the
manila.bash_completion script doesn't have to hard code them.
"""
commands = set()
options = set()
for sc_str, sc in list(self.subcommands.items()):
commands.add(sc_str)
for option in sc._optionals._option_string_actions:
options.add(option)
commands.remove('bash-completion')
commands.remove('bash_completion')
print(' '.join(commands | options))
@cliutils.arg('command', metavar='<subcommand>', nargs='?',
help='Display help for <subcommand>')
def do_help(self, args):
"""Display help about this program or one of its subcommands."""
if args.command:
if args.command in self.subcommands:
self.subcommands[args.command].print_help()
else:
raise exc.CommandError("'%s' is not a valid subcommand" %
args.command)
else:
self.parser.print_help()
# I'm picky about my shell help.
class OpenStackHelpFormatter(argparse.HelpFormatter):
def start_section(self, heading):
# Title-case the headings
heading = '%s%s' % (heading[0].upper(), heading[1:])
super(OpenStackHelpFormatter, self).start_section(heading)
def main():
try:
if sys.version_info >= (3, 0):
OpenStackManilaShell().main(sys.argv[1:])
else:
OpenStackManilaShell().main(
map(encodeutils.safe_decode, sys.argv[1:]))
except KeyboardInterrupt:
print("... terminating manila client", file=sys.stderr)
sys.exit(130)
except Exception as e:
logger.debug(e, exc_info=1)
message = e.message
if not isinstance(message, six.string_types):
message = str(message)
print("ERROR: %s" % encodeutils.safe_encode(message), file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1,326 +0,0 @@
# Copyright 2014 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import traceback
from oslo_log import log
from tempest.lib.cli import base
from tempest.lib import exceptions as lib_exc
from manilaclient import config
from manilaclient.tests.functional import client
from manilaclient.tests.functional import utils
CONF = config.CONF
LOG = log.getLogger(__name__)
class handle_cleanup_exceptions(object):
"""Handle exceptions raised with cleanup operations.
Always suppress errors when lib_exc.NotFound or lib_exc.Forbidden
are raised.
Suppress all other exceptions only in case config opt
'suppress_errors_in_cleanup' is True.
"""
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if not (isinstance(exc_value,
(lib_exc.NotFound, lib_exc.Forbidden)) or
CONF.suppress_errors_in_cleanup):
return False # Do not suppress error if any
if exc_traceback:
LOG.error("Suppressed cleanup error: "
"\n%s", traceback.format_exc())
return True # Suppress error if any
class BaseTestCase(base.ClientTestBase):
# Will be cleaned up after test suite run
class_resources = []
# Will be cleaned up after single test run
method_resources = []
def setUp(self):
super(BaseTestCase, self).setUp()
self.addCleanup(self.clear_resources)
@classmethod
def tearDownClass(cls):
super(BaseTestCase, cls).tearDownClass()
cls.clear_resources(cls.class_resources)
@classmethod
def clear_resources(cls, resources=None):
"""Deletes resources, that were created in test suites.
This method tries to remove resources from resource list,
if it is not found, assume it was deleted in test itself.
It is expected, that all resources were added as LIFO
due to restriction of deletion resources, that are in the chain.
:param resources: dict with keys 'type','id','client' and 'deleted'
"""
if resources is None:
resources = cls.method_resources
for res in resources:
if "deleted" not in res:
res["deleted"] = False
if "client" not in res:
res["client"] = cls.get_cleanup_client()
if not(res["deleted"]):
res_id = res["id"]
client = res["client"]
with handle_cleanup_exceptions():
# TODO(vponomaryov): add support for other resources
if res["type"] is "share_type":
client.delete_share_type(
res_id, microversion=res["microversion"])
client.wait_for_share_type_deletion(
res_id, microversion=res["microversion"])
elif res["type"] is "share_network":
client.delete_share_network(
res_id, microversion=res["microversion"])
client.wait_for_share_network_deletion(
res_id, microversion=res["microversion"])
elif res["type"] is "share":
client.delete_share(
res_id, microversion=res["microversion"])
client.wait_for_share_deletion(
res_id, microversion=res["microversion"])
elif res["type"] is "snapshot":
client.delete_snapshot(
res_id, microversion=res["microversion"])
client.wait_for_snapshot_deletion(
res_id, microversion=res["microversion"])
else:
LOG.warning("Provided unsupported resource type for "
"cleanup '%s'. Skipping.", res["type"])
res["deleted"] = True
@classmethod
def get_admin_client(cls):
manilaclient = client.ManilaCLIClient(
username=CONF.admin_username,
password=CONF.admin_password,
tenant_name=CONF.admin_tenant_name,
project_domain_name=CONF.admin_project_domain_name,
project_domain_id=CONF.admin_project_domain_id,
user_domain_name=CONF.admin_user_domain_name,
user_domain_id=CONF.admin_user_domain_id,
uri=CONF.admin_auth_url or CONF.auth_url,
insecure=CONF.insecure,
cli_dir=CONF.manila_exec_dir)
# Set specific for admin project share network
manilaclient.share_network = CONF.admin_share_network
return manilaclient
@classmethod
def get_user_client(cls):
manilaclient = client.ManilaCLIClient(
username=CONF.username,
password=CONF.password,
tenant_name=CONF.tenant_name,
project_domain_name=CONF.project_domain_name,
project_domain_id=CONF.project_domain_id,
user_domain_name=CONF.user_domain_name,
user_domain_id=CONF.user_domain_id,
uri=CONF.auth_url,
insecure=CONF.insecure,
cli_dir=CONF.manila_exec_dir)
# Set specific for user project share network
manilaclient.share_network = CONF.share_network
return manilaclient
@property
def admin_client(self):
if not hasattr(self, '_admin_client'):
self._admin_client = self.get_admin_client()
return self._admin_client
@property
def user_client(self):
if not hasattr(self, '_user_client'):
self._user_client = self.get_user_client()
return self._user_client
def _get_clients(self):
return {'admin': self.admin_client, 'user': self.user_client}
def skip_if_microversion_not_supported(self, microversion):
if not utils.is_microversion_supported(microversion):
raise self.skipException(
"Microversion '%s' is not supported." % microversion)
@classmethod
def create_share_type(cls, name=None, driver_handles_share_servers=True,
snapshot_support=None,
create_share_from_snapshot=None,
revert_to_snapshot=None, mount_snapshot=None,
is_public=True, client=None, cleanup_in_class=True,
microversion=None, extra_specs=None):
if client is None:
client = cls.get_admin_client()
share_type = client.create_share_type(
name=name,
driver_handles_share_servers=driver_handles_share_servers,
snapshot_support=snapshot_support,
is_public=is_public,
microversion=microversion,
extra_specs=extra_specs,
create_share_from_snapshot=create_share_from_snapshot,
revert_to_snapshot=revert_to_snapshot,
mount_snapshot=mount_snapshot,
)
resource = {
"type": "share_type",
"id": share_type["ID"],
"client": client,
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
return share_type
@classmethod
def create_share_network(cls, name=None, description=None,
neutron_net_id=None,
neutron_subnet_id=None, client=None,
cleanup_in_class=True, microversion=None):
if client is None:
client = cls.get_admin_client()
share_network = client.create_share_network(
name=name,
description=description,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
microversion=microversion,
)
resource = {
"type": "share_network",
"id": share_network["id"],
"client": client,
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
return share_network
@classmethod
def create_share(cls, share_protocol=None, size=None, share_network=None,
share_type=None, name=None, description=None,
public=False, snapshot=None, metadata=None,
client=None, cleanup_in_class=False,
wait_for_creation=True, microversion=None):
if client is None:
client = cls.get_admin_client()
data = {
'share_protocol': share_protocol or client.share_protocol,
'size': size or 1,
'name': name,
'description': description,
'public': public,
'snapshot': snapshot,
'metadata': metadata,
'microversion': microversion,
}
share_network = share_network or client.share_network
share_type = share_type or CONF.share_type
if share_network:
data['share_network'] = share_network
if share_type:
data['share_type'] = share_type
share = client.create_share(**data)
resource = {
"type": "share",
"id": share["id"],
"client": client,
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
if wait_for_creation:
client.wait_for_share_status(share['id'], 'available')
return share
@classmethod
def create_security_service(cls, type='ldap', name=None, description=None,
dns_ip=None, server=None, domain=None,
user=None, password=None, client=None,
cleanup_in_class=False, microversion=None):
if client is None:
client = cls.get_admin_client()
data = {
'type': type,
'name': name,
'description': description,
'user': user,
'password': password,
'server': server,
'domain': domain,
'dns_ip': dns_ip,
'microversion': microversion,
}
ss = client.create_security_service(**data)
resource = {
"type": "share",
"id": ss["id"],
"client": client,
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
return ss
@classmethod
def create_snapshot(cls, share, name=None, description=None,
force=False, client=None, wait_for_creation=True,
cleanup_in_class=False, microversion=None):
if client is None:
client = cls.get_admin_client()
data = {
'share': share,
'name': name,
'description': description,
'force': force,
'microversion': microversion,
}
snapshot = client.create_snapshot(**data)
resource = {
"type": "snapshot",
"id": snapshot["id"],
"client": client,
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
if wait_for_creation:
client.wait_for_snapshot_status(snapshot['id'], 'available')
return snapshot

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.lib import exceptions
"""
Exceptions for functional tests.
"""
class ResourceReleaseFailed(exceptions.TempestException):
message = "Failed to release resource '%(res_type)s' with id '%(res_id)s'."
class InvalidResource(exceptions.TempestException):
message = "Provided invalid resource: %(message)s"
class InvalidData(exceptions.TempestException):
message = "Provided invalid data: %(message)s"
class ShareTypeNotFound(exceptions.NotFound):
message = "Share type '%(share_type)s' was not found."
class InvalidConfiguration(exceptions.TempestException):
message = "Invalid configuration: %(reason)s"
class ShareBuildErrorException(exceptions.TempestException):
message = "Share %(share)s failed to build and is in ERROR status."
class SnapshotBuildErrorException(exceptions.TempestException):
message = "Snapshot %(snapshot)s failed to build and is in ERROR status."
class AccessRuleCreateErrorException(exceptions.TempestException):
message = "Access rule %(access)s failed to create and is in ERROR state."
class AccessRuleDeleteErrorException(exceptions.TempestException):
message = "Access rule %(access)s failed to delete and is in ERROR state."
class ShareMigrationException(exceptions.TempestException):
message = ("Share %(share_id)s failed to migrate from "
"host %(src)s to host %(dest)s.")

View File

@ -1,59 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_utils import uuidutils
from manilaclient.tests.functional import base
@ddt.ddt
class ManilaClientTestAvailabilityZonesReadOnly(base.BaseTestCase):
@ddt.data("2.6", "2.7", "2.22")
def test_availability_zone_list(self, microversion):
self.skip_if_microversion_not_supported(microversion)
azs = self.user_client.list_availability_zones(
microversion=microversion)
for az in azs:
self.assertEqual(4, len(az))
for key in ('Id', 'Name', 'Created_At', 'Updated_At'):
self.assertIn(key, az)
self.assertTrue(uuidutils.is_uuid_like(az['Id']))
self.assertIsNotNone(az['Name'])
self.assertIsNotNone(az['Created_At'])
@ddt.data(
('name', ['Name']),
('name,id', ['Name', 'Id']),
('name,created_at', ['Name', 'Created_At']),
('name,id,created_at', ['Name', 'Id', 'Created_At']),
)
@ddt.unpack
def test_availability_zone_list_with_columns(self, columns_arg, expected):
azs = self.user_client.list_availability_zones(columns=columns_arg)
for az in azs:
self.assertEqual(len(expected), len(az))
for key in expected:
self.assertIn(key, az)
if 'Id' in expected:
self.assertTrue(uuidutils.is_uuid_like(az['Id']))
if 'Name' in expected:
self.assertIsNotNone(az['Name'])
if 'Created_At' in expected:
self.assertIsNotNone(az['Created_At'])

View File

@ -1,63 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re
import ddt
from manilaclient.tests.functional import base
@ddt.ddt
class ManilaClientTestCommonReadOnly(base.BaseTestCase):
@ddt.data('admin', 'user')
def test_manila_version(self, role):
self.clients[role].manila('', flags='--version')
@ddt.data('admin', 'user')
def test_help(self, role):
help_text = self.clients[role].manila('help')
lines = help_text.split('\n')
self.assertFirstLineStartsWith(lines, 'usage: manila')
commands = []
cmds_start = lines.index('Positional arguments:')
cmds_end = lines.index('Optional arguments:')
command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
for line in lines[cmds_start:cmds_end]:
match = command_pattern.match(line)
if match:
commands.append(match.group(1))
commands = set(commands)
wanted_commands = set((
'absolute-limits', 'list', 'help', 'quota-show', 'access-list',
'snapshot-list', 'access-allow', 'access-deny',
'share-network-list', 'security-service-list'))
self.assertFalse(wanted_commands - commands)
@ddt.data('admin', 'user')
def test_credentials(self, role):
self.clients[role].manila('credentials')
@ddt.data('admin', 'user')
def test_list_extensions(self, role):
roles = self.parser.listing(
self.clients[role].manila('list-extensions'))
self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated'])
@ddt.data('admin', 'user')
def test_endpoints(self, role):
self.clients[role].manila('endpoints')

View File

@ -1,164 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_utils import uuidutils
from manilaclient.tests.functional import base
@ddt.ddt
class ExportLocationReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(ExportLocationReadWriteTest, cls).setUpClass()
cls.share = cls.create_share(
client=cls.get_user_client(),
cleanup_in_class=True)
@ddt.data('admin', 'user')
def test_list_share_export_locations(self, role):
self.skip_if_microversion_not_supported('2.14')
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_share_export_locations(
self.share['id'])
self.assertGreater(len(export_locations), 0)
expected_keys = ('ID', 'Path', 'Preferred')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['ID']))
self.assertIn(el['Preferred'], ('True', 'False'))
@ddt.data('admin', 'user')
def test_list_share_export_locations_with_columns(self, role):
self.skip_if_microversion_not_supported('2.9')
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_share_export_locations(
self.share['id'], columns='id,path')
self.assertGreater(len(export_locations), 0)
expected_keys = ('Id', 'Path')
unexpected_keys = ('Updated At', 'Created At')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
for key in unexpected_keys:
self.assertNotIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['Id']))
@ddt.data('admin', 'user')
def test_get_share_export_location(self, role):
self.skip_if_microversion_not_supported('2.14')
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_share_export_locations(
self.share['id'])
el = client.get_share_export_location(
self.share['id'], export_locations[0]['ID'])
expected_keys = ['path', 'updated_at', 'created_at', 'id', 'preferred']
if role == 'admin':
expected_keys.extend(['is_admin_only', 'share_instance_id'])
for key in expected_keys:
self.assertIn(key, el)
if role == 'admin':
self.assertTrue(uuidutils.is_uuid_like(el['share_instance_id']))
self.assertIn(el['is_admin_only'], ('True', 'False'))
self.assertTrue(uuidutils.is_uuid_like(el['id']))
self.assertIn(el['preferred'], ('True', 'False'))
for list_k, get_k in (
('ID', 'id'), ('Path', 'path'), ('Preferred', 'preferred')):
self.assertEqual(
export_locations[0][list_k], el[get_k])
def test_list_share_instance_export_locations(self):
self.skip_if_microversion_not_supported('2.14')
client = self.admin_client
share_instances = client.list_share_instances(self.share['id'])
self.assertGreater(len(share_instances), 0)
self.assertIn('ID', share_instances[0])
self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID']))
share_instance_id = share_instances[0]['ID']
export_locations = client.list_share_instance_export_locations(
share_instance_id)
self.assertGreater(len(export_locations), 0)
expected_keys = ('ID', 'Path', 'Is Admin only', 'Preferred')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['ID']))
def test_list_share_instance_export_locations_with_columns(self):
self.skip_if_microversion_not_supported('2.9')
client = self.admin_client
share_instances = client.list_share_instances(self.share['id'])
self.assertGreater(len(share_instances), 0)
self.assertIn('ID', share_instances[0])
self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID']))
share_instance_id = share_instances[0]['ID']
export_locations = client.list_share_instance_export_locations(
share_instance_id, columns='id,path')
self.assertGreater(len(export_locations), 0)
expected_keys = ('Id', 'Path')
unexpected_keys = ('Updated At', 'Created At', 'Is Admin only')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
for key in unexpected_keys:
self.assertNotIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['Id']))
def test_get_share_instance_export_location(self):
self.skip_if_microversion_not_supported('2.14')
client = self.admin_client
share_instances = client.list_share_instances(self.share['id'])
self.assertGreater(len(share_instances), 0)
self.assertIn('ID', share_instances[0])
self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID']))
share_instance_id = share_instances[0]['ID']
export_locations = client.list_share_instance_export_locations(
share_instance_id)
el = client.get_share_instance_export_location(
share_instance_id, export_locations[0]['ID'])
expected_keys = (
'path', 'updated_at', 'created_at', 'id', 'preferred',
'is_admin_only', 'share_instance_id',
)
for key in expected_keys:
self.assertIn(key, el)
self.assertIn(el['is_admin_only'], ('True', 'False'))
self.assertIn(el['preferred'], ('True', 'False'))
self.assertTrue(uuidutils.is_uuid_like(el['id']))
for list_k, get_k in (
('ID', 'id'), ('Path', 'path'), ('Preferred', 'preferred'),
('Is Admin only', 'is_admin_only')):
self.assertEqual(
export_locations[0][list_k], el[get_k])

View File

@ -1,30 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from manilaclient.tests.functional import base
@ddt.ddt
class ManilaClientTestLimitsReadOnly(base.BaseTestCase):
@ddt.data('admin', 'user')
def test_rate_limits(self, role):
self.clients[role].manila('rate-limits')
@ddt.data('admin', 'user')
def test_absolute_limits(self, role):
self.clients[role].manila('absolute-limits')

View File

@ -1,86 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib import exceptions
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
@ddt.ddt
class ManilaClientTestQuotasReadOnly(base.BaseTestCase):
def test_quota_class_show_by_admin(self):
roles = self.parser.listing(
self.clients['admin'].manila('quota-class-show', params='abc'))
self.assertTableStruct(roles, ('Property', 'Value'))
def test_quota_class_show_by_user(self):
self.assertRaises(
exceptions.CommandFailed,
self.clients['user'].manila,
'quota-class-show',
params='abc')
def _get_quotas(self, role, operation, microversion):
roles = self.parser.listing(self.clients[role].manila(
'quota-%s' % operation, microversion=microversion))
self.assertTableStruct(roles, ('Property', 'Value'))
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("1.0")
def test_quota_defaults_api_1_0(self, role):
self._get_quotas(role, "defaults", "1.0")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.0")
def test_quota_defaults_api_2_0(self, role):
self._get_quotas(role, "defaults", "2.0")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.6")
def test_quota_defaults_api_2_6(self, role):
self._get_quotas(role, "defaults", "2.6")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.7")
def test_quota_defaults_api_2_7(self, role):
self._get_quotas(role, "defaults", "2.7")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("1.0")
def test_quota_show_api_1_0(self, role):
self._get_quotas(role, "show", "1.0")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.0")
def test_quota_show_api_2_0(self, role):
self._get_quotas(role, "show", "2.0")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.6")
def test_quota_show_api_2_6(self, role):
self._get_quotas(role, "show", "2.6")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.7")
def test_quota_show_api_2_7(self, role):
self._get_quotas(role, "show", "2.7")
@ddt.data('admin', 'user')
@utils.skip_if_microversion_not_supported("2.25")
def test_quota_show_api_2_25(self, role):
self._get_quotas(role, "show --detail", "2.25")

View File

@ -1,49 +0,0 @@
# Copyright (c) 2015 Clinton Knight. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from manilaclient.tests.functional import base
class ManilaClientTestSchedulerStatsReadOnly(base.BaseTestCase):
def test_pools_list(self):
self.clients['admin'].manila('pool-list')
def test_pools_list_with_debug_flag(self):
self.clients['admin'].manila('pool-list', flags='--debug')
def test_pools_list_with_detail(self):
self.clients['admin'].manila('pool-list', params='--detail')
def test_pools_list_with_share_type_filter(self):
share_type = self.create_share_type(
name=data_utils.rand_name('manilaclient_functional_test'),
snapshot_support=True,
)
self.clients['admin'].manila('pool-list',
params='--share_type ' +
share_type['ID'])
def test_pools_list_with_filters(self):
self.clients['admin'].manila(
'pool-list',
params='--host myhost --backend mybackend --pool mypool')
def test_pools_list_by_user(self):
self.assertRaises(exceptions.CommandFailed,
self.clients['user'].manila,
'pool-list')

View File

@ -1,71 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib.common.utils import data_utils
from manilaclient.tests.functional import base
@ddt.ddt
class SecurityServiceReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SecurityServiceReadWriteTest, cls).setUpClass()
cls.name = data_utils.rand_name('autotest')
cls.description = 'fake_description'
cls.user = 'fake_user'
cls.password = 'fake_password'
cls.server = 'fake_server'
cls.domain = 'fake_domain'
cls.dns_ip = '1.2.3.4'
@ddt.data(
{'name': 'test_name'},
{'description': 'test_description'},
{'user': 'test_username'},
{'password': 'test_password'},
{'server': 'test_server'},
{'domain': 'test_domain'},
{'dns_ip': 'test_dns_ip'},
{'name': '""'},
{'description': '""'},
{'user': '""'},
{'password': '""'},
{'server': '""'},
{'domain': '""'},
{'dns_ip': '""'},
)
def test_create_update_security_service(self, ss_data):
expected_data = {
'name': self.name,
'description': self.description,
'user': self.user,
'password': self.password,
'server': self.server,
'domain': self.domain,
'dns_ip': self.dns_ip,
}
ss = self.create_security_service(**expected_data)
update = self.admin_client.update_security_service(ss['id'], **ss_data)
expected_data.update(ss_data)
for k, v in expected_data.items():
if v == '""':
self.assertEqual('None', update[k])
else:
self.assertEqual(v, update[k])

View File

@ -1,45 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from manilaclient.tests.functional import base
@ddt.ddt
class ManilaClientTestServicesReadOnly(base.BaseTestCase):
@ddt.data("1.0", "2.0", "2.6", "2.7")
def test_services_list(self, microversion):
self.skip_if_microversion_not_supported(microversion)
self.admin_client.manila('service-list', microversion=microversion)
def test_list_with_debug_flag(self):
self.clients['admin'].manila('service-list', flags='--debug')
def test_shares_list_filter_by_host(self):
self.clients['admin'].manila('service-list', params='--host host')
def test_shares_list_filter_by_binary(self):
self.clients['admin'].manila('service-list', params='--binary binary')
def test_shares_list_filter_by_zone(self):
self.clients['admin'].manila('service-list', params='--zone zone')
def test_shares_list_filter_by_status(self):
self.clients['admin'].manila('service-list', params='--status status')
def test_shares_list_filter_by_state(self):
self.clients['admin'].manila('service-list', params='--state state')

View File

@ -1,220 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib import exceptions as tempest_lib_exc
from manilaclient import api_versions
from manilaclient import config
from manilaclient.tests.functional import base
CONF = config.CONF
@ddt.ddt
class ShareAccessReadWriteBase(base.BaseTestCase):
protocol = None
access_level = None
@classmethod
def setUpClass(cls):
super(ShareAccessReadWriteBase, cls).setUpClass()
if cls.protocol not in CONF.enable_protocols:
message = "%s tests are disabled." % cls.protocol
raise cls.skipException(message)
if cls.access_level not in CONF.access_levels_mapping.get(
cls.protocol, '').split(' '):
raise cls.skipException("%(level)s tests for %(protocol)s share "
"access are disabled." % {
'level': cls.access_level,
'protocol': cls.protocol
})
cls.access_types = CONF.access_types_mapping.get(
cls.protocol, '').split(' ')
if not cls.access_types:
raise cls.skipException("No access levels were provided for %s "
"share access tests." % cls.protoco)
cls.share = cls.create_share(share_protocol=cls.protocol,
public=True,
cleanup_in_class=True)
cls.share_id = cls.share['id']
# NOTE(vponomaryov): increase following int range when significant
# amount of new tests is added.
int_range = range(20, 50)
cls.access_to = {
# NOTE(vponomaryov): list of unique values is required for ability
# to create lots of access rules for one share using different
# API microversions.
'ip': ['99.88.77.%d' % i for i in int_range],
# NOTE(vponomaryov): following users are fakes and access rules
# that use it are expected to fail, but they are used only for
# API testing.
'user': ['foo_user_%d' % i for i in int_range],
'cert': ['tenant_%d.example.com' % i for i in int_range],
}
def _test_create_list_access_rule_for_share(self, microversion):
access_type = self.access_types[0]
access = self.user_client.access_allow(
self.share['id'], access_type, self.access_to[access_type].pop(),
self.access_level, microversion=microversion)
return access
@ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33")
def test_create_list_access_rule_for_share(self, microversion):
self.skip_if_microversion_not_supported(microversion)
access = self._test_create_list_access_rule_for_share(
microversion=microversion)
access_list = self.user_client.list_access(
self.share['id'],
microversion=microversion
)
self.assertTrue(any(
[item for item in access_list if access['id'] == item['id']]))
self.assertTrue(any(a['access_type'] is not None for a in access_list))
self.assertTrue(any(a['access_to'] is not None for a in access_list))
self.assertTrue(any(a['access_level'] is not None
for a in access_list))
if (api_versions.APIVersion(microversion) >=
api_versions.APIVersion("2.33")):
self.assertTrue(
all(('access_key' and 'created_at' and 'updated_at')
in a for a in access_list))
elif (api_versions.APIVersion(microversion) >=
api_versions.APIVersion("2.21")):
self.assertTrue(all('access_key' in a for a in access_list))
else:
self.assertTrue(all('access_key' not in a for a in access_list))
@ddt.data("1.0", "2.0", "2.6", "2.7")
def test_create_list_access_rule_for_share_select_column(
self,
microversion):
self.skip_if_microversion_not_supported(microversion)
self._test_create_list_access_rule_for_share(
microversion=microversion)
access_list = self.user_client.list_access(
self.share['id'],
columns="access_type,access_to",
microversion=microversion
)
self.assertTrue(any(a['Access_Type'] is not None for a in access_list))
self.assertTrue(any(a['Access_To'] is not None for a in access_list))
self.assertTrue(all('Access_Level' not in a for a in access_list))
self.assertTrue(all('access_level' not in a for a in access_list))
def _create_delete_access_rule(self, share_id, access_type, access_to,
microversion=None):
self.skip_if_microversion_not_supported(microversion)
if access_type not in self.access_types:
raise self.skipException(
"'%(access_type)s' access rules is disabled for protocol "
"'%(protocol)s'." % {"access_type": access_type,
"protocol": self.protocol})
access = self.user_client.access_allow(
share_id, access_type, access_to, self.access_level,
microversion=microversion)
self.assertEqual(share_id, access.get('share_id'))
self.assertEqual(access_type, access.get('access_type'))
self.assertEqual(access_to.replace('\\\\', '\\'),
access.get('access_to'))
self.assertEqual(self.access_level, access.get('access_level'))
if (api_versions.APIVersion(microversion) >=
api_versions.APIVersion("2.33")):
self.assertIn('access_key', access)
self.assertIn('created_at', access)
self.assertIn('updated_at', access)
elif (api_versions.APIVersion(microversion) >=
api_versions.APIVersion("2.21")):
self.assertIn('access_key', access)
else:
self.assertNotIn('access_key', access)
self.user_client.wait_for_access_rule_status(share_id, access['id'])
self.user_client.access_deny(share_id, access['id'])
self.user_client.wait_for_access_rule_deletion(share_id, access['id'])
self.assertRaises(tempest_lib_exc.NotFound,
self.user_client.get_access, share_id, access['id'])
@ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33")
def test_create_delete_ip_access_rule(self, microversion):
self._create_delete_access_rule(
self.share_id, 'ip', self.access_to['ip'].pop(), microversion)
@ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33")
def test_create_delete_user_access_rule(self, microversion):
self._create_delete_access_rule(
self.share_id, 'user', CONF.username_for_user_rules, microversion)
@ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33")
def test_create_delete_cert_access_rule(self, microversion):
self._create_delete_access_rule(
self.share_id, 'cert', self.access_to['cert'].pop(), microversion)
class NFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'nfs'
access_level = 'rw'
class NFSShareROAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'nfs'
access_level = 'ro'
class CIFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'cifs'
access_level = 'rw'
class CIFSShareROAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'cifs'
access_level = 'ro'
class GlusterFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'glusterfs'
access_level = 'rw'
class GlusterFSShareROAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'glusterfs'
access_level = 'ro'
class HDFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'hdfs'
access_level = 'rw'
class HDFSShareROAccessReadWriteTest(ShareAccessReadWriteBase):
protocol = 'hdfs'
access_level = 'ro'
def load_tests(loader, tests, _):
result = []
for test_case in tests:
if type(test_case._tests[0]) is ShareAccessReadWriteBase:
continue
result.append(test_case)
return loader.suiteClass(result)

View File

@ -1,153 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as tempest_lib_exc
from manilaclient.tests.functional import base
@ddt.ddt
class ShareNetworksReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(ShareNetworksReadWriteTest, cls).setUpClass()
cls.name = data_utils.rand_name('autotest')
cls.description = 'fake_description'
cls.neutron_net_id = 'fake_neutron_net_id'
cls.neutron_subnet_id = 'fake_neutron_subnet_id'
cls.sn = cls.create_share_network(
name=cls.name,
description=cls.description,
neutron_net_id=cls.neutron_net_id,
neutron_subnet_id=cls.neutron_subnet_id,
)
@ddt.data(
{'name': data_utils.rand_name('autotest_share_network_name')},
{'description': 'fake_description'},
{'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id'},
)
def test_create_delete_share_network(self, net_data):
sn = self.create_share_network(cleanup_in_class=False, **net_data)
expected_data = {
'name': 'None',
'description': 'None',
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
}
expected_data.update(net_data)
for k, v in expected_data.items():
self.assertEqual(v, sn[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
def test_get_share_network_with_neutron_data(self):
get = self.admin_client.get_share_network(self.sn['id'])
self.assertEqual(self.name, get['name'])
self.assertEqual(self.description, get['description'])
self.assertEqual(self.neutron_net_id, get['neutron_net_id'])
self.assertEqual(self.neutron_subnet_id, get['neutron_subnet_id'])
@ddt.data(
{'name': data_utils.rand_name('autotest_share_network_name')},
{'description': 'fake_description'},
{'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id'},
{'name': '""'},
{'description': '""'},
{'neutron_net_id': '""'},
{'neutron_subnet_id': '""'},
)
def test_create_update_share_network(self, net_data):
sn = self.create_share_network(cleanup_in_class=False)
update = self.admin_client.update_share_network(sn['id'], **net_data)
expected_data = {
'name': 'None',
'description': 'None',
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
}
update_values = dict([(k, v) for k, v in net_data.items()
if v != '""'])
expected_data.update(update_values)
for k, v in expected_data.items():
self.assertEqual(v, update[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
@ddt.data(True, False)
def test_list_share_networks(self, all_tenants):
share_networks = self.admin_client.list_share_networks(all_tenants)
self.assertTrue(
any(self.sn['id'] == sn['id'] for sn in share_networks))
for sn in share_networks:
self.assertEqual(2, len(sn))
self.assertIn('id', sn)
self.assertIn('name', sn)
def test_list_share_networks_select_column(self):
share_networks = self.admin_client.list_share_networks(columns="id")
self.assertTrue(any(s['Id'] is not None for s in share_networks))
self.assertTrue(all('Name' not in s for s in share_networks))
self.assertTrue(all('name' not in s for s in share_networks))
def _list_share_networks_with_filters(self, filters):
share_networks = self.admin_client.list_share_networks(filters=filters)
self.assertGreater(len(share_networks), 0)
self.assertTrue(
any(self.sn['id'] == sn['id'] for sn in share_networks))
for sn in share_networks:
try:
get = self.admin_client.get_share_network(sn['id'])
except tempest_lib_exc.NotFound:
# NOTE(vponomaryov): Case when some share network was deleted
# between our 'list' and 'get' requests. Skip such case.
continue
for k, v in filters.items():
self.assertIn(k, get)
self.assertEqual(v, get[k])
def test_list_share_networks_filter_by_project_id(self):
project_id = self.admin_client.get_project_id(
self.admin_client.tenant_name)
filters = {'project_id': project_id}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_name(self):
filters = {'name': self.name}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_neutron_net_id(self):
filters = {'neutron_net_id': self.neutron_net_id}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_neutron_subnet_id(self):
filters = {'neutron_subnet_id': self.neutron_subnet_id}
self._list_share_networks_with_filters(filters)

View File

@ -1,146 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from manilaclient import config
from manilaclient.tests.functional import base
CONF = config.CONF
@ddt.ddt
class ShareServersReadOnlyTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(ShareServersReadOnlyTest, cls).setUpClass()
cls.client = cls.get_admin_client()
def test_share_server_list(self):
self.client.list_share_servers()
def test_share_server_list_with_host_param(self):
self.client.list_share_servers(filters={'host': 'fake_host'})
def test_share_server_list_with_status_param(self):
self.client.list_share_servers(filters={'status': 'fake_status'})
def test_share_server_list_with_share_network_param(self):
self.client.list_share_servers(filters={'share_network': 'fake_sn'})
def test_share_server_list_with_project_id_param(self):
self.client.list_share_servers(
filters={'project_id': 'fake_project_id'})
@ddt.data(
'host', 'status', 'project_id', 'share_network',
'host,status,project_id,share_network',
)
def test_share_server_list_with_specified_columns(self, columns):
self.client.list_share_servers(columns=columns)
def test_share_server_list_by_user(self):
self.assertRaises(
exceptions.CommandFailed, self.user_client.list_share_servers)
@ddt.ddt
class ShareServersReadWriteBase(base.BaseTestCase):
protocol = None
@classmethod
def setUpClass(cls):
super(ShareServersReadWriteBase, cls).setUpClass()
if not CONF.run_share_servers_tests:
message = "share-servers tests are disabled."
raise cls.skipException(message)
if cls.protocol not in CONF.enable_protocols:
message = "%s tests are disabled." % cls.protocol
raise cls.skipException(message)
cls.client = cls.get_admin_client()
if not cls.client.share_network:
message = "Can run only with DHSS=True mode"
raise cls.skipException(message)
def test_get_and_delete_share_server(self):
name = data_utils.rand_name('autotest_share_name')
description = data_utils.rand_name('autotest_share_description')
# We create separate share network to be able to delete share server
# further knowing that it is not used by any other concurrent test.
common_share_network = self.client.get_share_network(
self.client.share_network)
neutron_net_id = (
common_share_network['neutron_net_id']
if 'none' not in common_share_network['neutron_net_id'].lower()
else None)
neutron_subnet_id = (
common_share_network['neutron_subnet_id']
if 'none' not in common_share_network['neutron_subnet_id'].lower()
else None)
share_network = self.client.create_share_network(
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
)
self.share = self.create_share(
share_protocol=self.protocol,
size=1,
name=name,
description=description,
share_network=share_network['id'],
client=self.client,
)
share_server_id = self.client.get_share(
self.share['id'])['share_server_id']
# Get share server
server = self.client.get_share_server(share_server_id)
expected_keys = (
'id', 'host', 'status', 'created_at', 'updated_at',
'share_network_id', 'share_network_name', 'project_id',
)
for key in expected_keys:
self.assertIn(key, server)
# Delete share
self.client.delete_share(self.share['id'])
self.client.wait_for_share_deletion(self.share['id'])
# Delete share server
self.client.delete_share_server(share_server_id)
self.client.wait_for_share_server_deletion(share_server_id)
class ShareServersReadWriteNFSTest(ShareServersReadWriteBase):
protocol = 'nfs'
class ShareServersReadWriteCIFSTest(ShareServersReadWriteBase):
protocol = 'cifs'
def load_tests(loader, tests, _):
result = []
for test_case in tests:
if type(test_case._tests[0]) is ShareServersReadWriteBase:
continue
result.append(test_case)
return loader.suiteClass(result)

View File

@ -1,365 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib.common.utils import data_utils
from manilaclient import api_versions
from manilaclient.tests.functional import base
from manilaclient.tests.unit.v2 import test_types as unit_test_types
@ddt.ddt
class ShareTypesReadOnlyTest(base.BaseTestCase):
@ddt.data(
("admin", "1.0"),
("admin", "2.0"),
("admin", "2.6"),
("admin", "2.7"),
("user", "1.0"),
("user", "2.0"),
("user", "2.6"),
("user", "2.7"),
)
@ddt.unpack
def test_share_type_list(self, role, microversion):
self.skip_if_microversion_not_supported(microversion)
self.clients[role].manila("type-list", microversion=microversion)
@ddt.data("1.0", "2.0", "2.6", "2.7")
def test_extra_specs_list(self, microversion):
self.skip_if_microversion_not_supported(microversion)
self.admin_client.manila("extra-specs-list", microversion=microversion)
@ddt.ddt
class ShareTypesReadWriteTest(base.BaseTestCase):
create_keys = (
'ID', 'Name', 'Visibility', 'is_default', 'required_extra_specs',
'optional_extra_specs')
def _share_type_listed_by(self, share_type_id, by_admin=False,
list_all=False, microversion=None):
client = self.admin_client if by_admin else self.user_client
share_types = client.list_share_types(
list_all=list_all, microversion=microversion)
return any(share_type_id == st['ID'] for st in share_types)
def _verify_access(self, share_type_id, is_public, microversion=None):
if is_public:
# Verify that it is listed with common 'type-list' operation.
share_types = self.admin_client.list_share_types(
list_all=False, microversion=microversion)
self.assertTrue(
any(share_type_id == st['ID'] for st in share_types))
else:
# Verify that it is not listed for user
self.assertFalse(self._share_type_listed_by(
share_type_id=share_type_id, by_admin=False, list_all=True,
microversion=microversion))
# Verify it is listed for admin
self.assertTrue(self._share_type_listed_by(
share_type_id=share_type_id, by_admin=True, list_all=True,
microversion=microversion))
# Verify it is not listed by default
self.assertFalse(self._share_type_listed_by(
share_type_id=share_type_id, by_admin=True, list_all=False,
microversion=microversion))
@ddt.data(*unit_test_types.get_valid_type_create_data_2_0())
@ddt.unpack
def test_create_delete_share_type(
self, is_public, dhss, spec_snapshot_support, extra_specs):
self.skip_if_microversion_not_supported('2.0')
self._test_create_delete_share_type(
'2.0', is_public, dhss, spec_snapshot_support,
None, None, None, extra_specs)
@ddt.data(*unit_test_types.get_valid_type_create_data_2_24())
@ddt.unpack
def test_create_delete_share_type_2_24(
self, is_public, dhss, spec_snapshot_support,
spec_create_share_from_snapshot, extra_specs):
self.skip_if_microversion_not_supported('2.24')
self._test_create_delete_share_type(
'2.24', is_public, dhss, spec_snapshot_support,
spec_create_share_from_snapshot, None, None, extra_specs)
@ddt.data(*unit_test_types.get_valid_type_create_data_2_27())
@ddt.unpack
def test_create_delete_share_type_2_27(
self, is_public, dhss, spec_snapshot_support,
spec_create_share_from_snapshot, spec_revert_to_snapshot_support,
extra_specs):
self.skip_if_microversion_not_supported('2.27')
self._test_create_delete_share_type(
'2.27', is_public, dhss, spec_snapshot_support,
spec_create_share_from_snapshot, spec_revert_to_snapshot_support,
None, extra_specs)
def _test_create_delete_share_type(self, microversion, is_public, dhss,
spec_snapshot_support,
spec_create_share_from_snapshot,
spec_revert_to_snapshot_support,
spec_mount_snapshot_support,
extra_specs):
share_type_name = data_utils.rand_name('manilaclient_functional_test')
if extra_specs is None:
extra_specs = {}
# Create share type
share_type = self.create_share_type(
name=share_type_name,
driver_handles_share_servers=dhss,
snapshot_support=spec_snapshot_support,
create_share_from_snapshot=spec_create_share_from_snapshot,
revert_to_snapshot=spec_revert_to_snapshot_support,
mount_snapshot=spec_mount_snapshot_support,
is_public=is_public,
microversion=microversion,
extra_specs=extra_specs)
# Verify response body
for key in self.create_keys:
self.assertIn(key, share_type)
# Verify type name
self.assertEqual(share_type_name, share_type['Name'])
# Verify required DHSS extra spec
dhss_expected = 'driver_handles_share_servers : %s' % dhss
self.assertEqual(dhss_expected, share_type['required_extra_specs'])
# Determine expected extra specs. Note that prior to 2.24,
# the standard 'snapshot_support' extra spec was required.
expected_extra_specs = []
for key, val in extra_specs.items():
expected_extra_specs.append(('{} : {}'.format(key, val)).strip())
if (api_versions.APIVersion(microversion) <
api_versions.APIVersion('2.24')):
if 'snapshot_support' not in extra_specs:
if spec_snapshot_support is None:
expected_extra_specs.append(
('{} : {}'.format('snapshot_support', True)).strip())
else:
expected_extra_specs.append(
('{} : {}'.format(
'snapshot_support',
spec_snapshot_support)).strip())
else:
if spec_snapshot_support is not None:
expected_extra_specs.append(
('{} : {}'.format(
'snapshot_support',
spec_snapshot_support)).strip())
if spec_create_share_from_snapshot is not None:
expected_extra_specs.append(
('{} : {}'.format(
'create_share_from_snapshot_support',
spec_create_share_from_snapshot)).strip())
if spec_revert_to_snapshot_support is not None:
expected_extra_specs.append(
('{} : {}'.format(
'revert_to_snapshot_support',
spec_revert_to_snapshot_support)).strip())
if spec_mount_snapshot_support is not None:
expected_extra_specs.append(
('{} : {}'.format(
'mount_snapshot_support',
spec_mount_snapshot_support)).strip())
# Verify optional extra specs
optional_extra_specs = share_type['optional_extra_specs']
if optional_extra_specs == '':
optional_extra_specs = []
elif not isinstance(optional_extra_specs, list):
optional_extra_specs = [optional_extra_specs]
self.assertEqual(len(expected_extra_specs), len(optional_extra_specs))
for e in optional_extra_specs:
self.assertIn(e.strip(), expected_extra_specs)
# Verify public & default attributes
self.assertEqual('public' if is_public else 'private',
share_type['Visibility'].lower())
self.assertEqual('-', share_type['is_default'])
# Verify its access
st_id = share_type['ID']
self._verify_access(share_type_id=st_id,
is_public=is_public,
microversion=microversion)
# Delete share type
self.admin_client.delete_share_type(st_id, microversion=microversion)
# Wait for share type deletion
self.admin_client.wait_for_share_type_deletion(
st_id, microversion=microversion)
# Verify that it is not listed with common 'type-list' operation.
share_types = self.admin_client.list_share_types(
list_all=False, microversion=microversion)
self.assertFalse(any(st_id == st['ID'] for st in share_types))
@ddt.data("2.6", "2.7")
def test_add_remove_access_to_private_share_type(self, microversion):
self.skip_if_microversion_not_supported(microversion)
share_type_name = data_utils.rand_name('manilaclient_functional_test')
is_public = False
# Create share type
share_type = self.create_share_type(
name=share_type_name,
driver_handles_share_servers='False',
is_public=is_public,
microversion=microversion,
)
st_id = share_type['ID']
user_project_id = self.admin_client.get_project_id(
self.user_client.tenant_name)
self._verify_access(
share_type_id=st_id,
is_public=is_public,
microversion=microversion,
)
# Project ID is in access list - false
st_access_list = self.admin_client.list_share_type_access(
st_id, microversion=microversion)
self.assertNotIn(user_project_id, st_access_list)
# Add access for project of user
self.admin_client.add_share_type_access(
st_id, user_project_id, microversion=microversion)
# Verify it is listed for user as well as for admin
self.assertTrue(self._share_type_listed_by(
share_type_id=st_id, by_admin=False, list_all=True))
self.assertTrue(self._share_type_listed_by(
share_type_id=st_id, by_admin=True, list_all=True))
# Project ID is in access list - true
st_access_list = self.admin_client.list_share_type_access(
st_id, microversion=microversion)
self.assertIn(user_project_id, st_access_list)
# Remove access
self.admin_client.remove_share_type_access(
st_id, user_project_id, microversion=microversion)
self._verify_access(
share_type_id=st_id,
is_public=is_public,
microversion=microversion,
)
# Project ID is in access list - false
st_access_list = self.admin_client.list_share_type_access(
st_id, microversion=microversion)
self.assertNotIn(user_project_id, st_access_list)
@ddt.data("2.6", "2.7")
def test_list_share_type(self, microversion):
share_type_name = data_utils.rand_name('manilaclient_functional_test')
# Create share type
self.create_share_type(
name=share_type_name,
driver_handles_share_servers='False')
share_types = self.admin_client.list_share_types(
list_all=True,
microversion=microversion
)
self.assertTrue(any(s['ID'] is not None for s in share_types))
self.assertTrue(any(s['Name'] is not None for s in share_types))
self.assertTrue(any(s['visibility'] is not None for s in share_types))
@ddt.data("2.6", "2.7")
def test_list_share_type_select_column(self, microversion):
share_type_name = data_utils.rand_name('manilaclient_functional_test')
# Create share type
self.create_share_type(
name=share_type_name,
driver_handles_share_servers='False')
share_types = self.admin_client.list_share_types(
list_all=True,
columns="id,name",
microversion=microversion
)
self.assertTrue(any(s['id'] is not None for s in share_types))
self.assertTrue(any(s['name'] is not None for s in share_types))
self.assertTrue(all('visibility' not in s for s in share_types))
self.assertTrue(all('Visibility' not in s for s in share_types))
@ddt.ddt
class ShareTypeExtraSpecsReadWriteTest(base.BaseTestCase):
@ddt.data(
(True, False),
(True, True),
(False, True),
(False, False),
(False, False, "2.6"),
(False, False, "2.7"),
)
@ddt.unpack
def test_share_type_extra_specs_life_cycle(self, is_public, dhss,
microversion=None):
if microversion:
self.skip_if_microversion_not_supported(microversion)
# Create share type
st = self.create_share_type(
driver_handles_share_servers=dhss, is_public=is_public,
microversion=microversion)
# Add extra specs to share type
st_extra_specs = dict(foo_key='foo_value', bar_key='bar_value')
self.admin_client.set_share_type_extra_specs(
st['ID'], st_extra_specs, microversion=microversion)
# View list of extra specs
extra_specs = self.admin_client.list_share_type_extra_specs(
st['ID'], microversion=microversion)
for k, v in st_extra_specs.items():
self.assertIn('%s : %s' % (k, v), extra_specs)
# Remove one extra spec
self.admin_client.unset_share_type_extra_specs(
st['ID'], ('foo_key', ), microversion=microversion)
# Verify that removed extra spec is absent
extra_specs = self.admin_client.list_share_type_extra_specs(
st['ID'], microversion=microversion)
self.assertNotIn('foo_key : foo_value', extra_specs)
self.assertIn('bar_key : bar_value', extra_specs)
self.assertIn('driver_handles_share_servers : %s' % dhss, extra_specs)

View File

@ -1,229 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import testtools
from tempest.lib.common.utils import data_utils
from manilaclient.common import constants
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
class SharesReadWriteBase(base.BaseTestCase):
protocol = None
@classmethod
def setUpClass(cls):
super(SharesReadWriteBase, cls).setUpClass()
if cls.protocol not in CONF.enable_protocols:
message = "%s tests are disabled" % cls.protocol
raise cls.skipException(message)
cls.name = data_utils.rand_name('autotest_share_name')
cls.description = data_utils.rand_name('autotest_share_description')
# NOTE(vponomaryov): following share is used only in one test
# until tests for snapshots appear.
cls.share = cls.create_share(
share_protocol=cls.protocol,
size=1,
name=cls.name,
description=cls.description,
client=cls.get_user_client(),
cleanup_in_class=True)
def test_create_delete_share(self):
name = data_utils.rand_name('autotest_share_name')
create = self.create_share(
self.protocol, name=name, client=self.user_client)
self.assertEqual(name, create['name'])
self.assertEqual('1', create['size'])
self.assertEqual(self.protocol.upper(), create['share_proto'])
self.user_client.delete_share(create['id'])
self.user_client.wait_for_share_deletion(create['id'])
def test_create_update_share(self):
name = data_utils.rand_name('autotest_share_name')
new_name = 'new_' + name
description = data_utils.rand_name('autotest_share_description')
new_description = 'new_' + description
create = self.create_share(
self.protocol, name=name, description=description,
client=self.user_client)
self.assertEqual(name, create['name'])
self.assertEqual(description, create['description'])
self.assertEqual('False', create['is_public'])
self.user_client.update_share(
create['id'], new_name, new_description, True)
get = self.user_client.get_share(create['id'])
self.assertEqual(new_name, get['name'])
self.assertEqual(new_description, get['description'])
self.assertEqual('True', get['is_public'])
def test_get_share(self):
get = self.user_client.get_share(self.share['id'])
self.assertEqual(self.name, get['name'])
self.assertEqual(self.description, get['description'])
self.assertEqual('1', get['size'])
self.assertEqual(self.protocol.upper(), get['share_proto'])
@ddt.ddt
class SharesTestMigration(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SharesTestMigration, cls).setUpClass()
cls.old_type = cls.create_share_type(
data_utils.rand_name('test_share_type'),
driver_handles_share_servers=True)
cls.new_type = cls.create_share_type(
data_utils.rand_name('test_share_type'),
driver_handles_share_servers=True)
cls.error_type = cls.create_share_type(
data_utils.rand_name('test_share_type'),
driver_handles_share_servers=True,
extra_specs={'cause_error': 'no_valid_host'})
cls.old_share_net = cls.get_user_client().get_share_network(
cls.get_user_client().share_network)
cls.new_share_net = cls.create_share_network(
neutron_net_id=cls.old_share_net['neutron_net_id'],
neutron_subnet_id=cls.old_share_net['neutron_subnet_id'])
@utils.skip_if_microversion_not_supported('2.22')
@ddt.data('migration_error', 'migration_success', 'None')
def test_reset_task_state(self, state):
share = self.create_share(
share_protocol='nfs',
size=1,
name=data_utils.rand_name('autotest_share_name'),
client=self.get_user_client(),
share_type=self.old_type['ID'],
share_network=self.old_share_net['id'],
wait_for_creation=True)
share = self.user_client.get_share(share['id'])
self.admin_client.reset_task_state(share['id'], state)
share = self.user_client.get_share(share['id'])
self.assertEqual(state, share['task_state'])
@utils.skip_if_microversion_not_supported('2.29')
@testtools.skipUnless(
CONF.run_migration_tests, 'Share migration tests are disabled.')
@ddt.data('cancel', 'success', 'error')
def test_full_migration(self, test_type):
# We are testing with DHSS=True only because it allows us to specify
# new_share_network.
share = self.create_share(
share_protocol='nfs',
size=1,
name=data_utils.rand_name('autotest_share_name'),
client=self.get_user_client(),
share_type=self.old_type['ID'],
share_network=self.old_share_net['id'],
wait_for_creation=True)
share = self.admin_client.get_share(share['id'])
pools = self.admin_client.pool_list(detail=True)
dest_pool = utils.choose_matching_backend(
share, pools, self.new_type)
self.assertIsNotNone(dest_pool)
source_pool = share['host']
new_type = self.new_type
if test_type == 'error':
statuses = constants.TASK_STATE_MIGRATION_ERROR
new_type = self.error_type
else:
statuses = (constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
constants.TASK_STATE_DATA_COPYING_COMPLETED)
self.admin_client.migration_start(
share['id'], dest_pool, writable=True, nondisruptive=False,
preserve_metadata=True, preserve_snapshots=True,
force_host_assisted_migration=False,
new_share_network=self.new_share_net['id'],
new_share_type=new_type['ID'])
share = self.admin_client.wait_for_migration_task_state(
share['id'], dest_pool, statuses)
progress = self.admin_client.migration_get_progress(share['id'])
self.assertEqual('100', progress['total_progress'])
self.assertEqual(source_pool, share['host'])
self.assertEqual(self.old_type['ID'], share['share_type'])
self.assertEqual(self.old_share_net['id'], share['share_network_id'])
if test_type == 'error':
self.assertEqual(statuses, progress['task_state'])
else:
if test_type == 'success':
self.admin_client.migration_complete(share['id'])
statuses = constants.TASK_STATE_MIGRATION_SUCCESS
elif test_type == 'cancel':
self.admin_client.migration_cancel(share['id'])
statuses = constants.TASK_STATE_MIGRATION_CANCELLED
share = self.admin_client.wait_for_migration_task_state(
share['id'], dest_pool, statuses)
progress = self.admin_client.migration_get_progress(share['id'])
self.assertEqual(statuses, progress['task_state'])
if test_type == 'success':
self.assertEqual(dest_pool, share['host'])
self.assertEqual(new_type['ID'], share['share_type'])
self.assertEqual(self.new_share_net['id'],
share['share_network_id'])
else:
self.assertEqual(source_pool, share['host'])
self.assertEqual(self.old_type['ID'], share['share_type'])
self.assertEqual(self.old_share_net['id'],
share['share_network_id'])
class NFSSharesReadWriteTest(SharesReadWriteBase):
protocol = 'nfs'
class CIFSSharesReadWriteTest(SharesReadWriteBase):
protocol = 'cifs'
class GlusterFSSharesReadWriteTest(SharesReadWriteBase):
protocol = 'glusterfs'
class HDFSSharesReadWriteTest(SharesReadWriteBase):
protocol = 'hdfs'

View File

@ -1,242 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
import testtools
from manilaclient import config
from manilaclient.tests.functional import base
CONF = config.CONF
@ddt.ddt
class SharesListReadOnlyTest(base.BaseTestCase):
@ddt.data('admin', 'user')
def test_shares_list(self, role):
self.clients[role].manila('list')
@ddt.data('admin', 'user')
def test_list_with_debug_flag(self, role):
self.clients[role].manila('list', flags='--debug')
@ddt.data('admin', 'user')
def test_shares_list_all_tenants(self, role):
self.clients[role].manila('list', params='--all-tenants')
@ddt.data('admin', 'user')
def test_shares_list_filter_by_name(self, role):
self.clients[role].manila('list', params='--name name')
@ddt.data('admin', 'user')
def test_shares_list_filter_by_status(self, role):
self.clients[role].manila('list', params='--status status')
def test_shares_list_filter_by_share_server_as_admin(self):
self.clients['admin'].manila('list', params='--share-server fake')
def test_shares_list_filter_by_share_server_as_user(self):
self.assertRaises(
exceptions.CommandFailed,
self.clients['user'].manila,
'list',
params='--share-server fake')
@ddt.data('admin', 'user')
def test_shares_list_filter_by_project_id(self, role):
self.clients[role].manila('list', params='--project-id fake')
def test_shares_list_filter_by_host(self):
self.clients['admin'].manila('list', params='--host fake')
@ddt.data('admin', 'user')
def test_shares_list_with_limit_and_offset(self, role):
self.clients[role].manila('list', params='--limit 1 --offset 1')
@ddt.data(
{'role': 'admin', 'direction': 'asc'},
{'role': 'admin', 'direction': 'desc'},
{'role': 'user', 'direction': 'asc'},
{'role': 'user', 'direction': 'desc'})
@ddt.unpack
def test_shares_list_with_sorting(self, role, direction):
self.clients[role].manila(
'list', params='--sort-key host --sort-dir ' + direction)
@ddt.data('admin', 'user')
def test_snapshot_list(self, role):
self.clients[role].manila('snapshot-list')
@ddt.data('admin', 'user')
def test_snapshot_list_all_tenants(self, role):
self.clients[role].manila('snapshot-list', params='--all-tenants')
@ddt.data('admin', 'user')
def test_snapshot_list_filter_by_name(self, role):
self.clients[role].manila('snapshot-list', params='--name name')
@ddt.data('admin', 'user')
def test_snapshot_list_filter_by_status(self, role):
self.clients[role].manila('snapshot-list', params='--status status')
@ddt.ddt
class SharesListReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SharesListReadWriteTest, cls).setUpClass()
cls.private_name = data_utils.rand_name('autotest_share_name')
cls.private_description = data_utils.rand_name(
'autotest_share_description')
cls.public_name = data_utils.rand_name('autotest_public_share_name')
cls.public_description = data_utils.rand_name(
'autotest_public_share_description')
cls.admin_private_name = data_utils.rand_name(
'autotest_admin_private_share_name')
cls.admin_private_description = data_utils.rand_name(
'autotest_admin_private_share_description')
cls.admin_private_share = cls.create_share(
name=cls.admin_private_name,
description=cls.admin_private_description,
public=False,
cleanup_in_class=True,
client=None,
wait_for_creation=False)
cls.private_share = cls.create_share(
name=cls.private_name,
description=cls.private_description,
public=False,
cleanup_in_class=True,
client=cls.get_user_client(),
wait_for_creation=False)
cls.public_share = cls.create_share(
name=cls.public_name,
description=cls.public_description,
public=True,
client=cls.get_user_client(),
cleanup_in_class=True)
for share_id in (cls.private_share['id'], cls.public_share['id'],
cls.admin_private_share['id']):
cls.get_admin_client().wait_for_share_status(share_id, 'available')
def _list_shares(self, filters=None):
filters = filters or dict()
shares = self.user_client.list_shares(filters=filters)
self.assertGreater(len(shares), 0)
if filters:
for share in shares:
try:
get = self.user_client.get_share(share['ID'])
except exceptions.NotFound:
# NOTE(vponomaryov): Case when some share was deleted
# between our 'list' and 'get' requests. Skip such case.
# It occurs with concurrently running tests.
continue
for k, v in filters.items():
if k in ('share_network', 'share-network'):
k = 'share_network_id'
if v != 'deleting' and get[k] == 'deleting':
continue
self.assertEqual(v, get[k])
def test_list_shares(self):
self._list_shares()
@ddt.data(1, 0)
def test_list_shares_for_all_tenants(self, all_tenants):
shares = self.admin_client.list_shares(all_tenants=all_tenants)
self.assertLessEqual(1, len(shares))
if all_tenants:
self.assertTrue(all('Project ID' in s for s in shares))
for s_id in (self.private_share['id'], self.public_share['id'],
self.admin_private_share['id']):
self.assertTrue(any(s_id == s['ID'] for s in shares))
else:
self.assertTrue(all('Project ID' not in s for s in shares))
self.assertTrue(any(self.admin_private_share['id'] == s['ID']
for s in shares))
if self.private_share['project_id'] != (
self.admin_private_share['project_id']):
for s_id in (
self.private_share['id'], self.public_share['id']):
self.assertFalse(any(s_id == s['ID'] for s in shares))
@ddt.data(True, False)
def test_list_shares_with_public(self, public):
shares = self.user_client.list_shares(is_public=public)
self.assertGreater(len(shares), 1)
if public:
self.assertTrue(all('Project ID' in s for s in shares))
else:
self.assertTrue(all('Project ID' not in s for s in shares))
def test_list_shares_by_name(self):
shares = self.user_client.list_shares(
filters={'name': self.private_name})
self.assertEqual(1, len(shares))
self.assertTrue(
any(self.private_share['id'] == s['ID'] for s in shares))
for share in shares:
get = self.user_client.get_share(share['ID'])
self.assertEqual(self.private_name, get['name'])
def test_list_shares_by_share_type(self):
share_type_id = self.user_client.get_share_type(
self.private_share['share_type'])['ID']
# NOTE(vponomaryov): this is API 2.6+ specific
self._list_shares({'share_type': share_type_id})
def test_list_shares_by_status(self):
self._list_shares({'status': 'available'})
def test_list_shares_by_project_id(self):
project_id = self.user_client.get_project_id(
self.user_client.tenant_name)
self._list_shares({'project_id': project_id})
@testtools.skipUnless(
CONF.share_network, "Usage of Share networks is disabled")
def test_list_shares_by_share_network(self):
share_network_id = self.user_client.get_share_network(
CONF.share_network)['id']
self._list_shares({'share_network': share_network_id})
@ddt.data(
{'limit': 1},
{'limit': 2},
{'limit': 1, 'offset': 1},
{'limit': 2, 'offset': 0},
)
def test_list_shares_with_limit(self, filters):
shares = self.user_client.list_shares(filters=filters)
self.assertEqual(filters['limit'], len(shares))
def test_list_share_select_column(self):
shares = self.user_client.list_shares(columns="Name,Size")
self.assertTrue(any(s['Name'] is not None for s in shares))
self.assertTrue(any(s['Size'] is not None for s in shares))
self.assertTrue(all('Description' not in s for s in shares))

View File

@ -1,159 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from manilaclient.tests.functional import base
@ddt.ddt
class SharesMetadataReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SharesMetadataReadWriteTest, cls).setUpClass()
cls.share = cls.create_share(
client=cls.get_user_client(),
cleanup_in_class=True)
def test_set_metadata_in_share_creation(self):
md = {"key1": "value1", "key2": "value2"}
# Create share with metadata
share = self.create_share(
metadata=md, cleanup_in_class=False, client=self.get_user_client())
# Read share metadata
metadata = self.user_client.get_share_metadata(share["id"])
# Verify share metadata
self.assertEqual(2, len(metadata))
self.assertIn('key1', metadata)
self.assertIn('key2', metadata)
self.assertEqual(md['key1'], metadata['key1'])
self.assertEqual(md['key2'], metadata['key2'])
def test_set_and_get_metadata(self):
# Create share
share = self.create_share(
cleanup_in_class=False, client=self.get_user_client())
# Set share metadata
md = {"key3": "value3", "key4": "value4"}
self.user_client.set_share_metadata(share["id"], md)
# Read share metadata
metadata = self.user_client.get_share_metadata(share["id"])
# Verify share metadata
self.assertEqual(2, len(metadata))
self.assertIn('key3', metadata)
self.assertIn('key4', metadata)
self.assertEqual(md['key3'], metadata['key3'])
self.assertEqual(md['key4'], metadata['key4'])
def test_set_and_delete_metadata(self):
# Create share
share = self.create_share(
cleanup_in_class=False, client=self.get_user_client())
# Set share metadata
md = {"key3": "value3", "key4": "value4"}
self.user_client.set_share_metadata(share["id"], md)
# Unset share metadata
self.user_client.unset_share_metadata(share["id"], md.keys())
# Verify deletion of share metadata
metadata = self.user_client.get_share_metadata(share["id"])
self.assertEqual({}, metadata)
def test_set_and_add_metadata(self):
md = {'key5': 'value5'}
# Create share with metadata
share = self.create_share(
metadata=md, cleanup_in_class=False, client=self.get_user_client())
# Set share metadata
self.user_client.set_share_metadata(share["id"], {'key6': 'value6'})
self.user_client.set_share_metadata(share["id"], {'key7': 'value7'})
# Read share metadata
metadata = self.user_client.get_share_metadata(share["id"])
# Verify share metadata
self.assertEqual(3, len(metadata))
for i in (5, 6, 7):
key = 'key%s' % i
self.assertIn(key, metadata)
self.assertEqual('value%s' % i, metadata[key])
def test_set_and_replace_metadata(self):
md = {'key8': 'value8'}
# Create share with metadata
share = self.create_share(
metadata=md, cleanup_in_class=False, client=self.get_user_client())
# Set share metadata
self.user_client.set_share_metadata(share["id"], {'key9': 'value9'})
# Replace all existing share metadata
self.user_client.update_all_share_metadata(
share["id"], {'key10': 'value10'})
# Read share metadata
metadata = self.user_client.get_share_metadata(share["id"])
# Verify share metadata
self.assertEqual(1, len(metadata))
self.assertIn('key10', metadata)
self.assertEqual('value10', metadata['key10'])
@ddt.data(
{"k": "value"},
{"k" * 255: "value"},
{"key": "v"},
{"key": "v" * 1023})
def test_set_metadata_min_max_sizes_of_keys_and_values(self, metadata):
# Set share metadata
self.user_client.set_share_metadata(self.share["id"], metadata)
# Read share metadata
get = self.user_client.get_share_metadata(self.share["id"])
# Verify share metadata
key = list(metadata.keys())[0]
self.assertIn(key, get)
self.assertEqual(metadata[key], get[key])
@ddt.data(
{"k": "value"},
{"k" * 255: "value"},
{"key": "v"},
{"key": "v" * 1023})
def test_update_metadata_min_max_sizes_of_keys_and_values(self, metadata):
# Update share metadata
self.user_client.update_all_share_metadata(self.share["id"], metadata)
# Read share metadata
get = self.user_client.get_share_metadata(self.share["id"])
# Verify share metadata
self.assertEqual(len(metadata), len(get))
for key in metadata:
self.assertIn(key, get)
self.assertEqual(metadata[key], get[key])

View File

@ -1,171 +0,0 @@
# Copyright (c) 2017 Hitachi Data Systems
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.lib import exceptions as tempest_lib_exc
import testtools
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@testtools.skipUnless(CONF.run_snapshot_tests and
CONF.run_mount_snapshot_tests,
"Snapshots or mountable snapshots tests are disabled.")
@utils.skip_if_microversion_not_supported('2.32')
class SnapshotAccessReadBase(base.BaseTestCase):
protocol = None
@classmethod
def setUpClass(cls):
super(SnapshotAccessReadBase, cls).setUpClass()
if cls.protocol not in CONF.enable_protocols:
message = "%s tests are disabled." % cls.protocol
raise cls.skipException(message)
cls.access_types = CONF.access_types_mapping.get(
cls.protocol, '').split(' ')
if not cls.access_types:
raise cls.skipException("No access types were provided for %s "
"snapshot access tests." % cls.protocol)
cls.share = cls.create_share(share_protocol=cls.protocol,
public=True,
cleanup_in_class=True,
client=cls.get_user_client())
int_range = range(0, 10)
cls.access_to = {
'ip': ['99.88.77.%d' % i for i in int_range],
'user': ['foo_user_%d' % i for i in int_range],
'cert': ['tenant_%d.example.com' % i for i in int_range],
}
def _test_create_list_access_rule_for_snapshot(self, snapshot_id):
access = []
access_type = self.access_types[0]
for i in range(5):
access_ = self.user_client.snapshot_access_allow(
snapshot_id, access_type,
self.access_to[access_type][i])
access.append(access_)
return access
def test_create_list_access_rule_for_snapshot(self):
snapshot = self.create_snapshot(share=self.share['id'],
client=self.get_user_client(),
cleanup_in_class=False)
access = self._test_create_list_access_rule_for_snapshot(
snapshot['id'])
access_list = self.user_client.list_access(
snapshot['id'], is_snapshot=True)
for i in range(5):
self.assertIn(access[i]['id'],
[access_list[j]['id'] for j in range(5)])
self.assertIn(access[i]['access_type'],
[access_list[j]['access_type'] for j in range(5)])
self.assertIn(access[i]['access_to'],
[access_list[j]['access_to'] for j in range(5)])
self.assertIsNotNone(access_list[i]['access_type'])
self.assertIsNotNone(access_list[i]['access_to'])
def test_create_list_access_rule_for_snapshot_select_column(self):
snapshot = self.create_snapshot(share=self.share['id'],
client=self.get_user_client(),
cleanup_in_class=False)
self._test_create_list_access_rule_for_snapshot(snapshot['id'])
access_list = self.user_client.list_access(
snapshot['id'], columns="access_type,access_to", is_snapshot=True)
self.assertTrue(any(x['Access_Type'] is not None for x in access_list))
self.assertTrue(any(x['Access_To'] is not None for x in access_list))
def _create_delete_access_rule(self, snapshot_id, access_type, access_to):
if access_type not in self.access_types:
raise self.skipException(
"'%(access_type)s' access rules is disabled for protocol "
"'%(protocol)s'." % {"access_type": access_type,
"protocol": self.protocol})
access = self.user_client.snapshot_access_allow(
snapshot_id, access_type, access_to)
self.assertEqual(access_type, access.get('access_type'))
self.assertEqual(access_to.replace('\\\\', '\\'),
access.get('access_to'))
self.user_client.wait_for_access_rule_status(
snapshot_id, access['id'], is_snapshot=True)
self.user_client.snapshot_access_deny(snapshot_id, access['id'])
self.user_client.wait_for_access_rule_deletion(
snapshot_id, access['id'], is_snapshot=True)
self.assertRaises(tempest_lib_exc.NotFound,
self.user_client.get_access, snapshot_id,
access['id'], is_snapshot=True)
def test_create_delete_snapshot_ip_access_rule(self):
snapshot = self.create_snapshot(share=self.share['id'],
client=self.get_user_client(),
cleanup_in_class=False)
self._create_delete_access_rule(
snapshot['id'], 'ip', self.access_to['ip'][0])
def test_create_delete_snapshot_user_access_rule(self):
snapshot = self.create_snapshot(share=self.share['id'],
client=self.get_user_client(),
cleanup_in_class=False)
self._create_delete_access_rule(
snapshot['id'], 'user', CONF.username_for_user_rules)
def test_create_delete_snapshot_cert_access_rule(self):
snapshot = self.create_snapshot(share=self.share['id'],
client=self.get_user_client(),
cleanup_in_class=False)
self._create_delete_access_rule(
snapshot['id'], 'cert', self.access_to['cert'][0])
class NFSSnapshotAccessTest(SnapshotAccessReadBase):
protocol = 'nfs'
class CIFSSnapshotAccessTest(SnapshotAccessReadBase):
protocol = 'cifs'
class GlusterFSSnapshotAccessTest(SnapshotAccessReadBase):
protocol = 'glusterfs'
class HDFSSnapshotAccessTest(SnapshotAccessReadBase):
protocol = 'hdfs'
def load_tests(loader, tests, _):
result = []
for test_case in tests:
if type(test_case._tests[0]) is SnapshotAccessReadBase:
continue
result.append(test_case)
return loader.suiteClass(result)

View File

@ -1,122 +0,0 @@
# Copyright 2016 Huawei inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_utils import uuidutils
import testtools
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@ddt.ddt
@testtools.skipUnless(CONF.run_snapshot_tests,
'Snapshot tests disabled.')
@utils.skip_if_microversion_not_supported('2.19')
class SnapshotInstancesTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SnapshotInstancesTest, cls).setUpClass()
cls.share = cls.create_share(
client=cls.get_user_client(),
cleanup_in_class=True)
cls.snapshot = cls.create_snapshot(share=cls.share['id'],
client=cls.get_user_client(),
cleanup_in_class=True)
def test_list_all_snapshot_instances(self):
snapshot_instances = self.admin_client.list_snapshot_instances()
self.assertGreater(len(snapshot_instances), 0)
expected_keys = ('ID', 'Snapshot ID', 'Status')
for si in snapshot_instances:
for key in expected_keys:
self.assertIn(key, si)
self.assertTrue(uuidutils.is_uuid_like(si['ID']))
self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID']))
def test_list_all_snapshot_instances_details(self):
snapshot_instances = self.admin_client.list_snapshot_instances(
detailed=True)
self.assertGreater(len(snapshot_instances), 0)
expected_keys = ('ID', 'Snapshot ID', 'Status', 'Created_at',
'Updated_at', 'Share_id', 'Share_instance_id',
'Progress', 'Provider_location')
for si in snapshot_instances:
for key in expected_keys:
self.assertIn(key, si)
for key in ('ID', 'Snapshot ID', 'Share_id', 'Share_instance_id'):
self.assertTrue(
uuidutils.is_uuid_like(si[key]))
def test_list_snapshot_instance_with_snapshot(self):
snapshot_instances = self.admin_client.list_snapshot_instances(
snapshot_id=self.snapshot['id'])
self.assertEqual(1, len(snapshot_instances))
expected_keys = ('ID', 'Snapshot ID', 'Status')
for si in snapshot_instances:
for key in expected_keys:
self.assertIn(key, si)
self.assertTrue(uuidutils.is_uuid_like(si['ID']))
self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID']))
def test_list_snapshot_instance_with_columns(self):
snapshot_instances = self.admin_client.list_snapshot_instances(
self.snapshot['id'], columns='id,status')
self.assertGreater(len(snapshot_instances), 0)
expected_keys = ('Id', 'Status')
unexpected_keys = ('Snapshot ID', )
for si in snapshot_instances:
for key in expected_keys:
self.assertIn(key, si)
for key in unexpected_keys:
self.assertNotIn(key, si)
self.assertTrue(uuidutils.is_uuid_like(si['Id']))
def test_get_snapshot_instance(self):
snapshot_instances = self.admin_client.list_snapshot_instances(
self.snapshot['id'])
snapshot_instance = self.admin_client.get_snapshot_instance(
snapshot_instances[0]['ID'])
self.assertGreater(len(snapshot_instance), 0)
expected_keys = ('id', 'snapshot_id', 'status', 'created_at',
'updated_at', 'share_id', 'share_instance_id',
'progress', 'provider_location')
for key in expected_keys:
self.assertIn(key, snapshot_instance)
for key in ('id', 'snapshot_id', 'share_id', 'share_instance_id'):
self.assertTrue(
uuidutils.is_uuid_like(snapshot_instance[key]))
def test_snapshot_instance_reset_state(self):
snapshot_instances = self.admin_client.list_snapshot_instances(
self.snapshot['id'])
self.admin_client.reset_snapshot_instance(
snapshot_instances[0]['ID'], 'error')
snapshot_instance = self.admin_client.get_snapshot_instance(
snapshot_instances[0]['ID'])
self.assertEqual('error', snapshot_instance['status'])
self.admin_client.reset_snapshot_instance(snapshot_instance['id'],
'available')

View File

@ -1,120 +0,0 @@
# Copyright (c) 2017 Hitachi Data Systems
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_utils import uuidutils
import testtools
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@ddt.ddt
@testtools.skipUnless(CONF.run_snapshot_tests and
CONF.run_mount_snapshot_tests,
"Snapshots or mountable snapshots tests are disabled.")
@utils.skip_if_microversion_not_supported('2.32')
class SnapshotInstanceExportLocationReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SnapshotInstanceExportLocationReadWriteTest, cls).setUpClass()
cls.share = cls.create_share(
client=cls.get_user_client(),
cleanup_in_class=True)
cls.snapshot = cls.create_snapshot(share=cls.share['id'],
client=cls.get_user_client(),
cleanup_in_class=True)
def test_get_snapshot_instance_export_location(self):
client = self.admin_client
snapshot_instances = client.list_snapshot_instances(
self.snapshot['id'])
self.assertGreater(len(snapshot_instances), 0)
self.assertIn('ID', snapshot_instances[0])
self.assertTrue(uuidutils.is_uuid_like(
snapshot_instances[0]['ID']))
snapshot_instance_id = snapshot_instances[0]['ID']
export_locations = client.list_snapshot_instance_export_locations(
snapshot_instance_id)
el = client.get_snapshot_instance_export_location(
snapshot_instance_id, export_locations[0]['ID'])
expected_keys = ['path', 'id', 'is_admin_only',
'share_snapshot_instance_id', 'updated_at',
'created_at']
for key in expected_keys:
self.assertIn(key, el)
for key, key_el in (
('ID', 'id'), ('Path', 'path'),
('Is Admin only', 'is_admin_only')):
self.assertEqual(export_locations[0][key], el[key_el])
self.assertTrue(uuidutils.is_uuid_like(
el['share_snapshot_instance_id']))
self.assertTrue(uuidutils.is_uuid_like(el['id']))
self.assertIn(el['is_admin_only'], ('True', 'False'))
def test_list_snapshot_instance_export_locations(self):
client = self.admin_client
snapshot_instances = client.list_snapshot_instances(
self.snapshot['id'])
self.assertGreater(len(snapshot_instances), 0)
self.assertIn('ID', snapshot_instances[0])
self.assertTrue(uuidutils.is_uuid_like(snapshot_instances[0]['ID']))
snapshot_instance_id = snapshot_instances[0]['ID']
export_locations = client.list_snapshot_instance_export_locations(
snapshot_instance_id)
self.assertGreater(len(export_locations), 0)
expected_keys = ('ID', 'Path', 'Is Admin only')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['ID']))
def test_list_snapshot_instance_export_locations_with_columns(self):
client = self.admin_client
snapshot_instances = client.list_snapshot_instances(
self.snapshot['id'])
self.assertGreater(len(snapshot_instances), 0)
self.assertIn('ID', snapshot_instances[0])
self.assertTrue(uuidutils.is_uuid_like(snapshot_instances[0]['ID']))
snapshot_instance_id = snapshot_instances[0]['ID']
export_locations = client.list_snapshot_instance_export_locations(
snapshot_instance_id, columns='id,path')
self.assertGreater(len(export_locations), 0)
expected_keys = ('Id', 'Path')
unexpected_keys = ('Updated At', 'Created At', 'Is Admin only')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
for key in unexpected_keys:
self.assertNotIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['Id']))

View File

@ -1,93 +0,0 @@
# Copyright (c) 2017 Hitachi Data Systems
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_utils import uuidutils
import testtools
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@ddt.ddt
@testtools.skipUnless(CONF.run_snapshot_tests and
CONF.run_mount_snapshot_tests,
"Snapshots or mountable snapshots tests are disabled.")
@utils.skip_if_microversion_not_supported('2.32')
class SnapshotExportLocationReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(SnapshotExportLocationReadWriteTest, cls).setUpClass()
cls.share = cls.create_share(
client=cls.get_user_client(),
cleanup_in_class=True)
cls.snapshot = cls.create_snapshot(share=cls.share['id'],
client=cls.get_user_client(),
cleanup_in_class=True)
@ddt.data('admin', 'user')
def test_get_snapshot_export_location(self, role):
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_snapshot_export_locations(
self.snapshot['id'])
el = client.get_snapshot_export_location(
self.snapshot['id'], export_locations[0]['ID'])
expected_keys = ['path', 'id', 'updated_at', 'created_at']
if role == 'admin':
expected_keys.extend(['is_admin_only',
'share_snapshot_instance_id'])
self.assertTrue(uuidutils.is_uuid_like(
el['share_snapshot_instance_id']))
self.assertIn(el['is_admin_only'], ('True', 'False'))
self.assertTrue(uuidutils.is_uuid_like(el['id']))
for key in expected_keys:
self.assertIn(key, el)
@ddt.data('admin', 'user')
def test_list_snapshot_export_locations(self, role):
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_snapshot_export_locations(
self.snapshot['id'])
self.assertGreater(len(export_locations), 0)
expected_keys = ('ID', 'Path')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['ID']))
@ddt.data('admin', 'user')
def test_list_snapshot_export_locations_with_columns(self, role):
client = self.admin_client if role == 'admin' else self.user_client
export_locations = client.list_snapshot_export_locations(
self.snapshot['id'], columns='id,path')
self.assertGreater(len(export_locations), 0)
expected_keys = ('Id', 'Path')
unexpected_keys = ('Updated At', 'Created At')
for el in export_locations:
for key in expected_keys:
self.assertIn(key, el)
for key in unexpected_keys:
self.assertNotIn(key, el)
self.assertTrue(uuidutils.is_uuid_like(el['Id']))

View File

@ -1,140 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ast
import six
from tempest.lib.cli import output_parser
import testtools
from manilaclient import api_versions
from manilaclient import config
CONF = config.CONF
def multi_line_row_table(output_lines, group_by_column_index=0):
parsed_table = output_parser.table(output_lines)
rows = parsed_table['values']
row_index = 0
def get_column_index(column_name, headers, default):
return next(
(i for i, h in enumerate(headers) if h.lower() == column_name),
default
)
if group_by_column_index is None:
group_by_column_index = get_column_index(
'id', parsed_table['headers'], 0)
def is_embedded_table(parsed_rows):
def is_table_border(t):
return six.text_type(t).startswith('+')
return (isinstance(parsed_rows, list)
and len(parsed_rows) > 3
and is_table_border(parsed_rows[0])
and is_table_border(parsed_rows[-1]))
def merge_cells(master_cell, value_cell):
if value_cell:
if not isinstance(master_cell, list):
master_cell = [master_cell]
master_cell.append(value_cell)
if is_embedded_table(master_cell):
return multi_line_row_table('\n'.join(master_cell), None)
return master_cell
def is_empty_row(row):
empty_cells = 0
for cell in row:
if cell == '':
empty_cells += 1
return len(row) == empty_cells
while row_index < len(rows):
row = rows[row_index]
line_with_value = row_index > 0 and row[group_by_column_index] == ''
if line_with_value and not is_empty_row(row):
rows[row_index - 1] = list(map(merge_cells,
rows[row_index - 1],
rows.pop(row_index)))
else:
row_index += 1
return parsed_table
def listing(output_lines):
"""Return list of dicts with basic item info parsed from cli output."""
items = []
table_ = multi_line_row_table(output_lines)
for row in table_['values']:
item = {}
for col_idx, col_key in enumerate(table_['headers']):
item[col_key] = row[col_idx]
items.append(item)
return items
def details(output_lines):
"""Returns dict parsed from CLI output."""
result = listing(output_lines)
d = {}
for item in result:
d.update({item['Property']: item['Value']})
return d
def is_microversion_supported(microversion):
return (
api_versions.APIVersion(CONF.min_api_microversion) <=
api_versions.APIVersion(microversion) <=
api_versions.APIVersion(CONF.max_api_microversion)
)
def skip_if_microversion_not_supported(microversion):
"""Decorator for tests that are microversion-specific."""
if not is_microversion_supported(microversion):
reason = ("Skipped. Test requires microversion %s that is not "
"allowed to be used by configuration." % microversion)
return testtools.skip(reason)
return lambda f: f
def choose_matching_backend(share, pools, share_type):
extra_specs = {}
# convert extra-specs in provided type to dict format
pair = [x.strip() for x in share_type['required_extra_specs'].split(':')]
if len(pair) == 2:
value = (True if six.text_type(pair[1]).lower() == 'true'
else False if six.text_type(pair[1]).lower() == 'false'
else pair[1])
extra_specs[pair[0]] = value
selected_pool = next(
(x for x in pools if (x['Name'] != share['host'] and all(
y in ast.literal_eval(x['Capabilities']).items() for y in
extra_specs.items()))),
None)
return selected_pool['Name']

View File

@ -1,48 +0,0 @@
# 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 ddt
import mock
from manilaclient.common.apiclient import auth
from manilaclient.common import constants
from manilaclient.tests.unit import utils
@ddt.ddt
class DiscoverAuthSystems(utils.TestCase):
@ddt.unpack
@ddt.data(
{'plugins': {'a': 42, 'b': 'bar'}, 'discovered': {}},
{'plugins': {'a': 42, 'b': 'bar'}, 'discovered': {'b': 'overwrite'}},
{'plugins': {'a': 42, 'b': 'bar'}, 'discovered': {'c': 'reset'}}
)
@mock.patch.dict('stevedore.extension.ExtensionManager.ENTRY_POINT_CACHE',
clear=True)
def test_plugins(self, plugins, discovered):
mock_plugins = []
for name, return_value in plugins.items():
plugin = mock.Mock()
plugin.resolve = mock.Mock(return_value=return_value)
plugin.name = name
mock_plugins.append(plugin)
with mock.patch.dict(
'manilaclient.common.apiclient.auth._discovered_plugins',
discovered, clear=True):
with mock.patch('pkg_resources.iter_entry_points') as ep_mock:
ep_mock.return_value = mock_plugins
auth.discover_auth_systems()
ep_mock.assert_called_with(
constants.EXTENSION_PLUGIN_NAMESPACE
)
self.assertEqual(plugins, auth._discovered_plugins)

View File

@ -1,208 +0,0 @@
# 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
import requests
import manilaclient
from manilaclient.common import httpclient
from manilaclient import exceptions
from manilaclient.tests.unit import utils
fake_user_agent = "fake"
fake_response = utils.TestResponse({
"status_code": 200,
"text": '{"hi": "there"}',
})
mock_request = mock.Mock(return_value=(fake_response))
bad_400_response = utils.TestResponse({
"status_code": 400,
"text": '{"error": {"message": "n/a", "details": "Terrible!"}}',
})
bad_400_request = mock.Mock(return_value=(bad_400_response))
bad_401_response = utils.TestResponse({
"status_code": 401,
"text": '{"error": {"message": "FAILED!", "details": "DETAILS!"}}',
})
bad_401_request = mock.Mock(return_value=(bad_401_response))
bad_500_response = utils.TestResponse({
"status_code": 500,
"text": '{"error": {"message": "FAILED!", "details": "DETAILS!"}}',
})
bad_500_request = mock.Mock(return_value=(bad_500_response))
retry_after_response = utils.TestResponse({
"status_code": 413,
"text": '',
"headers": {
"retry-after": "5"
},
})
retry_after_mock_request = mock.Mock(return_value=retry_after_response)
retry_after_no_headers_response = utils.TestResponse({
"status_code": 413,
"text": '',
})
retry_after_no_headers_mock_request = mock.Mock(
return_value=retry_after_no_headers_response)
retry_after_non_supporting_response = utils.TestResponse({
"status_code": 403,
"text": '',
"headers": {
"retry-after": "5"
},
})
retry_after_non_supporting_mock_request = mock.Mock(
return_value=retry_after_non_supporting_response)
def get_authed_client(retries=0):
cl = httpclient.HTTPClient("http://example.com", "token", fake_user_agent,
retries=retries, http_log_debug=True,
api_version=manilaclient.API_MAX_VERSION)
return cl
class ClientTest(utils.TestCase):
def setUp(self):
super(ClientTest, self).setUp()
self.max_version = manilaclient.API_MAX_VERSION
self.max_version_str = self.max_version.get_string()
def test_get(self):
cl = get_authed_client()
@mock.patch.object(requests, "request", mock_request)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
headers = {
"X-Auth-Token": "token",
"User-Agent": fake_user_agent,
cl.API_VERSION_HEADER: self.max_version_str,
'Accept': 'application/json',
}
mock_request.assert_called_with(
"GET",
"http://example.com/hi",
headers=headers,
**self.TEST_REQUEST_BASE)
# Automatic JSON parsing
self.assertEqual(body, {"hi": "there"})
test_get_call()
def test_get_retry_500(self):
cl = get_authed_client(retries=1)
self.requests = [bad_500_request, mock_request]
def request(*args, **kwargs):
next_request = self.requests.pop(0)
return next_request(*args, **kwargs)
@mock.patch.object(requests, "request", request)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
test_get_call()
self.assertEqual(self.requests, [])
def test_retry_limit(self):
cl = get_authed_client(retries=1)
self.requests = [bad_500_request, bad_500_request, mock_request]
def request(*args, **kwargs):
next_request = self.requests.pop(0)
return next_request(*args, **kwargs)
@mock.patch.object(requests, "request", request)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
self.assertRaises(exceptions.ClientException, test_get_call)
self.assertEqual(self.requests, [mock_request])
def test_get_no_retry_400(self):
cl = get_authed_client(retries=0)
self.requests = [bad_400_request, mock_request]
def request(*args, **kwargs):
next_request = self.requests.pop(0)
return next_request(*args, **kwargs)
@mock.patch.object(requests, "request", request)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
self.assertRaises(exceptions.BadRequest, test_get_call)
self.assertEqual(self.requests, [mock_request])
def test_get_retry_400_socket(self):
cl = get_authed_client(retries=1)
self.requests = [bad_400_request, mock_request]
def request(*args, **kwargs):
next_request = self.requests.pop(0)
return next_request(*args, **kwargs)
@mock.patch.object(requests, "request", request)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
test_get_call()
self.assertEqual(self.requests, [])
def test_get_with_retries_none(self):
cl = get_authed_client(retries=None)
@mock.patch.object(requests, "request", bad_401_request)
def test_get_call():
resp, body = cl.get("/hi")
self.assertRaises(exceptions.Unauthorized, test_get_call)
def test_post(self):
cl = get_authed_client()
@mock.patch.object(requests, "request", mock_request)
def test_post_call():
cl.post("/hi", body=[1, 2, 3])
headers = {
"X-Auth-Token": "token",
"Content-Type": "application/json",
'Accept': 'application/json',
"X-Openstack-Manila-Api-Version": self.max_version_str,
"User-Agent": fake_user_agent
}
mock_request.assert_called_with(
"POST",
"http://example.com/hi",
headers=headers,
data='[1, 2, 3]',
**self.TEST_REQUEST_BASE)
test_post_call()

View File

@ -1,94 +0,0 @@
# 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.
"""
A fake server that "responds" to API methods with pre-canned responses.
All of these responses come from the spec, so if for some reason the spec's
wrong the tests might raise AssertionError. I've indicated in comments the
places where actual behavior differs from the spec.
"""
from __future__ import print_function
def assert_has_keys(dictonary, required=None, optional=None):
if required is None:
required = []
if optional is None:
optional = []
for k in required:
try:
assert k in dictonary
except AssertionError:
extra_keys = set(dictonary).difference(set(required + optional))
raise AssertionError("found unexpected keys: %s" %
list(extra_keys))
class FakeClient(object):
def assert_called(self, method, url, body=None, pos=-1, **kwargs):
"""Assert than an API method was just called."""
expected = (method, url)
called = self.client.callstack[pos][0:2]
assert self.client.callstack, ("Expected %s %s but no calls "
"were made." % expected)
assert expected == called, 'Expected %s %s; got %s %s' % (
expected + called)
if body is not None:
actual = self.client.callstack[pos][2]
if isinstance(actual, dict) and isinstance(body, dict):
assert sorted(list(actual)) == sorted(list(body))
else:
assert actual == body, "Expected %(b)s; got %(a)s" % {
'b': body,
'a': actual
}
def assert_called_anytime(self, method, url, body=None,
clear_callstack=True):
"""Assert than an API method was called anytime in the test."""
expected = (method, url)
assert self.client.callstack, ("Expected %s %s but no calls "
"were made." % expected)
found = False
for entry in self.client.callstack:
if expected == entry[0:2]:
found = True
break
assert found, 'Expected %s %s; got %s' % (
expected, self.client.callstack)
if body is not None:
try:
assert entry[2] == body
except AssertionError:
print(entry[2])
print("!=")
print(body)
raise
if clear_callstack:
self.client.callstack = []
def clear_callstack(self):
self.client.callstack = []
def authenticate(self):
pass

View File

@ -1,354 +0,0 @@
# Copyright 2015 Chuck Fouts
#
# 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 ddt
import mock
import manilaclient
from manilaclient import api_versions
from manilaclient.common import cliutils
from manilaclient import exceptions
from manilaclient.tests.unit import utils
@ddt.ddt
class APIVersionTestCase(utils.TestCase):
def test_valid_version_strings(self):
def _test_string(version, exp_major, exp_minor):
v = api_versions.APIVersion(version)
self.assertEqual(v.ver_major, exp_major)
self.assertEqual(v.ver_minor, exp_minor)
_test_string("1.1", 1, 1)
_test_string("2.10", 2, 10)
_test_string("5.234", 5, 234)
_test_string("12.5", 12, 5)
_test_string("2.0", 2, 0)
_test_string("2.200", 2, 200)
def test_null_version(self):
v = api_versions.APIVersion()
self.assertTrue(v.is_null())
@ddt.data(
"2",
"200",
"2.1.4",
"200.23.66.3",
"5 .3",
"5. 3",
"5.03",
"02.1",
"2.001",
"",
" 2.1",
"2.1 ",
"2.",
)
def test_invalid_version_strings(self, version):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, version)
def test_version_comparisons(self):
v1 = api_versions.APIVersion("2.0")
v2 = api_versions.APIVersion("2.5")
v3 = api_versions.APIVersion("5.23")
v4 = api_versions.APIVersion("2.0")
v5 = api_versions.APIVersion("1.0")
v_null = api_versions.APIVersion()
self.assertLess(v1, v2)
self.assertGreater(v3, v2)
self.assertNotEqual(v1, v2)
self.assertEqual(v1, v4)
self.assertNotEqual(v1, v_null)
self.assertLess(v5, v1)
self.assertLess(v5, v2)
self.assertEqual(v_null, v_null)
self.assertRaises(TypeError, v1.__le__, "2.1")
def test_version_matches(self):
v1 = api_versions.APIVersion("2.0")
v2 = api_versions.APIVersion("2.5")
v3 = api_versions.APIVersion("2.45")
v4 = api_versions.APIVersion("3.3")
v5 = api_versions.APIVersion("3.23")
v6 = api_versions.APIVersion("2.0")
v7 = api_versions.APIVersion("3.3")
v8 = api_versions.APIVersion("4.0")
v_null = api_versions.APIVersion()
v1_25 = api_versions.APIVersion("2.5")
v1_32 = api_versions.APIVersion("3.32")
v1_33 = api_versions.APIVersion("3.33")
self.assertTrue(v2.matches(v1, v3))
self.assertTrue(v2.matches(v1, v_null))
self.assertTrue(v1_32.matches(v1_25, v1_33))
self.assertTrue(v1.matches(v6, v2))
self.assertTrue(v4.matches(v2, v7))
self.assertTrue(v4.matches(v_null, v7))
self.assertTrue(v4.matches(v_null, v8))
self.assertFalse(v1.matches(v2, v3))
self.assertFalse(v5.matches(v2, v4))
self.assertFalse(v2.matches(v3, v1))
self.assertRaises(ValueError, v_null.matches, v1, v3)
def test_get_string(self):
v1_string = "3.23"
v1 = api_versions.APIVersion(v1_string)
self.assertEqual(v1_string, v1.get_string())
self.assertRaises(ValueError,
api_versions.APIVersion().get_string)
class GetAPIVersionTestCase(utils.TestCase):
def test_wrong_format(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.get_api_version, "something_wrong")
def test_wrong_major_version(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.get_api_version, "1")
@mock.patch("manilaclient.api_versions.APIVersion")
def test_major_and_minor_parts_is_presented(self, mock_apiversion):
version = "2.7"
self.assertEqual(mock_apiversion.return_value,
api_versions.get_api_version(version))
mock_apiversion.assert_called_once_with(version)
class WrapsTestCase(utils.TestCase):
def _get_obj_with_vers(self, vers):
return mock.MagicMock(api_version=api_versions.APIVersion(vers))
def _side_effect_of_vers_method(self, *args, **kwargs):
m = mock.MagicMock(start_version=args[1], end_version=args[2])
m.name = args[0]
return m
@mock.patch("manilaclient.utils.get_function_name")
@mock.patch("manilaclient.api_versions.VersionedMethod")
def test_end_version_is_none(self, mock_versioned_method, mock_name):
func_name = 'foo'
mock_name.return_value = func_name
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps('2.2')
def foo(*args, **kwargs):
pass
foo(self._get_obj_with_vers('2.4'))
mock_versioned_method.assert_called_once_with(
func_name, api_versions.APIVersion('2.2'),
api_versions.APIVersion(api_versions.MAX_VERSION), mock.ANY)
@mock.patch("manilaclient.utils.get_function_name")
@mock.patch("manilaclient.api_versions.VersionedMethod")
def test_start_and_end_version_are_presented(self, mock_versioned_method,
mock_name):
func_name = "foo"
mock_name.return_value = func_name
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps("2.2", "2.6")
def foo(*args, **kwargs):
pass
foo(self._get_obj_with_vers("2.4"))
mock_versioned_method.assert_called_once_with(
func_name, api_versions.APIVersion("2.2"),
api_versions.APIVersion("2.6"), mock.ANY)
@mock.patch("manilaclient.utils.get_function_name")
@mock.patch("manilaclient.api_versions.VersionedMethod")
def test_api_version_doesnt_match(self, mock_versioned_method, mock_name):
func_name = "foo"
mock_name.return_value = func_name
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps("2.2", "2.6")
def foo(*args, **kwargs):
pass
self.assertRaises(exceptions.UnsupportedVersion,
foo, self._get_obj_with_vers("2.1"))
mock_versioned_method.assert_called_once_with(
func_name, api_versions.APIVersion("2.2"),
api_versions.APIVersion("2.6"), mock.ANY)
def test_define_method_is_actually_called(self):
checker = mock.MagicMock()
@api_versions.wraps("2.2", "2.6")
def some_func(*args, **kwargs):
checker(*args, **kwargs)
obj = self._get_obj_with_vers("2.4")
some_args = ("arg_1", "arg_2")
some_kwargs = {"key1": "value1", "key2": "value2"}
some_func(obj, *some_args, **some_kwargs)
checker.assert_called_once_with(*((obj,) + some_args), **some_kwargs)
def test_cli_args_are_copied(self):
@api_versions.wraps("2.2", "2.6")
@cliutils.arg("name_1", help="Name of the something")
@cliutils.arg("action_1", help="Some action")
def some_func_1(cs, args):
pass
@cliutils.arg("name_2", help="Name of the something")
@cliutils.arg("action_2", help="Some action")
@api_versions.wraps("2.2", "2.6")
def some_func_2(cs, args):
pass
args_1 = [(('name_1',), {'help': 'Name of the something'}),
(('action_1',), {'help': 'Some action'})]
self.assertEqual(args_1, some_func_1.arguments)
args_2 = [(('name_2',), {'help': 'Name of the something'}),
(('action_2',), {'help': 'Some action'})]
self.assertEqual(args_2, some_func_2.arguments)
class DiscoverVersionTestCase(utils.TestCase):
def setUp(self):
super(DiscoverVersionTestCase, self).setUp()
self.orig_max = manilaclient.API_MAX_VERSION
self.orig_min = manilaclient.API_MIN_VERSION
self.addCleanup(self._clear_fake_version)
self.fake_client = mock.MagicMock()
def _clear_fake_version(self):
manilaclient.API_MAX_VERSION = self.orig_max
manilaclient.API_MIN_VERSION = self.orig_min
def _mock_returned_server_version(self, server_version,
server_min_version):
version_mock = mock.MagicMock(version=server_version,
min_version=server_min_version,
status='CURRENT')
val = [version_mock]
self.fake_client.services.server_api_version.return_value = val
def test_server_is_too_new(self):
self._mock_returned_server_version('2.7', '2.4')
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.3")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertRaisesRegex(exceptions.UnsupportedVersion,
".*range is '2.4' to '2.7'.*",
api_versions.discover_version,
self.fake_client,
api_versions.APIVersion("2.3"))
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_server_is_too_old(self):
self._mock_returned_server_version('2.2', '2.0')
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.10")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.9")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.discover_version,
self.fake_client,
api_versions.APIVersion("2.10"))
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_requested_version_is_less_than_server_max(self):
self._mock_returned_server_version('2.17', '2.14')
max_version = api_versions.APIVersion('2.15')
manilaclient.API_MAX_VERSION = max_version
manilaclient.API_MIN_VERSION = api_versions.APIVersion('2.12')
version = api_versions.discover_version(self.fake_client, max_version)
self.assertEqual(api_versions.APIVersion('2.15'), version)
def test_requested_version_is_downgraded(self):
server_end_version = '2.7'
self._mock_returned_server_version(server_end_version, '2.0')
max_version = api_versions.APIVersion("2.8")
manilaclient.API_MAX_VERSION = max_version
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5")
version = api_versions.discover_version(self.fake_client, max_version)
self.assertEqual(api_versions.APIVersion(server_end_version), version)
def test_server_and_client_max_are_same(self):
self._mock_returned_server_version('2.5', '2.0')
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5")
discovered_version = api_versions.discover_version(
self.fake_client,
manilaclient.API_MAX_VERSION)
self.assertEqual("2.5", discovered_version.get_string())
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_pre_microversion_server(self):
self.fake_client.services.server_api_version.return_value = []
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5")
discovered_version = api_versions.discover_version(
self.fake_client,
manilaclient.API_MAX_VERSION)
self.assertEqual("1.0", discovered_version.get_string())
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_requested_version_in_range(self):
self._mock_returned_server_version('2.7', '2.4')
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
discovered_version = api_versions.discover_version(
self.fake_client,
api_versions.APIVersion('2.7'))
self.assertEqual('2.7', discovered_version.get_string())
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_server_without_microversion(self):
self._mock_returned_server_version(None, None)
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
discovered_version = api_versions.discover_version(
self.fake_client,
api_versions.APIVersion('2.7'))
self.assertEqual(api_versions.DEPRECATED_VERSION,
discovered_version.get_string())
self.assertTrue(self.fake_client.services.server_api_version.called)
def test_requested_version_is_too_old(self):
self._mock_returned_server_version('2.5', '2.0')
manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5")
manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5")
self.assertRaisesRegex(exceptions.UnsupportedVersion,
".*range is '2.0' to '2.5'.*",
api_versions.discover_version,
self.fake_client,
api_versions.APIVersion("1.0"))

View File

@ -1,62 +0,0 @@
# 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 manilaclient.common.apiclient import base as common_base
from manilaclient import exceptions
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import shares
cs = fakes.FakeClient()
class BaseTest(utils.TestCase):
def test_resource_repr(self):
r = common_base.Resource(None, dict(foo="bar", baz="spam"))
self.assertEqual(repr(r), "<Resource baz=spam, foo=bar>")
def test_eq(self):
# Two resources of the same type with the same id: equal
# The truth of r1==r2 does not imply that r1!=r2 is false in PY2.
# Test that inequality operator is defined and that comparing equal
# items returns False.
r1 = common_base.Resource(None, {'id': 1, 'name': 'hi'})
r2 = common_base.Resource(None, {'id': 1, 'name': 'hello'})
self.assertTrue(r1 == r2)
self.assertFalse(r1 != r2)
# Two resources of different types: never equal
r1 = common_base.Resource(None, {'id': 1})
r2 = shares.Share(None, {'id': 1})
self.assertNotEqual(r1, r2)
self.assertTrue(r1 != r2)
# Two resources with no ID: equal if their info is equal
# The truth of r1==r2 does not imply that r1!=r2 is false in PY2.
# Test that inequality operator is defined and that comparing equal
# items returns False.
r1 = common_base.Resource(None, {'name': 'joe', 'age': 12})
r2 = common_base.Resource(None, {'name': 'joe', 'age': 12})
self.assertTrue(r1 == r2)
self.assertFalse(r1 != r2)
def test_findall_invalid_attribute(self):
# Make sure findall with an invalid attribute doesn't cause errors.
# The following should not raise an exception.
cs.shares.findall(vegetable='carrot')
# However, find() should raise an error
self.assertRaises(exceptions.NotFound,
cs.shares.find,
vegetable='carrot')

View File

@ -1,133 +0,0 @@
# 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 ddt
import mock
from manilaclient import api_versions
from manilaclient import client
from manilaclient import exceptions
from manilaclient.tests.unit import utils
import manilaclient.v1.client
import manilaclient.v2.client
@ddt.ddt
class ClientTest(utils.TestCase):
def test_get_client_class_v2(self):
output = manilaclient.client.get_client_class('2')
self.assertEqual(output, manilaclient.v2.client.Client)
def test_get_client_class_unknown(self):
self.assertRaises(manilaclient.exceptions.UnsupportedVersion,
manilaclient.client.get_client_class, '0')
@ddt.data('1', '1.0')
def test_init_client_with_string_v1_version(self, version):
with mock.patch.object(manilaclient.v1.client, 'Client'):
with mock.patch.object(api_versions, 'APIVersion'):
api_instance = api_versions.APIVersion.return_value
api_instance.get_major_version.return_value = '1'
manilaclient.client.Client(version, 'foo', bar='quuz')
manilaclient.v1.client.Client.assert_called_once_with(
'foo', api_version=api_instance, bar='quuz')
api_versions.APIVersion.assert_called_once_with('1.0')
@ddt.data(
('2', '2.0'),
('2.0', '2.0'),
('2.6', '2.6'),
)
@ddt.unpack
def test_init_client_with_string_v2_version(self, provided, expected):
with mock.patch.object(manilaclient.v2.client, 'Client'):
with mock.patch.object(api_versions, 'APIVersion'):
api_instance = api_versions.APIVersion.return_value
api_instance.get_major_version.return_value = '2'
manilaclient.client.Client(provided, 'foo', bar='quuz')
manilaclient.v2.client.Client.assert_called_once_with(
'foo', api_version=api_instance, bar='quuz')
api_versions.APIVersion.assert_called_once_with(expected)
def test_init_client_with_api_version_instance(self):
version = manilaclient.API_MAX_VERSION
with mock.patch.object(manilaclient.v2.client, 'Client'):
manilaclient.client.Client(version, 'foo', bar='quuz')
manilaclient.v2.client.Client.assert_called_once_with(
'foo', api_version=version, bar='quuz')
@ddt.data(None, '', '3', 'v1', 'v2', 'v1.0', 'v2.0')
def test_init_client_with_unsupported_version(self, v):
self.assertRaises(exceptions.UnsupportedVersion, client.Client, v)
@ddt.data(
('1', '1.0'),
('1', '2.0'),
('1', '2.7'),
('1', None),
('1.0', '1.0'),
('1.0', '2.0'),
('1.0', '2.7'),
('1.0', None),
('2', '1.0'),
('2', '2.0'),
('2', '2.7'),
('2', None),
)
@ddt.unpack
def test_init_client_with_version_parms(self, pos, kw):
major = int(float(pos))
pos_av = mock.Mock()
kw_av = mock.Mock()
with mock.patch.object(manilaclient.v1.client, 'Client'):
with mock.patch.object(manilaclient.v2.client, 'Client'):
with mock.patch.object(api_versions, 'APIVersion'):
api_versions.APIVersion.side_effect = [pos_av, kw_av]
pos_av.get_major_version.return_value = str(major)
if kw is None:
manilaclient.client.Client(pos, 'foo')
expected_av = pos_av
else:
manilaclient.client.Client(pos, 'foo', api_version=kw)
expected_av = kw_av
if int(float(pos)) == 1:
expected_client_ver = api_versions.DEPRECATED_VERSION
self.assertFalse(manilaclient.v2.client.Client.called)
manilaclient.v1.client.Client.assert_has_calls([
mock.call('foo', api_version=expected_av)
])
else:
expected_client_ver = api_versions.MIN_VERSION
self.assertFalse(manilaclient.v1.client.Client.called)
manilaclient.v2.client.Client.assert_has_calls([
mock.call('foo', api_version=expected_av)
])
if kw is None:
api_versions.APIVersion.assert_called_once_with(
expected_client_ver)
else:
api_versions.APIVersion.assert_has_calls([
mock.call(expected_client_ver),
mock.call(kw),
])

View File

@ -1,225 +0,0 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from manilaclient.tests.functional import utils as func_utils
from manilaclient.tests.unit import utils
@ddt.ddt
class ShellTest(utils.TestCase):
OUTPUT_LINES_SIMPLE = """
+----+------+---------+
| ID | Name | Status |
+----+------+---------+
| 11 | foo | BUILD |
| 21 | bar | ERROR |
+----+------+---------+
"""
OUTPUT_LINES_ONE_MULTI_ROW = """
+----+------+---------+
| ID | Name | Status |
+----+------+---------+
| 11 | foo | BUILD |
| 21 | bar | ERROR |
| | | ERROR2 |
| 31 | bee | None |
+----+------+---------+
"""
OUTPUT_LINES_COMPLICATED_MULTI_ROW = """
+----+------+---------+
| ID | Name | Status |
+----+------+---------+
| 11 | foo | BUILD |
| 21 | bar | ERROR |
| | | ERROR2 |
| | | ERROR3 |
| 31 | bee | None |
| | bee2 | |
| | bee3 | |
| 41 | rand | None |
| | rend | None2 |
| | | |
+----+------+---------+
"""
OUTPUT_LINES_COMPLICATED_MULTI_ROW_WITH_SHIFTED_ID = """
+----+----+------+---------+
| ** | ID | Name | Status |
+----+----+------+---------+
| ** | 11 | foo | BUILD |
| | 21 | bar | ERROR |
| | | | ERROR2 |
| | | | ERROR3 |
| | | | |
| ** | 31 | bee | None |
| | | bee2 | |
| | | | |
+----+----+------+---------+
"""
OUTPUT_LINES_NESTED_TABLE = """
+----+----+------+--------------+
| ** | ID | Name | Status |
+----+----+------+--------------+
| ** | 11 | foo | +----+----+ |
| | | | | aa | bb | |
| | | | +----+----+ |
| | | | +----+----+ |
| | 21 | bar | ERROR |
| | | | ERROR2 |
| | | | ERROR3 |
+----+----+------+--------------+
"""
OUTPUT_LINES_NESTED_TABLE_MULTI_LINE = """
+----+----+------+--------------+
| ** | ID | Name | Status |
+----+----+------+--------------+
| ** | 11 | foo | +----+----+ |
| | | | | id | bb | |
| | | | +----+----+ |
| | | | | 01 | a1 | |
| | | | | | a2 | |
| | | | +----+----+ |
| | 21 | bar | ERROR |
| | | | ERROR2 |
| | | | ERROR3 |
+----+----+------+--------------+
"""
OUTPUT_LINES_DETAILS = """
+----------+--------+
| Property | Value |
+----------+--------+
| foo | BUILD |
| bar | ERROR |
| | ERROR2 |
| | ERROR3 |
| bee | None |
+----------+--------+
"""
@ddt.data({'input': OUTPUT_LINES_SIMPLE,
'valid_values': [
['11', 'foo', 'BUILD'],
['21', 'bar', 'ERROR']
]},
{'input': OUTPUT_LINES_ONE_MULTI_ROW,
'valid_values': [
['11', 'foo', 'BUILD'],
['21', 'bar', ['ERROR', 'ERROR2']],
['31', 'bee', 'None'],
]},
{'input': OUTPUT_LINES_COMPLICATED_MULTI_ROW,
'valid_values': [
['11', 'foo', 'BUILD'],
['21', 'bar', ['ERROR', 'ERROR2', 'ERROR3']],
['31', ['bee', 'bee2', 'bee3'], 'None'],
['41', ['rand', 'rend'], ['None', 'None2']],
['', '', '']
]})
@ddt.unpack
def test_multi_line_row_table(self, input, valid_values):
actual_result = func_utils.multi_line_row_table(input)
self.assertEqual(['ID', 'Name', 'Status'], actual_result['headers'])
self.assertEqual(valid_values, actual_result['values'])
def test_multi_line_row_table_shifted_id_column(self):
input = self.OUTPUT_LINES_COMPLICATED_MULTI_ROW_WITH_SHIFTED_ID
valid_values = [
['**', '11', 'foo', 'BUILD'],
['', '21', 'bar', ['ERROR', 'ERROR2', 'ERROR3']],
['', '', '', ''],
['**', '31', ['bee', 'bee2'], 'None'],
['', '', '', '']
]
actual_result = func_utils.multi_line_row_table(
input, group_by_column_index=1)
self.assertEqual(['**', 'ID', 'Name', 'Status'],
actual_result['headers'])
self.assertEqual(valid_values, actual_result['values'])
@ddt.data({'input': OUTPUT_LINES_NESTED_TABLE,
'valid_nested': {
'headers': ['aa', 'bb'],
'values': []
}},
{'input': OUTPUT_LINES_NESTED_TABLE_MULTI_LINE,
'valid_nested': {
'headers': ['id', 'bb'],
'values': [['01', ['a1', 'a2']]]
}},)
@ddt.unpack
def test_nested_tables(self, input, valid_nested):
actual_result = func_utils.multi_line_row_table(
input, group_by_column_index=1)
self.assertEqual(['**', 'ID', 'Name', 'Status'],
actual_result['headers'])
self.assertEqual(2, len(actual_result['values']))
self.assertEqual(valid_nested, actual_result['values'][0][3])
@ddt.data({'input': OUTPUT_LINES_DETAILS,
'valid_values': [
['foo', 'BUILD'],
['bar', ['ERROR', 'ERROR2', 'ERROR3']],
['bee', 'None'],
]})
@ddt.unpack
def test_details(self, input, valid_values):
actual_result = func_utils.multi_line_row_table(input)
self.assertEqual(['Property', 'Value'], actual_result['headers'])
self.assertEqual(valid_values, actual_result['values'])
@ddt.data({'input_data': OUTPUT_LINES_DETAILS,
'output_data': [
{'Property': 'foo', 'Value': 'BUILD'},
{'Property': 'bar', 'Value': ['ERROR', 'ERROR2', 'ERROR3']},
{'Property': 'bee', 'Value': 'None'}]},
{'input_data': OUTPUT_LINES_SIMPLE,
'output_data': [
{'ID': '11', 'Name': 'foo', 'Status': 'BUILD'},
{'ID': '21', 'Name': 'bar', 'Status': 'ERROR'},
]},
{'input_data': OUTPUT_LINES_ONE_MULTI_ROW,
'output_data': [
{'ID': '11', 'Name': 'foo', 'Status': 'BUILD'},
{'ID': '21', 'Name': 'bar', 'Status': ['ERROR', 'ERROR2']},
{'ID': '31', 'Name': 'bee', 'Status': 'None'},
]},
{'input_data': OUTPUT_LINES_COMPLICATED_MULTI_ROW,
'output_data': [
{'ID': '11', 'Name': 'foo', 'Status': 'BUILD'},
{'ID': '21', 'Name': 'bar',
'Status': ['ERROR', 'ERROR2', 'ERROR3']},
{'ID': '31', 'Name': ['bee', 'bee2', 'bee3'],
'Status': 'None'},
{'ID': '41', 'Name': ['rand', 'rend'],
'Status': ['None', 'None2']},
{'ID': '', 'Name': '', 'Status': ''},
]})
@ddt.unpack
def test_listing(self, input_data, output_data):
actual_result = func_utils.listing(input_data)
self.assertEqual(output_data, actual_result)

View File

@ -1,382 +0,0 @@
# 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 re
import sys
import ddt
import fixtures
import mock
from six import moves
from tempest.lib.cli import output_parser
from testtools import matchers
import manilaclient
from manilaclient.common import cliutils
from manilaclient.common import constants
from manilaclient import exceptions
from manilaclient import shell
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
@ddt.ddt
class OpenstackManilaShellTest(utils.TestCase):
FAKE_ENV = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where',
}
# Patch os.environ to avoid required auth info.
def set_env_vars(self, env_vars):
for k, v in env_vars.items():
self.useFixture(fixtures.EnvironmentVariable(k, v))
def shell_discover_client(self,
current_client,
os_api_version,
os_endpoint_type,
os_service_type,
client_args):
return current_client, manilaclient.API_MAX_VERSION
def shell(self, argstr):
orig = sys.stdout
try:
sys.stdout = moves.StringIO()
_shell = shell.OpenStackManilaShell()
_shell._discover_client = self.shell_discover_client
_shell.main(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(exc_value.code, 0)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
return out
@ddt.data(
{},
{'OS_AUTH_URL': 'http://foo.bar'},
{'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo'},
{'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo_user',
'OS_PASSWORD': 'foo_password'},
{'OS_TENANT_NAME': 'foo_tenant', 'OS_USERNAME': 'foo_user',
'OS_PASSWORD': 'foo_password'},
{'OS_TOKEN': 'foo_token'},
{'OS_MANILA_BYPASS_URL': 'http://foo.foo'},
)
def test_main_failure(self, env_vars):
self.set_env_vars(env_vars)
with mock.patch.object(shell, 'client') as mock_client:
self.assertRaises(exceptions.CommandError, self.shell, 'list')
self.assertFalse(mock_client.Client.called)
def test_main_success(self):
env_vars = {
'OS_AUTH_URL': 'http://foo.bar',
'OS_USERNAME': 'foo_username',
'OS_USER_ID': 'foo_user_id',
'OS_PASSWORD': 'foo_password',
'OS_TENANT_NAME': 'foo_tenant',
'OS_TENANT_ID': 'foo_tenant_id',
'OS_PROJECT_NAME': 'foo_project',
'OS_PROJECT_ID': 'foo_project_id',
'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id',
'OS_PROJECT_DOMAIN_NAME': 'foo_project_domain_name',
'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id',
'OS_USER_DOMAIN_NAME': 'foo_user_domain_name',
'OS_USER_DOMAIN_ID': 'foo_user_domain_id',
'OS_CERT': 'foo_cert',
}
self.set_env_vars(env_vars)
with mock.patch.object(shell, 'client') as mock_client:
self.shell('list')
mock_client.Client.assert_called_with(
manilaclient.API_MAX_VERSION,
username=env_vars['OS_USERNAME'],
password=env_vars['OS_PASSWORD'],
project_name=env_vars['OS_PROJECT_NAME'],
auth_url=env_vars['OS_AUTH_URL'],
insecure=False,
region_name='',
tenant_id=env_vars['OS_PROJECT_ID'],
endpoint_type='publicURL',
extensions=mock.ANY,
service_type=constants.V1_SERVICE_TYPE,
service_name='',
retries=0,
http_log_debug=False,
cacert=None,
use_keyring=False,
force_new_token=False,
user_id=env_vars['OS_USER_ID'],
user_domain_id=env_vars['OS_USER_DOMAIN_ID'],
user_domain_name=env_vars['OS_USER_DOMAIN_NAME'],
project_domain_id=env_vars['OS_PROJECT_DOMAIN_ID'],
project_domain_name=env_vars['OS_PROJECT_DOMAIN_NAME'],
cert=env_vars['OS_CERT'],
input_auth_token='',
service_catalog_url='',
)
@ddt.data(
{"env_vars": {"OS_MANILA_BYPASS_URL": "http://foo.url",
"OS_TOKEN": "foo_token"},
"kwargs": {"--os-token": "bar_token",
"--bypass-url": "http://bar.url"},
"expected": {"input_auth_token": "bar_token",
"service_catalog_url": "http://bar.url"}},
{"env_vars": {"OS_MANILA_BYPASS_URL": "http://foo.url",
"OS_TOKEN": "foo_token"},
"kwargs": {},
"expected": {"input_auth_token": "foo_token",
"service_catalog_url": "http://foo.url"}},
{"env_vars": {},
"kwargs": {"--os-token": "bar_token",
"--bypass-url": "http://bar.url"},
"expected": {"input_auth_token": "bar_token",
"service_catalog_url": "http://bar.url"}},
{"env_vars": {"MANILACLIENT_BYPASS_URL": "http://foo.url",
"OS_TOKEN": "foo_token"},
"kwargs": {},
"expected": {"input_auth_token": "foo_token",
"service_catalog_url": "http://foo.url"}},
{"env_vars": {"OS_TOKEN": "foo_token"},
"kwargs": {"--bypass-url": "http://bar.url"},
"expected": {"input_auth_token": "foo_token",
"service_catalog_url": "http://bar.url"}},
{"env_vars": {"MANILACLIENT_BYPASS_URL": "http://foo.url",
"OS_MANILA_BYPASS_URL": "http://bar.url",
"OS_TOKEN": "foo_token"},
"kwargs": {"--os-token": "bar_token"},
"expected": {"input_auth_token": "bar_token",
"service_catalog_url": "http://bar.url"}},
)
@ddt.unpack
def test_main_success_with_token(self, env_vars, kwargs, expected):
self.set_env_vars(env_vars)
with mock.patch.object(shell, "client") as mock_client:
cmd = ""
for k, v in kwargs.items():
cmd += "%s=%s " % (k, v)
cmd += "list"
self.shell(cmd)
mock_client.Client.assert_called_with(
manilaclient.API_MAX_VERSION,
username="",
password="",
project_name="",
auth_url="",
insecure=False,
region_name="",
tenant_id="",
endpoint_type="publicURL",
extensions=mock.ANY,
service_type=constants.V1_SERVICE_TYPE,
service_name="",
retries=0,
http_log_debug=False,
cacert=None,
use_keyring=False,
force_new_token=False,
user_id="",
user_domain_id="",
user_domain_name="",
project_domain_id="",
project_domain_name="",
cert="",
input_auth_token=expected["input_auth_token"],
service_catalog_url=expected["service_catalog_url"],
)
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
def test_help_on_subcommand(self):
required = [
'.*?^usage: manila list',
'.*?(?m)^List NAS shares with filters.',
]
help_text = self.shell('help list')
for r in required:
self.assertThat(help_text,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_common_args_in_help_message(self):
expected_args = (
'--version', '', '--debug', '--os-cache', '--os-reset-cache',
'--os-user-id', '--os-username', '--os-password',
'--os-tenant-name', '--os-project-name', '--os-tenant-id',
'--os-project-id', '--os-user-domain-id', '--os-user-domain-name',
'--os-project-domain-id', '--os-project-domain-name',
'--os-auth-url', '--os-region-name', '--service-type',
'--service-name', '--share-service-name', '--endpoint-type',
'--os-share-api-version', '--os-cacert', '--retries', '--os-cert',
)
help_text = self.shell('help')
for expected_arg in expected_args:
self.assertIn(expected_arg, help_text)
class CustomOpenStackManilaShell(shell.OpenStackManilaShell):
@staticmethod
@cliutils.arg(
'--default-is-none',
'--default_is_none',
type=str,
metavar='<redefined_metavar>',
action='single_alias',
help='Default value is None and metavar set.',
default=None)
def do_foo(cs, args):
cliutils.print_dict({'key': args.default_is_none})
@staticmethod
@cliutils.arg(
'--default-is-not-none',
'--default_is_not_none',
type=str,
action='single_alias',
help='Default value is not None and metavar not set.',
default='bar')
def do_bar(cs, args):
cliutils.print_dict({'key': args.default_is_not_none})
@staticmethod
@cliutils.arg(
'--list-like',
'--list_like',
nargs='*',
action='single_alias',
help='Default value is None, metavar not set and result is list.',
default=None)
def do_quuz(cs, args):
cliutils.print_dict({'key': args.list_like})
@ddt.ddt
class AllowOnlyOneAliasAtATimeActionTest(utils.TestCase):
FAKE_ENV = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where',
}
def setUp(self):
super(self.__class__, self).setUp()
for k, v in self.FAKE_ENV.items():
self.useFixture(fixtures.EnvironmentVariable(k, v))
self.mock_object(
shell.client, 'get_client_class',
mock.Mock(return_value=fakes.FakeClient))
def shell_discover_client(self,
current_client,
os_api_version,
os_endpoint_type,
os_service_type,
client_args):
return current_client, manilaclient.API_MAX_VERSION
def shell(self, argstr):
orig = sys.stdout
try:
sys.stdout = moves.StringIO()
_shell = CustomOpenStackManilaShell()
_shell._discover_client = self.shell_discover_client
_shell.main(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(exc_value.code, 0)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
return out
@ddt.data(
('--default-is-none foo', 'foo'),
('--default-is-none foo --default-is-none foo', 'foo'),
('--default-is-none foo --default_is_none foo', 'foo'),
('--default_is_none None', 'None'),
)
@ddt.unpack
def test_foo_success(self, options_str, expected_result):
output = self.shell('foo %s' % options_str)
parsed_output = output_parser.details(output)
self.assertEqual({'key': expected_result}, parsed_output)
@ddt.data(
'--default-is-none foo --default-is-none bar',
'--default-is-none foo --default_is_none bar',
'--default-is-none foo --default_is_none FOO',
)
def test_foo_error(self, options_str):
self.assertRaises(
matchers.MismatchError, self.shell, 'foo %s' % options_str)
@ddt.data(
('--default-is-not-none bar', 'bar'),
('--default_is_not_none bar --default-is-not-none bar', 'bar'),
('--default_is_not_none bar --default_is_not_none bar', 'bar'),
('--default-is-not-none not_bar', 'not_bar'),
('--default_is_not_none None', 'None'),
)
@ddt.unpack
def test_bar_success(self, options_str, expected_result):
output = self.shell('bar %s' % options_str)
parsed_output = output_parser.details(output)
self.assertEqual({'key': expected_result}, parsed_output)
@ddt.data(
'--default-is-not-none foo --default-is-not-none bar',
'--default-is-not-none foo --default_is_not_none bar',
'--default-is-not-none bar --default_is_not_none BAR',
)
def test_bar_error(self, options_str):
self.assertRaises(
matchers.MismatchError, self.shell, 'bar %s' % options_str)
@ddt.data(
('--list-like q=w', "['q=w']"),
('--list-like q=w --list_like q=w', "['q=w']"),
('--list-like q=w e=r t=y --list_like e=r t=y q=w',
"['e=r', 'q=w', 't=y']"),
('--list_like q=w e=r t=y', "['e=r', 'q=w', 't=y']"),
)
@ddt.unpack
def test_quuz_success(self, options_str, expected_result):
output = self.shell('quuz %s' % options_str)
parsed_output = output_parser.details(output)
self.assertEqual({'key': expected_result}, parsed_output)
@ddt.data(
'--list-like q=w --list_like e=r t=y',
)
def test_quuz_error(self, options_str):
self.assertRaises(
matchers.MismatchError, self.shell, 'quuz %s' % options_str)

View File

@ -1,78 +0,0 @@
# 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 os
import fixtures
import mock
import requests
import testtools
class TestCase(testtools.TestCase):
TEST_REQUEST_BASE = {
'verify': True,
}
def setUp(self):
super(TestCase, self).setUp()
if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
os.environ.get('OS_STDOUT_CAPTURE') == '1'):
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
os.environ.get('OS_STDERR_CAPTURE') == '1'):
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
def mock_object(self, obj, attr_name, new_attr=None, **kwargs):
"""Mock an object attribute.
Use python mock to mock an object attribute
Mocks the specified objects attribute with the given value.
Automatically performs 'addCleanup' for the mock.
"""
if not new_attr:
new_attr = mock.Mock()
patcher = mock.patch.object(obj, attr_name, new_attr, **kwargs)
patcher.start()
self.addCleanup(patcher.stop)
return new_attr
class TestResponse(requests.Response):
"""Class used to wrap requests.Response.
Class used to wrap requests.Response and provide some
convenience to initialize with a dict.
"""
def __init__(self, data):
self._text = None
super(TestResponse, self)
if isinstance(data, dict):
self.status_code = data.get('status_code', None)
self.headers = data.get('headers', {})
self.headers['x-openstack-request-id'] = data.get(
'x-openstack-request-id', 'fake-request-id')
# Fake the text attribute to streamline Response creation
self._text = data.get('text', None)
else:
self.status_code = data
self.headers = {'x-openstack-request-id': 'fake-request-id'}
def __eq__(self, other):
return self.__dict__ == other.__dict__
@property
def text(self):
return self._text

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class LimitsV1Test(utils.TestCase):
def test_import_v1_limits_module(self):
try:
from manilaclient.v1 import limits
except Exception as e:
msg = ("module 'manilaclient.v1.limits' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('Limits', 'RateLimit', 'AbsoluteLimit', 'LimitsManager'):
msg = "Module 'limits' has no '%s' attr." % cls
self.assertTrue(hasattr(limits, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class QuotaClassesV1Test(utils.TestCase):
def test_import_v1_quota_classes_module(self):
try:
from manilaclient.v1 import quota_classes
except Exception as e:
msg = ("module 'manilaclient.v1.quota_classes' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('QuotaClassSet', 'QuotaClassSetManager'):
msg = "Module 'quota_classes' has no '%s' attr." % cls
self.assertTrue(hasattr(quota_classes, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class QuotasV1Test(utils.TestCase):
def test_import_v1_quotas_module(self):
try:
from manilaclient.v1 import quotas
except Exception as e:
msg = ("module 'manilaclient.v1.quotas' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('QuotaSet', 'QuotaSetManager'):
msg = "Module 'quotas' has no '%s' attr." % cls
self.assertTrue(hasattr(quotas, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class SchedulerStatsV1Test(utils.TestCase):
def test_import_v1_scheduler_stats_module(self):
try:
from manilaclient.v1 import scheduler_stats
except Exception as e:
msg = ("module 'manilaclient.v1.scheduler_stats' cannot be "
"imported with error: %s") % six.text_type(e)
assert False, msg
for cls in ('Pool', 'PoolManager'):
msg = "Module 'scheduler_stats' has no '%s' attr." % cls
self.assertTrue(hasattr(scheduler_stats, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class SecurityServicesV1Test(utils.TestCase):
def test_import_v1_security_services_module(self):
try:
from manilaclient.v1 import security_services
except Exception as e:
msg = ("module 'manilaclient.v1.security_services' cannot be "
"imported with error: %s") % six.text_type(e)
assert False, msg
for cls in ('SecurityService', 'SecurityServiceManager'):
msg = "Module 'security_services' has no '%s' attr." % cls
self.assertTrue(hasattr(security_services, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ServicesV1Test(utils.TestCase):
def test_import_v1_services_module(self):
try:
from manilaclient.v1 import services
except Exception as e:
msg = ("module 'manilaclient.v1.services' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('Service', 'ServiceManager'):
msg = "Module 'services' has no '%s' attr." % cls
self.assertTrue(hasattr(services, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ShareNetworksV1Test(utils.TestCase):
def test_import_v1_share_networks_module(self):
try:
from manilaclient.v1 import share_networks
except Exception as e:
msg = ("module 'manilaclient.v1.share_networks' cannot be "
"imported with error: %s") % six.text_type(e)
assert False, msg
for cls in ('ShareNetwork', 'ShareNetworkManager'):
msg = "Module 'share_networks' has no '%s' attr." % cls
self.assertTrue(hasattr(share_networks, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ShareServersV1Test(utils.TestCase):
def test_import_v1_share_servers_module(self):
try:
from manilaclient.v1 import share_servers
except Exception as e:
msg = ("module 'manilaclient.v1.share_servers' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('ShareServer', 'ShareServerManager'):
msg = "Module 'share_servers' has no '%s' attr." % cls
self.assertTrue(hasattr(share_servers, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ShareSnapshotsV1Test(utils.TestCase):
def test_import_v1_share_snapshots_module(self):
try:
from manilaclient.v1 import share_snapshots
except Exception as e:
msg = ("module 'manilaclient.v1.share_snapshots' cannot be "
"imported with error: %s") % six.text_type(e)
assert False, msg
for cls in ('ShareSnapshot', 'ShareSnapshotManager'):
msg = "Module 'share_snapshots' has no '%s' attr." % cls
self.assertTrue(hasattr(share_snapshots, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ShareTypeAccessV1Test(utils.TestCase):
def test_import_v1_share_type_access_module(self):
try:
from manilaclient.v1 import share_type_access
except Exception as e:
msg = ("module 'manilaclient.v1.share_type_access' cannot be "
"imported with error: %s") % six.text_type(e)
assert False, msg
for cls in ('ShareTypeAccess', 'ShareTypeAccessManager'):
msg = "Module 'share_type_access' has no '%s' attr." % cls
self.assertTrue(hasattr(share_type_access, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class ShareTypesV1Test(utils.TestCase):
def test_import_v1_share_types_module(self):
try:
from manilaclient.v1 import share_types
except Exception as e:
msg = ("module 'manilaclient.v1.share_types' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('ShareType', 'ShareTypeManager'):
msg = "Module 'share_types' has no '%s' attr." % cls
self.assertTrue(hasattr(share_types, cls), msg)

View File

@ -1,32 +0,0 @@
# Copyright 2016 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from manilaclient.tests.unit import utils
class SharesV1Test(utils.TestCase):
def test_import_v1_shares_module(self):
try:
from manilaclient.v1 import shares
except Exception as e:
msg = ("module 'manilaclient.v1.shares' cannot be imported "
"with error: %s") % six.text_type(e)
assert False, msg
for cls in ('Share', 'ShareManager'):
msg = "Module 'shares' has no '%s' attr." % cls
self.assertTrue(hasattr(shares, cls), msg)

View File

@ -1,211 +0,0 @@
# Copyright 2013 OpenStack Foundation
#
# 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 six.moves.urllib import parse
from manilaclient import api_versions
from manilaclient.common import httpclient
from manilaclient.tests.unit import fakes
from manilaclient.tests.unit import utils
from manilaclient.v2 import client
class FakeClient(fakes.FakeClient, client.Client):
def __init__(self, *args, **kwargs):
api_version = kwargs.get('version') or api_versions.MAX_VERSION
client.Client.__init__(self, 'username', 'password',
'project_id', 'auth_url',
extensions=kwargs.get('extensions'),
version=api_version)
self.client = FakeHTTPClient(**kwargs)
class FakeHTTPClient(httpclient.HTTPClient):
def __init__(self, **kwargs):
api_version = kwargs.get('version') or api_versions.MAX_VERSION
self.username = 'username'
self.password = 'password'
self.auth_url = 'auth_url'
self.callstack = []
self.base_url = 'localhost'
self.default_headers = {
'X-Auth-Token': 'xabc123',
'X-Openstack-Manila-Api-Version': api_version,
'Accept': 'application/json',
}
def _cs_request(self, url, method, **kwargs):
return self._cs_request_with_retries(url, method, **kwargs)
def _cs_request_base_url(self, url, method, **kwargs):
return self._cs_request_with_retries(url, method, **kwargs)
def _cs_request_with_retries(self, url, method, **kwargs):
# Check that certain things are called correctly
if method in ['GET', 'DELETE']:
assert 'body' not in kwargs
elif method == 'PUT':
assert 'body' in kwargs
# Call the method
args = parse.parse_qsl(parse.urlparse(url)[4])
kwargs.update(args)
munged_url = url.rsplit('?', 1)[0]
munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
munged_url = munged_url.replace('-', '_')
callback = "%s_%s" % (method.lower(), munged_url)
if not hasattr(self, callback):
raise AssertionError('Called unknown API method: %s %s, '
'expected fakes method name: %s' %
(method, url, callback))
# Note the call
self.callstack.append((method, url, kwargs.get('body', None)))
status, headers, body = getattr(self, callback)(**kwargs)
r = utils.TestResponse({
"status_code": status,
"text": body,
"headers": headers,
})
return r, body
#
# Quotas
#
def get_os_quota_sets_test(self, **kw):
quota_set = {
'quota_set': {
'tenant_id': 'test',
'metadata_items': [],
'shares': 1,
'snapshots': 1,
'gigabytes': 1,
'snapshot_gigabytes': 1,
'share_networks': 1,
}
}
return (200, {}, quota_set)
get_quota_sets_test = get_os_quota_sets_test
def get_os_quota_sets_test_defaults(self):
quota_set = {
'quota_set': {
'tenant_id': 'test',
'metadata_items': [],
'shares': 1,
'snapshots': 1,
'gigabytes': 1,
'snapshot_gigabytes': 1,
'share_networks': 1,
}
}
return (200, {}, quota_set)
get_quota_sets_test_defaults = get_os_quota_sets_test_defaults
def put_os_quota_sets_test(self, body, **kw):
assert list(body) == ['quota_set']
fakes.assert_has_keys(body['quota_set'],
required=['tenant_id'])
quota_set = {
'quota_set': {
'tenant_id': 'test',
'metadata_items': [],
'shares': 2,
'snapshots': 2,
'gigabytes': 1,
'snapshot_gigabytes': 1,
'share_networks': 1,
}
}
return (200, {}, quota_set)
put_quota_sets_test = put_os_quota_sets_test
#
# Quota Classes
#
def get_os_quota_class_sets_test(self, **kw):
quota_class_set = {
'quota_class_set': {
'class_name': 'test',
'metadata_items': [],
'shares': 1,
'snapshots': 1,
'gigabytes': 1,
'snapshot_gigabytes': 1,
'share_networks': 1,
}
}
return (200, {}, quota_class_set)
get_quota_class_sets_test = get_os_quota_class_sets_test
def put_os_quota_class_sets_test(self, body, **kw):
assert list(body) == ['quota_class_set']
fakes.assert_has_keys(body['quota_class_set'],
required=['class_name'])
quota_class_set = {
'quota_class_set': {
'class_name': 'test',
'metadata_items': [],
'shares': 2,
'snapshots': 2,
'gigabytes': 1,
'snapshot_gigabytes': 1,
'share_networks': 1,
}
}
return (200, {}, quota_class_set)
put_quota_class_sets_test = put_os_quota_class_sets_test
def delete_os_quota_sets_test(self, **kw):
return (202, {}, {})
delete_quota_sets_test = delete_os_quota_sets_test
#
# List all extensions
#
def get_extensions(self, **kw):
exts = [
{
"alias": "FAKE-1",
"description": "Fake extension number 1",
"links": [],
"name": "Fake1",
"namespace": ("http://docs.openstack.org/"
"/ext/fake1/api/v1.1"),
"updated": "2011-06-09T00:00:00+00:00"
},
{
"alias": "FAKE-2",
"description": "Fake extension number 2",
"links": [],
"name": "Fake2",
"namespace": ("http://docs.openstack.org/"
"/ext/fake1/api/v1.1"),
"updated": "2011-06-09T00:00:00+00:00"
},
]
return (200, {}, {"extensions": exts, })

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
# Copyright 2016 Mirantis, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient import api_versions
from manilaclient.tests.unit import utils
from manilaclient.v2 import availability_zones
@ddt.ddt
class AvailabilityZoneTest(utils.TestCase):
def _get_manager(self, microversion):
version = api_versions.APIVersion(microversion)
mock_microversion = mock.Mock(api_version=version)
return availability_zones.AvailabilityZoneManager(
api=mock_microversion)
def _get_resource_path(self, microversion):
if (api_versions.APIVersion(microversion) >
api_versions.APIVersion("2.6")):
return availability_zones.RESOURCE_PATH
return availability_zones.RESOURCE_PATH_LEGACY
@ddt.data("2.6", "2.7", api_versions.MIN_VERSION, api_versions.MAX_VERSION)
def test_list(self, microversion):
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
self.mock_object(manager, "_list")
result = manager.list()
manager._list.assert_called_once_with(
resource_path, availability_zones.RESOURCE_NAME)
self.assertEqual(manager._list.return_value, result)

View File

@ -1,302 +0,0 @@
# 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 ddt
import mock
import manilaclient
from manilaclient import exceptions
from manilaclient.tests.unit import utils
from manilaclient.v2 import client
from oslo_utils import uuidutils
@ddt.ddt
class ClientTest(utils.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.catalog = {
'share': [
{'region': 'TestRegion', 'publicURL': 'http://1.2.3.4'},
],
}
def test_adapter_properties(self):
# sample of properties, there are many more
retries = 3
base_url = uuidutils.generate_uuid(dashed=False)
s = client.session.Session()
c = client.Client(session=s,
api_version=manilaclient.API_MAX_VERSION,
service_catalog_url=base_url, retries=retries,
input_auth_token='token')
self.assertEqual(base_url, c.client.endpoint_url)
self.assertEqual(retries, c.client.retries)
def test_auth_via_token_invalid(self):
self.assertRaises(exceptions.ClientException, client.Client,
api_version=manilaclient.API_MAX_VERSION,
input_auth_token="token")
def test_auth_via_token_and_session(self):
s = client.session.Session()
base_url = uuidutils.generate_uuid(dashed=False)
c = client.Client(input_auth_token='token',
service_catalog_url=base_url, session=s,
api_version=manilaclient.API_MAX_VERSION)
self.assertIsNotNone(c.client)
self.assertIsNone(c.keystone_client)
def test_auth_via_token(self):
base_url = uuidutils.generate_uuid(dashed=False)
c = client.Client(input_auth_token='token',
service_catalog_url=base_url,
api_version=manilaclient.API_MAX_VERSION)
self.assertIsNotNone(c.client)
self.assertIsNone(c.keystone_client)
@mock.patch.object(client.Client, '_get_keystone_client', mock.Mock())
def test_valid_region_name_v1(self):
self.mock_object(client.httpclient, 'HTTPClient')
kc = client.Client._get_keystone_client.return_value
kc.service_catalog = mock.Mock()
kc.service_catalog.get_endpoints = mock.Mock(return_value=self.catalog)
c = client.Client(api_version=manilaclient.API_DEPRECATED_VERSION,
service_type="share",
region_name='TestRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_with('share')
client.httpclient.HTTPClient.assert_called_with(
'http://1.2.3.4',
mock.ANY,
'python-manilaclient',
insecure=False,
cacert=None,
timeout=None,
retries=None,
http_log_debug=False,
api_version=manilaclient.API_DEPRECATED_VERSION)
self.assertIsNotNone(c.client)
@mock.patch.object(client.Client, '_get_keystone_client', mock.Mock())
def test_nonexistent_region_name(self):
kc = client.Client._get_keystone_client.return_value
kc.service_catalog = mock.Mock()
kc.service_catalog.get_endpoints = mock.Mock(return_value=self.catalog)
self.assertRaises(RuntimeError, client.Client,
api_version=manilaclient.API_MAX_VERSION,
region_name='FakeRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_with('sharev2')
@mock.patch.object(client.Client, '_get_keystone_client', mock.Mock())
def test_regions_with_same_name(self):
self.mock_object(client.httpclient, 'HTTPClient')
catalog = {
'sharev2': [
{'region': 'FirstRegion', 'publicURL': 'http://1.2.3.4'},
{'region': 'secondregion', 'publicURL': 'http://1.1.1.1'},
{'region': 'SecondRegion', 'publicURL': 'http://2.2.2.2'},
],
}
kc = client.Client._get_keystone_client.return_value
kc.service_catalog = mock.Mock()
kc.service_catalog.get_endpoints = mock.Mock(return_value=catalog)
c = client.Client(api_version=manilaclient.API_MIN_VERSION,
service_type='sharev2',
region_name='SecondRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_with('sharev2')
client.httpclient.HTTPClient.assert_called_with(
'http://2.2.2.2',
mock.ANY,
'python-manilaclient',
insecure=False,
cacert=None,
timeout=None,
retries=None,
http_log_debug=False,
api_version=manilaclient.API_MIN_VERSION)
self.assertIsNotNone(c.client)
def _get_client_args(self, **kwargs):
client_args = {
'auth_url': 'both',
'api_version': manilaclient.API_DEPRECATED_VERSION,
'username': 'fake_username',
'service_type': 'sharev2',
'region_name': 'SecondRegion',
'input_auth_token': None,
'session': None,
'service_catalog_url': None,
'user_id': 'foo_user_id',
'user_domain_name': 'foo_user_domain_name',
'user_domain_id': 'foo_user_domain_id',
'project_name': 'foo_project_name',
'project_domain_name': 'foo_project_domain_name',
'project_domain_id': 'foo_project_domain_id',
'endpoint_type': 'publicUrl',
'cert': 'foo_cert',
}
client_args.update(kwargs)
return client_args
@ddt.data(
{'auth_url': 'only_v3', 'api_key': 'password_backward_compat',
'endpoint_type': 'publicURL', 'project_id': 'foo_tenant_project_id'},
{'password': 'renamed_api_key', 'endpoint_type': 'public',
'tenant_id': 'foo_tenant_project_id'},
)
def test_client_init_no_session_no_auth_token_v3(self, kwargs):
def fake_url_for(version):
if version == 'v3.0':
return 'url_v3.0'
elif version == 'v2.0' and self.auth_url == 'both':
return 'url_v2.0'
else:
return None
self.mock_object(client.httpclient, 'HTTPClient')
self.mock_object(client.ks_client, 'Client')
self.mock_object(client.discover, 'Discover')
self.mock_object(client.session, 'Session')
client_args = self._get_client_args(**kwargs)
client_args['api_version'] = manilaclient.API_MIN_VERSION
self.auth_url = client_args['auth_url']
catalog = {
'share': [
{'region': 'SecondRegion', 'region_id': 'SecondRegion',
'url': 'http://4.4.4.4', 'interface': 'public',
},
],
'sharev2': [
{'region': 'FirstRegion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://1.1.1.1'},
{'region': 'secondregion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://2.2.2.2'},
{'region': 'SecondRegion', 'interface': 'internal',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.1'},
{'region': 'SecondRegion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.3'},
{'region': 'SecondRegion', 'interface': 'admin',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.2'},
],
}
client.discover.Discover.return_value.url_for.side_effect = (
fake_url_for)
client.ks_client.Client.return_value.auth_token.return_value = (
'fake_token')
mocked_ks_client = client.ks_client.Client.return_value
mocked_ks_client.service_catalog.get_endpoints.return_value = catalog
client.Client(**client_args)
client.httpclient.HTTPClient.assert_called_with(
'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False,
cacert=None, timeout=None, retries=None, http_log_debug=False,
api_version=manilaclient.API_MIN_VERSION)
client.ks_client.Client.assert_called_with(
session=mock.ANY, version=(3, 0), auth_url='url_v3.0',
username=client_args['username'],
password=client_args.get('password', client_args.get('api_key')),
user_id=client_args['user_id'],
user_domain_name=client_args['user_domain_name'],
user_domain_id=client_args['user_domain_id'],
project_id=client_args.get('tenant_id',
client_args.get('project_id')),
project_name=client_args['project_name'],
project_domain_name=client_args['project_domain_name'],
project_domain_id=client_args['project_domain_id'],
region_name=client_args['region_name'],
)
mocked_ks_client.service_catalog.get_endpoints.assert_called_with(
client_args['service_type'])
mocked_ks_client.authenticate.assert_called_with()
@ddt.data(
{'auth_url': 'only_v2', 'api_key': 'foo', 'project_id': 'bar'},
{'password': 'foo', 'tenant_id': 'bar'},
)
def test_client_init_no_session_no_auth_token_v2(self, kwargs):
self.mock_object(client.httpclient, 'HTTPClient')
self.mock_object(client.ks_client, 'Client')
self.mock_object(client.discover, 'Discover')
self.mock_object(client.session, 'Session')
client_args = self._get_client_args(**kwargs)
client_args['api_version'] = manilaclient.API_MIN_VERSION
self.auth_url = client_args['auth_url']
catalog = {
'share': [
{'region': 'SecondRegion', 'publicUrl': 'http://4.4.4.4'},
],
'sharev2': [
{'region': 'FirstRegion', 'publicUrl': 'http://1.1.1.1'},
{'region': 'secondregion', 'publicUrl': 'http://2.2.2.2'},
{'region': 'SecondRegion', 'internalUrl': 'http://3.3.3.1',
'publicUrl': 'http://3.3.3.3', 'adminUrl': 'http://3.3.3.2'},
],
}
client.discover.Discover.return_value.url_for.side_effect = (
lambda v: 'url_v2.0' if v == 'v2.0' else None)
client.ks_client.Client.return_value.auth_token.return_value = (
'fake_token')
mocked_ks_client = client.ks_client.Client.return_value
mocked_ks_client.service_catalog.get_endpoints.return_value = catalog
client.Client(**client_args)
client.httpclient.HTTPClient.assert_called_with(
'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False,
cacert=None, timeout=None, retries=None, http_log_debug=False,
api_version=manilaclient.API_MIN_VERSION)
client.ks_client.Client.assert_called_with(
session=mock.ANY, version=(2, 0), auth_url='url_v2.0',
username=client_args['username'],
password=client_args.get('password', client_args.get('api_key')),
tenant_id=client_args.get('tenant_id',
client_args.get('project_id')),
tenant_name=client_args['project_name'],
region_name=client_args['region_name'], cert=client_args['cert'],
use_keyring=False, force_new_token=False, stale_duration=300)
mocked_ks_client.service_catalog.get_endpoints.assert_called_with(
client_args['service_type'])
mocked_ks_client.authenticate.assert_called_with()
@mock.patch.object(client.ks_client, 'Client', mock.Mock())
@mock.patch.object(client.discover, 'Discover', mock.Mock())
@mock.patch.object(client.session, 'Session', mock.Mock())
def test_client_init_no_session_no_auth_token_endpoint_not_found(self):
self.mock_object(client.httpclient, 'HTTPClient')
client_args = self._get_client_args(
auth_urli='fake_url',
password='foo_password',
tenant_id='foo_tenant_id')
client.discover.Discover.return_value.url_for.return_value = None
mocked_ks_client = client.ks_client.Client.return_value
self.assertRaises(
exceptions.CommandError, client.Client, **client_args)
self.assertTrue(client.session.Session.called)
self.assertTrue(client.discover.Discover.called)
self.assertFalse(client.httpclient.HTTPClient.called)
self.assertFalse(client.ks_client.Client.called)
self.assertFalse(mocked_ks_client.service_catalog.get_endpoints.called)
self.assertFalse(mocked_ks_client.authenticate.called)

View File

@ -1,74 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient import api_versions
from manilaclient.tests.unit import utils
from manilaclient.v2 import quota_classes
@ddt.ddt
class QuotaClassSetsTest(utils.TestCase):
def _get_manager(self, microversion):
version = api_versions.APIVersion(microversion)
mock_microversion = mock.Mock(api_version=version)
return quota_classes.QuotaClassSetManager(api=mock_microversion)
def _get_resource_path(self, microversion):
if (api_versions.APIVersion(microversion) >
api_versions.APIVersion("2.6")):
return quota_classes.RESOURCE_PATH
return quota_classes.RESOURCE_PATH_LEGACY
@ddt.data("2.6", "2.7")
def test_class_quotas_get(self, microversion):
class_name = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test" % resource_path
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.get(class_name)
manager._get.assert_called_once_with(
expected_url, "quota_class_set")
@ddt.data("2.6", "2.7")
def test_update_quota(self, microversion):
class_name = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test" % resource_path
expected_body = {
'quota_class_set': {
'class_name': class_name,
'shares': 1,
'snapshots': 2,
'gigabytes': 3,
'snapshot_gigabytes': 4,
'share_networks': 5,
},
}
with mock.patch.object(manager, '_update',
mock.Mock(return_value='fake_update')):
manager.update(
class_name, shares=1, snapshots=2, gigabytes=3,
snapshot_gigabytes=4, share_networks=5)
manager._update.assert_called_once_with(
expected_url, expected_body)

View File

@ -1,169 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient import api_versions
from manilaclient.tests.unit import utils
from manilaclient.v2 import quotas
@ddt.ddt
class QuotaSetsTest(utils.TestCase):
def _get_manager(self, microversion):
version = api_versions.APIVersion(microversion)
mock_microversion = mock.Mock(api_version=version)
return quotas.QuotaSetManager(api=mock_microversion)
def _get_resource_path(self, microversion):
if (api_versions.APIVersion(microversion) >
api_versions.APIVersion("2.6")):
return quotas.RESOURCE_PATH
return quotas.RESOURCE_PATH_LEGACY
@ddt.data("2.6", "2.7", "2.25")
def test_tenant_quotas_get(self, microversion):
tenant_id = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
version = api_versions.APIVersion(microversion)
if version >= api_versions.APIVersion('2.25'):
expected_url = "%s/test/detail" % resource_path
else:
expected_url = ("%s/test"
% resource_path)
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.get(tenant_id, detail=True)
manager._get.assert_called_once_with(expected_url, "quota_set")
@ddt.data("2.6", "2.7", "2.25")
def test_user_quotas_get(self, microversion):
tenant_id = 'test'
user_id = 'fake_user'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
version = api_versions.APIVersion(microversion)
if version >= api_versions.APIVersion('2.25'):
expected_url = ("%s/test/detail?user_id=fake_user"
% resource_path)
else:
expected_url = ("%s/test?user_id=fake_user"
% resource_path)
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.get(tenant_id, user_id=user_id, detail=True)
manager._get.assert_called_once_with(expected_url, "quota_set")
@ddt.data("2.6", "2.7")
def test_tenant_quotas_defaults(self, microversion):
tenant_id = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test/defaults" % resource_path
with mock.patch.object(manager, '_get',
mock.Mock(return_value='fake_get')):
manager.defaults(tenant_id)
manager._get.assert_called_once_with(expected_url, "quota_set")
@ddt.data(
("2.6", {}),
("2.6", {"force": True}),
("2.7", {}),
("2.7", {"force": True}),
)
@ddt.unpack
def test_update_quota(self, microversion, extra_data):
tenant_id = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test" % resource_path
expected_body = {
'quota_set': {
'tenant_id': tenant_id,
'shares': 1,
'snapshots': 2,
'gigabytes': 3,
'snapshot_gigabytes': 4,
'share_networks': 5,
},
}
expected_body['quota_set'].update(extra_data)
with mock.patch.object(manager, '_update',
mock.Mock(return_value='fake_update')):
manager.update(
tenant_id, shares=1, snapshots=2, gigabytes=3,
snapshot_gigabytes=4, share_networks=5, **extra_data)
manager._update.assert_called_once_with(
expected_url, expected_body, "quota_set")
@ddt.data("2.6", "2.7")
def test_update_user_quota(self, microversion):
tenant_id = 'test'
user_id = 'fake_user'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test?user_id=fake_user" % resource_path
expected_body = {
'quota_set': {
'tenant_id': tenant_id,
'shares': 1,
'snapshots': 2,
'gigabytes': 3,
'snapshot_gigabytes': 4,
'share_networks': 5,
},
}
with mock.patch.object(manager, '_update',
mock.Mock(return_value='fake_update')):
manager.update(
tenant_id, shares=1, snapshots=2, gigabytes=3,
snapshot_gigabytes=4, share_networks=5, user_id=user_id)
manager._update.assert_called_once_with(
expected_url, expected_body, "quota_set")
@ddt.data("2.6", "2.7")
def test_quotas_delete(self, microversion):
tenant_id = 'test'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test" % resource_path
with mock.patch.object(manager, '_delete',
mock.Mock(return_value='fake_delete')):
manager.delete(tenant_id)
manager._delete.assert_called_once_with(expected_url)
@ddt.data("2.6", "2.7")
def test_user_quotas_delete(self, microversion):
tenant_id = 'test'
user_id = 'fake_user'
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
expected_url = "%s/test?user_id=fake_user" % resource_path
with mock.patch.object(manager, '_delete',
mock.Mock(return_value='fake_delete')):
manager.delete(tenant_id, user_id=user_id)
manager._delete.assert_called_once_with(expected_url)

View File

@ -1,81 +0,0 @@
# Copyright (c) 2015 Clinton Knight. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import scheduler_stats
class PoolTest(utils.TestCase):
def setUp(self):
super(PoolTest, self).setUp()
self.name = 'fake_host@fake_backend#fake_pool'
info = {
'name': self.name,
'host': 'fake_host',
'backend': 'fake_backend',
'pool': 'fake_pool',
}
self.resource_class = scheduler_stats.Pool(manager=self, info=info)
def test_get_repr_of_share_server(self):
self.assertEqual('<Pool: %s>' % self.name, repr(self.resource_class))
class PoolManagerTest(utils.TestCase):
def setUp(self):
super(PoolManagerTest, self).setUp()
self.manager = scheduler_stats.PoolManager(fakes.FakeClient())
@mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock())
def test_list(self):
self.manager.list(detailed=False)
self.manager._list.assert_called_once_with(
scheduler_stats.RESOURCES_PATH,
scheduler_stats.RESOURCES_NAME)
@mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock())
def test_list_detail(self):
self.manager.list()
self.manager._list.assert_called_once_with(
scheduler_stats.RESOURCES_PATH + '/detail',
scheduler_stats.RESOURCES_NAME)
@mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock())
def test_list_with_one_search_opt(self):
host = 'fake_host'
query_string = "?host=%s" % host
self.manager.list(detailed=False, search_opts={'host': host})
self.manager._list.assert_called_once_with(
scheduler_stats.RESOURCES_PATH + query_string,
scheduler_stats.RESOURCES_NAME)
@mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock())
def test_list_detail_with_two_search_opts(self):
host = 'fake_host'
backend = 'fake_backend'
query_string = "?backend=%s&host=%s" % (backend, host)
self.manager.list(search_opts={'host': host, 'backend': backend})
self.manager._list.assert_called_once_with(
scheduler_stats.RESOURCES_PATH + '/detail' + query_string,
scheduler_stats.RESOURCES_NAME)

View File

@ -1,178 +0,0 @@
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from manilaclient import exceptions
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import security_services
class SecurityServiceTest(utils.TestCase):
class _FakeSecurityService(object):
id = 'fake_security_service_id'
def setUp(self):
super(SecurityServiceTest, self).setUp()
self.manager = security_services.SecurityServiceManager(
fakes.FakeClient())
def test_create_all_fields(self):
values = {
'type': 'ldap',
'dns_ip': 'fake dns ip',
'server': 'fake.ldap.server',
'domain': 'fake.ldap.domain',
'user': 'fake user',
'password': 'fake password',
'name': 'fake name',
'description': 'fake description',
}
with mock.patch.object(self.manager, '_create', fakes.fake_create):
result = self.manager.create(**values)
self.assertEqual(result['url'], security_services.RESOURCES_PATH)
self.assertEqual(result['resp_key'],
security_services.RESOURCE_NAME)
self.assertIn(security_services.RESOURCE_NAME, result['body'])
self.assertEqual(result['body'][security_services.RESOURCE_NAME],
values)
def test_create_some_fields(self):
values = {
'type': 'ldap',
'dns_ip': 'fake dns ip',
'server': 'fake.ldap.server',
'domain': 'fake.ldap.domain',
'user': 'fake user',
}
with mock.patch.object(self.manager, '_create', fakes.fake_create):
result = self.manager.create(**values)
self.assertEqual(result['url'], security_services.RESOURCES_PATH)
self.assertEqual(result['resp_key'],
security_services.RESOURCE_NAME)
self.assertIn(security_services.RESOURCE_NAME, result['body'])
self.assertEqual(result['body'][security_services.RESOURCE_NAME],
values)
def test_delete(self):
security_service = 'fake service'
with mock.patch.object(self.manager, '_delete', mock.Mock()):
self.manager.delete(security_service)
self.manager._delete.assert_called_once_with(
security_services.RESOURCE_PATH % security_service)
def test_delete_by_object(self):
security_service = self._FakeSecurityService()
with mock.patch.object(self.manager, '_delete', mock.Mock()):
self.manager.delete(security_service)
self.manager._delete.assert_called_once_with(
security_services.RESOURCE_PATH % security_service.id)
def test_get(self):
security_service = 'fake service'
with mock.patch.object(self.manager, '_get', mock.Mock()):
self.manager.get(security_service)
self.manager._get.assert_called_once_with(
security_services.RESOURCE_PATH % security_service,
security_services.RESOURCE_NAME)
def test_get_by_object(self):
security_service = self._FakeSecurityService()
with mock.patch.object(self.manager, '_get', mock.Mock()):
self.manager.get(security_service)
self.manager._get.assert_called_once_with(
security_services.RESOURCE_PATH % security_service.id,
security_services.RESOURCE_NAME)
def test_list_summary(self):
with mock.patch.object(self.manager, '_list',
mock.Mock(return_value=None)):
self.manager.list(detailed=False)
self.manager._list.assert_called_once_with(
security_services.RESOURCES_PATH,
security_services.RESOURCES_NAME)
def test_list_detail(self):
with mock.patch.object(self.manager, '_list',
mock.Mock(return_value=None)):
self.manager.list(detailed=True)
self.manager._list.assert_called_once_with(
security_services.RESOURCES_PATH + '/detail',
security_services.RESOURCES_NAME)
def test_list_no_filters(self):
with mock.patch.object(self.manager, '_list',
mock.Mock(return_value=None)):
self.manager.list()
self.manager._list.assert_called_once_with(
security_services.RESOURCES_PATH + '/detail',
security_services.RESOURCES_NAME)
def test_list_with_filters(self):
filters = {'all_tenants': 1, 'network': 'fake', 'status': 'ERROR'}
expected_postfix = ('/detail?all_tenants=1&network=fake&status=ERROR')
with mock.patch.object(self.manager, '_list',
mock.Mock(return_value=None)):
self.manager.list(search_opts=filters)
self.manager._list.assert_called_once_with(
security_services.RESOURCES_PATH + expected_postfix,
security_services.RESOURCES_NAME)
def test_update(self):
security_service = 'fake service'
values = {
'dns_ip': 'new dns ip',
'server': 'new.ldap.server',
'domain': 'new.ldap.domain',
'user': 'new user',
'password': 'fake password',
}
with mock.patch.object(self.manager, '_update', fakes.fake_update):
result = self.manager.update(security_service, **values)
self.assertEqual(
result['url'],
security_services.RESOURCE_PATH % security_service)
self.assertEqual(
result['resp_key'],
security_services.RESOURCE_NAME)
self.assertEqual(
result['body'][security_services.RESOURCE_NAME],
values)
def test_update_by_object(self):
security_service = self._FakeSecurityService()
values = {'user': 'fake_user'}
with mock.patch.object(self.manager, '_update', fakes.fake_update):
result = self.manager.update(security_service, **values)
self.assertEqual(
result['url'],
security_services.RESOURCE_PATH % security_service.id)
self.assertEqual(
result['resp_key'],
security_services.RESOURCE_NAME)
self.assertEqual(
result['body'][security_services.RESOURCE_NAME],
values)
def test_update_no_fields_specified(self):
security_service = 'fake service'
self.assertRaises(exceptions.CommandError,
self.manager.update,
security_service)

View File

@ -1,99 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient import api_versions
from manilaclient.tests.unit import utils
from manilaclient.v2 import services
@ddt.ddt
class ServicesTest(utils.TestCase):
def _get_manager(self, microversion):
version = api_versions.APIVersion(microversion)
mock_microversion = mock.Mock(api_version=version)
return services.ServiceManager(api=mock_microversion)
def _get_resource_path(self, microversion):
if (api_versions.APIVersion(microversion) >
api_versions.APIVersion("2.6")):
return services.RESOURCE_PATH
return services.RESOURCE_PATH_LEGACY
@ddt.data("2.6", "2.7")
def test_list(self, microversion):
manager = self._get_manager(microversion)
resource_path = self._get_resource_path(microversion)
with mock.patch.object(manager, '_list',
mock.Mock(return_value='fake')):
result = manager.list()
manager._list.assert_called_once_with(
resource_path, services.RESOURCE_NAME)
self.assertEqual("fake", result)
def test_list_services_with_one_search_opt(self):
manager = self._get_manager("2.7")
host = 'fake_host'
query_string = "?host=%s" % host
with mock.patch.object(manager, '_list',
mock.Mock(return_value=None)):
manager.list({'host': host})
manager._list.assert_called_once_with(
services.RESOURCE_PATH + query_string,
services.RESOURCE_NAME,
)
def test_list_services_with_two_search_opts(self):
manager = self._get_manager("2.7")
host = 'fake_host'
binary = 'fake_binary'
query_string = "?binary=%s&host=%s" % (binary, host)
with mock.patch.object(manager, '_list',
mock.Mock(return_value=None)):
manager.list({'binary': binary, 'host': host})
manager._list.assert_called_once_with(
services.RESOURCE_PATH + query_string,
services.RESOURCE_NAME,
)
@ddt.data("2.6", "2.7")
def test_enable_service(self, microversion):
manager = self._get_manager(microversion)
host = 'fake_host'
binary = 'fake_binary'
with mock.patch.object(manager, '_update'):
manager.enable(binary=binary, host=host)
manager._update.assert_called_once_with(
self._get_resource_path(microversion) + '/enable',
{"host": host, "binary": binary},
)
@ddt.data("2.6", "2.7")
def test_disable_service(self, microversion):
manager = self._get_manager(microversion)
host = 'fake_host'
binary = 'fake_binary'
with mock.patch.object(manager, '_update'):
manager.disable(binary=binary, host=host)
manager._update.assert_called_once_with(
self._get_resource_path(microversion) + '/disable',
{"host": host, "binary": binary},
)

View File

@ -1,57 +0,0 @@
# Copyright 2015 Mirantis inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient import api_versions
from manilaclient import extension
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import share_export_locations
extensions = [
extension.Extension('share_export_locations', share_export_locations),
]
cs = fakes.FakeClient(extensions=extensions)
@ddt.ddt
class ShareExportLocationsTest(utils.TestCase):
def _get_manager(self, microversion):
version = api_versions.APIVersion(microversion)
mock_microversion = mock.Mock(api_version=version)
return (
share_export_locations.ShareExportLocationManager(
api=mock_microversion)
)
def test_list_of_export_locations(self):
share_id = '1234'
cs.share_export_locations.list(share_id)
cs.assert_called(
'GET', '/shares/%s/export_locations' % share_id)
def test_get_single_export_location(self):
share_id = '1234'
el_uuid = 'fake_el_uuid'
cs.share_export_locations.get(share_id, el_uuid)
cs.assert_called(
'GET',
('/shares/%(share_id)s/export_locations/'
'%(el_uuid)s') % {
'share_id': share_id, 'el_uuid': el_uuid})

View File

@ -1,265 +0,0 @@
# Copyright 2016 Clinton Knight
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import ddt
import six
import manilaclient
from manilaclient import exceptions
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes as fake
from manilaclient.v2 import share_group_snapshots as snapshots
class ShareGroupSnapshotTest(utils.TestCase):
def setUp(self):
super(ShareGroupSnapshotTest, self).setUp()
self.manager = snapshots.ShareGroupSnapshotManager(fake.FakeClient())
self.share_group_snapshot = snapshots.ShareGroupSnapshot(
self.manager, {'id': 'fake_id'})
self.fake_kwargs = {'key': 'value'}
def test_repr(self):
result = six.text_type(self.share_group_snapshot)
self.assertEqual('<Share Group Snapshot: fake_id>', result)
def test_update(self):
mock_manager_update = self.mock_object(self.manager, 'update')
self.share_group_snapshot.update(**self.fake_kwargs)
mock_manager_update.assert_called_once_with(
self.share_group_snapshot, **self.fake_kwargs)
def test_delete(self):
mock_manager_delete = self.mock_object(self.manager, 'delete')
self.share_group_snapshot.delete()
mock_manager_delete.assert_called_once_with(self.share_group_snapshot)
def test_reset_state(self):
mock_manager_reset_state = self.mock_object(
self.manager, 'reset_state')
self.share_group_snapshot.reset_state('fake_state')
mock_manager_reset_state.assert_called_once_with(
self.share_group_snapshot, 'fake_state')
@ddt.ddt
class ShareGroupSnapshotManagerTest(utils.TestCase):
def setUp(self):
super(ShareGroupSnapshotManagerTest, self).setUp()
self.manager = snapshots.ShareGroupSnapshotManager(fake.FakeClient())
def test_create(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_create = self.mock_object(
self.manager, '_create',
mock.Mock(return_value=fake_share_group_snapshot))
create_args = {
'name': fake.ShareGroupSnapshot.name,
'description': fake.ShareGroupSnapshot.description,
}
result = self.manager.create(fake.ShareGroupSnapshot, **create_args)
self.assertIs(fake_share_group_snapshot, result)
expected_body = {
snapshots.RESOURCE_NAME: {
'name': fake.ShareGroupSnapshot.name,
'description': fake.ShareGroupSnapshot.description,
'share_group_id': fake.ShareGroupSnapshot().id,
},
}
mock_create.assert_called_once_with(
snapshots.RESOURCES_PATH, expected_body, snapshots.RESOURCE_NAME)
def test_create_minimal_args(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_create = self.mock_object(
self.manager, '_create',
mock.Mock(return_value=fake_share_group_snapshot))
result = self.manager.create(fake.ShareGroupSnapshot)
self.assertIs(fake_share_group_snapshot, result)
expected_body = {
snapshots.RESOURCE_NAME: {
'share_group_id': fake.ShareGroupSnapshot().id,
},
}
mock_create.assert_called_once_with(
snapshots.RESOURCES_PATH, expected_body, snapshots.RESOURCE_NAME)
def test_create_using_unsupported_microversion(self):
self.manager.api.api_version = manilaclient.API_MIN_VERSION
self.assertRaises(
exceptions.UnsupportedVersion,
self.manager.create, fake.ShareGroupSnapshot)
def test_get(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_get = self.mock_object(
self.manager, '_get',
mock.Mock(return_value=fake_share_group_snapshot))
result = self.manager.get(fake.ShareGroupSnapshot.id)
self.assertIs(fake_share_group_snapshot, result)
mock_get.assert_called_once_with(
snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id,
snapshots.RESOURCE_NAME)
def test_list(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_list = self.mock_object(
self.manager, '_list',
mock.Mock(return_value=[fake_share_group_snapshot]))
result = self.manager.list()
self.assertEqual([fake_share_group_snapshot], result)
mock_list.assert_called_once_with(
snapshots.RESOURCES_PATH + '/detail',
snapshots.RESOURCES_NAME)
def test_list_no_detail(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_list = self.mock_object(
self.manager, '_list',
mock.Mock(return_value=[fake_share_group_snapshot]))
result = self.manager.list(detailed=False)
self.assertEqual([fake_share_group_snapshot], result)
mock_list.assert_called_once_with(
snapshots.RESOURCES_PATH, snapshots.RESOURCES_NAME)
def test_list_with_filters(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_list = self.mock_object(
self.manager, '_list',
mock.Mock(return_value=[fake_share_group_snapshot]))
filters = {'all_tenants': 1, 'status': 'ERROR'}
result = self.manager.list(detailed=False, search_opts=filters)
self.assertEqual([fake_share_group_snapshot], result)
expected_path = (snapshots.RESOURCES_PATH +
'?all_tenants=1&status=ERROR')
mock_list.assert_called_once_with(
expected_path, snapshots.RESOURCES_NAME)
def test_list_with_sorting(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_list = self.mock_object(
self.manager, '_list',
mock.Mock(return_value=[fake_share_group_snapshot]))
result = self.manager.list(
detailed=False, sort_dir='asc', sort_key='name')
self.assertEqual([fake_share_group_snapshot], result)
expected_path = (
snapshots.RESOURCES_PATH + '?sort_dir=asc&sort_key=name')
mock_list.assert_called_once_with(
expected_path, snapshots.RESOURCES_NAME)
@ddt.data({'sort_key': 'name', 'sort_dir': 'invalid'},
{'sort_key': 'invalid', 'sort_dir': 'asc'})
@ddt.unpack
def test_list_with_invalid_sorting(self, sort_key, sort_dir):
self.assertRaises(
ValueError,
self.manager.list, sort_dir=sort_dir, sort_key=sort_key)
def test_update(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_get = self.mock_object(
self.manager, '_get',
mock.Mock(return_value=fake_share_group_snapshot))
mock_update = self.mock_object(
self.manager, '_update',
mock.Mock(return_value=fake_share_group_snapshot))
update_args = {
'name': fake.ShareGroupSnapshot.name,
'description': fake.ShareGroupSnapshot.description,
}
result = self.manager.update(fake.ShareGroupSnapshot(), **update_args)
self.assertIs(fake_share_group_snapshot, result)
self.assertFalse(mock_get.called)
mock_update.assert_called_once_with(
snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id,
{snapshots.RESOURCE_NAME: update_args},
snapshots.RESOURCE_NAME)
def test_update_no_data(self):
fake_share_group_snapshot = fake.ShareGroupSnapshot()
mock_get = self.mock_object(
self.manager, '_get',
mock.Mock(return_value=fake_share_group_snapshot))
mock_update = self.mock_object(
self.manager, '_update',
mock.Mock(return_value=fake_share_group_snapshot))
update_args = {}
result = self.manager.update(fake.ShareGroupSnapshot(), **update_args)
self.assertIs(fake_share_group_snapshot, result)
mock_get.assert_called_once_with(
snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id,
snapshots.RESOURCE_NAME)
self.assertFalse(mock_update.called)
def test_delete(self):
mock_delete = self.mock_object(self.manager, '_delete')
mock_post = self.mock_object(self.manager.api.client, 'post')
self.manager.delete(fake.ShareGroupSnapshot())
mock_delete.assert_called_once_with(
snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id)
self.assertFalse(mock_post.called)
def test_delete_force(self):
mock_delete = self.mock_object(self.manager, '_delete')
mock_post = self.mock_object(self.manager.api.client, 'post')
self.manager.delete(fake.ShareGroupSnapshot.id, force=True)
self.assertFalse(mock_delete.called)
mock_post.assert_called_once_with(
snapshots.RESOURCE_PATH_ACTION % fake.ShareGroupSnapshot.id,
body={'force_delete': None})
def test_reset_state(self):
mock_post = self.mock_object(self.manager.api.client, 'post')
self.manager.reset_state(fake.ShareGroupSnapshot(), 'fake_state')
mock_post.assert_called_once_with(
snapshots.RESOURCE_PATH_ACTION % fake.ShareGroupSnapshot.id,
body={'reset_status': {'status': 'fake_state'}})

Some files were not shown because too many files have changed in this diff Show More