python-manilaclient-0.1~git20140208/0000775000175000017500000000000012275236446016045 5ustar chuckchuckpython-manilaclient-0.1~git20140208/run_tests.sh0000775000175000017500000001271012275236132020423 0ustar chuckchuck#!/bin/bash set -eu function usage { echo "Usage: $0 [OPTION]..." echo "Run python-manilaclient test suite" echo "" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -s, --no-site-packages Isolate the virtualenv from the global Python environment" echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." echo " -p, --pep8 Just run pep8" echo " -P, --no-pep8 Don't run pep8" echo " -c, --coverage Generate coverage report" echo " -h, --help Print this usage message" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." exit } function process_option { case "$1" in -h|--help) usage;; -V|--virtual-env) always_venv=1; never_venv=0;; -N|--no-virtual-env) always_venv=0; never_venv=1;; -s|--no-site-packages) no_site_packages=1;; -f|--force) force=1;; -p|--pep8) just_pep8=1;; -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; -d|--debug) debug=1;; -*) testropts="$testropts $1";; *) testrargs="$testrargs $1" esac } venv=.venv with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 no_site_packages=0 installvenvopts= testrargs= testropts= wrapper="" just_pep8=0 no_pep8=0 coverage=0 debug=0 LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C for arg in "$@"; do process_option $arg done if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi function init_testr { if [ ! -d .testrepository ]; then ${wrapper} testr init fi } function run_tests { # Cleanup *pyc ${wrapper} find . -type f -name "*.pyc" -delete if [ $debug -eq 1 ]; then if [ "$testropts" = "" ] && [ "$testrargs" = "" ]; then # Default to running all tests if specific test is not # provided. testrargs="discover ./tests" fi ${wrapper} python -m testtools.run $testropts $testrargs # Short circuit because all of the testr and coverage stuff # below does not make sense when running testtools.run for # debugging purposes. return $? fi if [ $coverage -eq 1 ]; then # Do not test test_coverage_ext when gathering coverage. if [ "x$testrargs" = "x" ]; then testrargs="^(?!.*test_coverage_ext).*$" fi export PYTHON="${wrapper} coverage run --source manilaclient --parallel-mode" fi # Just run the test suites in current environment set +e TESTRTESTS="$TESTRTESTS $testrargs" echo "Running \`${wrapper} $TESTRTESTS\`" ${wrapper} $TESTRTESTS RESULT=$? set -e copy_subunit_log return $RESULT } function copy_subunit_log { LOGNAME=`cat .testrepository/next-stream` LOGNAME=$(($LOGNAME - 1)) LOGNAME=".testrepository/${LOGNAME}" cp $LOGNAME subunit.log } function run_pep8 { echo "Running pep8 ..." srcfiles="manilaclient tests" # Just run PEP8 in current environment # # NOTE(sirp): W602 (deprecated 3-arg raise) is being ignored for the # following reasons: # # 1. It's needed to preserve traceback information when re-raising # exceptions; this is needed b/c Eventlet will clear exceptions when # switching contexts. # # 2. There doesn't appear to be an alternative, "pep8-tool" compatible way of doing this # in Python 2 (in Python 3 `with_traceback` could be used). # # 3. Can find no corroborating evidence that this is deprecated in Python 2 # other than what the PEP8 tool claims. It is deprecated in Python 3, so, # perhaps the mistake was thinking that the deprecation applied to Python 2 # as well. pep8_opts="--ignore=E202,W602 --repeat" ${wrapper} pep8 ${pep8_opts} ${srcfiles} } TESTRTESTS="testr run --parallel $testropts" if [ $never_venv -eq 0 ] then # Remove the virtual environment if --force used if [ $force -eq 1 ]; then echo "Cleaning virtualenv..." rm -rf ${venv} fi if [ -e ${venv} ]; then wrapper="${with_venv}" else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv python tools/install_venv.py $installvenvopts wrapper="${with_venv}" else echo -e "No virtual environment found...create one? (Y/n) \c" read use_ve if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then # Install the virtualenv and run the test suite in it python tools/install_venv.py $installvenvopts wrapper=${with_venv} fi fi fi fi # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase fi if [ $just_pep8 -eq 1 ]; then run_pep8 exit fi init_testr run_tests # NOTE(sirp): we only want to run pep8 when we're running the full-test suite, # not when we're running tests individually. if [ -z "$testrargs" ]; then if [ $no_pep8 -eq 0 ]; then run_pep8 fi fi if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" ${wrapper} coverage combine ${wrapper} coverage html --include='manilaclient/*' --omit='manilaclient/openstack/common/*' -d covhtml -i fi python-manilaclient-0.1~git20140208/openstack-common.conf0000664000175000017500000000025012275236132022156 0ustar chuckchuck[DEFAULT] # The list of modules to copy from openstack-common modules=setup,version,strutils # The base module to hold the copy of openstack.common base=manilaclient python-manilaclient-0.1~git20140208/manilaclient/0000775000175000017500000000000012275236446020505 5ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/utils.py0000664000175000017500000002062512275236132022214 0ustar chuckchuckimport os import re import sys import uuid import prettytable from manilaclient import exceptions from manilaclient.openstack.common import strutils def arg(*args, **kwargs): """Decorator for CLI args.""" def _decorator(func): add_arg(func, *args, **kwargs) return func return _decorator def env(*vars, **kwargs): """ returns the first environment variable set if none are non-empty, defaults to '' or keyword arg default """ for v in vars: value = os.environ.get(v, None) if value: return value return kwargs.get('default', '') def add_arg(f, *args, **kwargs): """Bind CLI arguments to a shell.py `do_foo` function.""" if not hasattr(f, 'arguments'): f.arguments = [] # NOTE(sirp): avoid dups that can occur when the module is shared across # tests. if (args, kwargs) not in f.arguments: # Because of the sematics of decorator composition if we just append # to the options list positional options will appear to be backwards. f.arguments.insert(0, (args, kwargs)) def add_resource_manager_extra_kwargs_hook(f, hook): """Adds hook to bind CLI arguments to ResourceManager calls. The `do_foo` calls in shell.py will receive CLI args and then in turn pass them through to the ResourceManager. Before passing through the args, the hooks registered here will be called, giving us a chance to add extra kwargs (taken from the command-line) to what's passed to the ResourceManager. """ if not hasattr(f, 'resource_manager_kwargs_hooks'): f.resource_manager_kwargs_hooks = [] names = [h.__name__ for h in f.resource_manager_kwargs_hooks] if hook.__name__ not in names: f.resource_manager_kwargs_hooks.append(hook) def get_resource_manager_extra_kwargs(f, args, allow_conflicts=False): """Return extra_kwargs by calling resource manager kwargs hooks.""" hooks = getattr(f, "resource_manager_kwargs_hooks", []) extra_kwargs = {} for hook in hooks: hook_name = hook.__name__ hook_kwargs = hook(args) conflicting_keys = set(hook_kwargs.keys()) & set(extra_kwargs.keys()) if conflicting_keys and not allow_conflicts: raise Exception("Hook '%(hook_name)s' is attempting to redefine" " attributes '%(conflicting_keys)s'" % locals()) extra_kwargs.update(hook_kwargs) return extra_kwargs def unauthenticated(f): """ Adds 'unauthenticated' attribute to decorated function. Usage: @unauthenticated def mymethod(f): ... """ f.unauthenticated = True return f def isunauthenticated(f): """ Checks to see if the function is marked as not requiring authentication with the @unauthenticated decorator. Returns True if decorator is set to True, False otherwise. """ return getattr(f, 'unauthenticated', False) def service_type(stype): """ Adds 'service_type' attribute to decorated function. Usage: @service_type('share') 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 print_list(objs, fields, formatters={}): mixed_case_fields = ['serverId'] pt = prettytable.PrettyTable([f for f in fields], caching=False) pt.aligns = ['l' for f in fields] 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) print strutils.safe_encode(pt.get_string(sortby=fields[0])) def print_dict(d, property="Property"): pt = prettytable.PrettyTable([property, 'Value'], caching=False) pt.aligns = ['l', 'l'] [pt.add_row(list(r)) for r in d.iteritems()] print strutils.safe_encode(pt.get_string(sortby=property)) def find_resource(manager, name_or_id): """Helper for the _find_* methods.""" # first try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) except exceptions.NotFound: pass # now try to get entity as uuid try: uuid.UUID(strutils.safe_decode(name_or_id)) return manager.get(name_or_id) except (ValueError, exceptions.NotFound): pass try: try: return manager.find(human_id=name_or_id) except exceptions.NotFound: pass # finally try to find entity by name try: return manager.find(name=name_or_id) except exceptions.NotFound: try: return manager.find(display_name=name_or_id) except (UnicodeDecodeError, exceptions.NotFound): try: # Volumes does not have name, but display_name return manager.find(display_name=name_or_id) except exceptions.NotFound: msg = "No %s with a name or ID of '%s' exists." % \ (manager.resource_class.__name__.lower(), name_or_id) raise exceptions.CommandError(msg) except exceptions.NoUniqueMatch: msg = ("Multiple %s matches found for '%s', use an ID to be more" " specific." % (manager.resource_class.__name__.lower(), name_or_id)) raise exceptions.CommandError(msg) def find_share(cs, share): """Get a share by name or ID.""" return find_resource(cs.shares, share) def _format_servers_list_networks(server): output = [] for (network, addresses) in server.networks.items(): if len(addresses) == 0: continue addresses_csv = ', '.join(addresses) group = "%s=%s" % (network, addresses_csv) output.append(group) return '; '.join(output) class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): 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): hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) def safe_issubclass(*args): """Like issubclass, but will just return False if not a class.""" try: if issubclass(*args): return True except TypeError: pass return False def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') __import__(mod_str) return getattr(sys.modules[mod_str], class_str) _slugify_strip_re = re.compile(r'[^\w\s-]') _slugify_hyphenate_re = re.compile(r'[-\s]+') # http://code.activestate.com/recipes/ # 577257-slugify-make-a-string-usable-in-a-url-or-filename/ def slugify(value): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. From Django's "django/template/defaultfilters.py". """ import unicodedata if not isinstance(value, unicode): value = unicode(value) value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') value = unicode(_slugify_strip_re.sub('', value).strip().lower()) return _slugify_hyphenate_re.sub('-', value) def make_metadata_dict(metadata): """ Converts given metadata in form of list of 'key=value' strings into {'key': 'value'} dictionary """ metadata_dict = {} for item in metadata: try: key, value = item.split('=') except ValueError: msg = "Wrong argument format: '%s'" % item raise exceptions.CommandError(msg) if 'password' in key: value = value.strip('"').strip("'") metadata_dict[key] = value return metadata_dict python-manilaclient-0.1~git20140208/manilaclient/openstack/0000775000175000017500000000000012275236446022474 5ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/openstack/__init__.py0000664000175000017500000000000012275236132024563 0ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/openstack/common/0000775000175000017500000000000012275236446023764 5ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/openstack/common/setup.py0000664000175000017500000003376612275236132025505 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2011 OpenStack Foundation. # Copyright 2012-2013 Hewlett-Packard Development Company, L.P. # 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. """ Utilities with minimum-depends for use in setup.py """ import email import os import re import subprocess import sys from setuptools.command import sdist def parse_mailmap(mailmap='.mailmap'): mapping = {} if os.path.exists(mailmap): with open(mailmap, 'r') as fp: for l in fp: try: canonical_email, alias = re.match( r'[^#]*?(<.+>).*(<.+>).*', l).groups() except AttributeError: continue mapping[alias] = canonical_email return mapping def _parse_git_mailmap(git_dir, mailmap='.mailmap'): mailmap = os.path.join(os.path.dirname(git_dir), mailmap) return parse_mailmap(mailmap) def canonicalize_emails(changelog, mapping): """Takes in a string and an email alias mapping and replaces all instances of the aliases in the string with their real email. """ for alias, email_address in mapping.iteritems(): changelog = changelog.replace(alias, email_address) return changelog # Get requirements from the first file that exists def get_reqs_from_files(requirements_files): for requirements_file in requirements_files: if os.path.exists(requirements_file): with open(requirements_file, 'r') as fil: return fil.read().split('\n') return [] def parse_requirements(requirements_files=['requirements.txt', 'tools/pip-requires']): requirements = [] for line in get_reqs_from_files(requirements_files): # For the requirements list, we need to inject only the portion # after egg= so that distutils knows the package it's looking for # such as: # -e git://github.com/openstack/nova/master#egg=nova if re.match(r'\s*-e\s+', line): requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', line)) # such as: # http://github.com/openstack/nova/zipball/master#egg=nova elif re.match(r'\s*https?:', line): requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', line)) # -f lines are for index locations, and don't get used here elif re.match(r'\s*-f\s+', line): pass # argparse is part of the standard library starting with 2.7 # adding it to the requirements list screws distro installs elif line == 'argparse' and sys.version_info >= (2, 7): pass else: requirements.append(line) return requirements def parse_dependency_links(requirements_files=['requirements.txt', 'tools/pip-requires']): dependency_links = [] # dependency_links inject alternate locations to find packages listed # in requirements for line in get_reqs_from_files(requirements_files): # skip comments and blank lines if re.match(r'(\s*#)|(\s*$)', line): continue # lines with -e or -f need the whole line, minus the flag if re.match(r'\s*-[ef]\s+', line): dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) # lines that are only urls can go in unmolested elif re.match(r'\s*https?:', line): dependency_links.append(line) return dependency_links def _run_shell_command(cmd, throw_on_error=False): if os.name == 'nt': output = subprocess.Popen(["cmd.exe", "/C", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: output = subprocess.Popen(["/bin/sh", "-c", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out = output.communicate() if output.returncode and throw_on_error: raise Exception("%s returned %d" % cmd, output.returncode) if len(out) == 0: return None if len(out[0].strip()) == 0: return None return out[0].strip() def _get_git_directory(): parent_dir = os.path.dirname(__file__) while True: git_dir = os.path.join(parent_dir, '.git') if os.path.exists(git_dir): return git_dir parent_dir, child = os.path.split(parent_dir) if not child: # reached to root dir return None def write_git_changelog(): """Write a changelog based on the git changelog.""" new_changelog = 'ChangeLog' git_dir = _get_git_directory() if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): if git_dir: git_log_cmd = 'git --git-dir=%s log' % git_dir changelog = _run_shell_command(git_log_cmd) mailmap = _parse_git_mailmap(git_dir) with open(new_changelog, "w") as changelog_file: changelog_file.write(canonicalize_emails(changelog, mailmap)) else: open(new_changelog, 'w').close() def generate_authors(): """Create AUTHORS file using git commits.""" jenkins_email = 'jenkins@review.(openstack|stackforge).org' old_authors = 'AUTHORS.in' new_authors = 'AUTHORS' git_dir = _get_git_directory() if not os.getenv('SKIP_GENERATE_AUTHORS'): if git_dir: # don't include jenkins email address in AUTHORS file git_log_cmd = ("git --git-dir=" + git_dir + " log --format='%aN <%aE>' | sort -u | " "egrep -v '" + jenkins_email + "'") changelog = _run_shell_command(git_log_cmd) signed_cmd = ("git --git-dir=" + git_dir + " log | grep -i Co-authored-by: | sort -u") signed_entries = _run_shell_command(signed_cmd) if signed_entries: new_entries = "\n".join( [signed.split(":", 1)[1].strip() for signed in signed_entries.split("\n") if signed]) changelog = "\n".join((changelog, new_entries)) mailmap = _parse_git_mailmap(git_dir) with open(new_authors, 'w') as new_authors_fh: new_authors_fh.write(canonicalize_emails(changelog, mailmap)) if os.path.exists(old_authors): with open(old_authors, "r") as old_authors_fh: new_authors_fh.write('\n' + old_authors_fh.read()) else: open(new_authors, 'w').close() _rst_template = """%(heading)s %(underline)s .. automodule:: %(module)s :members: :undoc-members: :show-inheritance: """ def get_cmdclass(): """Return dict of commands to run from setup.py.""" cmdclass = dict() def _find_modules(arg, dirname, files): for filename in files: if filename.endswith('.py') and filename != '__init__.py': arg["%s.%s" % (dirname.replace('/', '.'), filename[:-3])] = True class LocalSDist(sdist.sdist): """Builds the ChangeLog and Authors files from VC first.""" def run(self): write_git_changelog() generate_authors() # sdist.sdist is an old style class, can't use super() sdist.sdist.run(self) cmdclass['sdist'] = LocalSDist # If Sphinx is installed on the box running setup.py, # enable setup.py to build the documentation, otherwise, # just ignore it try: from sphinx.setup_command import BuildDoc class LocalBuildDoc(BuildDoc): builders = ['html', 'man'] def generate_autoindex(self): print "**Autodocumenting from %s" % os.path.abspath(os.curdir) modules = {} option_dict = self.distribution.get_option_dict('build_sphinx') source_dir = os.path.join(option_dict['source_dir'][1], 'api') if not os.path.exists(source_dir): os.makedirs(source_dir) for pkg in self.distribution.packages: if '.' not in pkg: os.path.walk(pkg, _find_modules, modules) module_list = modules.keys() module_list.sort() autoindex_filename = os.path.join(source_dir, 'autoindex.rst') with open(autoindex_filename, 'w') as autoindex: autoindex.write(""".. toctree:: :maxdepth: 1 """) for module in module_list: output_filename = os.path.join(source_dir, "%s.rst" % module) heading = "The :mod:`%s` Module" % module underline = "=" * len(heading) values = dict(module=module, heading=heading, underline=underline) print "Generating %s" % output_filename with open(output_filename, 'w') as output_file: output_file.write(_rst_template % values) autoindex.write(" %s.rst\n" % module) def run(self): if not os.getenv('SPHINX_DEBUG'): self.generate_autoindex() for builder in self.builders: self.builder = builder self.finalize_options() self.project = self.distribution.get_name() self.version = self.distribution.get_version() self.release = self.distribution.get_version() BuildDoc.run(self) class LocalBuildLatex(LocalBuildDoc): builders = ['latex'] cmdclass['build_sphinx'] = LocalBuildDoc cmdclass['build_sphinx_latex'] = LocalBuildLatex except ImportError: pass return cmdclass def _get_revno(git_dir): """Return the number of commits since the most recent tag. We use git-describe to find this out, but if there are no tags then we fall back to counting commits since the beginning of time. """ describe = _run_shell_command( "git --git-dir=%s describe --always" % git_dir) if "-" in describe: return describe.rsplit("-", 2)[-2] # no tags found revlist = _run_shell_command( "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir) return len(revlist.splitlines()) def _get_version_from_git(pre_version): """Return a version which is equal to the tag that's on the current revision if there is one, or tag plus number of additional revisions if the current revision has no tag.""" git_dir = _get_git_directory() if git_dir: if pre_version: try: return _run_shell_command( "git --git-dir=" + git_dir + " describe --exact-match", throw_on_error=True).replace('-', '.') except Exception: sha = _run_shell_command( "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h") return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha) else: return _run_shell_command( "git --git-dir=" + git_dir + " describe --always").replace( '-', '.') return None def _get_version_from_pkg_info(package_name): """Get the version from PKG-INFO file if we can.""" try: pkg_info_file = open('PKG-INFO', 'r') except (IOError, OSError): return None try: pkg_info = email.message_from_file(pkg_info_file) except email.MessageError: return None # Check to make sure we're in our own dir if pkg_info.get('Name', None) != package_name: return None return pkg_info.get('Version', None) def _get_defined_version(): MANILA_CLIENT_VERSION = ['2013', '3', 0] FINAL = False # This becomes true at Release Candidate time def canonical_version_string(): return '.'.join(filter(None, MANILA_CLIENT_VERSION)) def version_string(): if FINAL: return canonical_version_string() else: return '%s-dev' % (canonical_version_string(),) return version_string() def version_string_with_vcs(): return '%s-%s' % (canonical_version_string(), vcs_version_string()) def get_version(package_name, pre_version=None): """Get the version of the project. First, try getting it from PKG-INFO, if it exists. If it does, that means we're in a distribution tarball or that install has happened. Otherwise, if there is no PKG-INFO file, pull the version from git. We do not support setup.py version sanity in git archive tarballs, nor do we support packagers directly sucking our git repo into theirs. We expect that a source tarball be made from our git repo - or that if someone wants to make a source tarball from a fork of our repo with additional tags in it that they understand and desire the results of doing that. """ version = os.environ.get("OSLO_PACKAGE_VERSION", None) if version: return version version = _get_version_from_pkg_info(package_name) if version: return version version = _get_defined_version() if version: return version version = _get_version_from_git(pre_version) if version: return version raise Exception("Versioning for this project requires either an sdist" " tarball, or access to an upstream git repository.") python-manilaclient-0.1~git20140208/manilaclient/openstack/common/__init__.py0000664000175000017500000000000012275236132026053 0ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/openstack/common/version.py0000664000175000017500000000670612275236132026024 0ustar chuckchuck # Copyright 2012 OpenStack Foundation # Copyright 2012-2013 Hewlett-Packard Development Company, L.P. # # 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. """ Utilities for consuming the version from pkg_resources. """ import pkg_resources class VersionInfo(object): def __init__(self, package): """Object that understands versioning for a package :param package: name of the python package, such as glance, or python-glanceclient """ self.package = package self.release = None self.version = None self._cached_version = None def __str__(self): """Make the VersionInfo object behave like a string.""" return self.version_string() def __repr__(self): """Include the name.""" return "VersionInfo(%s:%s)" % (self.package, self.version_string()) def _get_version_from_pkg_resources(self): """Get the version of the package from the pkg_resources record associated with the package.""" try: requirement = pkg_resources.Requirement.parse(self.package) provider = pkg_resources.get_provider(requirement) return provider.version except pkg_resources.DistributionNotFound: # The most likely cause for this is running tests in a tree # produced from a tarball where the package itself has not been # installed into anything. Revert to setup-time logic. from manilaclient.openstack.common import setup return setup.get_version(self.package) def release_string(self): """Return the full version of the package including suffixes indicating VCS status. """ if self.release is None: self.release = self._get_version_from_pkg_resources() return self.release def version_string(self): """Return the short version minus any alpha/beta tags.""" if self.version is None: parts = [] for part in self.release_string().split('.'): if part[0].isdigit(): parts.append(part) else: break self.version = ".".join(parts) return self.version # Compatibility functions canonical_version_string = version_string version_string_with_vcs = release_string def cached_version_string(self, prefix=""): """Generate an object which will expand in a string context to the results of version_string(). We do this so that don't call into pkg_resources every time we start up a program when passing version information into the CONF constructor, but rather only do the calculation when and if a version is requested """ if not self._cached_version: self._cached_version = "%s%s" % (prefix, self.version_string()) return self._cached_version python-manilaclient-0.1~git20140208/manilaclient/openstack/common/strutils.py0000664000175000017500000001015612275236132026222 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2011 OpenStack LLC. # 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. """ System-level utilities and helper functions. """ import logging import sys LOG = logging.getLogger(__name__) def int_from_bool_as_string(subject): """ Interpret a string as a boolean and return either 1 or 0. Any string value in: ('True', 'true', 'On', 'on', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ return bool_from_string(subject) and 1 or 0 def bool_from_string(subject): """ Interpret a string as a boolean. Any string value in: ('True', 'true', 'On', 'on', 'Yes', 'yes', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ if isinstance(subject, bool): return subject if isinstance(subject, basestring): if subject.strip().lower() in ('true', 'on', 'yes', '1'): return True return False def safe_decode(text, incoming=None, errors='strict'): """ Decodes incoming str using `incoming` if they're not already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. :raises TypeError: If text is not an isntance of basestring """ if not isinstance(text, basestring): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, unicode): return text if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) try: return text.decode(incoming, errors) except UnicodeDecodeError: # Note(flaper87) If we get here, it means that # sys.stdin.encoding / sys.getdefaultencoding # didn't return a suitable encoding to decode # text. This happens mostly when global LANG # var is not set correctly and there's no # default encoding. In this case, most likely # python will use ASCII or ANSI encoders as # default encodings but they won't be capable # of decoding non-ASCII characters. # # Also, UTF-8 is being used since it's an ASCII # extension. return text.decode('utf-8', errors) def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): """ Encodes incoming str/unicode using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) :param incoming: Text's current encoding :param encoding: Expected encoding for text (Default UTF-8) :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. :raises TypeError: If text is not an isntance of basestring """ if not isinstance(text, basestring): raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) if isinstance(text, unicode): return text.encode(encoding, errors) elif text and encoding != incoming: # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) return text.encode(encoding, errors) return text python-manilaclient-0.1~git20140208/manilaclient/v1/0000775000175000017500000000000012275236446021033 5ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/v1/shares.py0000664000175000017500000002137212275236132022667 0ustar chuckchuck# Copyright 2012 NetApp # 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. """Interface for shares extension.""" import collections import os import re import urllib from manilaclient import base from manilaclient import exceptions from manilaclient import utils class Share(base.Resource): """A share is an extra block level storage to the OpenStack instances.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this share.""" self.manager.update(self, **kwargs) def delete(self): """Delete this share.""" self.manager.delete(self) def allow(self, access_type, access): """Allow access to a share.""" self._validate_access(access_type, access) return self.manager.allow(self, access_type, access) def deny(self, id): """Deny access from IP to a share.""" return self.manager.deny(self, id) def access_list(self): """Deny access from IP to a share.""" return self.manager.access_list(self) def _validate_access(self, access_type, access): if access_type == 'ip': self._validate_ip_range(access) elif access_type == 'sid': self._validate_username(access) else: raise exceptions.CommandError( 'Only ip and sid type are supported') def update_all_metadata(self, metadata): """Update all metadata of this share.""" return self.manager.update_all_metadata(self, metadata) @staticmethod def _validate_username(access): valid_useraname_re = '[\w\.\-_\`;\'\{\}\[\]]{4,32}$' username = access if not re.match(valid_useraname_re, username): exc_str = ('Invalid user or group name. Must be 4-32 chars long ' 'and consist of alfanum and ]{.-_\'`;}[') raise exceptions.CommandError(exc_str) @staticmethod def _validate_ip_range(ip_range): ip_range = ip_range.split('/') exc_str = ('Supported ip format examples:\n' '\t10.0.0.2, 10.0.0.0/24') if len(ip_range) > 2: raise exceptions.CommandError(exc_str) if len(ip_range) == 2: try: prefix = int(ip_range[1]) if prefix < 0 or prefix > 32: raise ValueError() except ValueError: msg = 'IP prefix should be in range from 0 to 32' raise exceptions.CommandError(msg) ip_range = ip_range[0].split('.') if len(ip_range) != 4: raise exceptions.CommandError(exc_str) for item in ip_range: try: if 0 <= int(item) <= 255: continue raise ValueError() except ValueError: raise exceptions.CommandError(exc_str) def reset_state(self, state): """Update the share with the provided state.""" self.manager.reset_state(self, state) class ShareManager(base.ManagerWithFind): """Manage :class:`Share` resources.""" resource_class = Share def create(self, share_proto, size, snapshot_id=None, name=None, description=None, metadata=None, share_network_id=None): """Create NAS. :param size: Size of NAS in GB :param snapshot_id: ID of the snapshot :param name: Name of the NAS :param description: Short description of a share :param share_proto: Type of NAS (NFS or CIFS) :param metadata: Optional metadata to set on volume creation :rtype: :class:`Share` """ if metadata is None: share_metadata = {} else: share_metadata = metadata body = {'share': {'size': size, 'snapshot_id': snapshot_id, 'name': name, 'description': description, 'metadata': share_metadata, 'share_proto': share_proto, 'share_network_id': share_network_id}} return self._create('/shares', body, 'share') def get(self, share_id): """Get a share. :param share_id: The ID of the share to delete. :rtype: :class:`Share` """ return self._get("/shares/%s" % share_id, "share") def update(self, share, **kwargs): """Updates a share. :param share: Share to update. :rtype: :class:`Share` """ if not kwargs: return body = {'share': kwargs, } return self._update("/shares/%s" % share.id, body) def list(self, detailed=True, search_opts=None): """Get a list of all shares. :rtype: list of :class:`Share` """ if search_opts: query_string = urllib.urlencode([(key, value) for (key, value) in search_opts.items() if value]) if query_string: query_string = "?%s" % (query_string,) else: query_string = '' if detailed: path = "/shares/detail%s" % (query_string,) else: path = "/shares%s" % (query_string,) return self._list(path, 'shares') def delete(self, share): """Delete a share. :param share: The :class:`Share` to delete. """ self._delete("/shares/%s" % base.getid(share)) def allow(self, share, access_type, access): """Allow access from IP to a shares. :param share: The :class:`Share` to delete. :param access_type: string that represents access type ('ip','domain') :param access: string that represents access ('127.0.0.1') """ access = self._action('os-allow_access', share, {'access_type': access_type, 'access_to': access})[1]["access"] return access def deny(self, share, id): """Deny access from IP to a shares. :param share: The :class:`Share` to delete. :param ip: string that represents ip address """ return self._action('os-deny_access', share, {'access_id': id}) def access_list(self, share): """Get access list to the share.""" access_list = self._action("os-access_list", share)[1]["access_list"] if access_list: t = collections.namedtuple('Access', access_list[0].keys()) return [t(*value.values()) for value in access_list] else: return [] def get_metadata(self, share): """ Get a shares metadata. :param share: The :class:`Share`. """ return self._get("/shares/%s/metadata" % base.getid(share), "metadata") def set_metadata(self, share, metadata): """ Update/Set a shares metadata. :param share: The :class:`Share`. :param metadata: A list of keys to be set. """ body = {'metadata': metadata} return self._create("/shares/%s/metadata" % base.getid(share), body, "metadata") def delete_metadata(self, share, keys): """ Delete specified keys from volumes metadata. :param share: The :class:`Share`. :param keys: A list of keys to be removed. """ for k in keys: self._delete("/shares/%s/metadata/%s" % (base.getid(share), k)) def update_all_metadata(self, share, metadata): """Update all metadata of a share. :param share: The :class:`Volume`. :param metadata: A list of keys to be updated. """ body = {'metadata': metadata} return self._update("/shares/%s/metadata" % base.getid(share), body) def _action(self, action, share, info=None, **kwargs): """Perform a share 'action'.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/shares/%s/action' % base.getid(share) return self.api.client.post(url, body=body) def reset_state(self, share, state): """Update the provided share with the provided state.""" return self._action('os-reset_status', share, {'status': state}) python-manilaclient-0.1~git20140208/manilaclient/v1/share_snapshots.py0000664000175000017500000001005112275236132024576 0ustar chuckchuck# Copyright 2012 NetApp # 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. """Interface for shares extension.""" import os import urllib from manilaclient import base from manilaclient import utils class ShareSnapshot(base.Resource): """Represent a snapshot of a share.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this snapshot.""" self.manager.update(self, **kwargs) def reset_state(self, state): """Update the snapshot with the privided state.""" self.manager.reset_state(self, state) def delete(self): """Delete this snapshot.""" self.manager.delete(self) class ShareSnapshotManager(base.ManagerWithFind): """Manage :class:`ShareSnapshot` resources. """ resource_class = ShareSnapshot def create(self, share, force=False, name=None, description=None): """Create a snapshot of the given share. :param share_id: The ID of the share to snapshot. :param force: If force is True, create a snapshot even if the share is busy. Default is False. :param name: Name of the snapshot :param description: Description of the snapshot :rtype: :class:`ShareSnapshot` """ body = {'snapshot': {'share_id': base.getid(share), 'force': force, 'name': name, 'description': description}} return self._create('/snapshots', body, 'snapshot') def get(self, snapshot_id): """Get a snapshot. :param snapshot_id: The ID of the snapshot to get. :rtype: :class:`ShareSnapshot` """ return self._get('/snapshots/%s' % snapshot_id, 'snapshot') def list(self, detailed=True, search_opts=None): """Get a list of all snapshots of shares. :rtype: list of :class:`ShareSnapshot` """ if search_opts: query_string = urllib.urlencode([(key, value) for (key, value) in search_opts.items() if value]) if query_string: query_string = "?%s" % (query_string,) else: query_string = '' if detailed: path = "/snapshots/detail%s" % (query_string,) else: path = "/snapshots%s" % (query_string,) return self._list(path, 'snapshots') def delete(self, snapshot): """Delete a snapshot of a share. :param snapshot: The :class:`ShareSnapshot` to delete. """ self._delete("/snapshots/%s" % base.getid(snapshot)) def update(self, snapshot, **kwargs): """Update a snapshot. :param snapshot: Snapshot to update. :rtype: :class:`ShareSnapshot` """ if not kwargs: return body = {'snapshot': kwargs, } return self._update("/snapshots/%s" % snapshot.id, body) def reset_state(self, snapshot, state): """Update the specified share snapshot with the provided state.""" return self._action('os-reset_status', snapshot, {'status': state}) def _action(self, action, snapshot, info=None, **kwargs): """Perform a snapshot 'action'.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/snapshots/%s/action' % base.getid(snapshot) return self.api.client.post(url, body=body) python-manilaclient-0.1~git20140208/manilaclient/v1/quota_classes.py0000664000175000017500000000316312275236132024246 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 class QuotaClassSet(base.Resource): @property def id(self): """Needed by base.Resource to self-refresh and be indexed""" return self.class_name def update(self, *args, **kwargs): self.manager.update(self.class_name, *args, **kwargs) class QuotaClassSetManager(base.ManagerWithFind): resource_class = QuotaClassSet def get(self, class_name): return self._get("/os-quota-class-sets/%s" % (class_name), "quota_class_set") def update(self, class_name, shares=None, gigabytes=None): body = {'quota_class_set': { 'class_name': class_name, 'shares': shares, 'gigabytes': gigabytes}} for key in body['quota_class_set'].keys(): if body['quota_class_set'][key] is None: body['quota_class_set'].pop(key) self._update('/os-quota-class-sets/%s' % (class_name), body) python-manilaclient-0.1~git20140208/manilaclient/v1/quotas.py0000664000175000017500000000452612275236132022720 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 class QuotaSet(base.Resource): @property def id(self): """Needed by base.Resource to self-refresh and be indexed""" return self.tenant_id def update(self, *args, **kwargs): self.manager.update(self.tenant_id, *args, **kwargs) class QuotaSetManager(base.ManagerWithFind): resource_class = QuotaSet def get(self, tenant_id, user_id=None): if hasattr(tenant_id, 'tenant_id'): tenant_id = tenant_id.tenant_id if user_id: url = "/os-quota-sets/%s?user_id=%s" % (tenant_id, user_id) else: url = "/os-quota-sets/%s" % tenant_id return self._get(url, "quota_set") def update(self, tenant_id, shares=None, snapshots=None, gigabytes=None, force=None, user_id=None): body = {'quota_set': { 'tenant_id': tenant_id, 'shares': shares, 'snapshots': snapshots, 'gigabytes': gigabytes, 'force': force}} for key in body['quota_set'].keys(): if body['quota_set'][key] is None: body['quota_set'].pop(key) if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id return self._update(url, body, 'quota_set') def defaults(self, tenant_id): return self._get('/os-quota-sets/%s/defaults' % tenant_id, 'quota_set') def delete(self, tenant_id, user_id=None): if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id self._delete(url) python-manilaclient-0.1~git20140208/manilaclient/v1/__init__.py0000664000175000017500000000124712275236132023140 0ustar chuckchuck# Copyright (c) 2013 OpenStack, LLC. # # 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.v1.client import Client python-manilaclient-0.1~git20140208/manilaclient/v1/limits.py0000664000175000017500000000424612275236132022704 0ustar chuckchuck# Copyright 2013 OpenStack LLC. from manilaclient import base class Limits(base.Resource): """A collection of RateLimit and AbsoluteLimit objects""" def __repr__(self): return "" @property def absolute(self): for (name, value) in self._info['absolute'].items(): yield AbsoluteLimit(name, value) @property def rate(self): for group in self._info['rate']: uri = group['uri'] regex = group['regex'] for rate in group['limit']: yield RateLimit(rate['verb'], uri, regex, rate['value'], rate['remaining'], rate['unit'], rate['next-available']) class RateLimit(object): """Data model that represents a flattened view of a single rate limit""" def __init__(self, verb, uri, regex, value, remain, unit, next_available): self.verb = verb self.uri = uri self.regex = regex self.value = value self.remain = remain self.unit = unit self.next_available = next_available def __eq__(self, other): return self.uri == other.uri \ and self.regex == other.regex \ and self.value == other.value \ and self.verb == other.verb \ and self.remain == other.remain \ and self.unit == other.unit \ and self.next_available == other.next_available def __repr__(self): return "" % (self.method, self.uri) class AbsoluteLimit(object): """Data model that represents a single absolute limit""" def __init__(self, name, value): self.name = name self.value = value def __eq__(self, other): return self.value == other.value and self.name == other.name def __repr__(self): return "" % (self.name) class LimitsManager(base.Manager): """Manager object used to interact with limits resource""" resource_class = Limits def get(self): """Get a specific extension. :rtype: :class:`Limits` """ return self._get("/limits", "limits") python-manilaclient-0.1~git20140208/manilaclient/v1/security_services.py0000664000175000017500000001160412275236132025151 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 urllib from manilaclient import base from manilaclient import exceptions from manilaclient import utils RESOURCES_PATH = '/security-services' RESOURCE_PATH = "/security-services/%s" RESOURCE_NAME = 'security_service' RESOURCES_NAME = 'security_services' class SecurityService(base.Resource): """Security service for Manila shares """ def __repr__(self): return "" % self.id class SecurityServiceManager(base.Manager): """Manage :class:`SecurityService` resources.""" resource_class = SecurityService def create(self, type, dns_ip=None, server=None, domain=None, sid=None, password=None, name=None, description=None): """Create security service for NAS. :param type: security service type - 'ldap', 'kerberos' or 'active_directory' :param dns_ip: dns ip address used inside tenant's network :param server: security service server ip address or hostname :param domain: security service domain :param sid: security identifier used by tenant :param password: password used by sid :param name: security service name :param description: security service description :rtype: :class:`SecurityService` """ values = {'type': type} if dns_ip: values['dns_ip'] = dns_ip if server: values['server'] = server if domain: values['domain'] = domain if sid: values['sid'] = sid if password: values['password'] = password if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) def get(self, security_service): """Get a security service info. :param security_service: security service to get. :rtype: :class:`SecurityService` """ return self._get(RESOURCE_PATH % security_service, RESOURCE_NAME) def update(self, security_service, dns_ip=None, server=None, domain=None, password=None, sid=None, name=None, description=None): """Updates a security service. :param security_service: security service to update. :param dns_ip: dns ip address used inside tenant's network :param server: security service server ip address or hostname :param domain: security service domain :param sid: security identifier used by tenant :param password: password used by sid :param name: security service name :param description: security service description :rtype: :class:`SecurityService` """ values = {} if dns_ip: values['dns_ip'] = dns_ip if server: values['server'] = server if domain: values['domain'] = domain if sid: values['sid'] = sid if password: values['password'] = password if name: values['name'] = name if description: values['description'] = description if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update(RESOURCE_PATH % security_service, body, RESOURCE_NAME) def delete(self, security_service): """Delete a security service. :param security_service: security service to be deleted. """ self._delete(RESOURCE_PATH % security_service) def list(self, search_opts=None): """Get a list of all security services. :rtype: list of :class:`SecurityService` """ if search_opts: query_string = urllib.urlencode([(key, value) for (key, value) in search_opts.items() if value]) if query_string: query_string = "?%s" % query_string else: query_string = '' path = RESOURCES_PATH + "%s" % query_string return self._list(path, RESOURCES_NAME) python-manilaclient-0.1~git20140208/manilaclient/v1/contrib/0000775000175000017500000000000012275236446022473 5ustar chuckchuckpython-manilaclient-0.1~git20140208/manilaclient/v1/contrib/__init__.py0000664000175000017500000000117412275236132024577 0ustar chuckchuck# Copyright (c) 2013 OpenStack, LLC. # # 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. python-manilaclient-0.1~git20140208/manilaclient/v1/contrib/list_extensions.py0000664000175000017500000000263412275236132026274 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 ListExtResource(base.Resource): @property def summary(self): descr = self.description.strip() if not descr: return '??' lines = descr.split("\n") if len(lines) == 1: return lines[0] else: return lines[0] + "..." class ListExtManager(base.Manager): resource_class = ListExtResource def show_all(self): return self._list("/extensions", 'extensions') @utils.service_type('share') def do_list_extensions(client, _args): """ List all the os-api extensions that are available. """ extensions = client.list_extensions.show_all() fields = ["Name", "Summary", "Alias", "Updated"] utils.print_list(extensions, fields) python-manilaclient-0.1~git20140208/manilaclient/v1/client.py0000664000175000017500000000551712275236132022663 0ustar chuckchuckfrom manilaclient import client from manilaclient.v1 import limits from manilaclient.v1 import share_networks from manilaclient.v1 import security_services from manilaclient.v1 import quota_classes from manilaclient.v1 import quotas from manilaclient.v1 import shares from manilaclient.v1 import share_snapshots class Client(object): """Top-level object to access the OpenStack Volume API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Then call methods on its managers:: >>> client.share.list() ... """ def __init__(self, username, api_key, project_id=None, auth_url='', insecure=False, timeout=None, tenant_id=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type='share', service_name=None, share_service_name=None, retries=None, http_log_debug=False, cacert=None): # FIXME(comstud): Rename the api_key argument above when we # know it's not being used as keyword argument password = api_key self.limits = limits.LimitsManager(self) self.security_services = security_services.SecurityServiceManager(self) self.share_networks = share_networks.ShareNetworkManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quotas = quotas.QuotaSetManager(self) self.shares = shares.ShareManager(self) self.share_snapshots = share_snapshots.ShareSnapshotManager(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) self.client = client.HTTPClient( username, password, project_id, auth_url, insecure=insecure, timeout=timeout, tenant_id=tenant_id, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, endpoint_type=endpoint_type, service_type=service_type, service_name=service_name, share_service_name=share_service_name, retries=retries, http_log_debug=http_log_debug, cacert=cacert) def authenticate(self): """Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ self.client.authenticate() python-manilaclient-0.1~git20140208/manilaclient/v1/shell.py0000664000175000017500000007076112275236132022517 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 argparse import os import sys import time from manilaclient import exceptions from manilaclient.v1 import quotas from manilaclient import utils def _poll_for_status(poll_fn, obj_id, action, final_ok_states, poll_period=5, show_progress=True): """Block while action is performed, periodically printing progress.""" def print_progress(progress): if show_progress: msg = ('\rInstance %(action)s... %(progress)s%% complete' % dict(action=action, progress=progress)) else: msg = '\rInstance %(action)s...' % dict(action=action) sys.stdout.write(msg) sys.stdout.flush() print while True: obj = poll_fn(obj_id) status = obj.status.lower() progress = getattr(obj, 'progress', None) or 0 if status in final_ok_states: print_progress(100) print "\nFinished" break elif status == "error": print "\nError %(action)s instance" % locals() break else: print_progress(progress) time.sleep(poll_period) def _find_share(cs, share): """Get a share by ID.""" return utils.find_resource(cs.shares, share) def _print_share(cs, share): info = share._info.copy() utils.print_dict(info) def _find_share_snapshot(cs, snapshot): """Get a snapshot by ID.""" return utils.find_resource(cs.share_snapshots, snapshot) def _print_share_snapshot(cs, snapshot): info = snapshot._info.copy() info.pop('links') utils.print_dict(info) def _translate_keys(collection, convert): for item in collection: keys = item.__dict__.keys() for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item._info[from_key]) def _extract_metadata(args): metadata = {} for metadatum in args.metadata: # unset doesn't require a val, so we have the if/else if '=' in metadatum: (key, value) = metadatum.split('=', 1) else: key = metadatum value = None metadata[key] = value return metadata def do_endpoints(cs, args): """Discover endpoints that get returned from the authenticate services""" catalog = cs.client.service_catalog.catalog for e in catalog['access']['serviceCatalog']: utils.print_dict(e['endpoints'][0], e['name']) def do_credentials(cs, args): """Show user credentials returned from auth""" catalog = cs.client.service_catalog.catalog utils.print_dict(catalog['access']['user'], "User Credentials") utils.print_dict(catalog['access']['token'], "Token") _quota_resources = ['shares', 'snapshots', 'gigabytes'] def _quota_show(quotas): quota_dict = {} for resource in _quota_resources: quota_dict[resource] = getattr(quotas, resource, None) utils.print_dict(quota_dict) def _quota_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: # default value of force is None to make sure this client # will be compatibile with old nova server force_update = getattr(args, 'force', None) user_id = getattr(args, 'user', None) if isinstance(manager, quotas.QuotaSetManager): manager.update(identifier, force=force_update, user_id=user_id, **updates) else: manager.update(identifier, **updates) @utils.arg('--tenant', metavar='', default=None, help='ID of tenant to list the quotas for.') @utils.arg('--user', metavar='', default=None, help='ID of user to list the quotas for.') def do_quota_show(cs, args): """List the quotas for a tenant/user.""" if not args.tenant: _quota_show(cs.quotas.get(cs.client.tenant_id, user_id=args.user)) else: _quota_show(cs.quotas.get(args.tenant, user_id=args.user)) @utils.arg('--tenant', metavar='', default=None, help='ID of tenant to list the default quotas for.') def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" if not args.tenant: _quota_show(cs.quotas.defaults(cs.client.tenant_id)) else: _quota_show(cs.quotas.defaults(args.tenant)) @utils.arg('tenant', metavar='', help='UUID of tenant to set the quotas for.') @utils.arg('--user', metavar='', default=None, help='ID of user to set the quotas for.') @utils.arg('--shares', metavar='', type=int, default=None, help='New value for the "shares" quota.') @utils.arg('--snapshots', metavar='', type=int, default=None, help='New value for the "snapshots" quota.') @utils.arg('--gigabytes', metavar='', type=int, default=None, help='New value for the "gigabytes" quota.') @utils.arg('--force', dest='force', action="store_true", default=None, help='Whether force update the quota even if the already used' ' and reserved exceeds the new quota') @utils.service_type('share') def do_quota_update(cs, args): """Update the quotas for a tenant/user.""" _quota_update(cs.quotas, args.tenant, args) @utils.arg('--tenant', metavar='', help='ID of tenant to delete quota for.') @utils.arg('--user', metavar='', help='ID of user to delete quota for.') def do_quota_delete(cs, args): """Delete quota for a tenant/user so their quota will Revert back to default. """ if not args.tenant: cs.quotas.delete(cs.client.tenant_id, user_id=args.user) else: cs.quotas.delete(args.tenant, user_id=args.user) @utils.arg('class_name', metavar='', help='Name of quota class to list the quotas for.') @utils.service_type('share') def do_quota_class_show(cs, args): """List the quotas for a quota class.""" _quota_show(cs.quota_classes.get(args.class_name)) @utils.arg('class-name', metavar='', help='Name of quota class to set the quotas for.') @utils.arg('--shares', metavar='', type=int, default=None, help='New value for the "shares" quota.') @utils.arg('--snapshots', metavar='', type=int, default=None, help='New value for the "snapshots" quota.') @utils.arg('--gigabytes', metavar='', type=int, default=None, help='New value for the "gigabytes" quota.') @utils.service_type('share') def do_quota_class_update(cs, args): """Update the quotas for a quota class.""" _quota_update(cs.quota_classes, args.class_name, args) @utils.service_type('share') def do_absolute_limits(cs, args): """Print a list of absolute limits for a user""" limits = cs.limits.get().absolute columns = ['Name', 'Value'] utils.print_list(limits, columns) @utils.service_type('share') def do_rate_limits(cs, args): """Print a list of rate limits for a user""" limits = cs.limits.get().rate columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available'] utils.print_list(limits, columns) @utils.arg( 'share_protocol', metavar='', type=str, help='Share type (NFS or CIFS)') @utils.arg( 'size', metavar='', type=int, help='Share size in GB') @utils.arg( '--snapshot-id', metavar='', help='Optional snapshot id to create the share from. (Default=None)', default=None) @utils.arg( '--name', metavar='', help='Optional share name. (Default=None)', default=None) @utils.arg('--metadata', type=str, nargs='*', metavar='', help='Metadata key=value pairs (Optional, Default=None)', default=None) @utils.arg( '--share-network-id', metavar='', help='Optional network info id', default=None) @utils.arg( '--description', metavar='', help='Optional share description. (Default=None)', default=None) @utils.service_type('share') def do_create(cs, args): """Creates new NAS storage (NFS or CIFS).""" share_metadata = None if args.metadata is not None: share_metadata = _extract_metadata(args) share = cs.shares.create(args.share_protocol, args.size, args.snapshot_id, args.name, args.description, metadata=share_metadata, share_network_id=args.share_network_id) _print_share(cs, share) @utils.arg('share', metavar='', help='Name or ID of the share to update metadata on.') @utils.arg('action', metavar='', choices=['set', 'unset'], help="Actions: 'set' or 'unset'") @utils.arg('metadata', metavar='', nargs='+', default=[], help='Metadata to set/unset (only key is necessary on unset)') @utils.service_type('share') def do_metadata(cs, args): """Set or Delete metadata on a share.""" share = utils.find_share(cs, args.share) metadata = _extract_metadata(args) if args.action == 'set': cs.shares.set_metadata(share, metadata) elif args.action == 'unset': cs.shares.delete_metadata(share, sorted(metadata.keys(), reverse=True)) @utils.arg('share', metavar='', help='Name or ID of share') @utils.service_type('share') def do_metadata_show(cs, args): """Show metadata of given share.""" share = utils.find_share(cs, args.share) metadata = cs.shares.get_metadata(share)._info utils.print_dict(metadata, 'Metadata-property') @utils.arg('share', metavar='', help='Name or ID of the share to update metadata on.') @utils.arg('metadata', metavar='', nargs='+', default=[], help='Metadata entry/entries to update.') @utils.service_type('share') def do_metadata_update_all(cs, args): """Update all metadata of a share.""" share = utils.find_share(cs, args.share) metadata = _extract_metadata(args) metadata = share.update_all_metadata(metadata)._info['metadata'] utils.print_dict(metadata, 'Metadata-property') @utils.arg('share', metavar='', nargs='+', help='Name or ID of share(s).') def do_delete(cs, args): """Immediately shut down and delete specified server(s).""" failure_count = 0 for share in args.share: try: share_ref = _find_share(cs, share) share_ref.delete() except Exception as e: failure_count += 1 if 'Access was denied' in e.message: print('Error occurred while deleting share %s' % share_ref.id) else: print(e.message) if failure_count == len(args.share): raise exceptions.CommandError("Unable to delete any of the specified " "shares.") @utils.arg( 'share', metavar='', help='Name or ID of the NAS share.') @utils.service_type('share') def do_show(cs, args): """Show details about a NAS share.""" share = _find_share(cs, args.share) _print_share(cs, share) @utils.arg( 'share', metavar='', help='Name or ID of the NAS share to modify.') @utils.arg( 'access_type', metavar='', help='access rule type (only "ip" and "sid"(user or group) ' 'are supported).') @utils.arg( 'access_to', metavar='', help='Value that defines access') @utils.service_type('share') def do_access_allow(cs, args): """Allow access to the share.""" share = _find_share(cs, args.share) access = share.allow(args.access_type, args.access_to) utils.print_dict(access) @utils.arg( 'share', metavar='', help='Name or ID of the NAS share to modify.') @utils.arg( 'id', metavar='', help='id of the access rule to be deleted.') @utils.service_type('share') def do_access_deny(cs, args): """Deny access to a share.""" share = _find_share(cs, args.share) share.deny(args.id) @utils.arg( 'share', metavar='', help='Name or ID of the share.') @utils.service_type('share') def do_access_list(cs, args): """Show access list for share.""" share = _find_share(cs, args.share) access_list = share.access_list() utils.print_list(access_list, ['id', 'access type', 'access to', 'state']) @utils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @utils.arg( '--name', metavar='', default=None, help='Filter results by name') @utils.arg( '--status', metavar='', default=None, help='Filter results by status') @utils.service_type('share') def do_list(cs, args): """List all NAS shares.""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, 'name': args.name, 'status': args.status, } shares = cs.shares.list(search_opts=search_opts) utils.print_list(shares, ['ID', 'Name', 'Size', 'Share Proto', 'Status', 'Export location']) @utils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @utils.arg( '--name', metavar='', default=None, help='Filter results by name') @utils.arg( '--status', metavar='', default=None, help='Filter results by status') @utils.arg( '--share-id', metavar='', default=None, help='Filter results by share-id') @utils.service_type('share') def do_snapshot_list(cs, args): """List all the snapshots.""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, 'name': args.name, 'status': args.status, 'share_id': args.share_id, } snapshots = cs.share_snapshots.list(search_opts=search_opts) utils.print_list(snapshots, ['ID', 'Share ID', 'Status', 'Name', 'Share Size']) @utils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot.') @utils.service_type('share') def do_snapshot_show(cs, args): """Show details about a snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) _print_share_snapshot(cs, snapshot) @utils.arg( 'share', metavar='', help='Name or ID of the share to snapshot') @utils.arg( '--force', metavar='', help='Optional flag to indicate whether ' 'to snapshot a share even if it\'s busy.' ' (Default=False)', default=False) @utils.arg( '--name', metavar='', default=None, help='Optional snapshot name. (Default=None)') @utils.arg( '--description', metavar='', default=None, help='Optional snapshot description. (Default=None)') @utils.service_type('share') def do_snapshot_create(cs, args): """Add a new snapshot.""" share = utils.find_share(cs, args.share) snapshot = cs.share_snapshots.create(share, args.force, args.name, args.description) _print_share_snapshot(cs, snapshot) @utils.arg('share', metavar='', help='Name or ID of the share to rename.') @utils.arg('name', nargs='?', metavar='', help='New name for the share.') @utils.arg('--description', metavar='', help='Optional share description. (Default=None)', default=None) @utils.service_type('share') def do_rename(cs, args): """Rename a share.""" kwargs = {} if args.name is not None: kwargs['display_name'] = args.name if args.description is not None: kwargs['display_description'] = args.description if not kwargs: msg = "Must supply either name or description." raise exceptions.CommandError(msg) _find_share(cs, args.share).update(**kwargs) @utils.arg('snapshot', metavar='', help='Name or ID of the snapshot to rename.') @utils.arg('name', nargs='?', metavar='', help='New name for the snapshot.') @utils.arg('--description', metavar='', help='Optional snapshot description. (Default=None)', default=None) @utils.service_type('share') def do_snapshot_rename(cs, args): """Rename a snapshot.""" kwargs = {} if args.name is not None: kwargs['display_name'] = args.name if args.description is not None: kwargs['display_description'] = args.description if not kwargs: msg = "Must supply either name or description." raise exceptions.CommandError(msg) _find_share_snapshot(cs, args.snapshot).update(**kwargs) @utils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot to delete.') @utils.service_type('share') def do_snapshot_delete(cs, args): """Remove a snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) snapshot.delete() @utils.arg('snapshot', metavar='', help='Name or ID of the snapshot to modify.') @utils.arg('--state', metavar='', default='available', help=('Indicate which state to assign the snapshot. ' 'Options include available, error, creating, deleting, ' 'error_deleting. If no state is provided, ' 'available will be used.')) @utils.service_type('share') def do_snapshot_reset_state(cs, args): """Explicitly update the state of a snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) snapshot.reset_state(args.state) @utils.arg( 'share', metavar='', help='Name or ID of the share to modify.') @utils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the share. Options include ' 'available, error, creating, deleting, error_deleting. If no ' 'state is provided, available will be used.')) @utils.service_type('share') def do_reset_state(cs, args): """Explicitly update the state of a share.""" share = _find_share(cs, args.share) share.reset_state(args.state) @utils.arg( '--neutron-net-id', metavar='neutron-net-id', default=None, help="Neutron network id. If using manila network neutron plug-in, this " "value should be specified. Tenant can't have more than one share's" " network with the same neutron_net_id and neutron_subnet_id ") @utils.arg( '--neutron-subnet-id', metavar='neutron-subnet-id', default=None, help="Neutron subnet id. If using manila network neutron plug-in, this " "value should be specified. Tenant can't have more than one share's" " network with the same neutron_net_id and neutron_subnet_id ") @utils.arg( '--name', metavar='', default=None, help="Share network name") @utils.arg( '--description', metavar='', default=None, help="Share network description") def do_share_network_create(cs, args): """Create description for network used by the tenant""" values = {'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'name': args.name, 'description': args.description} share_network = cs.share_networks.create(**values) info = share_network._info.copy() utils.print_dict(info) @utils.arg( 'share_network', metavar='', help='Share network to update.') @utils.arg( '--neutron-net-id', metavar='neutron-net-id', default=None, help="Neutron network id. Tenant can't have more than one share's" " network with the same neutron_net_id and neutron_subnet_id ") @utils.arg( '--neutron-subnet-id', metavar='neutron-subnet-id', default=None, help="Neutron subnet id. Tenant can't have more than one share's" " network with the same neutron_net_id and neutron_subnet_id ") @utils.arg( '--name', metavar='', default=None, help="Share network name") @utils.arg( '--description', metavar='', default=None, help="Share network description") def do_share_network_update(cs, args): """Update share network data""" values = {'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'name': args.name, 'description': args.description} share_network = cs.share_networks.update(args.share_network, **values) info = share_network._info.copy() utils.print_dict(info) @utils.arg( 'share_network', metavar='', help='Share network to show.') def do_share_network_show(cs, args): """Get a description for network used by the tenant""" share_network = cs.share_networks.get(args.share_network) info = share_network._info.copy() utils.print_dict(info) @utils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @utils.arg( '--status', metavar='', default=None, help='Filter results by status') def do_share_network_list(cs, args): """Get a list of network info""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, 'status': args.status, } share_networks = cs.share_networks.list(search_opts=search_opts) fields = ['id', 'name', 'status'] utils.print_list(share_networks, fields=fields) @utils.arg( 'share_network', metavar='', help='Share network') @utils.arg( 'security_service', metavar='', help='Security service to associate with.') def do_share_network_security_service_add(cs, args): """Associate security service with share network""" cs.share_networks.add_security_service(args.share_network, args.security_service) @utils.arg( 'share_network', metavar='', help='Share network.') @utils.arg( 'security_service', metavar='', help='Security service to dissociate.') def do_share_network_security_service_remove(cs, args): """Dissociate security service from share network""" cs.share_networks.remove_security_service(args.share_network, args.security_service) @utils.arg( 'share_network', metavar='', help='Share network.') def do_share_network_security_service_list(cs, args): """Get a list of security services associated with a given share network""" search_opts = { 'share_network_id': args.share_network, } security_services = cs.security_services.list(search_opts=search_opts) fields = ['id', 'name', 'status'] utils.print_list(security_services, fields=fields) @utils.arg( 'share_network', metavar='', help='Share network to be deleted.') def do_share_network_delete(cs, args): """Delete share network""" cs.share_networks.delete(args.share_network) @utils.arg( 'type', metavar='', help="Security service type: 'ldap', 'kerberos' or 'active_directory'") @utils.arg( '--dns-ip', metavar='', default=None, help="dns ip address used inside tenant's network") @utils.arg( '--server', metavar='', default=None, help="security service ip address or hostname") @utils.arg( '--domain', metavar='', default=None, help="security service domain") @utils.arg( '--sid', metavar='', default=None, help="security service user or group used by tenant") @utils.arg( '--password', metavar='', default=None, help="password used by sid") @utils.arg( '--name', metavar='', default=None, help="security service name") @utils.arg( '--description', metavar='', default=None, help="security service description") def do_security_service_create(cs, args): """Create security service used by tenant""" values = {'dns_ip': args.dns_ip, 'server': args.server, 'domain': args.domain, 'sid': args.sid, 'password': args.password, 'name': args.name, 'description': args.description} security_service = cs.security_services.create(args.type, **values) info = security_service._info.copy() utils.print_dict(info) @utils.arg( 'security_service', metavar='', help='Security service to update.') @utils.arg( '--dns-ip', metavar='', default=None, help="dns ip address used inside tenant's network") @utils.arg( '--server', metavar='', default=None, help="security service ip address or hostname") @utils.arg( '--domain', metavar='', default=None, help="security service domain") @utils.arg( '--sid', metavar='', default=None, help="security service user or group used by tenant") @utils.arg( '--password', metavar='', default=None, help="password used by sid") @utils.arg( '--name', metavar='', default=None, help="security service name") @utils.arg( '--description', metavar='', default=None, help="security service description") def do_security_service_update(cs, args): """Update security service""" values = {'dns_ip': args.dns_ip, 'server': args.server, 'domain': args.domain, 'sid': args.sid, 'password': args.password, 'name': args.name, 'description': args.description} security_service = cs.security_services.update(args.security_service, **values) info = security_service._info.copy() utils.print_dict(info) @utils.arg( 'security_service', metavar='', help='Security service to show.') def do_security_service_show(cs, args): """Show security service""" security_service = cs.security_services.get(args.security_service) info = security_service._info.copy() utils.print_dict(info) @utils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @utils.arg( '--status', metavar='', default=None, help='Filter results by status') def do_security_service_list(cs, args): """Get a list of security services""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, 'status': args.status, } security_services = cs.security_services.list(search_opts=search_opts) fields = ['id', 'name', 'status'] utils.print_list(security_services, fields=fields) @utils.arg( 'security_service', metavar='', help='Security service to delete.') def do_security_service_delete(cs, args): """Delete security service""" cs.security_services.delete(args.security_service) python-manilaclient-0.1~git20140208/manilaclient/v1/share_networks.py0000664000175000017500000001167712275236132024447 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 urllib from manilaclient import base from manilaclient import exceptions RESOURCES_PATH = '/share-networks' RESOURCE_PATH = "/share-networks/%s" RESOURCE_NAME = 'share_network' RESOURCES_NAME = 'share_networks' class ShareNetwork(base.Resource): """Network info for Manila shares """ def __repr__(self): return "" % self.id class ShareNetworkManager(base.Manager): """Manage :class:`ShareNetwork` resources.""" resource_class = ShareNetwork def create(self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None): """Create share network. :param metadata: metadata specific to the manila network plugin in use :param name: share network name :param description: share network description :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) def add_security_service(self, share_network, security_service): """Associate given security service with a share network :param share_network: share network name or id :param security_service: security service name or id :rtype: :class:`ShareNetwork` """ body = {'add_security_service': {'security_service_id': security_service}} return self._create(RESOURCE_PATH % share_network + '/action', body, RESOURCE_NAME) def remove_security_service(self, share_network, security_service): """Dissociate security service from a share network :param share_network: share network name or id :param security_service: security service name or id :rtype: :class:`ShareNetwork` """ body = {'remove_security_service': {'security_service_id': security_service}} return self._create(RESOURCE_PATH % share_network + '/action', body, RESOURCE_NAME) def get(self, share_network): """Get a share network. :param policy: share network to get. :rtype: :class:`NetworkInfo` """ return self._get(RESOURCE_PATH % base.getid(share_network), RESOURCE_NAME) def update(self, share_network, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None): """Updates a share network. :param share_network: share network to update. :rtype: :class:`NetworkInfo` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update(RESOURCE_PATH % share_network, body, RESOURCE_NAME) def delete(self, share_network): """Delete a share network. :param share_network: share network to be deleted. """ self._delete(RESOURCE_PATH % share_network) def list(self, search_opts=None): """Get a list of all share network. :rtype: list of :class:`NetworkInfo` """ if search_opts: query_string = urllib.urlencode([(key, value) for (key, value) in search_opts.items() if value]) if query_string: query_string = "?%s" % query_string else: query_string = '' path = RESOURCES_PATH + "%s" % query_string return self._list(path, RESOURCES_NAME) python-manilaclient-0.1~git20140208/manilaclient/__init__.py0000664000175000017500000000171212275236132022607 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 OpenStack LLC # # 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.openstack.common import version version_info = 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 python-manilaclient-0.1~git20140208/manilaclient/base.py0000664000175000017500000002300212275236132021756 0ustar chuckchuck# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack LLC. # 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 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) def getid(obj): """ Abstracts the common pattern of allowing both an object or an object's ID as a parameter when dealing with relationships. """ try: return obj.id except AttributeError: return obj class Manager(utils.HookableMixin): """ Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, api): self.api = api 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): """ 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 = utils.env('manilaclient_UUID_CACHE_DIR', default="~/.manilaclient") # NOTE(sirp): Keep separate UUID caches for each username + endpoint # pair username = utils.env('OS_USERNAME', 'MANILA_USERNAME') url = utils.env('OS_URL', 'MANILA_URL') uniqifier = hashlib.md5(username + url).hexdigest() cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier)) try: os.makedirs(cache_dir, 0755) 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 _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) 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 = 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 class Resource(object): """ A resource represents a particular instance of an object (server, flavor, etc). This is pretty much just a bag for attributes. :param manager: Manager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ HUMAN_ID = False def __init__(self, manager, info, loaded=False): self.manager = manager self._info = info self._add_details(info) self._loaded = loaded # NOTE(sirp): ensure `id` is already present because if it isn't we'll # enter an infinite loop of __getattr__ -> get -> __init__ -> # __getattr__ -> ... if 'id' in self.__dict__ and len(str(self.id)) == 36: self.manager.write_to_completion_cache('uuid', self.id) human_id = self.human_id if human_id: self.manager.write_to_completion_cache('human_id', human_id) @property def human_id(self): """Subclasses may override this provide a pretty ID which can be used for bash completion. """ if 'name' in self.__dict__ and self.HUMAN_ID: return utils.slugify(self.name) return None def _add_details(self, info): for (k, v) in info.iteritems(): try: setattr(self, 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 __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) def get(self): # 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, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val python-manilaclient-0.1~git20140208/manilaclient/service_catalog.py0000664000175000017500000000576712275236132024220 0ustar chuckchuck# Copyright 2011 OpenStack LLC. # 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. import manilaclient.exceptions class ServiceCatalog(object): """Helper methods for dealing with a Keystone Service Catalog.""" def __init__(self, resource_dict): self.catalog = resource_dict def get_token(self): return self.catalog['access']['token']['id'] def get_tenant_id(self): return self.catalog['access']['token']['tenant']['id'] def url_for(self, attr=None, filter_value=None, service_type=None, endpoint_type='publicURL', service_name=None, share_service_name=None): """Fetch the public URL from the Compute service for a particular endpoint attribute. If none given, return the first. See tests for sample service catalog.""" matching_endpoints = [] if 'endpoints' in self.catalog: # We have a bastardized service catalog. Treat it special. :/ for endpoint in self.catalog['endpoints']: if not filter_value or endpoint[attr] == filter_value: matching_endpoints.append(endpoint) if not matching_endpoints: raise manilaclient.exceptions.EndpointNotFound() # We don't always get a service catalog back ... if not 'serviceCatalog' in self.catalog['access']: return None # Full catalog ... catalog = self.catalog['access']['serviceCatalog'] for service in catalog: if service.get("type") != service_type: continue if (service_name and service_type == 'compute' and service.get('name') != service_name): continue if (share_service_name and service_type == 'share' and service.get('name') != share_service_name): continue endpoints = service['endpoints'] for endpoint in endpoints: if not filter_value or endpoint.get(attr) == filter_value: endpoint["serviceName"] = service.get("name") matching_endpoints.append(endpoint) if not matching_endpoints: raise manilaclient.exceptions.EndpointNotFound() elif len(matching_endpoints) > 1: raise manilaclient.exceptions.AmbiguousEndpoints( endpoints=matching_endpoints) else: return matching_endpoints[0][endpoint_type] python-manilaclient-0.1~git20140208/manilaclient/client.py0000664000175000017500000003317112275236132022332 0ustar chuckchuck# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack LLC. # Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. """ OpenStack Client interface. Handles the REST calls and responses. """ import logging import os import urlparse try: from eventlet import sleep except ImportError: from time import sleep try: import json except ImportError: import simplejson as json # Python 2.5 compat fix if not hasattr(urlparse, 'parse_qsl'): import cgi urlparse.parse_qsl = cgi.parse_qsl import requests from manilaclient import exceptions from manilaclient import service_catalog from manilaclient import utils class HTTPClient(object): USER_AGENT = 'python-manilaclient' def __init__(self, user, password, projectid, auth_url, insecure=False, timeout=None, tenant_id=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', service_type=None, service_name=None, share_service_name=None, retries=None, http_log_debug=False, cacert=None): self.user = user self.password = password self.projectid = projectid self.tenant_id = tenant_id self.auth_url = auth_url.rstrip('/') self.version = 'v1' self.region_name = region_name self.endpoint_type = endpoint_type self.service_type = service_type self.service_name = service_name self.share_service_name = share_service_name self.retries = int(retries or 0) self.http_log_debug = http_log_debug self.management_url = None self.auth_token = None self.proxy_token = proxy_token self.proxy_tenant_id = proxy_tenant_id if insecure: self.verify_cert = False else: if cacert: self.verify_cert = cacert else: self.verify_cert = True self._logger = logging.getLogger(__name__) if self.http_log_debug: ch = logging.StreamHandler() self._logger.setLevel(logging.DEBUG) self._logger.addHandler(ch) if hasattr(requests, 'logging'): requests.logging.getLogger(requests.__name__).addHandler(ch) def http_log_req(self, args, kwargs): if not self.http_log_debug: return string_parts = ['curl -i'] for element in args: if element in ('GET', 'POST', 'DELETE', 'PUT'): string_parts.append(' -X %s' % element) else: string_parts.append(' %s' % element) for element in kwargs['headers']: header = ' -H "%s: %s"' % (element, kwargs['headers'][element]) string_parts.append(header) if 'data' in kwargs: string_parts.append(" -d '%s'" % (kwargs['data'])) self._logger.debug("\nREQ: %s\n" % "".join(string_parts)) def http_log_resp(self, resp): if not self.http_log_debug: return self._logger.debug( "RESP: [%s] %s\nRESP BODY: %s\n", resp.status_code, resp.headers, resp.text) def request(self, url, method, **kwargs): kwargs.setdefault('headers', kwargs.get('headers', {})) kwargs['headers']['User-Agent'] = self.USER_AGENT kwargs['headers']['Accept'] = 'application/json' if 'body' in kwargs: kwargs['headers']['Content-Type'] = 'application/json' kwargs['data'] = json.dumps(kwargs['body']) del kwargs['body'] self.http_log_req((url, method,), kwargs) resp = requests.request( method, url, verify=self.verify_cert, **kwargs) self.http_log_resp(resp) if resp.text: try: body = json.loads(resp.text) except ValueError: pass body = None else: body = None if resp.status_code >= 400: raise exceptions.from_response(resp, body) return resp, body def _cs_request(self, url, method, **kwargs): auth_attempts = 0 attempts = 0 backoff = 1 while True: attempts += 1 if not self.management_url or not self.auth_token: self.authenticate() kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token if self.projectid: kwargs['headers']['X-Auth-Project-Id'] = self.projectid try: resp, body = self.request(self.management_url + url, method, **kwargs) return resp, body except exceptions.BadRequest as e: if attempts > self.retries: raise except exceptions.Unauthorized: if auth_attempts > 0: raise self._logger.debug("Unauthorized, reauthenticating.") self.management_url = self.auth_token = None # First reauth. Discount this attempt. attempts -= 1 auth_attempts += 1 continue except exceptions.ClientException as e: if attempts > self.retries: raise if 500 <= e.code <= 599: pass else: raise except requests.exceptions.ConnectionError as e: # Catch a connection refused from requests.request self._logger.debug("Connection refused: %s" % e) raise self._logger.debug( "Failed attempt(%s of %s), retrying in %s seconds" % (attempts, self.retries, backoff)) sleep(backoff) backoff *= 2 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 _extract_service_catalog(self, url, resp, body, extract_token=True): """See what the auth service told us and process the response. We may get redirected to another site, fail or actually get back a service catalog with a token and our endpoints.""" if resp.status_code == 200: # content must always present try: self.auth_url = url self.service_catalog = \ service_catalog.ServiceCatalog(body) if extract_token: self.auth_token = self.service_catalog.get_token() self.tenant_id = self.service_catalog.get_tenant_id() management_url = self.service_catalog.url_for( attr='region', filter_value=self.region_name, endpoint_type=self.endpoint_type, service_type=self.service_type, service_name=self.service_name, share_service_name=self.share_service_name) self.management_url = management_url.rstrip('/') return None except exceptions.AmbiguousEndpoints: print "Found more than one valid endpoint. Use a more " \ "restrictive filter" raise except KeyError: raise exceptions.AuthorizationFailure() except exceptions.EndpointNotFound: print "Could not find any suitable endpoint. Correct region?" raise elif resp.status_code == 305: return resp['location'] else: raise exceptions.from_response(resp, body) def _fetch_endpoints_from_auth(self, url): """We have a token, but don't know the final endpoint for the region. We have to go back to the auth service and ask again. This request requires an admin-level token to work. The proxy token supplied could be from a low-level enduser. We can't get this from the keystone service endpoint, we have to use the admin endpoint. This will overwrite our admin token with the user token. """ # GET ...:5001/v2.0/tokens/#####/endpoints url = '/'.join([url, 'tokens', '%s?belongsTo=%s' % (self.proxy_token, self.proxy_tenant_id)]) self._logger.debug("Using Endpoint URL: %s" % url) resp, body = self.request(url, "GET", headers={'X-Auth-Token': self.auth_token}) return self._extract_service_catalog(url, resp, body, extract_token=False) def authenticate(self): magic_tuple = urlparse.urlsplit(self.auth_url) scheme, netloc, path, query, frag = magic_tuple port = magic_tuple.port if port is None: port = 80 path_parts = path.split('/') for part in path_parts: if len(part) > 0 and part[0] == 'v': self.version = part break # TODO(sandy): Assume admin endpoint is 35357 for now. # Ideally this is going to have to be provided by the service catalog. new_netloc = netloc.replace(':%d' % port, ':%d' % (35357,)) admin_url = urlparse.urlunsplit((scheme, new_netloc, path, query, frag)) auth_url = self.auth_url if self.version == "v2.0": while auth_url: if "MANILA_RAX_AUTH" in os.environ: auth_url = self._rax_auth(auth_url) else: auth_url = self._v2_auth(auth_url) # Are we acting on behalf of another user via an # existing token? If so, our actual endpoints may # be different than that of the admin token. if self.proxy_token: self._fetch_endpoints_from_auth(admin_url) # Since keystone no longer returns the user token # with the endpoints any more, we need to replace # our service account token with the user token. self.auth_token = self.proxy_token else: try: while auth_url: auth_url = self._v1_auth(auth_url) # In some configurations manila makes redirection to # v2.0 keystone endpoint. Also, new location does not contain # real endpoint, only hostname and port. except exceptions.AuthorizationFailure: if auth_url.find('v2.0') < 0: auth_url = auth_url + '/v2.0' self._v2_auth(auth_url) def _v1_auth(self, url): if self.proxy_token: raise exceptions.NoTokenLookupException() headers = {'X-Auth-User': self.user, 'X-Auth-Key': self.password} if self.projectid: headers['X-Auth-Project-Id'] = self.projectid resp, body = self.request(url, 'GET', headers=headers) if resp.status_code in (200, 204): # in some cases we get No Content try: mgmt_header = 'x-server-management-url' self.management_url = resp.headers[mgmt_header].rstrip('/') self.auth_token = resp.headers['x-auth-token'] self.auth_url = url except (KeyError, TypeError): raise exceptions.AuthorizationFailure() elif resp.status_code == 305: return resp.headers['location'] else: raise exceptions.from_response(resp, body) def _v2_auth(self, url): """Authenticate against a v2.0 auth service.""" body = {"auth": { "passwordCredentials": {"username": self.user, "password": self.password}}} if self.projectid: body['auth']['tenantName'] = self.projectid elif self.tenant_id: body['auth']['tenantId'] = self.tenant_id self._authenticate(url, body) def _rax_auth(self, url): """Authenticate against the Rackspace auth service.""" body = {"auth": { "RAX-KSKEY:apiKeyCredentials": { "username": self.user, "apiKey": self.password, "tenantName": self.projectid}}} self._authenticate(url, body) def _authenticate(self, url, body): """Authenticate and extract the service catalog.""" token_url = url + "/tokens" # Make sure we follow redirects when trying to reach Keystone resp, body = self.request( token_url, "POST", body=body, allow_redirects=True) return self._extract_service_catalog(url, resp, body) 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.keys()))) raise exceptions.UnsupportedVersion(msg) return utils.import_class(client_path) def Client(version, *args, **kwargs): client_class = get_client_class(version) return client_class(*args, **kwargs) python-manilaclient-0.1~git20140208/manilaclient/shell.py0000664000175000017500000004425112275236132022164 0ustar chuckchuck # Copyright 2011 OpenStack LLC. # 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. """ import argparse import glob import imp import itertools import os import pkgutil import sys import logging from manilaclient import client from manilaclient import exceptions as exc import manilaclient.extension from manilaclient.openstack.common import strutils from manilaclient import utils from manilaclient.v1 import shell as shell_v1 # from manilaclient.v2 import shell as shell_v2 DEFAULT_OS_SHARE_API_VERSION = "1" DEFAULT_MANILA_ENDPOINT_TYPE = 'publicURL' DEFAULT_MANILA_SERVICE_TYPE = 'share' logger = logging.getLogger(__name__) class ManilaClientArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(ManilaClientArgumentParser, self).__init__(*args, **kwargs) 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]}) 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('--debug', action='store_true', default=utils.env('manilaclient_DEBUG', default=False), help="Print debugging output") parser.add_argument('--os-username', metavar='', default=utils.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='', default=utils.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='', default=utils.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-tenant-id', metavar='', default=utils.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-auth-url', metavar='', default=utils.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='', default=utils.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('--service-type', metavar='', help='Defaults to compute for most actions') parser.add_argument('--service_type', help=argparse.SUPPRESS) parser.add_argument('--service-name', metavar='', default=utils.env('MANILA_SERVICE_NAME'), help='Defaults to env[MANILA_SERVICE_NAME]') parser.add_argument('--service_name', help=argparse.SUPPRESS) parser.add_argument('--share-service-name', metavar='', default=utils.env('MANILA_share_service_name'), help='Defaults to env[MANILA_share_service_name]') parser.add_argument('--share_service_name', help=argparse.SUPPRESS) parser.add_argument('--endpoint-type', metavar='', default=utils.env( 'MANILA_ENDPOINT_TYPE', default=DEFAULT_MANILA_ENDPOINT_TYPE), help='Defaults to env[MANILA_ENDPOINT_TYPE] or ' + DEFAULT_MANILA_ENDPOINT_TYPE + '.') parser.add_argument('--endpoint_type', help=argparse.SUPPRESS) parser.add_argument('--os-share-api-version', metavar='', default=utils.env( 'OS_SHARE_API_VERSION', default=DEFAULT_OS_SHARE_API_VERSION), help='Accepts 1 or 2,defaults ' 'to env[OS_SHARE_API_VERSION].') parser.add_argument('--os_share_api_version', help=argparse.SUPPRESS) parser.add_argument('--os-cacert', metavar='', default=utils.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=utils.env('manilaclient_INSECURE', default=False), action='store_true', help=argparse.SUPPRESS) parser.add_argument('--retries', metavar='', type=int, default=0, help='Number of retries.') return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') try: actions_module = { '1.1': shell_v1, }[version] except KeyError: actions_module = shell_v1 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, version): extensions = [] for name, module in itertools.chain( self._discover_via_python_path(version), self._discover_via_contrib_path(version)): extension = manilaclient.extension.Extension(name, module) extensions.append(extension) return extensions def _discover_via_python_path(self, version): 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, version): module_path = os.path.dirname(os.path.abspath(__file__)) version_str = "v%s" % version.replace('.', '_') 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 streamhandler = logging.StreamHandler() streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" streamhandler.setFormatter(logging.Formatter(streamformat)) logger.setLevel(logging.DEBUG) logger.addHandler(streamhandler) 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) # build available subcommands based on version self.extensions = self._discover_extensions( options.os_share_api_version) self._run_extension_hooks('__pre_parse_args__') subcommand_parser = self.get_subcommand_parser( options.os_share_api_version) self.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) # 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_username, os_password, os_tenant_name, os_auth_url, os_region_name, os_tenant_id, endpoint_type, insecure, service_type, service_name, share_service_name, cacert) = ( args.os_username, args.os_password, args.os_tenant_name, args.os_auth_url, args.os_region_name, args.os_tenant_id, args.endpoint_type, args.insecure, args.service_type, args.service_name, args.share_service_name, args.os_cacert) if not endpoint_type: endpoint_type = DEFAULT_MANILA_ENDPOINT_TYPE if not service_type: service_type = DEFAULT_MANILA_SERVICE_TYPE service_type = utils.get_service_type(args.func) or service_type #FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if not utils.isunauthenticated(args.func): if not os_username: raise exc.CommandError( "You must provide a username " "via either --os-username or env[OS_USERNAME]") if not os_password: raise exc.CommandError("You must provide a password " "via either --os-password or via " "env[OS_PASSWORD]") if not (os_tenant_name or os_tenant_id): raise exc.CommandError("You must provide a tenant_id " "via either --os-tenant-id or " "env[OS_TENANT_ID]") if not os_auth_url: raise exc.CommandError( "You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]") if not (os_tenant_name or os_tenant_id): raise exc.CommandError( "You must provide a tenant_id " "via either --os-tenant-id or env[OS_TENANT_ID]") if not os_auth_url: raise exc.CommandError( "You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]") self.cs = client.Client(options.os_share_api_version, os_username, os_password, os_tenant_name, os_auth_url, insecure, region_name=os_region_name, tenant_id=os_tenant_id, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, share_service_name=share_service_name, retries=options.retries, http_log_debug=args.debug, cacert=cacert) try: if not utils.isunauthenticated(args.func): self.cs.authenticate() except exc.Unauthorized: raise exc.CommandError("Invalid OpenStack Manila credentials.") except exc.AuthorizationFailure: raise exc.CommandError("Unable to authorize user") args.func(self.cs, args) 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 self.subcommands.items(): commands.add(sc_str) for option in sc._optionals._option_string_actions.keys(): options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print ' '.join(commands | options) @utils.arg('command', metavar='', nargs='?', help='Display help for ') 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: OpenStackManilaShell().main(map(strutils.safe_decode, sys.argv[1:])) except KeyboardInterrupt: print >> sys.stderr, "... terminating manila client" sys.exit(130) except Exception, e: logger.debug(e, exc_info=1) message = e.message if not isinstance(message, basestring): message = str(message) print >> sys.stderr, "ERROR: %s" % strutils.safe_encode(message) sys.exit(1) if __name__ == "__main__": main() python-manilaclient-0.1~git20140208/manilaclient/extension.py0000664000175000017500000000256112275236132023067 0ustar chuckchuck# Copyright 2011 OpenStack LLC. # 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 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 "" % self.name python-manilaclient-0.1~git20140208/manilaclient/exceptions.py0000664000175000017500000000742712275236132023242 0ustar chuckchuck# Copyright 2010 Jacob Kaplan-Moss """ Exception definitions. """ class UnsupportedVersion(Exception): """Indicates that the user is trying to use an unsupported version of the API""" pass class CommandError(Exception): pass class AuthorizationFailure(Exception): pass class NoUniqueMatch(Exception): pass class NoTokenLookupException(Exception): """This form of authentication does not support looking up endpoints from an existing token.""" pass class EndpointNotFound(Exception): """Could not find Service or Region in Service Catalog.""" pass class AmbiguousEndpoints(Exception): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): self.endpoints = endpoints def __str__(self): return "AmbiguousEndpoints: %s" % repr(self.endpoints) class ClientException(Exception): """ The base exception class for all exceptions this library raises. """ def __init__(self, code, message=None, details=None, request_id=None): self.code = code self.message = message or self.__class__.message self.details = details self.request_id = request_id def __str__(self): formatted_string = "%s (HTTP %s)" % (self.message, self.code) if self.request_id: formatted_string += " (Request-ID: %s)" % self.request_id return formatted_string class BadRequest(ClientException): """ HTTP 400 - Bad request: you sent some malformed data. """ http_status = 400 message = "Bad request" class Unauthorized(ClientException): """ HTTP 401 - Unauthorized: bad credentials. """ http_status = 401 message = "Unauthorized" class Forbidden(ClientException): """ HTTP 403 - Forbidden: your credentials don't give you access to this resource. """ http_status = 403 message = "Forbidden" class NotFound(ClientException): """ HTTP 404 - Not found """ http_status = 404 message = "Not found" class OverLimit(ClientException): """ HTTP 413 - Over limit: you're over the API limits for this time period. """ http_status = 413 message = "Over limit" # NotImplemented is a python keyword. class HTTPNotImplemented(ClientException): """ HTTP 501 - Not Implemented: the server does not support this operation. """ http_status = 501 message = "Not Implemented" # In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() # so we can do this: # _code_map = dict((c.http_status, c) # for c in ClientException.__subclasses__()) # # Instead, we have to hardcode it: _code_map = dict((c.http_status, c) for c in [BadRequest, Unauthorized, Forbidden, NotFound, OverLimit, HTTPNotImplemented]) def from_response(response, body): """ Return an instance of an ClientException or subclass based on an requests response. Usage:: resp, body = requests.request(...) if resp.status_code != 200: raise exception_from_response(resp, rest.text) """ cls = _code_map.get(response.status_code, ClientException) if response.headers: request_id = response.headers.get('x-compute-request-id') else: request_id = None if body: message = "n/a" details = "n/a" if hasattr(body, 'keys'): error = body[body.keys()[0]] message = error.get('message', None) details = error.get('details', None) return cls(code=response.status_code, message=message, details=details, request_id=request_id) else: return cls(code=response.status_code, request_id=request_id) python-manilaclient-0.1~git20140208/LICENSE0000664000175000017500000002707512275236132017055 0ustar chuckchuckCopyright (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. python-manilaclient-0.1~git20140208/README.rst0000664000175000017500000001505212275236132017527 0ustar chuckchuckPython bindings to the OpenStack Manila API =========================================== 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: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://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: http://wiki.openstack.org/GerritWorkflow This code 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. __ http://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=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_VOLUME_API_VERSION=1 If you are using Keystone, you need to set the MANILA_URL to the keystone endpoint:: 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``:: usage: manila [--debug] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-volume-api-version ] [--os-cacert ] [--retries ] ... Command-line interface to the OpenStack Manila API. Positional arguments: absolute-limits Print a list of absolute limits for a user create Add a new volume. credentials Show user credentials returned from auth delete Remove a volume. endpoints Discover endpoints that get returned from the authenticate services extra-specs-list Print a list of current 'volume types and extra specs' (Admin Only). list List all the volumes. quota-class-show List the quotas for a quota class. quota-class-update Update the quotas for a quota class. quota-defaults List the default quotas for a tenant. quota-show List the quotas for a tenant. quota-update Update the quotas for a tenant. rate-limits Print a list of rate limits for a user rename Rename a volume. show Show details about a volume. snapshot-create Add a new snapshot. snapshot-delete Remove a snapshot. snapshot-list List all the snapshots. snapshot-rename Rename a snapshot. snapshot-show Show details about a snapshot. type-create Create a new volume type. type-delete Delete a specific volume type type-key Set or unset extra_spec for a volume type. type-list Print a list of available 'volume types'. bash-completion Prints all of the commands and options to stdout so that the help Display help about this program or one of its subcommands. list-extensions List all the os-api extensions that are available. Optional arguments: --debug Print debugging output --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --service-type Defaults to compute for most actions --service-name Defaults to env[MANILA_SERVICE_NAME] --volume-service-name Defaults to env[MANILA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[MANILA_ENDPOINT_TYPE] or publicURL. --os-volume-api-version Accepts 1,defaults to env[OS_VOLUME_API_VERSION]. --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --retries Number of retries. 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="volume") >>> nt.volumes.list() [...] python-manilaclient-0.1~git20140208/.testr.conf0000664000175000017500000000050512275236132020123 0ustar chuckchuck[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 ./ ./tests $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--listpython-manilaclient-0.1~git20140208/doc/0000775000175000017500000000000012275236446016612 5ustar chuckchuckpython-manilaclient-0.1~git20140208/doc/.gitignore0000664000175000017500000000000712275236132020567 0ustar chuckchuckbuild/ python-manilaclient-0.1~git20140208/doc/source/0000775000175000017500000000000012275236446020112 5ustar chuckchuckpython-manilaclient-0.1~git20140208/doc/source/conf.py0000664000175000017500000001511612275236132021405 0ustar chuckchuck# -*- coding: utf-8 -*- # # python-manilaclient documentation build configuration file, created by # sphinx-quickstart on Sun Dec 6 14:19:25 2009. # # This file is execfile()d with current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. 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. #sys.path.append(os.path.abspath('.')) BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) # -- 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', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # 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' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # 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 # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'python-manilaclientdoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) # . latex_documents = [ ('index', 'python-manilaclient.tex', u'python-manilaclient Documentation', u'Rackspace - based on work by Jacob Kaplan-Moss', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} python-manilaclient-0.1~git20140208/doc/source/shell.rst0000664000175000017500000000255112275236132021746 0ustar chuckchuckThe :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 :option:`--os-username`, :option:`--os-password` and :option:`--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_VOLUME_API_VERSION The OpenStack Block Storage API version. For example, in Bash you'd use:: export OS_USERNAME=yourname export OS_PASSWORD=yadayadayada export OS_TENANT_NAME=myproject export OS_AUTH_URL=http://... export OS_VOLUME_API_VERSION=1 From there, all shell commands take the form:: manila [arguments...] Run :program:`manila help` to get a full list of all possible commands, and run :program:`manila help ` to get detailed help for that command. python-manilaclient-0.1~git20140208/doc/source/index.rst0000664000175000017500000000514712275236132021752 0ustar chuckchuckPython API ========== 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.volumes.list() [] >>> myvol = manila.volumes.create(display_name="test-vol", size=1) >>> myvol.id ce06d0a8-5c1b-4e2c-81d2-39eca6bbfb70 >>> manila.volumes.list() [] >>>myvol.delete Command-line Tool ================= In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-id``, and ``--os-auth-url``) or set them in environment variables:: export OS_USERNAME=user export OS_PASSWORD=pass export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b export OS_AUTH_URL=http://auth.example.com:5000/v2.0 Once you've configured your authentication parameters, you can run ``manila help`` to see a complete listing of available commands. Release Notes ============= 1.0.4 ----- * Added suport for backup-service commands .. _1163546: http://bugs.launchpad.net/python-manilaclient/+bug/1163546 .. _1161857: http://bugs.launchpad.net/python-manilaclient/+bug/1161857 .. _1160898: http://bugs.launchpad.net/python-manilaclient/+bug/1160898 .. _1161857: http://bugs.launchpad.net/python-manilaclient/+bug/1161857 .. _1156994: http://bugs.launchpad.net/python-manilaclient/+bug/1156994 1.0.3 ----- * Added support for V2 Manila API * Corected upload-volume-to-image help messaging * Align handling of metadata args for all methods * Update OSLO version * Correct parsing of volume metadata * Enable force delete of volumes and snapshots in error state * Implement clone volume API call * Add list-extensions call to manilaclient * Add bootable column to list output * Add retries to manilaclient operations * Add Type/Extra-Specs support * Add volume and snapshot rename commands .. _1155655: http://bugs.launchpad.net/python-manilaclient/+bug/1155655 .. _1130730: http://bugs.launchpad.net/python-manilaclient/+bug/1130730 .. _1068521: http://bugs.launchpad.net/python-manilaclient/+bug/1068521 .. _1052161: http://bugs.launchpad.net/python-manilaclient/+bug/1052161 .. _1071003: http://bugs.launchpad.net/python-manilaclient/+bug/1071003 .. _1065275: http://bugs.launchpad.net/python-manilaclient/+bug/1065275 .. _1053432: http://bugs.launchpad.net/python-manilaclient/+bug/1053432 python-manilaclient-0.1~git20140208/doc/Makefile0000664000175000017500000000616412275236132020251 0ustar chuckchuck# 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 ' where 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." python-manilaclient-0.1~git20140208/HACKING0000664000175000017500000000607412275236132017033 0ustar chuckchuckManila Style Commandments ========================= Step 1: Read http://www.python.org/dev/peps/pep-0008/ Step 2: Read http://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 http://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) RIGHT: >>> u = u'foo' >>> u u'foo' >>> type(u) - 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) python-manilaclient-0.1~git20140208/setup.py0000664000175000017500000000404312275236132017550 0ustar chuckchuck# Copyright 2011 OpenStack, LLC # # 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 setuptools from manilaclient.openstack.common import setup requires = setup.parse_requirements() depend_links = setup.parse_dependency_links() tests_require = setup.parse_requirements(['tools/test-requires']) project = 'python-manilaclient' def read_file(file_name): return open(os.path.join(os.path.dirname(__file__), file_name)).read() setuptools.setup( name=project, version=setup.get_version(project), author="OpenStack Contributors", author_email="openstack-dev@lists.openstack.org", description="Client library for OpenStack Manila API.", long_description=read_file("README.rst"), license="Apache License, Version 2.0", url="https://github.com/openstack/python-manilaclient", packages=setuptools.find_packages(exclude=['tests', 'tests.*']), cmdclass=setup.get_cmdclass(), install_requires=requires, tests_require=tests_require, setup_requires=['setuptools-git>=0.4'], include_package_data=True, dependency_links=depend_links, classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: OpenStack", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python" ], entry_points={ "console_scripts": ["manila = manilaclient.shell:main"] } ) python-manilaclient-0.1~git20140208/__init__.py0000664000175000017500000000003112275236132020140 0ustar chuckchuck__author__ = 'vkostenko' python-manilaclient-0.1~git20140208/tools/0000775000175000017500000000000012275236446017205 5ustar chuckchuckpython-manilaclient-0.1~git20140208/tools/with_venv.sh0000775000175000017500000000012412275236132021542 0ustar chuckchuck#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-manilaclient-0.1~git20140208/tools/generate_authors.sh0000775000175000017500000000005112275236132023067 0ustar chuckchuck#!/bin/bash git shortlog -se | cut -c8- python-manilaclient-0.1~git20140208/tools/pip-requires0000664000175000017500000000007712275236132021551 0ustar chuckchuckargparse PrettyTable>=0.6,<0.8 requests>=1.1 simplejson>=2.0.9 python-manilaclient-0.1~git20140208/tools/cinder.bash_completion0000664000175000017500000000055112275236132023532 0ustar chuckchuck_manila() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="$(manila bash_completion)" COMPLETION_CACHE=~/.manilaclient/*/*-cache opts+=" "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) } complete -F _manila manila python-manilaclient-0.1~git20140208/tools/test-requires0000664000175000017500000000020112275236132021725 0ustar chuckchuckcoverage>=3.6 discover fixtures>=0.3.14 mock>=1.0 ordereddict pep8==1.3.3 sphinx>=1.1.2 testrepository>=0.0.17 testtools>=0.9.32 python-manilaclient-0.1~git20140208/tools/install_venv.py0000664000175000017500000001626512275236132022265 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack, LLC # # 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. """ Installation script for Nova's development virtualenv """ import optparse import os import subprocess import sys import platform ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) VENV = os.path.join(ROOT, '.venv') PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires') TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires') PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) def die(message, *args): print >> sys.stderr, message % args sys.exit(1) def check_python_version(): if sys.version_info < (2, 6): die("Need Python Version >= 2.6") def run_command_with_code(cmd, redirect_output=True, check_exit_code=True): """ Runs a command in an out-of-process shell, returning the output of that command. Working directory is ROOT. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(cmd, redirect_output=True, check_exit_code=True): return run_command_with_code(cmd, redirect_output, check_exit_code)[0] class Distro(object): def check_cmd(self, cmd): return bool(run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print 'Installing virtualenv via easy_install...', if run_command(['easy_install', 'virtualenv']): print 'Succeeded' return else: print 'Failed' die('ERROR: virtualenv not found.\n\nDevelopment' ' requires virtualenv, please install it using your' ' favorite package management tool') def post_process(self): """Any distribution-specific post-processing gets done here. In particular, this is useful for applying patches to code inside the venv.""" pass class Debian(Distro): """This covers all Debian-based distributions.""" def check_pkg(self, pkg): return run_command_with_code(['dpkg', '-l', pkg], check_exit_code=False)[1] == 0 def apt_install(self, pkg, **kwargs): run_command(['sudo', 'apt-get', 'install', '-y', pkg], **kwargs) def apply_patch(self, originalfile, patchfile): run_command(['patch', originalfile, patchfile]) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.apt_install('python-virtualenv', check_exit_code=False) super(Debian, self).install_virtualenv() class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux""" def check_pkg(self, pkg): return run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def yum_install(self, pkg, **kwargs): run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) def apply_patch(self, originalfile, patchfile): run_command(['patch', originalfile, patchfile]) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.yum_install('python-virtualenv', check_exit_code=False) super(Fedora, self).install_virtualenv() def get_distro(): if os.path.exists('/etc/fedora-release') or \ os.path.exists('/etc/redhat-release'): return Fedora() elif os.path.exists('/etc/debian_version'): return Debian() else: return Distro() def check_dependencies(): get_distro().install_virtualenv() def create_virtualenv(venv=VENV, no_site_packages=True): """Creates the virtual environment and installs PIP only into the virtual environment """ print 'Creating venv...', if no_site_packages: run_command(['virtualenv', '-q', '--no-site-packages', VENV]) else: run_command(['virtualenv', '-q', VENV]) print 'done.' print 'Installing pip in virtualenv...', if not run_command(['tools/with_venv.sh', 'easy_install', 'pip>1.0']).strip(): die("Failed to install pip.") print 'done.' def pip_install(*args): run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(venv=VENV): print 'Installing dependencies with pip (this can take a while)...' # First things first, make sure our venv has the latest pip and distribute. pip_install('pip') pip_install('distribute') pip_install('-r', PIP_REQUIRES) pip_install('-r', TEST_REQUIRES) # Tell the virtual env how to "import manila" pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", "manilaclient.pth") f = open(pthfile, 'w') f.write("%s\n" % ROOT) def post_process(): get_distro().post_process() def print_help(): help = """ python-manilaclient development environment setup is complete. python-manilaclient development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the python-manilaclient virtualenv for the extent of your current shell session you can run: $ source .venv/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print help def parse_args(): """Parse command-line arguments""" parser = optparse.OptionParser() parser.add_option("-n", "--no-site-packages", dest="no_site_packages", default=False, action="store_true", help="Do not inherit packages from global Python install") return parser.parse_args() def main(argv): (options, args) = parse_args() check_python_version() check_dependencies() create_virtualenv(no_site_packages=options.no_site_packages) install_dependencies() post_process() print_help() if __name__ == '__main__': main(sys.argv) python-manilaclient-0.1~git20140208/PKG-INFO0000664000175000017500000002063512275236446017150 0ustar chuckchuckMetadata-Version: 1.1 Name: python-manilaclient Version: 2013.3-dev Summary: Client library for OpenStack Manila API. Home-page: https://github.com/openstack/python-manilaclient Author: OpenStack Contributors Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description: Python bindings to the OpenStack Manila API =========================================== 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: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://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: http://wiki.openstack.org/GerritWorkflow This code 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. __ http://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=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_VOLUME_API_VERSION=1 If you are using Keystone, you need to set the MANILA_URL to the keystone endpoint:: 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``:: usage: manila [--debug] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-volume-api-version ] [--os-cacert ] [--retries ] ... Command-line interface to the OpenStack Manila API. Positional arguments: absolute-limits Print a list of absolute limits for a user create Add a new volume. credentials Show user credentials returned from auth delete Remove a volume. endpoints Discover endpoints that get returned from the authenticate services extra-specs-list Print a list of current 'volume types and extra specs' (Admin Only). list List all the volumes. quota-class-show List the quotas for a quota class. quota-class-update Update the quotas for a quota class. quota-defaults List the default quotas for a tenant. quota-show List the quotas for a tenant. quota-update Update the quotas for a tenant. rate-limits Print a list of rate limits for a user rename Rename a volume. show Show details about a volume. snapshot-create Add a new snapshot. snapshot-delete Remove a snapshot. snapshot-list List all the snapshots. snapshot-rename Rename a snapshot. snapshot-show Show details about a snapshot. type-create Create a new volume type. type-delete Delete a specific volume type type-key Set or unset extra_spec for a volume type. type-list Print a list of available 'volume types'. bash-completion Prints all of the commands and options to stdout so that the help Display help about this program or one of its subcommands. list-extensions List all the os-api extensions that are available. Optional arguments: --debug Print debugging output --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --service-type Defaults to compute for most actions --service-name Defaults to env[MANILA_SERVICE_NAME] --volume-service-name Defaults to env[MANILA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[MANILA_ENDPOINT_TYPE] or publicURL. --os-volume-api-version Accepts 1,defaults to env[OS_VOLUME_API_VERSION]. --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --retries Number of retries. 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="volume") >>> nt.volumes.list() [...] Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python python-manilaclient-0.1~git20140208/tests/0000775000175000017500000000000012275236446017207 5ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/utils.py0000664000175000017500000000265712275236132020723 0ustar chuckchuckimport os import fixtures 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)) class TestResponse(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', None) # Fake the text attribute to streamline Response creation self._text = data.get('text', None) else: self.status_code = data def __eq__(self, other): return self.__dict__ == other.__dict__ @property def text(self): return self._text python-manilaclient-0.1~git20140208/tests/test_shell.py0000664000175000017500000000401612275236132021720 0ustar chuckchuckimport cStringIO import os import re import sys import fixtures from testtools import matchers from manilaclient import exceptions import manilaclient.shell from tests import utils class ShellTest(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 setUp(self): super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) def shell(self, argstr): orig = sys.stdout try: sys.stdout = cStringIO.StringIO() _shell = manilaclient.shell.OpenStackManilaShell() _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 def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') def test_help(self): required = [ '.*?^usage: ', '.*?^\s+create\s+Creates new NAS storage \(NFS or CIFS\).', '.*?(?m)^See "manila help COMMAND" for help on a specific command.', ] help_text = self.shell('help') for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help_on_subcommand(self): required = [ '.*?^usage: manila list', '.*?(?m)^List all NAS shares.', ] help_text = self.shell('help list') for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) python-manilaclient-0.1~git20140208/tests/test_utils.py0000664000175000017500000000425212275236132021753 0ustar chuckchuck from manilaclient import exceptions from manilaclient import utils from manilaclient import base from tests import utils as test_utils UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0' class FakeResource(object): def __init__(self, _id, properties): self.id = _id try: self.name = properties['name'] except KeyError: pass try: self.display_name = properties['display_name'] except KeyError: pass class FakeManager(base.ManagerWithFind): resource_class = FakeResource resources = [ FakeResource('1234', {'name': 'entity_one'}), FakeResource(UUID, {'name': 'entity_two'}), FakeResource('4242', {'display_name': 'entity_three'}), FakeResource('5678', {'name': '9876'}) ] def get(self, resource_id): for resource in self.resources: if resource.id == str(resource_id): return resource raise exceptions.NotFound(resource_id) def list(self): return self.resources class FindResourceTestCase(test_utils.TestCase): def setUp(self): super(FindResourceTestCase, self).setUp() self.manager = FakeManager(None) def test_find_none(self): self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, 'asdf') def test_find_by_integer_id(self): output = utils.find_resource(self.manager, 1234) self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_id(self): output = utils.find_resource(self.manager, '1234') self.assertEqual(output, self.manager.get('1234')) def test_find_by_uuid(self): output = utils.find_resource(self.manager, UUID) self.assertEqual(output, self.manager.get(UUID)) def test_find_by_str_name(self): output = utils.find_resource(self.manager, 'entity_one') self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_displayname(self): output = utils.find_resource(self.manager, 'entity_three') self.assertEqual(output, self.manager.get('4242')) python-manilaclient-0.1~git20140208/tests/v1/0000775000175000017500000000000012275236446017535 5ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/v1/test_share_snapshots.py0000664000175000017500000000256412275236132024351 0ustar chuckchuck# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack LLC. # 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 extension from manilaclient.v1 import share_snapshots from tests import utils from tests.v1 import fakes extensions = [ extension.Extension('share_snapshots', share_snapshots), ] cs = fakes.FakeClient(extensions=extensions) class ShareSnapshotsTest(utils.TestCase): def test_create_share_snapshot(self): cs.share_snapshots.create(1234) cs.assert_called('POST', '/snapshots') def test_delete_share(self): snapshot = cs.share_snapshots.get(1234) cs.share_snapshots.delete(snapshot) cs.assert_called('DELETE', '/snapshots/1234') def test_list_shares(self): cs.share_snapshots.list() cs.assert_called('GET', '/snapshots/detail') python-manilaclient-0.1~git20140208/tests/v1/test_shares.py0000664000175000017500000000444212275236132022427 0ustar chuckchuck# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack LLC. # 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 extension from manilaclient.v1 import shares from tests import utils from tests.v1 import fakes extensions = [ extension.Extension('shares', shares), ] cs = fakes.FakeClient(extensions=extensions) class SharesTest(utils.TestCase): def test_create_nfs_share(self): cs.shares.create('nfs', 1) cs.assert_called('POST', '/shares') def test_create_cifs_share(self): cs.shares.create('cifs', 2) cs.assert_called('POST', '/shares') def test_delete_share(self): share = cs.shares.get('1234') cs.shares.delete(share) cs.assert_called('DELETE', '/shares/1234') def test_list_shares(self): cs.shares.list() cs.assert_called('GET', '/shares/detail') def test_allow_access_to_share(self): share = cs.shares.get(1234) ip = '192.168.0.1' cs.shares.allow(share, 'ip', ip) cs.assert_called('POST', '/shares/1234/action') def test_get_metadata(self): cs.shares.get_metadata(1234) cs.assert_called('GET', '/shares/1234/metadata') def test_set_metadata(self): cs.shares.set_metadata(1234, {'k1': 'v2'}) cs.assert_called('POST', '/shares/1234/metadata', {'metadata': {'k1': 'v2'}}) def test_delete_metadata(self): keys = ['key1'] cs.shares.delete_metadata(1234, keys) cs.assert_called('DELETE', '/shares/1234/metadata/key1') def test_metadata_update_all(self): cs.shares.update_all_metadata(1234, {'k1': 'v1'}) cs.assert_called('PUT', '/shares/1234/metadata', {'metadata': {'k1': 'v1'}}) python-manilaclient-0.1~git20140208/tests/v1/test_shell.py0000664000175000017500000002044012275236132022245 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 fixtures from manilaclient import client from manilaclient import shell from manilaclient import exceptions from manilaclient.v1 import shell as shell_v1 from tests import utils from tests.v1 import fakes class ShellTest(utils.TestCase): FAKE_ENV = { 'MANILA_USERNAME': 'username', 'MANILA_PASSWORD': 'password', 'MANILA_PROJECT_ID': 'project_id', 'OS_SHARE_API_VERSION': '2', 'MANILA_URL': 'http://no.where', } # Patch os.environ to avoid required auth info. def setUp(self): """Run before each test.""" super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) self.shell = shell.OpenStackManilaShell() #HACK(bcwaldon): replace this when we start using stubs self.old_get_client_class = client.get_client_class client.get_client_class = lambda *_: fakes.FakeClient def tearDown(self): # For some method like test_image_meta_bad_action we are # testing a SystemExit to be thrown and object self.shell has # no time to get instantatiated which is OK in this case, so # we make sure the method is there before launching it. if hasattr(self.shell, 'cs'): self.shell.cs.clear_callstack() #HACK(bcwaldon): replace this when we start using stubs client.get_client_class = self.old_get_client_class super(ShellTest, self).tearDown() def run_command(self, cmd): self.shell.main(cmd.split()) def assert_called(self, method, url, body=None, **kwargs): return self.shell.cs.assert_called(method, url, body, **kwargs) def assert_called_anytime(self, method, url, body=None): return self.shell.cs.assert_called_anytime(method, url, body) def test_list(self): self.run_command('list') # NOTE(jdg): we default to detail currently self.assert_called('GET', '/shares/detail') def test_list_filter_status(self): self.run_command('list --status=available') self.assert_called('GET', '/shares/detail?status=available') def test_list_filter_name(self): self.run_command('list --name=1234') self.assert_called('GET', '/shares/detail?name=1234') def test_list_all_tenants(self): self.run_command('list --all-tenants=1') self.assert_called('GET', '/shares/detail?all_tenants=1') def test_show(self): self.run_command('show 1234') self.assert_called('GET', '/shares/1234') def test_delete(self): self.run_command('delete 1234') self.assert_called('DELETE', '/shares/1234') def test_snapshot_list_filter_share_id(self): self.run_command('snapshot-list --share-id=1234') self.assert_called('GET', '/snapshots/detail?share_id=1234') def test_snapshot_list_filter_status_and_share_id(self): self.run_command('snapshot-list --status=available --share-id=1234') self.assert_called('GET', '/snapshots/detail?' 'status=available&share_id=1234') def test_rename(self): # basic rename with positional agruments self.run_command('rename 1234 new-name') expected = {'share': {'display_name': 'new-name'}} self.assert_called('PUT', '/shares/1234', body=expected) # change description only self.run_command('rename 1234 --description=new-description') expected = {'share': {'display_description': 'new-description'}} self.assert_called('PUT', '/shares/1234', body=expected) # rename and change description self.run_command('rename 1234 new-name ' '--description=new-description') expected = {'share': { 'display_name': 'new-name', 'display_description': 'new-description', }} self.assert_called('PUT', '/shares/1234', body=expected) self.assertRaises(exceptions.CommandError, self.run_command, 'rename 1234') def test_rename_snapshot(self): # basic rename with positional agruments self.run_command('snapshot-rename 1234 new-name') expected = {'snapshot': {'display_name': 'new-name'}} self.assert_called('PUT', '/snapshots/1234', body=expected) # change description only self.run_command('snapshot-rename 1234 ' '--description=new-description') expected = {'snapshot': {'display_description': 'new-description'}} self.assert_called('PUT', '/snapshots/1234', body=expected) # snapshot-rename and change description self.run_command('snapshot-rename 1234 new-name ' '--description=new-description') expected = {'snapshot': { 'display_name': 'new-name', 'display_description': 'new-description', }} self.assert_called('PUT', '/snapshots/1234', body=expected) # noop, the only all will be the lookup self.assertRaises(exceptions.CommandError, self.run_command, 'rename 1234') def test_set_metadata_set(self): self.run_command('metadata 1234 set key1=val1 key2=val2') self.assert_called('POST', '/shares/1234/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}) def test_set_metadata_delete_dict(self): self.run_command('metadata 1234 unset key1=val1 key2=val2') self.assert_called('DELETE', '/shares/1234/metadata/key1') self.assert_called('DELETE', '/shares/1234/metadata/key2', pos=-2) def test_set_metadata_delete_keys(self): self.run_command('metadata 1234 unset key1 key2') self.assert_called('DELETE', '/shares/1234/metadata/key1') self.assert_called('DELETE', '/shares/1234/metadata/key2', pos=-2) def test_share_metadata_update_all(self): self.run_command('metadata-update-all 1234 key1=val1 key2=val2') self.assert_called('PUT', '/shares/1234/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}) def test_extract_metadata(self): # mimic the result of argparse's parse_args() method class Arguments: def __init__(self, metadata=[]): self.metadata = metadata inputs = [ ([], {}), (["key=value"], {"key": "value"}), (["key"], {"key": None}), (["k1=v1", "k2=v2"], {"k1": "v1", "k2": "v2"}), (["k1=v1", "k2"], {"k1": "v1", "k2": None}), (["k1", "k2=v2"], {"k1": None, "k2": "v2"}) ] for input in inputs: args = Arguments(metadata=input[0]) self.assertEqual(shell_v1._extract_metadata(args), input[1]) def test_reset_state(self): self.run_command('reset-state 1234') expected = {'os-reset_status': {'status': 'available'}} self.assert_called('POST', '/shares/1234/action', body=expected) def test_reset_state_with_flag(self): self.run_command('reset-state --state error 1234') expected = {'os-reset_status': {'status': 'error'}} self.assert_called('POST', '/shares/1234/action', body=expected) def test_snapshot_reset_state(self): self.run_command('snapshot-reset-state 1234') expected = {'os-reset_status': {'status': 'available'}} self.assert_called('POST', '/snapshots/1234/action', body=expected) def test_snapshot_reset_state_with_flag(self): self.run_command('snapshot-reset-state --state error 1234') expected = {'os-reset_status': {'status': 'error'}} self.assert_called('POST', '/snapshots/1234/action', body=expected) python-manilaclient-0.1~git20140208/tests/v1/__init__.py0000664000175000017500000000000012275236132021624 0ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/v1/test_quotas.py0000664000175000017500000000576012275236132022462 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 tests import utils from tests.v1 import fakes cs = fakes.FakeClient() class QuotaSetsTest(utils.TestCase): def test_tenant_quotas_get(self): tenant_id = 'test' cs.quotas.get(tenant_id) cs.assert_called('GET', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_get(self): tenant_id = 'test' user_id = 'fake_user' cs.quotas.get(tenant_id, user_id=user_id) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) cs.assert_called('GET', url) def test_tenant_quotas_defaults(self): tenant_id = 'test' cs.quotas.defaults(tenant_id) cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id) def test_update_quota(self): q = cs.quotas.get('test') q.update(shares=2) q.update(snapshots=2) cs.assert_called('PUT', '/os-quota-sets/test') def test_update_user_quota(self): tenant_id = 'test' user_id = 'fake_user' q = cs.quotas.get(tenant_id) q.update(shares=2, user_id=user_id) q.update(snapshots=2, user_id=user_id) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) cs.assert_called('PUT', url) def test_force_update_quota(self): q = cs.quotas.get('test') q.update(shares=2, force=True) cs.assert_called( 'PUT', '/os-quota-sets/test', {'quota_set': {'force': True, 'shares': 2, 'tenant_id': 'test'}}) def test_quotas_delete(self): tenant_id = 'test' cs.quotas.delete(tenant_id) cs.assert_called('DELETE', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_delete(self): tenant_id = 'test' user_id = 'fake_user' cs.quotas.delete(tenant_id, user_id=user_id) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) cs.assert_called('DELETE', url) def test_refresh_quota(self): q = cs.quotas.get('test') q2 = cs.quotas.get('test') self.assertEqual(q.shares, q2.shares) self.assertEqual(q.snapshots, q2.snapshots) q2.shares = 0 self.assertNotEqual(q.shares, q2.shares) q2.snapshots = 0 self.assertNotEqual(q.snapshots, q2.snapshots) q2.get() self.assertEqual(q.shares, q2.shares) self.assertEqual(q.snapshots, q2.snapshots) python-manilaclient-0.1~git20140208/tests/v1/fakes.py0000664000175000017500000001112412275236132021167 0ustar chuckchuck# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack, LLC # # 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.v1 import shares as shares_ext_module from manilaclient.v1 import client from tests.v1 import fake_clients as fakes class FakeClient(fakes.FakeClient): def __init__(self, *args, **kwargs): client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions')) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(fakes.FakeHTTPClient): def get_shares_1234(self, **kw): share = {'share': {'id': 1234, 'name': 'sharename'}} return (200, {}, share) def get_shares_detail(self, **kw): shares = {'shares': [{'id': 1234, 'name': 'sharename', 'attachments': [{'server_id': 111}]}]} return (200, {}, shares) def get_snapshots_1234(self, **kw): snapshot = {'snapshot': {'id': 1234, 'name': 'sharename'}} return (200, {}, snapshot) def post_snapshots_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action == 'os-reset_status': assert 'status' in body['os-reset_status'] else: raise AssertionError("Unexpected action: %s" % action) return (resp, {}, _body) def get_snapshots_detail(self, **kw): print kw # print kw['share_id'] snapshots = {'snapshots': [{ 'id': 1234, 'created_at': '2012-08-27T00:00:00.000000', 'share_size': 1, 'share_id': 4321, 'status': 'available', 'name': 'sharename', 'display_description': 'description', 'share_proto': 'type', 'export_location': 'location', }]} return (200, {}, snapshots) def post_shares_1234_action(self, body, **kw): _body = None resp = 202 assert len(body.keys()) == 1 action = body.keys()[0] if action == 'os-allow_access': assert body[action].keys() == ['access_type', 'access_to'] _body = {'access': {}} elif action == 'os-deny_access': assert body[action].keys() == ['access_id'] elif action == 'os-access_list': assert body[action] is None elif action == 'os-reset_status': assert 'status' in body['os-reset_status'] else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) def post_shares(self, **kwargs): return (202, {}, {'share': {}}) def post_snapshots(self, **kwargs): return (202, {}, {'snapshot': {}}) def delete_shares_1234(self, **kw): return (202, {}, None) def delete_snapshots_1234(self, **kwargs): return (202, {}, None) def put_shares_1234(self, **kwargs): share = {'share': {'id': 1234, 'name': 'sharename'}} return (200, {}, share) def put_snapshots_1234(self, **kwargs): snapshot = {'snapshot': {'id': 1234, 'name': 'snapshot_name'}} return (200, {}, snapshot) # # Set/Unset metadata # def delete_shares_1234_metadata_test_key(self, **kw): return (204, {}, None) def delete_shares_1234_metadata_key1(self, **kw): return (204, {}, None) def delete_shares_1234_metadata_key2(self, **kw): return (204, {}, None) def post_shares_1234_metadata(self, **kw): return (204, {}, {'metadata': {'test_key': 'test_value'}}) def put_shares_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) def get_shares_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) def fake_create(url, body, response_key): return {'url': url, 'body': body, 'resp_key': response_key} def fake_update(url, body, response_key): return {'url': url, 'body': body, 'resp_key': response_key} python-manilaclient-0.1~git20140208/tests/v1/fake_clients.py0000664000175000017500000001253312275236132022532 0ustar chuckchuck# Copyright 2013 OpenStack, LLC # # 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 urlparse from manilaclient import client as base_client from manilaclient.v1 import client from tests import fakes import tests.utils as utils class FakeClient(fakes.FakeClient, client.Client): def __init__(self, *args, **kwargs): client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions')) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(base_client.HTTPClient): def __init__(self, **kwargs): self.username = 'username' self.password = 'password' self.auth_url = 'auth_url' self.callstack = [] def _cs_request(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 = urlparse.parse_qsl(urlparse.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 if hasattr(status, 'items'): return utils.TestResponse(status), body else: return utils.TestResponse({"status": status}), body # # Quotas # def get_os_quota_sets_test(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1}}) def get_os_quota_sets_test_defaults(self): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1}}) def put_os_quota_sets_test(self, body, **kw): assert body.keys() == ['quota_set'] fakes.assert_has_keys(body['quota_set'], required=['tenant_id']) return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 2, 'snapshots': 2, 'gigabytes': 1}}) # # Quota Classes # def get_os_quota_class_sets_test(self, **kw): return (200, {}, {'quota_class_set': { 'class_name': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1}}) def put_os_quota_class_sets_test(self, body, **kw): assert body.keys() == ['quota_class_set'] fakes.assert_has_keys(body['quota_class_set'], required=['class_name']) return (200, {}, {'quota_class_set': { 'class_name': 'test', 'metadata_items': [], 'shares': 2, 'snapshots': 2, 'gigabytes': 1}}) def delete_os_quota_sets_test(self, **kw): return (202, {}, {}) # # 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, }) python-manilaclient-0.1~git20140208/tests/v1/test_security_services.py0000664000175000017500000001241112275236132024707 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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. try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict import mock import unittest from manilaclient import exceptions from manilaclient.v1 import security_services from tests.v1 import fakes class SecurityServiceTest(unittest.TestCase): def setUp(self): super(SecurityServiceTest, self).setUp() self.manager = security_services.SecurityServiceManager(api=None) def test_create_all_fields(self): values = {'type': 'ldap', 'dns_ip': 'fake dns ip', 'server': 'fake.ldap.server', 'domain': 'fake.ldap.domain', 'sid': 'fake sid', '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.assertTrue(security_services.RESOURCE_NAME in 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', 'sid': 'fake sid'} 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.assertTrue(security_services.RESOURCE_NAME in 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_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_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, security_services.RESOURCES_NAME) def test_list_with_filters(self): filters = OrderedDict([('all_tenants', 1), ('status', 'ERROR'), ('network', 'fake_network')]) expected_postfix = '?all_tenants=1&status=ERROR&network=fake_network' 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', 'sid': 'new sid', '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_no_fields_specified(self): security_service = 'fake service' self.assertRaises(exceptions.CommandError, self.manager.update, security_service) python-manilaclient-0.1~git20140208/tests/v1/test_share_networks.py0000664000175000017500000001246312275236132024202 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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. try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict import mock import unittest from manilaclient import exceptions from manilaclient.v1 import share_networks from tests.v1 import fakes class ShareNetworkTest(unittest.TestCase): def setUp(self): super(ShareNetworkTest, self).setUp() self.manager = share_networks.ShareNetworkManager(api=None) def test_create(self): values = {'neutron_net_id': 'fake net id', 'neutron_subnet_id': 'fake subnet id', 'name': 'fake name', 'description': 'whatever'} body_expected = {share_networks.RESOURCE_NAME: values} with mock.patch.object(self.manager, '_create', fakes.fake_create): result = self.manager.create(**values) self.assertEqual(result['url'], share_networks.RESOURCES_PATH) self.assertEqual(result['resp_key'], share_networks.RESOURCE_NAME) self.assertEqual( result['body'], body_expected) def test_delete(self): share_nw = 'fake share nw' with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(share_nw) self.manager._delete.assert_called_once_with( share_networks.RESOURCE_PATH % share_nw) def test_get(self): share_nw = 'fake share nw' with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(share_nw) self.manager._get.assert_called_once_with( share_networks.RESOURCE_PATH % share_nw, share_networks.RESOURCE_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( share_networks.RESOURCES_PATH, share_networks.RESOURCES_NAME) def test_list_with_filters(self): filters = OrderedDict([('all_tenants', 1), ('status', 'ERROR')]) expected_path = \ share_networks.RESOURCES_PATH + '?all_tenants=1&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( expected_path, share_networks.RESOURCES_NAME) def test_update(self): share_nw = 'fake share nw' values = {'neutron_net_id': 'new net id', 'neutron_subnet_id': 'new subnet id', 'name': 'new name', 'description': 'new whatever'} body_expected = {share_networks.RESOURCE_NAME: values} with mock.patch.object(self.manager, '_update', fakes.fake_update): result = self.manager.update(share_nw, **values) self.assertEqual(result['url'], share_networks.RESOURCE_PATH % share_nw) self.assertEqual(result['resp_key'], share_networks.RESOURCE_NAME) self.assertEqual(result['body'], body_expected) def test_update_with_exception(self): share_nw = 'fake share nw' self.assertRaises(exceptions.CommandError, self.manager.update, share_nw) def test_add_security_service(self): security_service = 'fake security service' share_nw = 'fake share nw' expected_path = (share_networks.RESOURCE_PATH + '/action') % share_nw expected_body = {'add_security_service': {'security_service_id': security_service}} with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.add_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) def test_remove_security_service(self): security_service = 'fake security service' share_nw = 'fake share nw' expected_path = (share_networks.RESOURCE_PATH + '/action') % share_nw expected_body = {'remove_security_service': {'security_service_id': security_service}} with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.remove_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) python-manilaclient-0.1~git20140208/tests/v1/test_auth.py0000664000175000017500000003441012275236132022101 0ustar chuckchuck# Copyright (c) 2013 OpenStack, LLC. # # 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 json import mock import requests from manilaclient import exceptions from manilaclient.v1 import client from tests import utils class AuthenticateAgainstKeystoneTests(utils.TestCase): def test_authenticate_success(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0", service_type='compute') resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID" }, }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "region": "RegionOne", "adminURL": "http://localhost:8774/v2", "internalURL": "http://localhost:8774/v2", "publicURL": "http://localhost:8774/v2/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data=json.dumps(body), allow_redirects=True, **self.TEST_REQUEST_BASE) endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) test_auth_call() def test_authenticate_tenant_id(self): cs = client.Client("username", "password", auth_url="auth_url/v2.0", tenant_id='tenant_id', service_type='compute') resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "description": None, "enabled": True, "id": "tenant_id", "name": "demo" } # tenant associated with token }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "region": "RegionOne", "adminURL": "http://localhost:8774/v2", "internalURL": "http://localhost:8774/v2", "publicURL": "http://localhost:8774/v2/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantId': cs.client.tenant_id, }, } token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data=json.dumps(body), allow_redirects=True, **self.TEST_REQUEST_BASE) endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) tenant_id = resp["access"]["token"]["tenant"]["id"] self.assertEqual(cs.client.tenant_id, tenant_id) test_auth_call() def test_authenticate_failure(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0") resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}} auth_response = utils.TestResponse({ "status_code": 401, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.Unauthorized, cs.client.authenticate) test_auth_call() def test_auth_redirect(self): cs = client.Client("username", "password", "project_id", "auth_url/v2", service_type='compute') dict_correct_response = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID" }, }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "adminURL": "http://localhost:8774/v2", "region": "RegionOne", "internalURL": "http://localhost:8774/v2", "publicURL": "http://localhost:8774/v2/", }, ], }, ], }, } correct_response = json.dumps(dict_correct_response) dict_responses = [ {"headers": {'location':'http://127.0.0.1:5001'}, "status_code": 305, "text": "Use proxy"}, # Configured on admin port, manila redirects to v2.0 port. # When trying to connect on it, keystone auth succeed by v1.0 # protocol (through headers) but tokens are being returned in # body (looks like keystone bug). Leaved for compatibility. {"headers": {}, "status_code": 200, "text": correct_response}, {"headers": {}, "status_code": 200, "text": correct_response} ] responses = [(utils.TestResponse(resp)) for resp in dict_responses] def side_effect(*args, **kwargs): return responses.pop(0) mock_request = mock.Mock(side_effect=side_effect) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data=json.dumps(body), allow_redirects=True, **self.TEST_REQUEST_BASE) resp = dict_correct_response endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) test_auth_call() def test_ambiguous_endpoints(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0", service_type='compute') resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID" }, }, "serviceCatalog": [ { "adminURL": "http://localhost:8774/v2", "type": "compute", "name": "Compute CLoud", "endpoints": [ { "region": "RegionOne", "internalURL": "http://localhost:8774/v2", "publicURL": "http://localhost:8774/v2/", }, ], }, { "adminURL": "http://localhost:8774/v2", "type": "compute", "name": "Hyper-compute Cloud", "endpoints": [ { "internalURL": "http://localhost:8774/v2", "publicURL": "http://localhost:8774/v2/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.AmbiguousEndpoints, cs.client.authenticate) test_auth_call() class AuthenticationTests(utils.TestCase): def test_authenticate_success(self): cs = client.Client("username", "password", "project_id", "auth_url") management_url = 'https://localhost/v2.1/443470' auth_response = utils.TestResponse({ 'status_code': 204, 'headers': { 'x-server-management-url': management_url, 'x-auth-token': '1b751d74-de0c-46ae-84f0-915744b582d1', }, }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'Accept': 'application/json', 'X-Auth-User': 'username', 'X-Auth-Key': 'password', 'X-Auth-Project-Id': 'project_id', 'User-Agent': cs.client.USER_AGENT } mock_request.assert_called_with( "GET", cs.client.auth_url, headers=headers, **self.TEST_REQUEST_BASE) self.assertEqual(cs.client.management_url, auth_response.headers['x-server-management-url']) self.assertEqual(cs.client.auth_token, auth_response.headers['x-auth-token']) test_auth_call() def test_authenticate_failure(self): cs = client.Client("username", "password", "project_id", "auth_url") auth_response = utils.TestResponse({"status_code": 401}) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.Unauthorized, cs.client.authenticate) test_auth_call() def test_auth_automatic(self): cs = client.Client("username", "password", "project_id", "auth_url") http_client = cs.client http_client.management_url = '' mock_request = mock.Mock(return_value=(None, None)) @mock.patch.object(http_client, 'request', mock_request) @mock.patch.object(http_client, 'authenticate') def test_auth_call(m): http_client.get('/') m.assert_called() mock_request.assert_called() test_auth_call() def test_auth_manual(self): cs = client.Client("username", "password", "project_id", "auth_url") @mock.patch.object(cs.client, 'authenticate') def test_auth_call(m): cs.authenticate() m.assert_called() test_auth_call() python-manilaclient-0.1~git20140208/tests/v1/test_quota_classes.py0000664000175000017500000000255712275236132024015 0ustar chuckchuck# Copyright 2013 OpenStack LLC. # 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 tests import utils from tests.v1 import fakes cs = fakes.FakeClient() class QuotaClassSetsTest(utils.TestCase): def test_class_quotas_get(self): class_name = 'test' cs.quota_classes.get(class_name) cs.assert_called('GET', '/os-quota-class-sets/%s' % class_name) def test_update_quota(self): q = cs.quota_classes.get('test') q.update(shares=2) cs.assert_called('PUT', '/os-quota-class-sets/test') def test_refresh_quota(self): q = cs.quota_classes.get('test') q2 = cs.quota_classes.get('test') self.assertEqual(q.shares, q2.shares) q2.shares = 0 self.assertNotEqual(q.shares, q2.shares) q2.get() self.assertEqual(q.shares, q2.shares) python-manilaclient-0.1~git20140208/tests/__init__.py0000664000175000017500000000000012275236132021276 0ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/test_base.py0000664000175000017500000000307612275236132021530 0ustar chuckchuckfrom manilaclient import base from manilaclient import exceptions from manilaclient.v1 import shares from tests import utils from tests.v1 import fakes cs = fakes.FakeClient() class BaseTest(utils.TestCase): def test_resource_repr(self): r = base.Resource(None, dict(foo="bar", baz="spam")) self.assertEqual(repr(r), "") def test_getid(self): self.assertEqual(base.getid(4), 4) class TmpObject(object): id = 4 self.assertEqual(base.getid(TmpObject), 4) def test_eq(self): # Two resources of the same type with the same id: equal r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertEqual(r1, r2) # Two resoruces of different types: never equal r1 = base.Resource(None, {'id': 1}) r2 = shares.Share(None, {'id': 1}) self.assertNotEqual(r1, r2) # Two resources with no ID: equal if their info is equal r1 = base.Resource(None, {'name': 'joe', 'age': 12}) r2 = base.Resource(None, {'name': 'joe', 'age': 12}) self.assertEqual(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') python-manilaclient-0.1~git20140208/tests/fakes.py0000664000175000017500000000417212275236132020646 0ustar chuckchuck""" 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. """ def assert_has_keys(dict, required=[], optional=[]): keys = dict.keys() for k in required: try: assert k in keys except AssertionError: extra_keys = set(keys).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: assert self.client.callstack[pos][2] == body def assert_called_anytime(self, method, url, body=None): """ 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 self.client.callstack = [] def clear_callstack(self): self.client.callstack = [] def authenticate(self): pass python-manilaclient-0.1~git20140208/tests/old/0000775000175000017500000000000012275236446017765 5ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/old/contrib/0000775000175000017500000000000012275236446021425 5ustar chuckchuckpython-manilaclient-0.1~git20140208/tests/old/contrib/__init__.py0000664000175000017500000000003112275236132023520 0ustar chuckchuck__author__ = 'vkostenko' python-manilaclient-0.1~git20140208/tests/old/contrib/test_list_extensions.py0000664000175000017500000000243412275236132026263 0ustar chuckchuck# # Copyright (c) 2013 OpenStack, LLC. # # # # 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 extension # from manilaclient.v2.contrib import list_extensions # from tests import utils # from tests.v1 import fakes # # # extensions = [ # extension.Extension(list_extensions.__name__.split(".")[-1], # list_extensions), # ] # cs = fakes.FakeClient(extensions=extensions) # # # class ListExtensionsTests(utils.TestCase): # def test_list_extensions(self): # all_exts = cs.list_extensions.show_all() # cs.assert_called('GET', '/extensions') # self.assertTrue(len(all_exts) > 0) # for r in all_exts: # self.assertTrue(len(r.summary) > 0) python-manilaclient-0.1~git20140208/tests/test_service_catalog.py0000664000175000017500000001123112275236132023740 0ustar chuckchuckfrom manilaclient import exceptions from manilaclient import service_catalog from tests import utils # Taken directly from keystone/content/common/samples/auth.json # Do not edit this structure. Instead, grab the latest from there. SERVICE_CATALOG = { "access": { "token": { "id": "ab48a9efdfedb23ty3494", "expires": "2010-11-01T03:32:15-05:00", "tenant": { "id": "345", "name": "My Project" } }, "user": { "id": "123", "name": "jqsmith", "roles": [ { "id": "234", "name": "compute:admin", }, { "id": "235", "name": "object-store:admin", "tenantId": "1", } ], "roles_links": [], }, "serviceCatalog": [ { "name": "Cloud Servers", "type": "compute", "endpoints": [ { "tenantId": "1", "publicURL": "https://compute1.host/v1/1234", "internalURL": "https://compute1.host/v1/1234", "region": "North", "versionId": "1.0", "versionInfo": "https://compute1.host/v1/", "versionList": "https://compute1.host/" }, { "tenantId": "2", "publicURL": "https://compute1.host/v1/3456", "internalURL": "https://compute1.host/v1/3456", "region": "North", "versionId": "1.1", "versionInfo": "https://compute1.host/v1/", "versionList": "https://compute1.host/" }, ], "endpoints_links": [], }, { "name": "Nova Shares", "type": "share", "endpoints": [ { "tenantId": "1", "publicURL": "https://share1.host/v1/1234", "internalURL": "https://share1.host/v1/1234", "region": "South", "versionId": "1.0", "versionInfo": "uri", "versionList": "uri" }, { "tenantId": "2", "publicURL": "https://share1.host/v1/3456", "internalURL": "https://share1.host/v1/3456", "region": "South", "versionId": "1.1", "versionInfo": "https://share1.host/v1/", "versionList": "https://share1.host/" }, ], "endpoints_links": [ { "rel": "next", "href": "https://identity1.host/v2.0/endpoints" }, ], }, ], "serviceCatalog_links": [ { "rel": "next", "href": "https://identity.host/v2.0/endpoints?session=2hfh8Ar", }, ], }, } class ServiceCatalogTest(utils.TestCase): def test_building_a_service_catalog(self): sc = service_catalog.ServiceCatalog(SERVICE_CATALOG) self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for, service_type='compute') self.assertEquals(sc.url_for('tenantId', '1', service_type='compute'), "https://compute1.host/v1/1234") self.assertEquals(sc.url_for('tenantId', '2', service_type='compute'), "https://compute1.host/v1/3456") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "South", service_type='compute') def test_alternate_service_type(self): sc = service_catalog.ServiceCatalog(SERVICE_CATALOG) self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for, service_type='share') self.assertEquals(sc.url_for('tenantId', '1', service_type='share'), "https://share1.host/v1/1234") self.assertEquals(sc.url_for('tenantId', '2', service_type='share'), "https://share1.host/v1/3456") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "North", service_type='share') python-manilaclient-0.1~git20140208/tests/test_http.py0000664000175000017500000001353512275236132021576 0ustar chuckchuckimport mock import requests from manilaclient import client from manilaclient import exceptions from tests import utils 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)) def get_client(retries=0): cl = client.HTTPClient("username", "password", "project_id", "auth_test", retries=retries) return cl def get_authed_client(retries=0): cl = get_client(retries=retries) cl.management_url = "http://example.com" cl.auth_token = "token" return cl class ClientTest(utils.TestCase): 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", "X-Auth-Project-Id": "project_id", "User-Agent": cl.USER_AGENT, '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_reauth_0_retries(self): cl = get_authed_client(retries=0) self.requests = [bad_401_request, mock_request] def request(*args, **kwargs): next_request = self.requests.pop(0) return next_request(*args, **kwargs) def reauth(): cl.management_url = "http://example.com" cl.auth_token = "token" @mock.patch.object(cl, 'authenticate', reauth) @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_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_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", "X-Auth-Project-Id": "project_id", "Content-Type": "application/json", 'Accept': 'application/json', "User-Agent": cl.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() def test_auth_failure(self): cl = get_client() # response must not have x-server-management-url header @mock.patch.object(requests, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.AuthorizationFailure, cl.authenticate) test_auth_call() python-manilaclient-0.1~git20140208/tests/test_client.py0000664000175000017500000000072412275236132022071 0ustar chuckchuck import manilaclient.client import manilaclient.v1.client from tests import utils class ClientTest(utils.TestCase): def test_get_client_class_v1(self): output = manilaclient.client.get_client_class('1') self.assertEqual(output, manilaclient.v1.client.Client) def test_get_client_class_unknown(self): self.assertRaises(manilaclient.exceptions.UnsupportedVersion, manilaclient.client.get_client_class, '0') python-manilaclient-0.1~git20140208/tox.ini0000664000175000017500000000103212275236132017344 0ustar chuckchuck[tox] envlist = py26,py27,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C deps = -r{toxinidir}/tools/pip-requires -r{toxinidir}/tools/test-requires commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] deps = pep8 commands = pep8 --repeat --show-source manilaclient setup.py [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip python-manilaclient-0.1~git20140208/setup.cfg0000664000175000017500000000031312275236446017663 0ustar chuckchuck[build_sphinx] all_files = 1 source-dir = doc/source build-dir = doc/build [upload_sphinx] upload-dir = doc/build/html [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-manilaclient-0.1~git20140208/python_manilaclient.egg-info/0000775000175000017500000000000012275236446023600 5ustar chuckchuckpython-manilaclient-0.1~git20140208/python_manilaclient.egg-info/top_level.txt0000664000175000017500000000001512275236445026325 0ustar chuckchuckmanilaclient python-manilaclient-0.1~git20140208/python_manilaclient.egg-info/entry_points.txt0000664000175000017500000000006412275236445027075 0ustar chuckchuck[console_scripts] manila = manilaclient.shell:main python-manilaclient-0.1~git20140208/python_manilaclient.egg-info/requires.txt0000664000175000017500000000006512275236445026200 0ustar chuckchuckPrettyTable>=0.6,<0.8 requests>=1.1 simplejson>=2.0.9python-manilaclient-0.1~git20140208/python_manilaclient.egg-info/SOURCES.txt0000664000175000017500000000361712275236446025473 0ustar chuckchuck.testr.conf AUTHORS ChangeLog HACKING LICENSE MANIFEST.in README.rst __init__.py openstack-common.conf run_tests.sh setup.cfg setup.py tox.ini doc/.gitignore doc/Makefile doc/source/conf.py doc/source/index.rst doc/source/shell.rst manilaclient/__init__.py manilaclient/base.py manilaclient/client.py manilaclient/exceptions.py manilaclient/extension.py manilaclient/service_catalog.py manilaclient/shell.py manilaclient/utils.py manilaclient/openstack/__init__.py manilaclient/openstack/common/__init__.py manilaclient/openstack/common/setup.py manilaclient/openstack/common/strutils.py manilaclient/openstack/common/version.py manilaclient/v1/__init__.py manilaclient/v1/client.py manilaclient/v1/limits.py manilaclient/v1/quota_classes.py manilaclient/v1/quotas.py manilaclient/v1/security_services.py manilaclient/v1/share_networks.py manilaclient/v1/share_snapshots.py manilaclient/v1/shares.py manilaclient/v1/shell.py manilaclient/v1/contrib/__init__.py manilaclient/v1/contrib/list_extensions.py python_manilaclient.egg-info/PKG-INFO python_manilaclient.egg-info/SOURCES.txt python_manilaclient.egg-info/dependency_links.txt python_manilaclient.egg-info/entry_points.txt python_manilaclient.egg-info/requires.txt python_manilaclient.egg-info/top_level.txt tests/__init__.py tests/fakes.py tests/test_base.py tests/test_client.py tests/test_http.py tests/test_service_catalog.py tests/test_shell.py tests/test_utils.py tests/utils.py tests/old/contrib/__init__.py tests/old/contrib/test_list_extensions.py tests/v1/__init__.py tests/v1/fake_clients.py tests/v1/fakes.py tests/v1/test_auth.py tests/v1/test_quota_classes.py tests/v1/test_quotas.py tests/v1/test_security_services.py tests/v1/test_share_networks.py tests/v1/test_share_snapshots.py tests/v1/test_shares.py tests/v1/test_shell.py tools/cinder.bash_completion tools/generate_authors.sh tools/install_venv.py tools/pip-requires tools/test-requires tools/with_venv.shpython-manilaclient-0.1~git20140208/python_manilaclient.egg-info/PKG-INFO0000664000175000017500000002063512275236445024702 0ustar chuckchuckMetadata-Version: 1.1 Name: python-manilaclient Version: 2013.3-dev Summary: Client library for OpenStack Manila API. Home-page: https://github.com/openstack/python-manilaclient Author: OpenStack Contributors Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description: Python bindings to the OpenStack Manila API =========================================== 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: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://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: http://wiki.openstack.org/GerritWorkflow This code 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. __ http://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=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_VOLUME_API_VERSION=1 If you are using Keystone, you need to set the MANILA_URL to the keystone endpoint:: 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``:: usage: manila [--debug] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-volume-api-version ] [--os-cacert ] [--retries ] ... Command-line interface to the OpenStack Manila API. Positional arguments: absolute-limits Print a list of absolute limits for a user create Add a new volume. credentials Show user credentials returned from auth delete Remove a volume. endpoints Discover endpoints that get returned from the authenticate services extra-specs-list Print a list of current 'volume types and extra specs' (Admin Only). list List all the volumes. quota-class-show List the quotas for a quota class. quota-class-update Update the quotas for a quota class. quota-defaults List the default quotas for a tenant. quota-show List the quotas for a tenant. quota-update Update the quotas for a tenant. rate-limits Print a list of rate limits for a user rename Rename a volume. show Show details about a volume. snapshot-create Add a new snapshot. snapshot-delete Remove a snapshot. snapshot-list List all the snapshots. snapshot-rename Rename a snapshot. snapshot-show Show details about a snapshot. type-create Create a new volume type. type-delete Delete a specific volume type type-key Set or unset extra_spec for a volume type. type-list Print a list of available 'volume types'. bash-completion Prints all of the commands and options to stdout so that the help Display help about this program or one of its subcommands. list-extensions List all the os-api extensions that are available. Optional arguments: --debug Print debugging output --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --service-type Defaults to compute for most actions --service-name Defaults to env[MANILA_SERVICE_NAME] --volume-service-name Defaults to env[MANILA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[MANILA_ENDPOINT_TYPE] or publicURL. --os-volume-api-version Accepts 1,defaults to env[OS_VOLUME_API_VERSION]. --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --retries Number of retries. 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="volume") >>> nt.volumes.list() [...] Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python python-manilaclient-0.1~git20140208/python_manilaclient.egg-info/dependency_links.txt0000664000175000017500000000000112275236445027645 0ustar chuckchuck python-manilaclient-0.1~git20140208/MANIFEST.in0000664000175000017500000000011012275236132017563 0ustar chuckchuckinclude AUTHORS include ChangeLog exclude .gitignore exclude .gitreview python-manilaclient-0.1~git20140208/AUTHORS0000664000175000017500000000047112275236445017116 0ustar chuckchuck119Vik Aleks Chirko Andrei V. Ostapenko Ben Swartzlander bswartz Sascha Peilicke Shane Wang vik Yulia Portnova python-manilaclient-0.1~git20140208/ChangeLog0000664000175000017500000003156512275236445017630 0ustar chuckchuckcommit af7cfcf34aae52dd05162d20d59b4904760b7d2c Author: Shane Wang Date: Fri Feb 7 13:37:34 2014 +0800 Fix misspellings in python manilaclient Fix misspellings detected by: * pip install misspellings * git ls-files | grep -v locale | misspellings -f - Change-Id: I4dc676e82052ac49b2021431ec03bcb6f7c9758e Closes-Bug: #1257295 commit 4478daa80356726d302dd9399c9ad739a8fee957 Merge: e7b5f18 ae0c15c Author: Jenkins Date: Thu Jan 23 12:39:06 2014 +0000 Merge "Adds an ability to delete multiple shares at once" commit e7b5f18a986f46767951cf2ebfc11aec28fa307c Author: Aleks Chirko Date: Mon Dec 2 18:02:48 2013 +0200 Add client API for share-networks and security-services. Partially implements bp: join-tenant-network Change-Id: I964240d8041a90446c16f879447e6bec85a45b35 commit ae0c15c3ca28aab0341867fb2e2db06e8c3535ec Author: Andrei V. Ostapenko Date: Mon Jan 13 01:41:35 2014 +0200 Adds an ability to delete multiple shares at once Change-Id: Ic744fcd2910c48ccc6031de652001112ef46c984 commit a7a1d9758beb2deaa31ae5d6660f72cf9d65893d Author: Andrei V. Ostapenko Date: Thu Dec 19 17:12:41 2013 +0200 Adds snapshot-reset-state command Partially implements: bp manila-client-enhancements Change-Id: I7be36be35a5eabe38fb4f03d69d89cede75345ab commit f091fa4d99a1ac36f563862bb4a08377084b9bd2 Merge: 235493e ebad118 Author: Jenkins Date: Tue Dec 24 12:04:53 2013 +0000 Merge "Fixes bug with checking length of sid access arg" commit ebad1188d0273869fd6f42eb8996b4689f0ffd49 Author: Andrei V. Ostapenko Date: Wed Dec 18 16:48:53 2013 +0200 Fixes bug with checking length of sid access arg Fixes: bug 1262219 Change-Id: I39a8c1a4f6cd4e6a0e03a5e731dc7f724de75b82 commit 235493e6166c262aa210f6d0d6bfe66084872e67 Author: Andrei V. Ostapenko Date: Wed Dec 18 18:42:04 2013 +0200 Adds reset-state command Partially implements: bp manila-client-enhancements Change-Id: I0221f3d6a7c0271f69e9657b6c24c47b0c7b151b commit b3879c2558fc1a71a4eadf0f4d49e63663d6510b Author: Andrei V. Ostapenko Date: Wed Dec 18 12:59:26 2013 +0200 Adds args check on share and snapshot rename Adds check if at least one argument to change is supplied Fixes: bug 1262135 Change-Id: Ief7c35cf1b1044a0c35bbb7254366582b2e2b26d commit ee545c433558d677fa3c645e4d396e5ad872b8e5 Author: Andrei V. Ostapenko Date: Tue Dec 17 17:20:31 2013 +0200 Removes support of passing ip address using asterisk Change-Id: I9599ec98a426374cde5d3dcf9d9c6d0b2dca57d2 commit b395be4d19c09848eb405893fd50e7a50619acca Merge: 0b55515 5cfef3d Author: Jenkins Date: Wed Dec 18 10:22:12 2013 +0000 Merge "Adds an ability to pass share and snapshot names to commands" commit 0b55515f2a873a94c3268834d5d38d7a50a3adb4 Merge: 7751f84 4d0604b Author: Jenkins Date: Wed Dec 18 10:21:06 2013 +0000 Merge "Adds ip prefix validation in allow-access" commit 7751f84a0e8939027160aba43b0c1b3fbf436d67 Merge: 17a29bd d4331d6 Author: Jenkins Date: Wed Dec 18 10:14:08 2013 +0000 Merge "Renames access management commands" commit 5cfef3dcce6adec942596bcfe2ed9668af0ada33 Author: Andrei V. Ostapenko Date: Tue Dec 17 19:02:46 2013 +0200 Adds an ability to pass share and snapshot names to commands Partially implements: bp manila-client-enhancements Change-Id: Id14a504d894032ce32b16092278813f35dd7ffba commit d4331d6ea56b39e2ff2e27e3529614e0dfe4b0ec Author: Andrei V. Ostapenko Date: Tue Dec 17 17:39:00 2013 +0200 Renames access management commands Renames 'allow-access' to 'access-allow' Renames 'deny-access' to 'access-deny' Partially implements: bp manila-client-enhancements Change-Id: Ief56733d6173926ebfe6c319d443b8ed9e581c76 commit 4d0604b6616d3a6e0992d7f86521f97d36386580 Author: Andrei V. Ostapenko Date: Tue Dec 17 16:42:33 2013 +0200 Adds ip prefix validation in allow-access Partially implements: bp improve-acl-functionality Change-Id: I8d3aea69f7353e0078f0d9a0067a28a14b2449f9 commit 17a29bd38c052b80ce1dd158ae3c9f2c6425f0bf Author: Andrei V. Ostapenko Date: Tue Dec 17 13:47:46 2013 +0200 Rename 'passwd' access type to 'sid' Change-Id: I31e0c6678591a2a64da8276f400f621167a2abc3 commit e7f189285ed24aa063575aee8b66bf7c3efbc9a6 Author: Andrei V. Ostapenko Date: Fri Dec 6 14:35:03 2013 +0200 Changes logic of retrieving share metadata Fixes bug 1258505 Change-Id: Ia190150ea861e0696845ac65cea8ada1d94d5239 commit 7599f15b363dc636ec9c491aeaace6e5f00749b9 Author: Andrei V. Ostapenko Date: Thu Nov 28 16:06:25 2013 +0200 Adds new API for share metadata operating Adds command 'metadata' for setting and unsetting metadata keys. Adds command 'metadata-update-all' for updating all metadata. Adds command 'metadata-show' for viewing share metadata. Adds key '--metadata' to command 'create' Partially implements bp manila-client-enhancements Change-Id: I10d20e12e71065d6e40be062715fa01998828ab2 commit e0be9fa65cb58751281c532bfa33ed837bfb8e5e Author: Sascha Peilicke Date: Tue Nov 19 11:25:48 2013 +0100 Support building wheels (PEP-427) With that, building and uploading wheels to PyPI is only one "python setup.py bdist_wheel" away. Change-Id: I0627e2afcc7af27cff9f268d42e07194ba370d85 commit e5562354e4b64c57dd66537755aa585c2af75ac4 Author: Andrei V. Ostapenko Date: Fri Oct 11 18:09:52 2013 +0300 Added support of per-tenant-user quotas Added new keys '--user' in v1.shell to quota-show, quota-update. Added command quota-delete, that provides ability for admins to be able to delete a non-default quota (absolute limit) for a tenant or user, so that tenant's quota will revert back to the configured default. Added 'responce_key' parameter to Manager._update function Added '--force' parameter to quota-update to support new functionality on server Fixes help output, when in 'manila help' was displayed only first string of command documentation Implement bp: user-quota-support Change-Id: Ibfe9ccf83ea43d5af6652342f1fe6a88c78a0364 commit c6fa2fcf4b577612713f606d2337cb3c84f098f8 Merge: bcb5ba0 eae1a99 Author: Jenkins Date: Thu Oct 10 08:50:23 2013 +0000 Merge "Removes deprecated global opts from python-manilaclient/shell.py" commit bcb5ba08a2a1e317442677ef2673499bab6e1ce1 Author: Andrei V. Ostapenko Date: Tue Oct 8 20:27:45 2013 +0300 Fixes using quota-show with default tenant id Closes-Bug: #1234182 Change-Id: I0ce502e96693c9892dea87988f9435dd522ddcc1 commit eae1a991c99df032dbf579ac40ff995434997d47 Author: Andrei V. Ostapenko Date: Tue Oct 8 23:33:02 2013 +0300 Removes deprecated global opts from python-manilaclient/shell.py Closes-Bug: #1237407 Change-Id: If4054d4363c5ead5a707b9b17b194c26399c0be0 commit d04a9595051bac81873b8483421087c554239347 Merge: 8075739 c82cfc8 Author: Jenkins Date: Fri Sep 27 15:15:53 2013 +0000 Merge "Rename functionality implementation for manila client." commit 8075739f3382088407d2b27362571ab633b00299 Author: 119Vik Date: Wed Sep 25 06:05:17 2013 -0400 Default tenant id for quota_defaults. For now manila quota_defaults can't be called without explisit specificaton of tenant id. To fix this issue shell py was modified with changes for do_quota_defaults that implemets default value for tenant id. Change-Id: Ic44929f7941d1fb8c6626295853f56f8a561215b commit c82cfc8a61b96ffef5b1996b22890f365c164475 Author: 119Vik Date: Wed Sep 25 10:09:56 2013 -0400 Rename functionality implementation for manila client. Currently manila api has no rename functionality for shares and snapshots. Rename functionality added to manilaclient and covered with unittests. Change-Id: Iad2424a9c31985d4f7846de0540779458cb2f088 commit 0805ebb2d458ac891d00b2a982a4de37c216664c Author: Yulia Portnova Date: Mon Sep 23 14:45:16 2013 +0300 Add .gitreview file. Change-Id: Icb26fcff613bc122d29baad6fc6f66a5fb8f44ab commit 382ce9afff472f9b167650781cdebd5128d5459f Author: Yulia Portnova Date: Wed Sep 18 15:25:53 2013 +0300 fixed pep8 commit 95c64adea34cd24b5bf61a2222899cc9595dba39 Merge: a380863 d20a98e Author: Yulia Portnova Date: Tue Sep 17 16:41:56 2013 +0300 merged cinder-to-manila commit d20a98e6926a3c313830e1e9bd68b6a0505c1ad3 Author: Yulia Portnova Date: Tue Sep 17 16:22:28 2013 +0300 access allow test fixed commit 2b9993e548f5812a2764353906f49a3e55144054 Author: Yulia Portnova Date: Thu Sep 12 15:36:59 2013 +0300 share create empty body fix commit f957c15ad25b9c28971fa8d7d471e7b368b32e31 Author: 119Vik Date: Thu Sep 12 10:24:37 2013 +0300 print list fixed commit ae4c9266cd474de95cb6701d6aaeabbdb2f1e8da Merge: 9a3e8e1 cfdd9b3 Author: 119Vik Date: Thu Sep 12 09:48:20 2013 +0300 Merge branch 'manila-cleanup' into cinder-to-manila commit cfdd9b348035d3b1099bb2a176b178e91306c50b Author: 119Vik Date: Wed Sep 11 12:15:29 2013 +0300 fix for package version commit d6204d90bb3f348c1b228191864c6ccfc2cc1a24 Author: 119Vik Date: Tue Sep 10 09:09:53 2013 -0400 added testr conf. pep8 tests fixed commit cf1873ba435171d6b40ba26f2be44865c894454e Author: 119Vik Date: Tue Sep 10 08:11:02 2013 -0400 fixed tests commit 60ae92833b7237332b07f1fb5620a857de8d7a51 Author: 119Vik Date: Tue Sep 10 04:05:45 2013 -0400 log files added to gitignore commit 175d76fa223393305afb390bd637c2995e58d9cb Author: 119Vik Date: Tue Sep 10 04:05:04 2013 -0400 cleaned log files commit 8749dbca442b93a68bdb9dc7c14c0fc7012fa2e0 Author: 119Vik Date: Tue Sep 10 03:52:59 2013 -0400 cleaned testfiles and pyc files commit bcc917973f12a47a73337752fa3da7c082f1f314 Author: 119Vik Date: Tue Sep 10 03:51:16 2013 -0400 env folders removed commit db3cf9f46b93c5011e0aa75bfd59ca8e7a618192 Author: 119Vik Date: Tue Sep 10 03:48:12 2013 -0400 removed disabled volume functionality commit 041bfa455cfcd379aad9d1f406460f3e3a58124a Author: vik Date: Tue Sep 10 03:26:11 2013 -0400 cleaned client with fixed tests. no quotas functionality implemented commit 9a3e8e15af4de98f1c6078dfeea7bd2db18d8146 Author: Yulia Portnova Date: Thu Sep 5 13:45:47 2013 +0300 renamed service_type to share commit e69f2bea627a8ef57156a34dcd09a65ceb458831 Author: Yulia Portnova Date: Thu Sep 5 13:45:16 2013 +0300 deleted volume v1 commit 77dffa2c99ab1dd08eef6c465bd9fcc8a8e0c6a0 Author: Yulia Portnova Date: Thu Sep 5 13:32:38 2013 +0300 mass replace cinder to manila commit 2d2bedf6b0bdbcc9a28c75189f1ff2bf2af779e9 Author: Yulia Portnova Date: Thu Sep 5 13:12:56 2013 +0300 fixed service type commit a3808636b0d91cfc82245e13b26fec80f7459d93 Author: Yulia Portnova Date: Thu Sep 5 13:12:56 2013 +0300 fixed service type commit fa125b77147f31fafde719075150bc267ce82286 Author: Yulia Portnova Date: Tue Sep 3 14:37:34 2013 +0300 setup.py fixed commit 1702374292af47a9f82f44bed1f82745878ada38 Author: Yulia Portnova Date: Tue Sep 3 14:36:25 2013 +0300 setup.py fixed commit c476d1c5936b400e4524f4db5c228c207863a760 Author: Yulia Portnova Date: Tue Sep 3 14:33:50 2013 +0300 setup.py fixed commit 7c1a8bcbefea5fa30ee24bc61588f3d4f6a7d5fb Author: Yulia Portnova Date: Tue Sep 3 14:28:38 2013 +0300 cinderclient to manilaclient commit 5e9ada9fc68c175da007a92e6d8d307df3315326 Author: Ben Swartzlander Date: Mon Sep 2 23:42:41 2013 -0400 Initial checkin commit 24be39395d5df7e1642bb3b98a42e60221c96f99 Author: bswartz Date: Mon Sep 2 20:40:07 2013 -0700 Initial commit