Add keystone v3 support to client

All clients should support keystone v3 in kilo. This patch adds
support for keystone sessions, keystone endpoint discovery and support
for the CLI to provide keystone v3 options.

Bumps python-keystoneclient to >=0.11.1 to bring in session support.

This patch was previously dependent on a separate one to add session
support but that work has been squashed into this and its review
(https://review.openstack.org/#/c/131309) will be abandoned.

Co-Authored-By: David Hu <david.hu@hp.com>
Change-Id: I0f4931f1e6566a7b83280302f884248c8db909c5
Implements: blueprint support-keystone-v3
This commit is contained in:
Steve McLellan 2014-10-27 19:34:29 -05:00
parent 8efcc05d63
commit 65f51420e5
7 changed files with 305 additions and 162 deletions

View File

@ -18,6 +18,7 @@ import logging
import os
import socket
import keystoneclient.adapter as keystone_adapter
import requests
import six
from six.moves.urllib import parse
@ -289,3 +290,79 @@ class HTTPClient(object):
def patch(self, url, **kwargs):
return self.client_request("PATCH", url, **kwargs)
class SessionClient(keystone_adapter.LegacyJsonAdapter):
def request(self, url, method, **kwargs):
raise_exc = kwargs.pop('raise_exc', True)
resp, body = super(SessionClient, self).request(url,
method,
raise_exc=False,
**kwargs)
if raise_exc and resp.status_code >= 400:
raise exc.from_response(resp)
return resp, body
def json_request(self, method, url, **kwargs):
# Legacy adapter expects the payload in 'body', but
# will pass it to the non-legacy adapter in the
# 'json' spot so encoding happens later
if 'data' in kwargs:
if 'body' in kwargs:
raise ValueError("Can't provide both 'data' and "
"'body' to a request")
kwargs['body'] = kwargs.pop('data')
# The argument order is different, beware
return self.request(url, method, **kwargs)
def json_patch_request(self, url, method='PATCH', **kwargs):
content_type = 'application/murano-packages-json-patch'
return self.json_request(
method, url, content_type=content_type, **kwargs)
def raw_request(self, method, url, **kwargs):
# A non-json request; instead of calling
# super.request, need to call the grandparent
# adapter.request
raise_exc = kwargs.pop('raise_exc', True)
if 'body' in kwargs:
if 'data' in kwargs:
raise ValueError("Can't provide both 'data' and "
"'body' to a request")
kwargs['data'] = kwargs.pop('body')
resp = keystone_adapter.Adapter.request(self,
url,
method,
raise_exc=False,
**kwargs)
body = resp.text
if raise_exc and resp.status_code >= 400:
raise exc.from_response(resp)
return resp, body
def _construct_http_client(*args, **kwargs):
session = kwargs.pop('session', None)
auth = kwargs.pop('auth', None)
if session:
service_type = kwargs.pop('service_type', None)
endpoint_type = kwargs.pop('endpoint_type', None)
region_name = kwargs.pop('region_name', None)
service_name = kwargs.pop('service_name', None)
return SessionClient(session=session,
auth=auth,
interface=endpoint_type,
service_type=service_type,
region_name=region_name,
service_name=service_name,
user_agent='python-muranoclient',
**kwargs)
else:
return HTTPClient(*args, **kwargs)

View File

@ -22,10 +22,13 @@ import argparse
import logging
import sys
from keystoneclient.v2_0 import client as ksclient
from keystoneclient.auth.identity.generic import password
from keystoneclient.auth.identity.generic import token
from keystoneclient.auth.identity import v3 as identity
from keystoneclient import session as ksession
import six
from muranoclient import client as apiclient
from muranoclient import client as murano_client
from muranoclient.common import utils
from muranoclient.openstack.common.apiclient import exceptions as exc
from muranoclient.openstack.common import strutils
@ -35,7 +38,15 @@ logger = logging.getLogger(__name__)
class MuranoShell(object):
def _append_global_identity_args(self, parser):
# Register the CLI arguments that have moved to the session object.
ksession.Session.register_cli_options(parser)
identity.Password.register_argparse_arguments(parser)
def get_base_parser(self):
parser = argparse.ArgumentParser(
prog='murano',
description=__doc__.strip(),
@ -59,52 +70,25 @@ class MuranoShell(object):
default=False, action="store_true",
help="Print more verbose output")
parser.add_argument('-k', '--insecure',
default=False,
action='store_true',
help="Explicitly allow muranoclient to perform "
"\"insecure\" SSL (https) requests. "
"The server's certificate will "
"not be verified against any certificate "
"authorities. This option should be used "
"with caution.")
parser.add_argument('--os-cacert',
metavar='<ca-certificate>',
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]')
# os-cert, os-key, insecure, ca-file are all added
# by keystone session register_cli_opts later
parser.add_argument('--cert-file',
help='Path of certificate file to use in SSL '
'connection. This file can optionally be '
'prepended with the private key.')
dest='os_cert',
help='DEPRECATED! Use --os-cert.')
parser.add_argument('--key-file',
help='Path of client key to use in SSL connection.'
' This option is not necessary if your '
'key is prepended to your cert file.')
dest='os_key',
help='DEPRECATED! Use --os-key.')
parser.add_argument('--ca-file',
help='Path of CA SSL certificate(s) used to verify'
' the remote server certificate. Without '
'this option glance looks for the default '
'system CA certificates.')
dest='os_cacert',
help='DEPRECATED! Use --os-cacert.')
parser.add_argument('--api-timeout',
help='Number of seconds to wait for an '
'API response, '
'defaults to system socket timeout')
parser.add_argument('--os-username',
default=utils.env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME]')
parser.add_argument('--os-password',
default=utils.env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD]')
parser.add_argument('--os-tenant-id',
default=utils.env('OS_TENANT_ID'),
help='Defaults to env[OS_TENANT_ID]')
@ -113,10 +97,6 @@ class MuranoShell(object):
default=utils.env('OS_TENANT_NAME'),
help='Defaults to env[OS_TENANT_NAME]')
parser.add_argument('--os-auth-url',
default=utils.env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL]')
parser.add_argument('--os-region-name',
default=utils.env('OS_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME]')
@ -130,7 +110,6 @@ class MuranoShell(object):
action='store_true',
help="Do not contact keystone for a token. "
"Defaults to env[OS_NO_CLIENT_AUTH].")
parser.add_argument('--murano-url',
default=utils.env('MURANO_URL'),
help='Defaults to env[MURANO_URL]')
@ -154,6 +133,8 @@ class MuranoShell(object):
action='store_true',
help='Send os-username and os-password to murano.')
self._append_global_identity_args(parser)
return parser
def get_subcommand_parser(self, version):
@ -196,38 +177,20 @@ class MuranoShell(object):
subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback)
def _get_ksclient(self, **kwargs):
"""Get an endpoint and auth token from Keystone.
:param username: name of user
:param password: user's password
:param tenant_id: unique identifier of tenant
:param tenant_name: name of tenant
:param auth_url: endpoint to authenticate against
"""
kc_args = {
'auth_url': kwargs.get('auth_url'),
'insecure': kwargs.get('insecure'),
'cacert': kwargs.get('cacert')}
if kwargs.get('tenant_id'):
kc_args['tenant_id'] = kwargs.get('tenant_id')
def _get_keystone_auth(self, session, auth_url, **kwargs):
auth_token = kwargs.pop('auth_token', None)
if auth_token:
return token.Token(auth_url, auth_token, **kwargs)
else:
kc_args['tenant_name'] = kwargs.get('tenant_name')
if kwargs.get('token'):
kc_args['token'] = kwargs.get('token')
else:
kc_args['username'] = kwargs.get('username')
kc_args['password'] = kwargs.get('password')
return ksclient.Client(**kc_args)
def _get_endpoint(self, client, **kwargs):
"""Get an endpoint using the provided keystone client."""
return client.service_catalog.url_for(
service_type=kwargs.get('service_type') or 'application_catalog',
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
user_domain_id = kwargs.pop('user_domain_id')
user_domain_name = kwargs.pop('user_domain_name')
return password.Password(auth_url,
username=kwargs.pop('username'),
user_id=kwargs.pop('user_id'),
password=kwargs.pop('password'),
user_domain_id=user_domain_id,
user_domain_name=user_domain_name,
**kwargs)
def _setup_logging(self, debug):
log_lvl = logging.DEBUG if debug else logging.WARNING
@ -251,6 +214,9 @@ class MuranoShell(object):
subcommand_parser = self.get_subcommand_parser(api_version)
self.parser = subcommand_parser
keystone_session = None
keystone_auth = None
# Handle top-level --help/-h before attempting to parse
# a command off the command line.
if (not args and options.help) or not argv:
@ -274,11 +240,14 @@ class MuranoShell(object):
" or a token via --os-auth-token or"
" env[OS_AUTH_TOKEN]")
if not args.os_password and not args.os_auth_token:
raise exc.CommandError("You must provide a password via"
" either --os-password or env[OS_PASSWORD]"
" or a token via --os-auth-token or"
" env[OS_AUTH_TOKEN]")
if not any([args.os_tenant_name, args.os_tenant_id,
args.os_project_id, args.os_project_name]):
raise exc.CommandError("You must provide a project name or"
" project id via --os-project-name,"
" --os-project-id, env[OS_PROJECT_ID]"
" or env[OS_PROJECT_NAME]. You may"
" use os-project and os-tenant"
" interchangeably.")
if args.os_no_client_auth:
if not args.murano_url:
@ -291,58 +260,76 @@ class MuranoShell(object):
# service catalog, it's not required if os_no_client_auth is
# specified, neither is the auth URL.
if not (args.os_tenant_id or args.os_tenant_name):
raise exc.CommandError("You must provide a tenant name "
"or tenant id via --os-tenant-name, "
"--os-tenant-id, env[OS_TENANT_NAME] "
"or env[OS_TENANT_ID]")
raise exc.CommandError(
"You must provide a tenant name "
"or tenant id via --os-tenant-name, "
"--os-tenant-id, env[OS_TENANT_NAME] "
"or env[OS_TENANT_ID] OR a project name "
"or project id via --os-project-name, "
"--os-project-id, env[OS_PROJECT_ID] or "
"env[OS_PROJECT_NAME]")
if not args.os_auth_url:
raise exc.CommandError("You must provide an auth url via"
" either --os-auth-url or via"
" env[OS_AUTH_URL]")
kwargs = {
'username': args.os_username,
'password': args.os_password,
'token': args.os_auth_token,
'tenant_id': args.os_tenant_id,
'tenant_name': args.os_tenant_name,
'auth_url': args.os_auth_url,
'service_type': args.os_service_type,
'endpoint_type': args.os_endpoint_type,
'insecure': args.insecure,
'cacert': args.os_cacert,
'include_pass': args.include_password
}
endpoint = args.murano_url
if not args.os_no_client_auth:
_ksclient = self._get_ksclient(**kwargs)
token = args.os_auth_token or _ksclient.auth_token
if args.os_no_client_auth:
# Authenticate through murano, don't use session
kwargs = {
'token': token,
'insecure': args.insecure,
'ca_file': args.ca_file,
'cert_file': args.cert_file,
'key_file': args.key_file,
'username': args.os_username,
'password': args.os_password,
'endpoint_type': args.os_endpoint_type,
'include_pass': args.include_password
'auth_token': args.os_auth_token,
'auth_url': args.os_auth_url,
'token': args.os_auth_token,
'insecure': args.insecure,
'timeout': args.api_timeout
}
else:
# Create a keystone session and keystone auth
keystone_session = ksession.Session.load_from_cli_options(args)
project_id = args.os_project_id or args.os_tenant_id
project_name = args.os_project_name or args.os_tenant_name
if args.os_region_name:
kwargs['region_name'] = args.os_region_name
keystone_session = ksession.Session.load_from_cli_options(args)
keystone_auth = self._get_keystone_auth(
keystone_session,
args.os_auth_url,
username=args.os_username,
user_id=args.os_user_id,
user_domain_id=args.os_user_domain_id,
user_domain_name=args.os_user_domain_name,
password=args.os_password,
auth_token=args.os_auth_token,
project_id=project_id,
project_name=project_name,
project_domain_id=args.os_project_domain_id,
project_domain_name=args.os_project_domain_name)
endpoint_type = args.os_endpoint_type or 'publicURL'
service_type = args.os_service_type or 'application_catalog'
if not endpoint:
endpoint = self._get_endpoint(_ksclient, **kwargs)
endpoint = keystone_auth.get_endpoint(
keystone_session,
service_type=service_type,
region_name=args.os_region_name)
kwargs = {
'session': keystone_session,
'auth': keystone_auth,
'service_type': service_type,
'endpoint_type': endpoint_type,
'region_name': args.os_region_name,
}
if args.api_timeout:
kwargs['timeout'] = args.api_timeout
client = apiclient.Client(api_version, endpoint, **kwargs)
client = murano_client.Client(api_version, endpoint, **kwargs)
args.func(client, args)

View File

@ -18,7 +18,11 @@ import sys
import tempfile
import fixtures
from keystoneclient import fixture
from keystoneclient.fixture import v2 as ks_v2_fixture
from keystoneclient.fixture import v3 as ks_v3_fixture
import mock
import requests_mock
import six
from testtools import matchers
@ -34,12 +38,22 @@ FIXTURE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
FAKE_ENV = {'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where'}
'OS_AUTH_URL': 'http://no.where/v2.0'}
FAKE_ENV2 = {'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_ID': 'tenant_id',
'OS_AUTH_URL': 'http://no.where'}
'OS_AUTH_URL': 'http://no.where/v2.0'}
FAKE_ENV_v3 = {'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_ID': 'tenant_id',
'OS_USER_DOMAIN_NAME': 'domain_name',
'OS_AUTH_URL': 'http://no.where/3'}
def _create_ver_list(versions):
return {'versions': {'values': versions}}
class TestArgs(object):
@ -52,18 +66,24 @@ class ShellTest(base.TestCaseShell):
env = dict((k, v) for k, v in fake_env.items() if k != exclude)
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
def setUp(self):
super(ShellTest, self).setUp()
self.useFixture(fixtures.MonkeyPatch(
'keystoneclient.v2_0.client.Client', mock.MagicMock))
self.client = mock.MagicMock()
# We don't set an endpoint (client.service_catalog.url_for is a mock)
# and get_proxy_url doesn't like that. We don't care about testing
# that functionality, so mock it out.
class ShellCommandTest(ShellTest):
_msg_no_tenant_project = ('You must provide a project name or project'
' id via --os-project-name, --os-project-id,'
' env[OS_PROJECT_ID] or env[OS_PROJECT_NAME].'
' You may use os-project and os-tenant'
' interchangeably.',)
def setUp(self):
super(ShellCommandTest, self).setUp()
def get_auth_endpoint(bound_self, args):
return ('test', {})
self.useFixture(fixtures.MonkeyPatch(
'muranoclient.common.http.HTTPClient.get_proxy_url',
mock.MagicMock))
'muranoclient.shell.MuranoShell._get_endpoint_and_kwargs',
get_auth_endpoint))
self.client = mock.MagicMock()
def shell(self, argstr, exitcodes=(0,)):
orig = sys.stdout
@ -85,6 +105,21 @@ class ShellTest(base.TestCaseShell):
sys.stderr = orig_stderr
return (stdout, stderr)
def register_keystone_discovery_fixture(self, mreq):
v2_url = "http://no.where/v2.0"
v2_version = fixture.V2Discovery(v2_url)
mreq.register_uri('GET', v2_url, json=_create_ver_list([v2_version]),
status_code=200)
def register_keystone_token_fixture(self, mreq):
v2_token = ks_v2_fixture.Token(token_id='token')
service = v2_token.add_service('application_catalog')
service.add_endpoint('http://no.where', region='RegionOne')
mreq.register_uri('POST',
'http://no.where/v2.0/tokens',
json=v2_token,
status_code=200)
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
@ -133,10 +168,7 @@ class ShellTest(base.TestCaseShell):
self.fail('CommandError not raised')
def test_no_tenant_name(self):
required = ('You must provide a tenant name '
'or tenant id via --os-tenant-name, '
'--os-tenant-id, env[OS_TENANT_NAME] '
'or env[OS_TENANT_ID]',)
required = self._msg_no_tenant_project
self.make_env(exclude='OS_TENANT_NAME')
try:
self.shell('package-list')
@ -146,10 +178,7 @@ class ShellTest(base.TestCaseShell):
self.fail('CommandError not raised')
def test_no_tenant_id(self):
required = ('You must provide a tenant name '
'or tenant id via --os-tenant-name, '
'--os-tenant-id, env[OS_TENANT_NAME] '
'or env[OS_TENANT_ID]',)
required = self._msg_no_tenant_project
self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2)
try:
self.shell('package-list')
@ -170,15 +199,19 @@ class ShellTest(base.TestCaseShell):
self.fail('CommandError not raised')
@mock.patch('muranoclient.v1.packages.PackageManager')
def test_package_list(self, mock_package_manager):
@requests_mock.Mocker()
def test_package_list(self, mock_package_manager, m_requests):
self.client.packages = mock_package_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('package-list')
self.client.packages.filter.assert_called_once_with(
include_disabled=False)
@mock.patch('muranoclient.v1.packages.PackageManager')
def test_package_show(self, mock_package_manager):
@requests_mock.Mocker()
def test_package_show(self, mock_package_manager, m_requests):
self.client.packages = mock_package_manager()
mock_package = mock.MagicMock()
mock_package.class_definitions = ''
@ -187,57 +220,77 @@ class ShellTest(base.TestCaseShell):
mock_package.description = ''
self.client.packages.get.return_value = mock_package
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('package-show 1234')
self.client.packages.get.assert_called_once_with('1234')
@mock.patch('muranoclient.v1.packages.PackageManager')
def test_package_delete(self, mock_package_manager):
@requests_mock.Mocker()
def test_package_delete(self, mock_package_manager, m_requests):
self.client.packages = mock_package_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('package-delete 1234')
self.client.packages.delete.assert_called_once_with('1234')
@mock.patch('muranoclient.v1.environments.EnvironmentManager')
def test_environment_delete(self, mock_manager):
@requests_mock.Mocker()
def test_environment_delete(self, mock_manager, m_requests):
self.client.environments = mock_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('environment-delete env1 env2')
self.client.environments.delete.assert_has_calls([
mock.call('env1'), mock.call('env2')])
@mock.patch('muranoclient.v1.environments.EnvironmentManager')
def test_environment_rename(self, mock_manager):
@requests_mock.Mocker()
def test_environment_rename(self, mock_manager, m_requests):
self.client.environments = mock_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('environment-rename env-id new-name')
self.client.environments.update.assert_called_once_with(
'env-id', 'new-name')
@mock.patch('muranoclient.v1.environments.EnvironmentManager')
def test_environment_show(self, mock_manager):
@requests_mock.Mocker()
def test_environment_show(self, mock_manager, m_requests):
self.client.environments = mock_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('environment-show env-id')
self.client.environments.get.assert_called_once_with('env-id')
@mock.patch('muranoclient.v1.deployments.DeploymentManager')
def test_deployments_show(self, mock_manager):
@requests_mock.Mocker()
def test_deployments_show(self, mock_manager, m_requests):
self.client.deployments = mock_manager()
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
self.shell('deployment-list env-id')
self.client.deployments.list.assert_called_once_with('env-id')
class ShellPackagesOperations(ShellTest):
class ShellPackagesOperations(ShellCommandTest):
def tearDown(self):
super(ShellPackagesOperations, self).tearDown()
def test_create_hot_based_package(self):
@requests_mock.Mocker()
def test_create_hot_based_package(self, m_requests):
self.useFixture(fixtures.MonkeyPatch(
'muranoclient.v1.client.Client', mock.MagicMock))
heat_template = os.path.join(FIXTURE_DIR, 'heat-template.yaml')
logo = os.path.join(FIXTURE_DIR, 'logo.png')
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
with tempfile.NamedTemporaryFile() as f:
RESULT_PACKAGE = f.name
c = "package-create --template={0} --output={1} -l={2}".format(
@ -247,13 +300,16 @@ class ShellPackagesOperations(ShellTest):
"Application package "
"is available at {0}".format(RESULT_PACKAGE))
def test_create_mpl_package(self):
@requests_mock.Mocker()
def test_create_mpl_package(self, m_requests):
self.useFixture(fixtures.MonkeyPatch(
'muranoclient.v1.client.Client', mock.MagicMock))
classes_dir = os.path.join(FIXTURE_DIR, 'test-app', 'Classes')
resources_dir = os.path.join(FIXTURE_DIR, 'test-app', 'Resources')
ui = os.path.join(FIXTURE_DIR, 'test-app', 'ui.yaml')
self.make_env()
self.register_keystone_discovery_fixture(m_requests)
self.register_keystone_token_fixture(m_requests)
with tempfile.NamedTemporaryFile() as f:
RESULT_PACKAGE = f.name
stdout, stderr = self.shell(
@ -299,3 +355,27 @@ class ShellPackagesOperations(ShellTest):
self.assertRaises(IOError,
v1_shell.do_package_import, self.client, args)
class ShellPackagesOperationsV3(ShellPackagesOperations):
def make_env(self, exclude=None, fake_env=FAKE_ENV):
if 'OS_AUTH_URL' in fake_env:
fake_env.update({'OS_AUTH_URL': 'http://no.where/v3'})
env = dict((k, v) for k, v in fake_env.items() if k != exclude)
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
def register_keystone_discovery_fixture(self, mreq):
v3_url = "http://no.where/v3"
v3_version = fixture.V3Discovery(v3_url)
mreq.register_uri('GET', v3_url, json=_create_ver_list([v3_version]),
status_code=200)
def register_keystone_token_fixture(self, mreq):
v3_token = ks_v3_fixture.Token()
service = v3_token.add_service('application_catalog')
service.add_standard_endpoints(public='http://no.where')
mreq.register_uri('POST',
'http://no.where/v3/auth/tokens',
json=v3_token,
headers={'X-Subject-Token': 'tokenid'},
status_code=200)

View File

@ -23,7 +23,7 @@ from muranoclient.v1 import services
from muranoclient.v1 import sessions
class Client(http.HTTPClient):
class Client(object):
"""Client for the Murano v1 API.
:param string endpoint: A user-supplied endpoint URL for the service.
@ -34,14 +34,14 @@ class Client(http.HTTPClient):
def __init__(self, *args, **kwargs):
"""Initialize a new client for the Murano v1 API."""
super(Client, self).__init__(*args, **kwargs)
self.environments = environments.EnvironmentManager(self)
self.sessions = sessions.SessionManager(self)
self.services = services.ServiceManager(self)
self.deployments = deployments.DeploymentManager(self)
self.http_client = http._construct_http_client(*args, **kwargs)
self.environments = environments.EnvironmentManager(self.http_client)
self.sessions = sessions.SessionManager(self.http_client)
self.services = services.ServiceManager(self.http_client)
self.deployments = deployments.DeploymentManager(self.http_client)
self.request_statistics = \
request_statistics.RequestStatisticsManager(self)
request_statistics.RequestStatisticsManager(self.http_client)
self.instance_statistics = \
instance_statistics.InstanceStatisticsManager(self)
self.packages = packages.PackageManager(self)
self.actions = actions.ActionManager(self)
instance_statistics.InstanceStatisticsManager(self.http_client)
self.packages = packages.PackageManager(self.http_client)
self.actions = actions.ActionManager(self.http_client)

View File

@ -12,16 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import urllib
import requests
import yaml
from muranoclient.common import base
from muranoclient.common import exceptions
from muranoclient.common import http
from muranoclient.openstack.common import jsonutils
DEFAULT_PAGE_SIZE = 20
@ -53,15 +50,16 @@ class PackageManager(base.Manager):
response_key='categories', obj_class=Category)
def create(self, data, files):
data = {'data': json.dumps(data)}
url = '{0}/v1/catalog/packages'.format(self.api.endpoint)
headers = {'X-Auth-Token': self.api.auth_token}
response = requests.post(url, data=data, files=files, headers=headers)
http.HTTPClient.log_http_response(response)
response, body = self.api.raw_request(
'POST',
'/v1/catalog/packages',
body=data,
files=files)
if not response.ok:
setattr(response, 'status', response.status_code)
raise exceptions.from_response(response)
return self.resource_class(self, json.loads(response.content))
body = jsonutils.loads(response.text)
return self.resource_class(self, body)
def get(self, app_id):
return self._get('/v1/catalog/packages/{0}'.format(app_id))

View File

@ -4,7 +4,7 @@
pbr>=0.6,!=0.7,<1.0
argparse
PrettyTable>=0.7,<0.8
python-keystoneclient>=0.10.0
python-keystoneclient>=0.11.1
httplib2>=0.7.5
iso8601>=0.1.9
six>=1.7.0

View File

@ -4,6 +4,7 @@ coverage>=3.6
discover
fixtures>=0.3.14
mock>=1.0
requests-mock>=0.5.1 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=0.9.34