Switch to keystoneauth
move cinderclient to keystoneauth as keystoneclient's auth session, plugins and adapter code has been deprecated. Co-Authored-By: Paulo Ewerton <pauloewerton@lsd.ufcg.edu.br> Co-Authored-By: Sean McGinnis <sean.mcginnis@gmail.com> Co-Authored-By: Jamie Lennox <jamielennox@gmail.com> Change-Id: Id4bf0e2088e8ad99e83cd4f9b8549c2aca1f65a2
This commit is contained in:
parent
b61ec1e6a7
commit
16f83c4a53
cinderclient
requirements.txt@ -30,10 +30,10 @@ import pkgutil
|
|||||||
import re
|
import re
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from keystoneclient import access
|
from keystoneauth1 import access
|
||||||
from keystoneclient import adapter
|
from keystoneauth1 import adapter
|
||||||
from keystoneclient.auth.identity import base
|
from keystoneauth1.identity import base
|
||||||
from keystoneclient import discover
|
from keystoneauth1 import discover
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
@ -109,7 +109,7 @@ class SessionClient(adapter.LegacyJsonAdapter):
|
|||||||
api_versions.update_headers(kwargs["headers"], self.api_version)
|
api_versions.update_headers(kwargs["headers"], self.api_version)
|
||||||
kwargs.setdefault('authenticated', False)
|
kwargs.setdefault('authenticated', False)
|
||||||
# Note(tpatil): The standard call raises errors from
|
# Note(tpatil): The standard call raises errors from
|
||||||
# keystoneclient, here we need to raise the cinderclient errors.
|
# keystoneauth, here we need to raise the cinderclient errors.
|
||||||
raise_exc = kwargs.pop('raise_exc', True)
|
raise_exc = kwargs.pop('raise_exc', True)
|
||||||
resp, body = super(SessionClient, self).request(*args,
|
resp, body = super(SessionClient, self).request(*args,
|
||||||
raise_exc=False,
|
raise_exc=False,
|
||||||
@ -427,7 +427,7 @@ class HTTPClient(object):
|
|||||||
if resp.status_code == 200: # content must always present
|
if resp.status_code == 200: # content must always present
|
||||||
try:
|
try:
|
||||||
self.auth_url = url
|
self.auth_url = url
|
||||||
self.auth_ref = access.AccessInfo.factory(resp, body)
|
self.auth_ref = access.create(resp=resp, body=body)
|
||||||
self.service_catalog = self.auth_ref.service_catalog
|
self.service_catalog = self.auth_ref.service_catalog
|
||||||
|
|
||||||
if extract_token:
|
if extract_token:
|
||||||
@ -435,7 +435,7 @@ class HTTPClient(object):
|
|||||||
|
|
||||||
management_url = self.service_catalog.url_for(
|
management_url = self.service_catalog.url_for(
|
||||||
region_name=self.region_name,
|
region_name=self.region_name,
|
||||||
endpoint_type=self.endpoint_type,
|
interface=self.endpoint_type,
|
||||||
service_type=self.service_type,
|
service_type=self.service_type,
|
||||||
service_name=self.service_name)
|
service_name=self.service_name)
|
||||||
self.management_url = management_url.rstrip('/')
|
self.management_url = management_url.rstrip('/')
|
||||||
@ -444,7 +444,10 @@ class HTTPClient(object):
|
|||||||
print("Found more than one valid endpoint. Use a more "
|
print("Found more than one valid endpoint. Use a more "
|
||||||
"restrictive filter")
|
"restrictive filter")
|
||||||
raise
|
raise
|
||||||
except KeyError:
|
except ValueError:
|
||||||
|
# ValueError is raised when you pass an invalid response to
|
||||||
|
# access.create. This should never happen in reality if the
|
||||||
|
# status code is 200.
|
||||||
raise exceptions.AuthorizationFailure()
|
raise exceptions.AuthorizationFailure()
|
||||||
except exceptions.EndpointNotFound:
|
except exceptions.EndpointNotFound:
|
||||||
print("Could not find any suitable endpoint. Correct region?")
|
print("Could not find any suitable endpoint. Correct region?")
|
||||||
|
@ -34,11 +34,12 @@ from cinderclient import utils
|
|||||||
import cinderclient.auth_plugin
|
import cinderclient.auth_plugin
|
||||||
from cinderclient._i18n import _
|
from cinderclient._i18n import _
|
||||||
|
|
||||||
from keystoneclient import discover
|
from keystoneauth1 import discover
|
||||||
from keystoneclient import session
|
from keystoneauth1 import loading
|
||||||
from keystoneclient.auth.identity import v2 as v2_auth
|
from keystoneauth1 import session
|
||||||
from keystoneclient.auth.identity import v3 as v3_auth
|
from keystoneauth1.identity import v2 as v2_auth
|
||||||
from keystoneclient.exceptions import DiscoveryFailure
|
from keystoneauth1.identity import v3 as v3_auth
|
||||||
|
from keystoneauth1.exceptions import DiscoveryFailure
|
||||||
import six.moves.urllib.parse as urlparse
|
import six.moves.urllib.parse as urlparse
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
@ -390,7 +391,7 @@ class OpenStackCinderShell(object):
|
|||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Register the CLI arguments that have moved to the session object.
|
# Register the CLI arguments that have moved to the session object.
|
||||||
session.Session.register_cli_options(parser)
|
loading.register_session_argparse_arguments(parser)
|
||||||
parser.set_defaults(insecure=utils.env('CINDERCLIENT_INSECURE',
|
parser.set_defaults(insecure=utils.env('CINDERCLIENT_INSECURE',
|
||||||
default=False))
|
default=False))
|
||||||
|
|
||||||
@ -468,9 +469,8 @@ class OpenStackCinderShell(object):
|
|||||||
if hasattr(requests, 'logging'):
|
if hasattr(requests, 'logging'):
|
||||||
requests.logging.getLogger(requests.__name__).addHandler(ch)
|
requests.logging.getLogger(requests.__name__).addHandler(ch)
|
||||||
|
|
||||||
# required for logging when using a keystone session
|
ks_logger = logging.getLogger("keystoneauth")
|
||||||
self.ks_logger = logging.getLogger("keystoneclient")
|
ks_logger.setLevel(logging.DEBUG)
|
||||||
self.ks_logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
def _delimit_metadata_args(self, argv):
|
def _delimit_metadata_args(self, argv):
|
||||||
"""This function adds -- separator at the appropriate spot
|
"""This function adds -- separator at the appropriate spot
|
||||||
@ -788,7 +788,7 @@ class OpenStackCinderShell(object):
|
|||||||
v2_auth_url = None
|
v2_auth_url = None
|
||||||
v3_auth_url = None
|
v3_auth_url = None
|
||||||
try:
|
try:
|
||||||
ks_discover = discover.Discover(session=session, auth_url=auth_url)
|
ks_discover = discover.Discover(session=session, url=auth_url)
|
||||||
v2_auth_url = ks_discover.url_for('2.0')
|
v2_auth_url = ks_discover.url_for('2.0')
|
||||||
v3_auth_url = ks_discover.url_for('3.0')
|
v3_auth_url = ks_discover.url_for('3.0')
|
||||||
except DiscoveryFailure:
|
except DiscoveryFailure:
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from keystoneclient import fixture
|
from keystoneauth1 import fixture
|
||||||
|
|
||||||
from cinderclient.tests.unit.fixture_data import base
|
from cinderclient.tests.unit.fixture_data import base
|
||||||
from cinderclient.v1 import client as v1client
|
from cinderclient.v1 import client as v1client
|
||||||
|
@ -11,19 +11,20 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
|
from keystoneauth1 import adapter
|
||||||
|
from keystoneauth1 import exceptions as keystone_exception
|
||||||
import mock
|
import mock
|
||||||
|
import six
|
||||||
|
|
||||||
import cinderclient.client
|
import cinderclient.client
|
||||||
import cinderclient.v1.client
|
import cinderclient.v1.client
|
||||||
import cinderclient.v2.client
|
import cinderclient.v2.client
|
||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from keystoneclient import adapter
|
|
||||||
from keystoneclient import exceptions as keystone_exception
|
|
||||||
|
|
||||||
|
|
||||||
class ClientTest(utils.TestCase):
|
class ClientTest(utils.TestCase):
|
||||||
@ -115,7 +116,7 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
mock_response = utils.TestResponse({
|
mock_response = utils.TestResponse({
|
||||||
"status_code": 202,
|
"status_code": 202,
|
||||||
"text": json.dumps(resp),
|
"text": six.b(json.dumps(resp)),
|
||||||
})
|
})
|
||||||
|
|
||||||
# 'request' method of Adaptor will return 202 response
|
# 'request' method of Adaptor will return 202 response
|
||||||
@ -155,7 +156,7 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
mock_response = utils.TestResponse({
|
mock_response = utils.TestResponse({
|
||||||
"status_code": 400,
|
"status_code": 400,
|
||||||
"text": json.dumps(resp),
|
"text": six.b(json.dumps(resp)),
|
||||||
})
|
})
|
||||||
|
|
||||||
# 'request' method of Adaptor will return 400 response
|
# 'request' method of Adaptor will return 400 response
|
||||||
@ -182,7 +183,7 @@ class ClientTest(utils.TestCase):
|
|||||||
|
|
||||||
mock_response = utils.TestResponse({
|
mock_response = utils.TestResponse({
|
||||||
"status_code": 413,
|
"status_code": 413,
|
||||||
"text": json.dumps(resp),
|
"text": six.b(json.dumps(resp)),
|
||||||
})
|
})
|
||||||
|
|
||||||
# 'request' method of Adaptor will return 413 response
|
# 'request' method of Adaptor will return 413 response
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from cinderclient import client
|
from cinderclient import client
|
||||||
@ -24,18 +23,17 @@ fake_response = utils.TestResponse({
|
|||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"text": '{"hi": "there"}',
|
"text": '{"hi": "there"}',
|
||||||
})
|
})
|
||||||
|
|
||||||
fake_response_empty = utils.TestResponse({
|
|
||||||
"status_code": 200,
|
|
||||||
"text": '{"access": {}}'
|
|
||||||
})
|
|
||||||
|
|
||||||
mock_request = mock.Mock(return_value=(fake_response))
|
mock_request = mock.Mock(return_value=(fake_response))
|
||||||
mock_request_empty = mock.Mock(return_value=(fake_response_empty))
|
|
||||||
|
refused_response = utils.TestResponse({
|
||||||
|
"status_code": 400,
|
||||||
|
"text": '[Errno 111] Connection refused',
|
||||||
|
})
|
||||||
|
refused_mock_request = mock.Mock(return_value=(refused_response))
|
||||||
|
|
||||||
bad_400_response = utils.TestResponse({
|
bad_400_response = utils.TestResponse({
|
||||||
"status_code": 400,
|
"status_code": 400,
|
||||||
"text": '{"error": {"message": "n/a", "details": "Terrible!"}}',
|
"text": '',
|
||||||
})
|
})
|
||||||
bad_400_request = mock.Mock(return_value=(bad_400_response))
|
bad_400_request = mock.Mock(return_value=(bad_400_response))
|
||||||
|
|
||||||
@ -74,6 +72,7 @@ def get_authed_client(retries=0):
|
|||||||
cl = get_client(retries=retries)
|
cl = get_client(retries=retries)
|
||||||
cl.management_url = "http://example.com"
|
cl.management_url = "http://example.com"
|
||||||
cl.auth_token = "token"
|
cl.auth_token = "token"
|
||||||
|
cl.get_service_url = mock.Mock(return_value="http://example.com")
|
||||||
return cl
|
return cl
|
||||||
|
|
||||||
|
|
||||||
@ -287,24 +286,13 @@ class ClientTest(utils.TestCase):
|
|||||||
cl = get_client()
|
cl = get_client()
|
||||||
|
|
||||||
# response must not have x-server-management-url header
|
# response must not have x-server-management-url header
|
||||||
@mock.patch.object(requests, "request", mock_request_empty)
|
@mock.patch.object(requests, "request", mock_request)
|
||||||
def test_auth_call():
|
def test_auth_call():
|
||||||
self.assertRaises(exceptions.AuthorizationFailure,
|
self.assertRaises(exceptions.AuthorizationFailure,
|
||||||
cl.authenticate)
|
cl.authenticate)
|
||||||
|
|
||||||
test_auth_call()
|
test_auth_call()
|
||||||
|
|
||||||
def test_auth_not_implemented(self):
|
|
||||||
cl = get_client()
|
|
||||||
|
|
||||||
# response must not have x-server-management-url header
|
|
||||||
# {'hi': 'there'} is neither V2 or V3
|
|
||||||
@mock.patch.object(requests, "request", mock_request)
|
|
||||||
def test_auth_call():
|
|
||||||
self.assertRaises(NotImplementedError, cl.authenticate)
|
|
||||||
|
|
||||||
test_auth_call()
|
|
||||||
|
|
||||||
def test_get_retry_timeout_error(self):
|
def test_get_retry_timeout_error(self):
|
||||||
cl = get_authed_client(retries=1)
|
cl = get_authed_client(retries=1)
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
|
import keystoneauth1.exceptions as ks_exc
|
||||||
|
from keystoneauth1.exceptions import DiscoveryFailure
|
||||||
|
from keystoneauth1 import session
|
||||||
import mock
|
import mock
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import requests_mock
|
import requests_mock
|
||||||
@ -31,8 +34,6 @@ from cinderclient.tests.unit.test_auth_plugins import mock_http_request
|
|||||||
from cinderclient.tests.unit.test_auth_plugins import requested_headers
|
from cinderclient.tests.unit.test_auth_plugins import requested_headers
|
||||||
from cinderclient.tests.unit.fixture_data import keystone_client
|
from cinderclient.tests.unit.fixture_data import keystone_client
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
import keystoneclient.exceptions as ks_exc
|
|
||||||
from keystoneclient.exceptions import DiscoveryFailure
|
|
||||||
|
|
||||||
|
|
||||||
class ShellTest(utils.TestCase):
|
class ShellTest(utils.TestCase):
|
||||||
@ -103,23 +104,27 @@ class ShellTest(utils.TestCase):
|
|||||||
@requests_mock.Mocker()
|
@requests_mock.Mocker()
|
||||||
def test_version_discovery(self, mocker):
|
def test_version_discovery(self, mocker):
|
||||||
_shell = shell.OpenStackCinderShell()
|
_shell = shell.OpenStackCinderShell()
|
||||||
|
sess = session.Session()
|
||||||
|
|
||||||
os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0"
|
os_auth_url = "https://WrongDiscoveryResponse.discovery.com:35357/v2.0"
|
||||||
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
||||||
self.assertRaises(DiscoveryFailure, _shell._discover_auth_versions,
|
|
||||||
None, auth_url=os_auth_url)
|
self.assertRaises(DiscoveryFailure,
|
||||||
|
_shell._discover_auth_versions,
|
||||||
|
sess,
|
||||||
|
auth_url=os_auth_url)
|
||||||
|
|
||||||
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
|
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v2.0"
|
||||||
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
||||||
v2_url, v3_url = _shell._discover_auth_versions(
|
v2_url, v3_url = _shell._discover_auth_versions(sess,
|
||||||
None, auth_url=os_auth_url)
|
auth_url=os_auth_url)
|
||||||
self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
|
self.assertEqual(os_auth_url, v2_url, "Expected v2 url")
|
||||||
self.assertIsNone(v3_url, "Expected no v3 url")
|
self.assertIsNone(v3_url, "Expected no v3 url")
|
||||||
|
|
||||||
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
|
os_auth_url = "https://DiscoveryNotSupported.discovery.com:35357/v3.0"
|
||||||
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
self.register_keystone_auth_fixture(mocker, os_auth_url)
|
||||||
v2_url, v3_url = _shell._discover_auth_versions(
|
v2_url, v3_url = _shell._discover_auth_versions(sess,
|
||||||
None, auth_url=os_auth_url)
|
auth_url=os_auth_url)
|
||||||
self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
|
self.assertEqual(os_auth_url, v3_url, "Expected v3 url")
|
||||||
self.assertIsNone(v2_url, "Expected no v2 url")
|
self.assertIsNone(v2_url, "Expected no v2 url")
|
||||||
|
|
||||||
@ -142,18 +147,18 @@ class ShellTest(utils.TestCase):
|
|||||||
for count in range(1, 4):
|
for count in range(1, 4):
|
||||||
self.list_volumes_on_service(count)
|
self.list_volumes_on_service(count)
|
||||||
|
|
||||||
@mock.patch('keystoneclient.auth.identity.v2.Password')
|
@mock.patch('keystoneauth1.identity.v2.Password')
|
||||||
@mock.patch('keystoneclient.adapter.Adapter.get_token',
|
@mock.patch('keystoneauth1.adapter.Adapter.get_token',
|
||||||
side_effect=ks_exc.ConnectionRefused())
|
side_effect=ks_exc.ConnectFailure())
|
||||||
@mock.patch('keystoneclient.discover.Discover',
|
@mock.patch('keystoneauth1.discover.Discover',
|
||||||
side_effect=ks_exc.ConnectionRefused())
|
side_effect=ks_exc.ConnectFailure())
|
||||||
@mock.patch('sys.stdin', side_effect=mock.MagicMock)
|
@mock.patch('sys.stdin', side_effect=mock.MagicMock)
|
||||||
@mock.patch('getpass.getpass', return_value='password')
|
@mock.patch('getpass.getpass', return_value='password')
|
||||||
def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
|
def test_password_prompted(self, mock_getpass, mock_stdin, mock_discover,
|
||||||
mock_token, mock_password):
|
mock_token, mock_password):
|
||||||
self.make_env(exclude='OS_PASSWORD')
|
self.make_env(exclude='OS_PASSWORD')
|
||||||
_shell = shell.OpenStackCinderShell()
|
_shell = shell.OpenStackCinderShell()
|
||||||
self.assertRaises(ks_exc.ConnectionRefused, _shell.main, ['list'])
|
self.assertRaises(ks_exc.ConnectFailure, _shell.main, ['list'])
|
||||||
mock_getpass.assert_called_with('OS Password: ')
|
mock_getpass.assert_called_with('OS Password: ')
|
||||||
# Verify that Password() is called with value of param 'password'
|
# Verify that Password() is called with value of param 'password'
|
||||||
# equal to mock_getpass.return_value.
|
# equal to mock_getpass.return_value.
|
||||||
@ -223,7 +228,7 @@ class ShellTest(utils.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(False, _shell.cs.client.verify_cert)
|
self.assertEqual(False, _shell.cs.client.verify_cert)
|
||||||
|
|
||||||
@mock.patch('keystoneclient.session.Session.__init__',
|
@mock.patch('keystoneauth1.session.Session.__init__',
|
||||||
side_effect=RuntimeError())
|
side_effect=RuntimeError())
|
||||||
def test_http_client_with_cert(self, mock_session):
|
def test_http_client_with_cert(self, mock_session):
|
||||||
_shell = shell.OpenStackCinderShell()
|
_shell = shell.OpenStackCinderShell()
|
||||||
@ -234,7 +239,7 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assertRaises(RuntimeError, _shell.main, args)
|
self.assertRaises(RuntimeError, _shell.main, args)
|
||||||
mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)
|
mock_session.assert_called_once_with(cert='minnie', verify=mock.ANY)
|
||||||
|
|
||||||
@mock.patch('keystoneclient.session.Session.__init__',
|
@mock.patch('keystoneauth1.session.Session.__init__',
|
||||||
side_effect=RuntimeError())
|
side_effect=RuntimeError())
|
||||||
def test_http_client_with_cert_and_key(self, mock_session):
|
def test_http_client_with_cert_and_key(self, mock_session):
|
||||||
_shell = shell.OpenStackCinderShell()
|
_shell = shell.OpenStackCinderShell()
|
||||||
|
@ -87,25 +87,34 @@ class FixturedTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TestResponse(requests.Response):
|
class TestResponse(requests.Response):
|
||||||
"""Class used to wrap requests.Response and provide some
|
"""Class used to wrap requests.Response.
|
||||||
convenience to initialize with a dict.
|
|
||||||
|
Provides some convenience to initialize with a dict.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
super(TestResponse, self).__init__()
|
||||||
|
self._content = None
|
||||||
self._text = None
|
self._text = None
|
||||||
super(TestResponse, self)
|
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
self.status_code = data.get('status_code', None)
|
self.status_code = data.get('status_code', None)
|
||||||
self.headers = data.get('headers', None)
|
self.headers = data.get('headers', None)
|
||||||
self.reason = data.get('reason', '')
|
self.reason = data.get('reason', '')
|
||||||
# Fake the text attribute to streamline Response creation
|
# Fake text and content attributes to streamline Response creation
|
||||||
self._text = data.get('text', None)
|
text = data.get('text', None)
|
||||||
|
self._content = text
|
||||||
|
self._text = text
|
||||||
else:
|
else:
|
||||||
self.status_code = data
|
self.status_code = data
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.__dict__ == other.__dict__
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
@property
|
||||||
|
def content(self):
|
||||||
|
return self._content
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
return self._text
|
return self._text
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
pbr>=1.6 # Apache-2.0
|
pbr>=1.6 # Apache-2.0
|
||||||
PrettyTable<0.8,>=0.7 # BSD
|
PrettyTable<0.8,>=0.7 # BSD
|
||||||
python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0
|
keystoneauth1>=2.7.0 # Apache-2.0
|
||||||
requests>=2.10.0 # Apache-2.0
|
requests>=2.10.0 # Apache-2.0
|
||||||
simplejson>=2.2.0 # MIT
|
simplejson>=2.2.0 # MIT
|
||||||
Babel>=2.3.4 # BSD
|
Babel>=2.3.4 # BSD
|
||||||
|
Loading…
x
Reference in New Issue
Block a user