GBP Client repo init
Adding essential code artifacts for bootstrapping GBP client repo. Change-Id: I3e09ed06a0cf719ccfeacb240829900da17d9f65
This commit is contained in:
parent
4e0fcf16ab
commit
2b08b68478
7
.coveragerc
Normal file
7
.coveragerc
Normal file
@ -0,0 +1,7 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = gbpclient
|
||||
omit = gbpclient/openstack/*,gbpclient/tests/*
|
||||
|
||||
[report]
|
||||
ignore-errors = True
|
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
*.pyc
|
||||
*.DS_Store
|
||||
*.egg
|
||||
*.sw?
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
build/*
|
||||
build-stamp
|
||||
cover/*
|
||||
doc/build/
|
||||
doc/source/api/
|
||||
python_group_based_policy_client.egg-info/*
|
||||
gbp/vcsversion.py
|
||||
gbpclient/versioninfo
|
||||
run_tests.err.log
|
||||
run_tests.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.testrepository/
|
||||
.tox/
|
||||
.venv/
|
39
.pylintrc
Normal file
39
.pylintrc
Normal file
@ -0,0 +1,39 @@
|
||||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
[MASTER]
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=test
|
||||
|
||||
[Messages Control]
|
||||
# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
|
||||
# C0111: Don't require docstrings on every method
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable=C0111,W0511,W0142,W0622
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowecased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[Design]
|
||||
max-public-methods=100
|
||||
min-public-methods=0
|
||||
max-args=6
|
||||
|
||||
[Variables]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
4
.testr.conf
Normal file
4
.testr.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
16
CONTRIBUTING.rst
Normal file
16
CONTRIBUTING.rst
Normal file
@ -0,0 +1,16 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps documented at:
|
||||
|
||||
https://wiki.openstack.org/wiki/How_To_Contribute#If_you.27re_a_developer
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
https://wiki.openstack.org/GerritWorkflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/python-group-based-policy-client
|
6
MANIFEST.in
Normal file
6
MANIFEST.in
Normal file
@ -0,0 +1,6 @@
|
||||
include tox.ini
|
||||
include LICENSE README.rst HACKING.rst
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include tools/*
|
||||
recursive-include tests *
|
1
README.rst
Normal file
1
README.rst
Normal file
@ -0,0 +1 @@
|
||||
This is the client API library for Group Based Policy.
|
52
doc/source/conf.py
Normal file
52
doc/source/conf.py
Normal file
@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
project = 'python-group-based-policy-client'
|
||||
|
||||
# -- 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', 'oslosphinx']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
copyright = u'OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme = 'nature'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
41
doc/source/index.rst
Normal file
41
doc/source/index.rst
Normal file
@ -0,0 +1,41 @@
|
||||
Python bindings to the Group Based Policy API
|
||||
=============================================
|
||||
|
||||
In order to use the python group-based-policy- client 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::
|
||||
|
||||
>>> import logging
|
||||
>>> from gbpclient.gbp import client
|
||||
>>> logging.basicConfig(level=logging.DEBUG)
|
||||
>>> gbp = client.Client('2.0', endpoint_url=OS_URL, token=OS_TOKEN)
|
||||
>>> gbp.format = 'json'
|
||||
>>> ptg = {'name': 'my_ptg'}
|
||||
>>> gbp.create_policy_target_group({'policy_target_group':ptg})
|
||||
>>> policy_target_groups = gbp.list_policy_target_groups(name='my_ptg')
|
||||
>>> print policy_target_groups
|
||||
>>> ptg_id = policy_target_groups['policy_target_groups'][0]['id']
|
||||
>>> gbp.delete_policy_target_group(ptg_id)
|
||||
|
||||
|
||||
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-name``, and ``--os-auth-url``) or set them in environment variables::
|
||||
|
||||
export OS_USERNAME=user
|
||||
export OS_PASSWORD=pass
|
||||
export OS_TENANT_NAME=tenant
|
||||
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
|
||||
The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can alternatively set these environment variables::
|
||||
|
||||
export OS_URL=http://neutron.example.org:9696/
|
||||
export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
|
||||
|
||||
If neutron server does not require authentication, besides these two arguments or environment variables (We can use any value as token.), we need manually supply ``--os-auth-strategy`` or set the environment variable::
|
||||
|
||||
export OS_AUTH_STRATEGY=noauth
|
||||
|
||||
Once you've configured your authentication parameters, you can run ``gbp -h`` to see a complete listing of available commands.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
51
gbp_test.sh
Executable file
51
gbp_test.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
function die() {
|
||||
local exitcode=$?
|
||||
set +o xtrace
|
||||
echo $@
|
||||
cleanup
|
||||
exit $exitcode
|
||||
}
|
||||
|
||||
ptg_name=myptg1
|
||||
## TODO Sumit: Test for other resources as well after renaming
|
||||
function cleanup() {
|
||||
echo Removing test ptg...
|
||||
gbp endpointgroup-delete ptg_name
|
||||
}
|
||||
|
||||
noauth_tenant_id=me
|
||||
if [ "$1" == "noauth" ]; then
|
||||
NOAUTH="--tenant_id $noauth_tenant_id"
|
||||
else
|
||||
NOAUTH=
|
||||
fi
|
||||
|
||||
echo "NOTE: User should be admin in order to perform all operations."
|
||||
sleep 3
|
||||
|
||||
FORMAT=" --request-format xml"
|
||||
|
||||
# test the CRUD of network
|
||||
ptg=$ptg_name
|
||||
gbp endpointgroup-create $FORMAT $NOAUTH $ptg || die "fail to create ptg $ptg"
|
||||
temp=`gbp endpointgroup-list $FORMAT -- --name $ptg --fields id | wc -l`
|
||||
echo $temp
|
||||
if [ $temp -ne 5 ]; then
|
||||
die "PTGs with name $ptg is not unique or found"
|
||||
fi
|
||||
ptg_id=`gbp gbp-list -- --name $ptg --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
|
||||
echo "ID of PTG with name $ptg is $ptg_id"
|
||||
|
||||
gbp endpointgroup-show $FORMAT $ptg || die "fail to show PTG $ptg"
|
||||
gbp endpointgroup-show $FORMAT $ptg_id || die "fail to show PTG $ptg_id"
|
||||
|
||||
gbp endpointgroup-update $FORMAT $ptg --description "desc" || die "fail to update PTG $ptg"
|
||||
gbp endpointgroup-update $FORMAT $ptg_id --description "new" || die "fail to update PTG $ptg_id"
|
||||
|
||||
gbp endpointgroup-list $FORMAT -c id -- --id fakeid || die "fail to list PTGs with column selection on empty list"
|
||||
|
||||
cleanup
|
||||
echo "Success! :)"
|
||||
|
0
gbpclient/__init__.py
Normal file
0
gbpclient/__init__.py
Normal file
0
gbpclient/tests/__init__.py
Normal file
0
gbpclient/tests/__init__.py
Normal file
0
gbpclient/tests/unit/__init__.py
Normal file
0
gbpclient/tests/unit/__init__.py
Normal file
567
gbpclient/tests/unit/test_auth.py
Normal file
567
gbpclient/tests/unit/test_auth.py
Normal file
@ -0,0 +1,567 @@
|
||||
# Copyright 2012 NEC Corporation
|
||||
# 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 uuid
|
||||
|
||||
import fixtures
|
||||
import httpretty
|
||||
from mox3 import mox
|
||||
import requests
|
||||
import six
|
||||
import testtools
|
||||
|
||||
from keystoneclient.auth.identity import v2 as ks_v2_auth
|
||||
from keystoneclient.auth.identity import v3 as ks_v3_auth
|
||||
from keystoneclient import exceptions as ks_exceptions
|
||||
from keystoneclient.fixture import v2 as ks_v2_fixture
|
||||
from keystoneclient.fixture import v3 as ks_v3_fixture
|
||||
from keystoneclient import session
|
||||
|
||||
from neutronclient import client
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.openstack.common import jsonutils
|
||||
|
||||
|
||||
USERNAME = 'testuser'
|
||||
USER_ID = 'testuser_id'
|
||||
TENANT_NAME = 'testtenant'
|
||||
TENANT_ID = 'testtenant_id'
|
||||
PASSWORD = 'password'
|
||||
ENDPOINT_URL = 'localurl'
|
||||
PUBLIC_ENDPOINT_URL = 'public_%s' % ENDPOINT_URL
|
||||
ADMIN_ENDPOINT_URL = 'admin_%s' % ENDPOINT_URL
|
||||
INTERNAL_ENDPOINT_URL = 'internal_%s' % ENDPOINT_URL
|
||||
ENDPOINT_OVERRIDE = 'otherurl'
|
||||
TOKEN = 'tokentoken'
|
||||
TOKENID = uuid.uuid4().hex
|
||||
REGION = 'RegionOne'
|
||||
NOAUTH = 'noauth'
|
||||
|
||||
KS_TOKEN_RESULT = {
|
||||
'access': {
|
||||
'token': {'id': TOKEN,
|
||||
'expires': '2012-08-11T07:49:01Z',
|
||||
'tenant': {'id': str(uuid.uuid1())}},
|
||||
'user': {'id': str(uuid.uuid1())},
|
||||
'serviceCatalog': [
|
||||
{'endpoints_links': [],
|
||||
'endpoints': [{'adminURL': ENDPOINT_URL,
|
||||
'internalURL': ENDPOINT_URL,
|
||||
'publicURL': ENDPOINT_URL,
|
||||
'region': REGION}],
|
||||
'type': 'network',
|
||||
'name': 'Neutron Service'}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
ENDPOINTS_RESULT = {
|
||||
'endpoints': [{
|
||||
'type': 'network',
|
||||
'name': 'Neutron Service',
|
||||
'region': REGION,
|
||||
'adminURL': ENDPOINT_URL,
|
||||
'internalURL': ENDPOINT_URL,
|
||||
'publicURL': ENDPOINT_URL
|
||||
}]
|
||||
}
|
||||
|
||||
BASE_HOST = 'http://keystone.example.com'
|
||||
BASE_URL = "%s:5000/" % BASE_HOST
|
||||
UPDATED = '2013-03-06T00:00:00Z'
|
||||
|
||||
# FIXME (bklei): A future release of keystoneclient will support
|
||||
# a discovery fixture which can replace these constants and clean
|
||||
# this up.
|
||||
V2_URL = "%sv2.0" % BASE_URL
|
||||
V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/'
|
||||
'openstack-identity-service/2.0/content/',
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html'}
|
||||
|
||||
V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident'
|
||||
'ity-service/2.0/identity-dev-guide-2.0.pdf',
|
||||
'rel': 'describedby',
|
||||
'type': 'application/pdf'}
|
||||
|
||||
V2_VERSION = {'id': 'v2.0',
|
||||
'links': [{'href': V2_URL, 'rel': 'self'},
|
||||
V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
|
||||
V3_URL = "%sv3" % BASE_URL
|
||||
V3_MEDIA_TYPES = [{'base': 'application/json',
|
||||
'type': 'application/vnd.openstack.identity-v3+json'},
|
||||
{'base': 'application/xml',
|
||||
'type': 'application/vnd.openstack.identity-v3+xml'}]
|
||||
|
||||
V3_VERSION = {'id': 'v3.0',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
|
||||
|
||||
def _create_version_entry(version):
|
||||
return jsonutils.dumps({'version': version})
|
||||
|
||||
|
||||
def _create_version_list(versions):
|
||||
return jsonutils.dumps({'versions': {'values': versions}})
|
||||
|
||||
|
||||
V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION])
|
||||
V3_VERSION_ENTRY = _create_version_entry(V3_VERSION)
|
||||
V2_VERSION_ENTRY = _create_version_entry(V2_VERSION)
|
||||
|
||||
|
||||
def get_response(status_code, headers=None):
|
||||
response = mox.Mox().CreateMock(requests.Response)
|
||||
response.headers = headers or {}
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
||||
|
||||
def setup_keystone_v2():
|
||||
v2_token = ks_v2_fixture.Token(token_id=TOKENID)
|
||||
service = v2_token.add_service('network')
|
||||
service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION)
|
||||
|
||||
httpretty.register_uri(httpretty.POST,
|
||||
'%s/tokens' % (V2_URL),
|
||||
body=json.dumps(v2_token))
|
||||
|
||||
auth_session = session.Session()
|
||||
auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx')
|
||||
return auth_session, auth_plugin
|
||||
|
||||
|
||||
def setup_keystone_v3():
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
V3_URL,
|
||||
body=V3_VERSION_ENTRY)
|
||||
|
||||
v3_token = ks_v3_fixture.Token()
|
||||
service = v3_token.add_service('network')
|
||||
service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL,
|
||||
admin=ADMIN_ENDPOINT_URL,
|
||||
internal=INTERNAL_ENDPOINT_URL,
|
||||
region=REGION)
|
||||
|
||||
httpretty.register_uri(httpretty.POST,
|
||||
'%s/auth/tokens' % (V3_URL),
|
||||
body=json.dumps(v3_token),
|
||||
adding_headers={'X-Subject-Token': TOKENID})
|
||||
|
||||
auth_session = session.Session()
|
||||
auth_plugin = ks_v3_auth.Password(V3_URL,
|
||||
username='xx',
|
||||
user_id='xx',
|
||||
user_domain_name='xx',
|
||||
user_domain_id='xx')
|
||||
return auth_session, auth_plugin
|
||||
|
||||
|
||||
AUTH_URL = V2_URL
|
||||
|
||||
|
||||
class CLITestAuthNoAuth(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthNoAuth, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.client = client.HTTPClient(username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
endpoint_url=ENDPOINT_URL,
|
||||
auth_strategy=NOAUTH,
|
||||
region_name=REGION)
|
||||
self.addCleanup(self.mox.VerifyAll)
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
def test_get_noauth(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.IsA(dict),
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.client.do_request('/resource', 'GET')
|
||||
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
|
||||
|
||||
|
||||
class CLITestAuthKeystone(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystone, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
|
||||
for var in ('http_proxy', 'HTTP_PROXY'):
|
||||
self.useFixture(fixtures.EnvironmentVariableFixture(var))
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
self.addCleanup(self.mox.VerifyAll)
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
def test_reused_token_get_auth_info(self):
|
||||
"""Test that Client.get_auth_info() works even if client was
|
||||
instantiated with predefined token.
|
||||
"""
|
||||
client_ = client.HTTPClient(username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
token=TOKEN,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
expected = {'auth_token': TOKEN,
|
||||
'auth_tenant_id': None,
|
||||
'auth_user_id': None,
|
||||
'endpoint_url': self.client.endpoint_url}
|
||||
self.assertEqual(client_.get_auth_info(), expected)
|
||||
|
||||
@httpretty.activate
|
||||
def test_get_token(self):
|
||||
auth_session, auth_plugin = setup_keystone_v2()
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
def test_refresh_token(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
self.client.endpoint_url = ENDPOINT_URL
|
||||
|
||||
res200 = get_response(200)
|
||||
res401 = get_response(401)
|
||||
|
||||
# If a token is expired, neutron server retruns 401
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res401, ''))
|
||||
self.client.request(
|
||||
AUTH_URL + '/tokens', 'POST',
|
||||
body=mox.IsA(str), headers=mox.IsA(dict)
|
||||
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
def test_refresh_token_no_auth_url(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
self.client.auth_url = None
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
self.client.endpoint_url = ENDPOINT_URL
|
||||
|
||||
res401 = get_response(401)
|
||||
|
||||
# If a token is expired, neutron server returns 401
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res401, ''))
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(exceptions.NoAuthURLProvided,
|
||||
self.client.do_request,
|
||||
'/resource',
|
||||
'GET')
|
||||
|
||||
def test_get_endpoint_url_with_invalid_auth_url(self):
|
||||
# Handle the case when auth_url is not provided
|
||||
self.client.auth_url = None
|
||||
self.assertRaises(exceptions.NoAuthURLProvided,
|
||||
self.client._get_endpoint_url)
|
||||
|
||||
def test_get_endpoint_url(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
|
||||
headers=mox.IsA(dict)
|
||||
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
def test_use_given_endpoint_url(self):
|
||||
self.client = client.HTTPClient(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION,
|
||||
endpoint_url=ENDPOINT_OVERRIDE)
|
||||
self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
|
||||
|
||||
def test_get_endpoint_url_other(self):
|
||||
self.client = client.HTTPClient(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL')
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
|
||||
headers=mox.IsA(dict)
|
||||
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(exceptions.EndpointTypeNotFound,
|
||||
self.client.do_request,
|
||||
'/resource',
|
||||
'GET')
|
||||
|
||||
def test_get_endpoint_url_failed(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.auth_token = TOKEN
|
||||
|
||||
res200 = get_response(200)
|
||||
res401 = get_response(401)
|
||||
|
||||
self.client.request(
|
||||
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
|
||||
headers=mox.IsA(dict)
|
||||
).AndReturn((res401, ''))
|
||||
self.client.request(
|
||||
AUTH_URL + '/tokens', 'POST',
|
||||
body=mox.IsA(str), headers=mox.IsA(dict)
|
||||
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
@httpretty.activate
|
||||
def test_endpoint_type(self):
|
||||
auth_session, auth_plugin = setup_keystone_v3()
|
||||
|
||||
# Test default behavior is to choose public.
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION,
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test admin url
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL)
|
||||
|
||||
# Test public url
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test internal url
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL)
|
||||
|
||||
# Test url that isn't found in the service catalog
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.assertRaises(
|
||||
ks_exceptions.EndpointNotFound,
|
||||
self.client.authenticate)
|
||||
|
||||
def test_strip_credentials_from_log(self):
|
||||
def verify_no_credentials(kwargs):
|
||||
return ('REDACTED' in kwargs['body']) and (
|
||||
self.client.password not in kwargs['body'])
|
||||
|
||||
def verify_credentials(body):
|
||||
return 'REDACTED' not in body and self.client.password in body
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
self.mox.StubOutWithMock(utils, "http_log_req")
|
||||
|
||||
res200 = get_response(200)
|
||||
|
||||
utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func(
|
||||
verify_no_credentials))
|
||||
self.client.request(
|
||||
mox.IsA(six.string_types), mox.IsA(six.string_types),
|
||||
body=mox.Func(verify_credentials),
|
||||
headers=mox.IgnoreArg()
|
||||
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
|
||||
utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.client.request(
|
||||
mox.IsA(six.string_types), mox.IsA(six.string_types),
|
||||
headers=mox.IsA(dict)
|
||||
).AndReturn((res200, ''))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
|
||||
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystoneWithId, self).setUp()
|
||||
self.client = client.HTTPClient(user_id=USER_ID,
|
||||
tenant_id=TENANT_ID,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
|
||||
class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystoneWithIdandName, self).setUp()
|
||||
self.client = client.HTTPClient(username=USERNAME,
|
||||
user_id=USER_ID,
|
||||
tenant_id=TENANT_ID,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
|
||||
class TestKeystoneClientVersions(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(TestKeystoneClientVersions, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.addCleanup(self.mox.VerifyAll)
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
@httpretty.activate
|
||||
def test_v2_auth(self):
|
||||
auth_session, auth_plugin = setup_keystone_v2()
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.request(
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
@httpretty.activate
|
||||
def test_v3_auth(self):
|
||||
auth_session, auth_plugin = setup_keystone_v3()
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
user_id=USER_ID,
|
||||
tenant_id=TENANT_ID,
|
||||
password=PASSWORD,
|
||||
auth_url=V3_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.request(
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
766
gbpclient/tests/unit/test_cli20.py
Normal file
766
gbpclient/tests/unit/test_cli20.py
Normal file
@ -0,0 +1,766 @@
|
||||
# Copyright 2012 OpenStack Foundation.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import contextlib
|
||||
import itertools
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
import fixtures
|
||||
from mox3 import mox
|
||||
from oslotest import base
|
||||
import requests
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from neutronclient.common import constants
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0 as neutronV2_0
|
||||
from neutronclient import shell
|
||||
from neutronclient.v2_0 import client
|
||||
|
||||
API_VERSION = "2.0"
|
||||
FORMAT = 'json'
|
||||
TOKEN = 'testtoken'
|
||||
ENDURL = 'localurl'
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def capture_std_streams():
|
||||
fake_stdout, fake_stderr = six.StringIO(), six.StringIO()
|
||||
stdout, stderr = sys.stdout, sys.stderr
|
||||
try:
|
||||
sys.stdout, sys.stderr = fake_stdout, fake_stderr
|
||||
yield fake_stdout, fake_stderr
|
||||
finally:
|
||||
sys.stdout, sys.stderr = stdout, stderr
|
||||
|
||||
|
||||
class FakeStdout:
|
||||
|
||||
def __init__(self):
|
||||
self.content = []
|
||||
|
||||
def write(self, text):
|
||||
self.content.append(text)
|
||||
|
||||
def make_string(self):
|
||||
result = ''
|
||||
for line in self.content:
|
||||
result = result + line
|
||||
return result
|
||||
|
||||
|
||||
class MyResp(object):
|
||||
def __init__(self, status_code, headers=None, reason=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
self.reason = reason
|
||||
|
||||
|
||||
class MyApp(object):
|
||||
def __init__(self, _stdout):
|
||||
self.stdout = _stdout
|
||||
|
||||
|
||||
def end_url(path, query=None, format=FORMAT):
|
||||
_url_str = ENDURL + "/v" + API_VERSION + path + "." + format
|
||||
return query and _url_str + "?" + query or _url_str
|
||||
|
||||
|
||||
class MyUrlComparator(mox.Comparator):
|
||||
def __init__(self, lhs, client):
|
||||
self.lhs = lhs
|
||||
self.client = client
|
||||
|
||||
def equals(self, rhs):
|
||||
lhsp = urlparse.urlparse(self.lhs)
|
||||
rhsp = urlparse.urlparse(rhs)
|
||||
|
||||
return (lhsp.scheme == rhsp.scheme and
|
||||
lhsp.netloc == rhsp.netloc and
|
||||
lhsp.path == rhsp.path and
|
||||
urlparse.parse_qs(lhsp.query) == urlparse.parse_qs(rhsp.query))
|
||||
|
||||
def __str__(self):
|
||||
if self.client and self.client.format != FORMAT:
|
||||
lhs_parts = self.lhs.split("?", 1)
|
||||
if len(lhs_parts) == 2:
|
||||
lhs = ("%s.%s?%s" % (lhs_parts[0][:-4],
|
||||
self.client.format,
|
||||
lhs_parts[1]))
|
||||
else:
|
||||
lhs = ("%s.%s" % (lhs_parts[0][:-4],
|
||||
self.client.format))
|
||||
return lhs
|
||||
return self.lhs
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class MyComparator(mox.Comparator):
|
||||
def __init__(self, lhs, client):
|
||||
self.lhs = lhs
|
||||
self.client = client
|
||||
|
||||
def _com_dict(self, lhs, rhs):
|
||||
if len(lhs) != len(rhs):
|
||||
return False
|
||||
for key, value in six.iteritems(lhs):
|
||||
if key not in rhs:
|
||||
return False
|
||||
rhs_value = rhs[key]
|
||||
if not self._com(value, rhs_value):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _com_list(self, lhs, rhs):
|
||||
if len(lhs) != len(rhs):
|
||||
return False
|
||||
for lhs_value in lhs:
|
||||
if lhs_value not in rhs:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _com(self, lhs, rhs):
|
||||
if lhs is None:
|
||||
return rhs is None
|
||||
if isinstance(lhs, dict):
|
||||
if not isinstance(rhs, dict):
|
||||
return False
|
||||
return self._com_dict(lhs, rhs)
|
||||
if isinstance(lhs, list):
|
||||
if not isinstance(rhs, list):
|
||||
return False
|
||||
return self._com_list(lhs, rhs)
|
||||
if isinstance(lhs, tuple):
|
||||
if not isinstance(rhs, tuple):
|
||||
return False
|
||||
return self._com_list(lhs, rhs)
|
||||
return lhs == rhs
|
||||
|
||||
def equals(self, rhs):
|
||||
if self.client:
|
||||
rhs = self.client.deserialize(rhs, 200)
|
||||
return self._com(self.lhs, rhs)
|
||||
|
||||
def __repr__(self):
|
||||
if self.client:
|
||||
return self.client.serialize(self.lhs)
|
||||
return str(self.lhs)
|
||||
|
||||
|
||||
class CLITestV20Base(base.BaseTestCase):
|
||||
|
||||
format = 'json'
|
||||
test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
||||
id_field = 'id'
|
||||
|
||||
def _find_resourceid(self, client, resource, name_or_id,
|
||||
cmd_resource=None, parent_id=None):
|
||||
return name_or_id
|
||||
|
||||
def _get_attr_metadata(self):
|
||||
return self.metadata
|
||||
client.Client.EXTED_PLURALS.update(constants.PLURALS)
|
||||
client.Client.EXTED_PLURALS.update({'tags': 'tag'})
|
||||
return {'plurals': client.Client.EXTED_PLURALS,
|
||||
'xmlns': constants.XML_NS_V20,
|
||||
constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}}
|
||||
|
||||
def setUp(self, plurals=None):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestV20Base, self).setUp()
|
||||
client.Client.EXTED_PLURALS.update(constants.PLURALS)
|
||||
if plurals is not None:
|
||||
client.Client.EXTED_PLURALS.update(plurals)
|
||||
self.metadata = {'plurals': client.Client.EXTED_PLURALS,
|
||||
'xmlns': constants.XML_NS_V20,
|
||||
constants.EXT_NS: {'prefix':
|
||||
'http://xxxx.yy.com'}}
|
||||
self.mox = mox.Mox()
|
||||
self.endurl = ENDURL
|
||||
self.fake_stdout = FakeStdout()
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'neutronclient.neutron.v2_0.find_resourceid_by_name_or_id',
|
||||
self._find_resourceid))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'neutronclient.neutron.v2_0.find_resourceid_by_id',
|
||||
self._find_resourceid))
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'neutronclient.v2_0.client.Client.get_attr_metadata',
|
||||
self._get_attr_metadata))
|
||||
self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
|
||||
|
||||
def _test_create_resource(self, resource, cmd, name, myid, args,
|
||||
position_names, position_values,
|
||||
tenant_id=None, tags=None, admin_state_up=True,
|
||||
extra_body=None, cmd_resource=None,
|
||||
parent_id=None, **kwargs):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
|
||||
'security_group_rule', 'qos_queue',
|
||||
'network_gateway', 'gateway_device',
|
||||
'credential', 'network_profile',
|
||||
'policy_profile', 'ikepolicy',
|
||||
'ipsecpolicy', 'metering_label',
|
||||
'metering_label_rule', 'net_partition']
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
if (resource in non_admin_status_resources):
|
||||
body = {resource: {}, }
|
||||
else:
|
||||
body = {resource: {'admin_state_up': admin_state_up, }, }
|
||||
if tenant_id:
|
||||
body[resource].update({'tenant_id': tenant_id})
|
||||
if tags:
|
||||
body[resource].update({'tags': tags})
|
||||
if extra_body:
|
||||
body[resource].update(extra_body)
|
||||
body[resource].update(kwargs)
|
||||
|
||||
for i in range(len(position_names)):
|
||||
body[resource].update({position_names[i]: position_values[i]})
|
||||
ress = {resource:
|
||||
{self.id_field: myid}, }
|
||||
if name:
|
||||
ress[resource].update({'name': name})
|
||||
self.client.format = self.format
|
||||
resstr = self.client.serialize(ress)
|
||||
# url method body
|
||||
resource_plural = neutronV2_0._get_resource_plural(cmd_resource,
|
||||
self.client)
|
||||
path = getattr(self.client, resource_plural + "_path")
|
||||
if parent_id:
|
||||
path = path % parent_id
|
||||
# Work around for LP #1217791. XML deserializer called from
|
||||
# MyComparator does not decodes XML string correctly.
|
||||
if self.format == 'json':
|
||||
mox_body = MyComparator(body, self.client)
|
||||
else:
|
||||
mox_body = self.client.serialize(body)
|
||||
self.client.httpclient.request(
|
||||
end_url(path, format=self.format), 'POST',
|
||||
body=mox_body,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser('create_' + resource)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn(myid, _str)
|
||||
if name:
|
||||
self.assertIn(name, _str)
|
||||
|
||||
def _test_list_columns(self, cmd, resources,
|
||||
resources_out, args=('-f', 'json'),
|
||||
cmd_resources=None, parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
self.client.format = self.format
|
||||
if not cmd_resources:
|
||||
cmd_resources = resources
|
||||
|
||||
resstr = self.client.serialize(resources_out)
|
||||
|
||||
path = getattr(self.client, cmd_resources + "_path")
|
||||
if parent_id:
|
||||
path = path % parent_id
|
||||
self.client.httpclient.request(
|
||||
end_url(path, format=self.format), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
||||
args = tuple(args) + ('--request-format', self.format)
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def _test_list_resources(self, resources, cmd, detail=False, tags=(),
|
||||
fields_1=(), fields_2=(), page_size=None,
|
||||
sort_key=(), sort_dir=(), response_contents=None,
|
||||
base_args=None, path=None, cmd_resources=None,
|
||||
parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resources:
|
||||
cmd_resources = resources
|
||||
if response_contents is None:
|
||||
contents = [{self.id_field: 'myid1', },
|
||||
{self.id_field: 'myid2', }, ]
|
||||
else:
|
||||
contents = response_contents
|
||||
reses = {resources: contents}
|
||||
self.client.format = self.format
|
||||
resstr = self.client.serialize(reses)
|
||||
# url method body
|
||||
query = ""
|
||||
args = base_args if base_args is not None else []
|
||||
if detail:
|
||||
args.append('-D')
|
||||
args.extend(['--request-format', self.format])
|
||||
if fields_1:
|
||||
for field in fields_1:
|
||||
args.append('--fields')
|
||||
args.append(field)
|
||||
|
||||
if tags:
|
||||
args.append('--')
|
||||
args.append("--tag")
|
||||
for tag in tags:
|
||||
args.append(tag)
|
||||
if isinstance(tag, unicode):
|
||||
tag = urllib.quote(tag.encode('utf-8'))
|
||||
if query:
|
||||
query += "&tag=" + tag
|
||||
else:
|
||||
query = "tag=" + tag
|
||||
if (not tags) and fields_2:
|
||||
args.append('--')
|
||||
if fields_2:
|
||||
args.append("--fields")
|
||||
for field in fields_2:
|
||||
args.append(field)
|
||||
if detail:
|
||||
query = query and query + '&verbose=True' or 'verbose=True'
|
||||
for field in itertools.chain(fields_1, fields_2):
|
||||
if query:
|
||||
query += "&fields=" + field
|
||||
else:
|
||||
query = "fields=" + field
|
||||
if page_size:
|
||||
args.append("--page-size")
|
||||
args.append(str(page_size))
|
||||
if query:
|
||||
query += "&limit=%s" % page_size
|
||||
else:
|
||||
query = "limit=%s" % page_size
|
||||
if sort_key:
|
||||
for key in sort_key:
|
||||
args.append('--sort-key')
|
||||
args.append(key)
|
||||
if query:
|
||||
query += '&'
|
||||
query += 'sort_key=%s' % key
|
||||
if sort_dir:
|
||||
len_diff = len(sort_key) - len(sort_dir)
|
||||
if len_diff > 0:
|
||||
sort_dir = tuple(sort_dir) + ('asc',) * len_diff
|
||||
elif len_diff < 0:
|
||||
sort_dir = sort_dir[:len(sort_key)]
|
||||
for dir in sort_dir:
|
||||
args.append('--sort-dir')
|
||||
args.append(dir)
|
||||
if query:
|
||||
query += '&'
|
||||
query += 'sort_dir=%s' % dir
|
||||
if path is None:
|
||||
path = getattr(self.client, cmd_resources + "_path")
|
||||
if parent_id:
|
||||
path = path % parent_id
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(path, query, format=self.format),
|
||||
self.client),
|
||||
'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
if response_contents is None:
|
||||
self.assertIn('myid1', _str)
|
||||
return _str
|
||||
|
||||
def _test_list_resources_with_pagination(self, resources, cmd,
|
||||
cmd_resources=None,
|
||||
parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resources:
|
||||
cmd_resources = resources
|
||||
|
||||
path = getattr(self.client, cmd_resources + "_path")
|
||||
if parent_id:
|
||||
path = path % parent_id
|
||||
fake_query = "marker=myid2&limit=2"
|
||||
reses1 = {resources: [{'id': 'myid1', },
|
||||
{'id': 'myid2', }],
|
||||
'%s_links' % resources: [{'href': end_url(path, fake_query),
|
||||
'rel': 'next'}]}
|
||||
reses2 = {resources: [{'id': 'myid3', },
|
||||
{'id': 'myid4', }]}
|
||||
self.client.format = self.format
|
||||
resstr1 = self.client.serialize(reses1)
|
||||
resstr2 = self.client.serialize(reses2)
|
||||
self.client.httpclient.request(
|
||||
end_url(path, "", format=self.format), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(path, fake_query, format=self.format),
|
||||
self.client), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("list_" + cmd_resources)
|
||||
args = ['--request-format', self.format]
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def _test_update_resource(self, resource, cmd, myid, args, extrafields,
|
||||
cmd_resource=None, parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
|
||||
body = {resource: extrafields}
|
||||
path = getattr(self.client, cmd_resource + "_path")
|
||||
if parent_id:
|
||||
path = path % (parent_id, myid)
|
||||
else:
|
||||
path = path % myid
|
||||
self.client.format = self.format
|
||||
# Work around for LP #1217791. XML deserializer called from
|
||||
# MyComparator does not decodes XML string correctly.
|
||||
if self.format == 'json':
|
||||
mox_body = MyComparator(body, self.client)
|
||||
else:
|
||||
mox_body = self.client.serialize(body)
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(path, format=self.format),
|
||||
self.client),
|
||||
'PUT',
|
||||
body=mox_body,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("update_" + cmd_resource)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn(myid, _str)
|
||||
|
||||
def _test_show_resource(self, resource, cmd, myid, args, fields=(),
|
||||
cmd_resource=None, parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
|
||||
query = "&".join(["fields=%s" % field for field in fields])
|
||||
expected_res = {resource:
|
||||
{self.id_field: myid,
|
||||
'name': 'myname', }, }
|
||||
self.client.format = self.format
|
||||
resstr = self.client.serialize(expected_res)
|
||||
path = getattr(self.client, cmd_resource + "_path")
|
||||
if parent_id:
|
||||
path = path % (parent_id, myid)
|
||||
else:
|
||||
path = path % myid
|
||||
self.client.httpclient.request(
|
||||
end_url(path, query, format=self.format), 'GET',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("show_" + cmd_resource)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn(myid, _str)
|
||||
self.assertIn('myname', _str)
|
||||
|
||||
def _test_delete_resource(self, resource, cmd, myid, args,
|
||||
cmd_resource=None, parent_id=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
path = getattr(self.client, cmd_resource + "_path")
|
||||
if parent_id:
|
||||
path = path % (parent_id, myid)
|
||||
else:
|
||||
path = path % (myid)
|
||||
self.client.httpclient.request(
|
||||
end_url(path, format=self.format), 'DELETE',
|
||||
body=None,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("delete_" + cmd_resource)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn(myid, _str)
|
||||
|
||||
def _test_update_resource_action(self, resource, cmd, myid, action, args,
|
||||
body, retval=None, cmd_resource=None):
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
if not cmd_resource:
|
||||
cmd_resource = resource
|
||||
path = getattr(self.client, cmd_resource + "_path")
|
||||
path_action = '%s/%s' % (myid, action)
|
||||
self.client.httpclient.request(
|
||||
end_url(path % path_action, format=self.format), 'PUT',
|
||||
body=MyComparator(body, self.client),
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval))
|
||||
args.extend(['--request-format', self.format])
|
||||
self.mox.ReplayAll()
|
||||
cmd_parser = cmd.get_parser("delete_" + cmd_resource)
|
||||
shell.run_command(cmd, cmd_parser, args)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
_str = self.fake_stdout.make_string()
|
||||
self.assertIn(myid, _str)
|
||||
|
||||
|
||||
class ClientV2TestJson(CLITestV20Base):
|
||||
def test_do_request_unicode(self):
|
||||
self.client.format = self.format
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
unicode_text = u'\u7f51\u7edc'
|
||||
# url with unicode
|
||||
action = u'/test'
|
||||
expected_action = action.encode('utf-8')
|
||||
# query string with unicode
|
||||
params = {'test': unicode_text}
|
||||
expect_query = urllib.urlencode({'test':
|
||||
unicode_text.encode('utf-8')})
|
||||
# request body with unicode
|
||||
body = params
|
||||
expect_body = self.client.serialize(body)
|
||||
# headers with unicode
|
||||
self.client.httpclient.auth_token = unicode_text
|
||||
expected_auth_token = unicode_text.encode('utf-8')
|
||||
|
||||
self.client.httpclient.request(
|
||||
end_url(expected_action, query=expect_query, format=self.format),
|
||||
'PUT', body=expect_body,
|
||||
headers=mox.ContainsKeyValue(
|
||||
'X-Auth-Token',
|
||||
expected_auth_token)).AndReturn((MyResp(200), expect_body))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res_body = self.client.do_request('PUT', action, body=body,
|
||||
params=params)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
# test response with unicode
|
||||
self.assertEqual(res_body, body)
|
||||
|
||||
def test_do_request_error_without_response_body(self):
|
||||
self.client.format = self.format
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
params = {'test': 'value'}
|
||||
expect_query = six.moves.urllib.parse.urlencode(params)
|
||||
self.client.httpclient.auth_token = 'token'
|
||||
|
||||
self.client.httpclient.request(
|
||||
MyUrlComparator(end_url(
|
||||
'/test', query=expect_query, format=self.format), self.client),
|
||||
'PUT', body='',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', 'token')
|
||||
).AndReturn((MyResp(400, reason='An error'), ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
error = self.assertRaises(exceptions.NeutronClientException,
|
||||
self.client.do_request, 'PUT', '/test',
|
||||
body='', params=params)
|
||||
self.assertEqual("An error", str(error))
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
|
||||
class ClientV2UnicodeTestXML(ClientV2TestJson):
|
||||
format = 'xml'
|
||||
|
||||
|
||||
class CLITestV20ExceptionHandler(CLITestV20Base):
|
||||
|
||||
def _test_exception_handler_v20(
|
||||
self, expected_exception, status_code, expected_msg,
|
||||
error_type=None, error_msg=None, error_detail=None,
|
||||
error_content=None):
|
||||
if error_content is None:
|
||||
error_content = {'NeutronError': {'type': error_type,
|
||||
'message': error_msg,
|
||||
'detail': error_detail}}
|
||||
|
||||
e = self.assertRaises(expected_exception,
|
||||
client.exception_handler_v20,
|
||||
status_code, error_content)
|
||||
self.assertEqual(status_code, e.status_code)
|
||||
|
||||
if expected_msg is None:
|
||||
if error_detail:
|
||||
expected_msg = '\n'.join([error_msg, error_detail])
|
||||
else:
|
||||
expected_msg = error_msg
|
||||
self.assertEqual(expected_msg, e.message)
|
||||
|
||||
def test_exception_handler_v20_ip_address_in_use(self):
|
||||
err_msg = ('Unable to complete operation for network '
|
||||
'fake-network-uuid. The IP address fake-ip is in use.')
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.IpAddressInUseClient, 409, err_msg,
|
||||
'IpAddressInUse', err_msg, '')
|
||||
|
||||
def test_exception_handler_v20_neutron_known_error(self):
|
||||
known_error_map = [
|
||||
('NetworkNotFound', exceptions.NetworkNotFoundClient, 404),
|
||||
('PortNotFound', exceptions.PortNotFoundClient, 404),
|
||||
('NetworkInUse', exceptions.NetworkInUseClient, 409),
|
||||
('PortInUse', exceptions.PortInUseClient, 409),
|
||||
('StateInvalid', exceptions.StateInvalidClient, 400),
|
||||
('IpAddressInUse', exceptions.IpAddressInUseClient, 409),
|
||||
('IpAddressGenerationFailure',
|
||||
exceptions.IpAddressGenerationFailureClient, 409),
|
||||
('MacAddressInUse', exceptions.MacAddressInUseClient, 409),
|
||||
('ExternalIpAddressExhausted',
|
||||
exceptions.ExternalIpAddressExhaustedClient, 400),
|
||||
('OverQuota', exceptions.OverQuotaClient, 409),
|
||||
]
|
||||
|
||||
error_msg = 'dummy exception message'
|
||||
error_detail = 'sample detail'
|
||||
for server_exc, client_exc, status_code in known_error_map:
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
error_msg + '\n' + error_detail,
|
||||
server_exc, error_msg, error_detail)
|
||||
|
||||
def test_exception_handler_v20_neutron_known_error_without_detail(self):
|
||||
error_msg = 'Network not found'
|
||||
error_detail = ''
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NetworkNotFoundClient, 404,
|
||||
error_msg,
|
||||
'NetworkNotFound', error_msg, error_detail)
|
||||
|
||||
def test_exception_handler_v20_unknown_error_to_per_code_exception(self):
|
||||
for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items():
|
||||
error_msg = 'Unknown error'
|
||||
error_detail = 'This is detail'
|
||||
self._test_exception_handler_v20(
|
||||
client_exc, status_code,
|
||||
error_msg + '\n' + error_detail,
|
||||
'UnknownError', error_msg, error_detail)
|
||||
|
||||
def test_exception_handler_v20_neutron_unknown_status_code(self):
|
||||
error_msg = 'Unknown error'
|
||||
error_detail = 'This is detail'
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 501,
|
||||
error_msg + '\n' + error_detail,
|
||||
'UnknownError', error_msg, error_detail)
|
||||
|
||||
def test_exception_handler_v20_bad_neutron_error(self):
|
||||
error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}}
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg={'unknown_key': 'UNKNOWN'},
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_handler_v20_error_dict_contains_message(self):
|
||||
error_content = {'message': 'This is an error message'}
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg='This is an error message',
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_handler_v20_error_dict_not_contain_message(self):
|
||||
error_content = {'error': 'This is an error message'}
|
||||
expected_msg = '%s-%s' % (500, error_content)
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg=expected_msg,
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_handler_v20_default_fallback(self):
|
||||
error_content = 'This is an error message'
|
||||
expected_msg = '%s-%s' % (500, error_content)
|
||||
self._test_exception_handler_v20(
|
||||
exceptions.NeutronClientException, 500,
|
||||
expected_msg=expected_msg,
|
||||
error_content=error_content)
|
||||
|
||||
def test_exception_status(self):
|
||||
e = exceptions.BadRequest()
|
||||
self.assertEqual(e.status_code, 400)
|
||||
|
||||
e = exceptions.BadRequest(status_code=499)
|
||||
self.assertEqual(e.status_code, 499)
|
||||
|
||||
# SslCertificateValidationError has no explicit status_code,
|
||||
# but should have a 'safe' defined fallback.
|
||||
e = exceptions.SslCertificateValidationError()
|
||||
self.assertIsNotNone(e.status_code)
|
||||
|
||||
e = exceptions.SslCertificateValidationError(status_code=599)
|
||||
self.assertEqual(e.status_code, 599)
|
||||
|
||||
def test_connection_failed(self):
|
||||
self.mox.StubOutWithMock(self.client.httpclient, 'request')
|
||||
self.client.httpclient.auth_token = 'token'
|
||||
|
||||
self.client.httpclient.request(
|
||||
end_url('/test'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', 'token')
|
||||
).AndRaise(requests.exceptions.ConnectionError('Connection refused'))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
error = self.assertRaises(exceptions.ConnectionFailed,
|
||||
self.client.get, '/test')
|
||||
# NB: ConnectionFailed has no explicit status_code, so this
|
||||
# tests that there is a fallback defined.
|
||||
self.assertIsNotNone(error.status_code)
|
||||
self.mox.VerifyAll()
|
||||
self.mox.UnsetStubs()
|
19
gbpclient/version.py
Normal file
19
gbpclient/version.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
import pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'python-group-based-policy-client').version_string()
|
7
openstack-common.conf
Normal file
7
openstack-common.conf
Normal file
@ -0,0 +1,7 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from openstack-common
|
||||
modules=gettextutils,jsonutils,strutils,timeutils
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=gbpclient
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
python-neutronclient>=2.3.6,<3
|
39
setup.cfg
Normal file
39
setup.cfg
Normal file
@ -0,0 +1,39 @@
|
||||
[metadata]
|
||||
name = python-group-based-policy-client
|
||||
summary = CLI and Client Library for Group Based Policy
|
||||
description-file =
|
||||
README.rst
|
||||
author = Group Based Policy
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 2.6
|
||||
|
||||
[files]
|
||||
packages =
|
||||
gbpclient
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
gbp = gbpclient.shell:main
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
30
setup.py
Normal file
30
setup.py
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 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.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
17
test-requirements.txt
Normal file
17
test-requirements.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
hacking>=0.8.0,<0.9
|
||||
|
||||
cliff-tablib>=1.0
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
httpretty>=0.8.0,!=0.8.1,!=0.8.2,!=0.8.3
|
||||
mox3>=0.7.0
|
||||
oslosphinx>=2.2.0.0a2
|
||||
oslotest>=1.1.0.0a2
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||
testrepository>=0.0.18
|
||||
testtools>=0.9.34
|
27
tools/gbp.bash_completion
Normal file
27
tools/gbp.bash_completion
Normal file
@ -0,0 +1,27 @@
|
||||
_gbp_opts="" # lazy init
|
||||
_gbp_flags="" # lazy init
|
||||
_gbp_opts_exp="" # lazy init
|
||||
_gbp()
|
||||
{
|
||||
local cur prev nbc cflags
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
if [ "x$_gbp_opts" == "x" ] ; then
|
||||
nbc="`gbp bash-completion`"
|
||||
_gbp_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
||||
_gbp_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
||||
_gbp_opts_exp="`echo "$_gbp_opts" | sed -e "s/\s/|/g"`"
|
||||
fi
|
||||
|
||||
if [[ " ${COMP_WORDS[@]} " =~ " "($_gbp_opts_exp)" " && "$prev" != "help" ]] ; then
|
||||
COMPLETION_CACHE=~/.gbpclient/*/*-cache
|
||||
cflags="$_gbp_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ')
|
||||
COMPREPLY=($(compgen -W "${cflags}" -- ${cur}))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "${_gbp_opts}" -- ${cur}))
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
complete -F _gbp gbp
|
27
tools/policy.bash_completion
Normal file
27
tools/policy.bash_completion
Normal file
@ -0,0 +1,27 @@
|
||||
_policy_opts="" # lazy init
|
||||
_policy_flags="" # lazy init
|
||||
_policy_opts_exp="" # lazy init
|
||||
_policy()
|
||||
{
|
||||
local cur prev nbc cflags
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
if [ "x$_policy_opts" == "x" ] ; then
|
||||
nbc="`policy bash-completion`"
|
||||
_policy_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
||||
_policy_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/\s\s*/ /g"`"
|
||||
_policy_opts_exp="`echo "$_policy_opts" | sed -e "s/\s/|/g"`"
|
||||
fi
|
||||
|
||||
if [[ " ${COMP_WORDS[@]} " =~ " "($_policy_opts_exp)" " && "$prev" != "help" ]] ; then
|
||||
COMPLETION_CACHE=~/.policyclient/*/*-cache
|
||||
cflags="$_policy_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ')
|
||||
COMPREPLY=($(compgen -W "${cflags}" -- ${cur}))
|
||||
else
|
||||
COMPREPLY=($(compgen -W "${_policy_opts}" -- ${cur}))
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
complete -F _policy policy
|
39
tox.ini
Normal file
39
tox.ini
Normal file
@ -0,0 +1,39 @@
|
||||
[tox]
|
||||
envlist = py26,py27,py33,pypy,pep8
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
LANG=en_US.UTF-8
|
||||
LANGUAGE=en_US:en
|
||||
LC_ALL=C
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py testr --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
distribute = false
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:docs]
|
||||
commands=
|
||||
python setup.py build_sphinx
|
||||
|
||||
[tox:jenkins]
|
||||
downloadcache = ~/cache/pip
|
||||
|
||||
[flake8]
|
||||
# E125 continuation line does not distinguish itself from next logical line
|
||||
# H302 import only modules
|
||||
ignore = E125,H302
|
||||
show-source = true
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools
|
Loading…
x
Reference in New Issue
Block a user