diff --git a/magnumclient/common/httpclient.py b/magnumclient/common/httpclient.py index c11196e..54c06d7 100644 --- a/magnumclient/common/httpclient.py +++ b/magnumclient/common/httpclient.py @@ -34,6 +34,7 @@ USER_AGENT = 'python-magnumclient' CHUNKSIZE = 1024 * 64 # 64kB API_VERSION = '/v1' +DEFAULT_API_VERSION = 'latest' def _extract_error_json(body): @@ -51,8 +52,11 @@ def _extract_error_json(body): else: error_body = body_json['errors'][0] raw_msg = error_body['title'] - error_json = {'faultstring': error_body['title'], - 'debuginfo': error_body['detail']} + error_json = {'faultstring': error_body['title']} + if 'detail' in error_body: + error_json['debuginfo'] = error_body['detail'] + elif 'description' in error_body: + error_json['debuginfo'] = error_body['description'] except ValueError: return {} @@ -62,10 +66,11 @@ def _extract_error_json(body): class HTTPClient(object): - def __init__(self, endpoint, **kwargs): + def __init__(self, endpoint, api_version=DEFAULT_API_VERSION, **kwargs): self.endpoint = endpoint self.auth_token = kwargs.get('token') self.auth_ref = kwargs.get('auth_ref') + self.api_version = kwargs.get('api_version') self.connection_params = self.get_connection_params(endpoint, **kwargs) @staticmethod @@ -152,6 +157,10 @@ class HTTPClient(object): # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) + if self.api_version: + version_string = 'container-infra %s' % self.api_version + kwargs['headers'].setdefault( + 'OpenStack-API-Version', version_string) if self.auth_token: kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) @@ -304,7 +313,10 @@ class VerifiedHTTPSConnection(six.moves.http_client.HTTPSConnection): class SessionClient(adapter.LegacyJsonAdapter): """HTTP client based on Keystone client session.""" - def __init__(self, user_agent=USER_AGENT, logger=LOG, *args, **kwargs): + def __init__(self, user_agent=USER_AGENT, logger=LOG, + api_version=DEFAULT_API_VERSION, *args, **kwargs): + self.user_agent = USER_AGENT + self.api_version = api_version super(SessionClient, self).__init__(*args, **kwargs) def _http_request(self, url, method, **kwargs): @@ -315,6 +327,14 @@ class SessionClient(adapter.LegacyJsonAdapter): kwargs.setdefault('auth', self.auth) kwargs.setdefault('endpoint_override', self.endpoint_override) + # Copy the kwargs so we can reuse the original in case of redirects + kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) + kwargs['headers'].setdefault('User-Agent', self.user_agent) + if self.api_version: + version_string = 'container-infra %s' % self.api_version + kwargs['headers'].setdefault( + 'OpenStack-API-Version', version_string) + endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) endpoint_filter.setdefault('service_type', self.service_type) @@ -340,8 +360,6 @@ class SessionClient(adapter.LegacyJsonAdapter): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') - kwargs['headers'].setdefault( - 'OpenStack-API-Version', 'container-infra latest') if 'body' in kwargs: kwargs['data'] = json.dumps(kwargs.pop('body')) diff --git a/magnumclient/shell.py b/magnumclient/shell.py index 00791c0..5ddb992 100644 --- a/magnumclient/shell.py +++ b/magnumclient/shell.py @@ -55,7 +55,7 @@ from magnumclient.v1 import client as client_v1 from magnumclient.v1 import shell as shell_v1 from magnumclient import version -DEFAULT_API_VERSION = '1' +LATEST_API_VERSION = ('1', 'latest') DEFAULT_INTERFACE = 'public' DEFAULT_SERVICE_TYPE = 'container-infra' @@ -386,7 +386,7 @@ class OpenStackMagnumShell(object): metavar='', default=cliutils.env( 'MAGNUM_API_VERSION', - default=DEFAULT_API_VERSION), + default='latest'), help='Accepts "api", ' 'defaults to env[MAGNUM_API_VERSION].') parser.add_argument('--magnum_api_version', @@ -429,7 +429,7 @@ class OpenStackMagnumShell(object): try: actions_modules = { - '1': shell_v1.COMMAND_MODULES, + '1': shell_v1.COMMAND_MODULES }[version] except KeyError: actions_modules = shell_v1.COMMAND_MODULES @@ -487,6 +487,34 @@ class OpenStackMagnumShell(object): logging.basicConfig(level=logging.CRITICAL, format=streamformat) + def _check_version(self, api_version): + if api_version == 'latest': + return LATEST_API_VERSION + else: + try: + versions = tuple(int(i) for i in api_version.split('.')) + except ValueError: + versions = () + if len(versions) == 1: + # Default value of magnum_api_version is '1'. + # If user not specify the value of api version, not passing + # headers at all. + magnum_api_version = None + elif len(versions) == 2: + magnum_api_version = api_version + # In the case of '1.0' + if versions[1] == 0: + magnum_api_version = None + else: + msg = _("The requested API version %(ver)s is an unexpected " + "format. Acceptable formats are 'X', 'X.Y', or the " + "literal string '%(latest)s'." + ) % {'ver': api_version, 'latest': 'latest'} + raise exc.CommandError(msg) + + api_major_version = versions[0] + return (api_major_version, magnum_api_version) + def main(self, argv): # NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map. @@ -506,8 +534,12 @@ class OpenStackMagnumShell(object): spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' + # build available subcommands based on version + (api_major_version, magnum_api_version) = ( + self._check_version(options.magnum_api_version)) + subcommand_parser = ( - self.get_subcommand_parser(options.magnum_api_version) + self.get_subcommand_parser(api_major_version) ) self.parser = subcommand_parser @@ -566,7 +598,7 @@ class OpenStackMagnumShell(object): try: client = { '1': client_v1, - }[options.magnum_api_version] + }[api_major_version] except KeyError: client = client_v1 @@ -595,6 +627,7 @@ class OpenStackMagnumShell(object): magnum_url=args.os_endpoint_override, interface=args.os_interface, insecure=args.insecure, + api_version=args.magnum_api_version, ) args.func(self.cs, args) diff --git a/magnumclient/tests/test_shell.py b/magnumclient/tests/test_shell.py index a0c9e55..b61f211 100644 --- a/magnumclient/tests/test_shell.py +++ b/magnumclient/tests/test_shell.py @@ -222,7 +222,8 @@ class ShellTest(utils.TestCase): 'project_domain_id': None, 'project_domain_name': None, 'region_name': None, 'service_type': 'container-infra', 'user_id': None, 'username': 'username', - 'user_domain_id': None, 'user_domain_name': None + 'user_domain_id': None, 'user_domain_name': None, + 'api_version': 'latest' } @mock.patch('magnumclient.v1.client.Client') diff --git a/magnumclient/tests/v1/test_client.py b/magnumclient/tests/v1/test_client.py index 1cccc0d..ed6c36f 100644 --- a/magnumclient/tests/v1/test_client.py +++ b/magnumclient/tests/v1/test_client.py @@ -51,6 +51,7 @@ class ClientInitializeTest(testtools.TestCase): kwargs = self._load_service_type_kwargs() kwargs['endpoint_override'] = None kwargs['session'] = session + kwargs['api_version'] = None return kwargs @@ -130,14 +131,17 @@ class ClientInitializeTest(testtools.TestCase): mock_http_client,): expected_token = 'expected_password' expected_magnum_url = 'expected_magnum_url' + expected_api_version = 'expected_api_version' expected_kwargs = {'expected_key': 'expected_value'} client.Client(auth_token=expected_token, magnum_url=expected_magnum_url, + api_version=expected_api_version, **expected_kwargs) mock_http_client.assert_called_once_with( expected_magnum_url, token=expected_token, + api_version=expected_api_version, **expected_kwargs) def _test_init_with_interface(self, diff --git a/magnumclient/v1/client.py b/magnumclient/v1/client.py index 330fa01..83e1571 100644 --- a/magnumclient/v1/client.py +++ b/magnumclient/v1/client.py @@ -80,7 +80,8 @@ def _load_session_client(session=None, endpoint_override=None, username=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, auth_token=None, timeout=None, service_type=None, service_name=None, - interface=None, region_name=None, **kwargs): + interface=None, region_name=None, api_version=None, + **kwargs): if not session: session = _load_session( username=username, @@ -115,6 +116,7 @@ def _load_session_client(session=None, endpoint_override=None, username=None, region_name=region_name, session=session, endpoint_override=endpoint_override, + api_version=api_version, ) @@ -128,7 +130,8 @@ class Client(object): interface=None, service_name=None, insecure=False, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, - auth_token=None, timeout=600, **kwargs): + auth_token=None, timeout=600, api_version=None, + **kwargs): # We have to keep the api_key are for backwards compat, but let's # remove it from the rest of our code since it's not a keystone @@ -158,6 +161,7 @@ class Client(object): self.http_client = httpclient.HTTPClient( endpoint_override, token=auth_token, + api_version=api_version, **kwargs ) else: @@ -181,6 +185,7 @@ class Client(object): service_name=service_name, interface=interface, region_name=region_name, + api_version=api_version, **kwargs )