Skip to content

Commit 4ae4dc3

Browse files
Add support for extension list
- Add support in the common section for extension list. This only supports Identity for now. Once the APIs for volume and compute are supported in the respective APIs, they will be added. Once network is added to this client, it will be added (the API already supports it). - Include extension fakes for volume and compute for pre-enablement. Change-Id: Iebb0156a779887d2ab06488a2a27b70b56369376 Closes-Bug: #1319115
1 parent 7f6a901 commit 4ae4dc3

6 files changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2012-2013 OpenStack Foundation
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
16+
"""Extension action implementations"""
17+
18+
import logging
19+
20+
from cliff import lister
21+
22+
from openstackclient.common import exceptions as exc
23+
from openstackclient.common import utils
24+
25+
26+
class ListExtension(lister.Lister):
27+
"""List extension command"""
28+
29+
# TODO(mfisch): add support for volume and compute
30+
# when the underlying APIs support it. Add support
31+
# for network when it's added to openstackclient.
32+
33+
log = logging.getLogger(__name__ + '.ListExtension')
34+
35+
def get_parser(self, prog_name):
36+
parser = super(ListExtension, self).get_parser(prog_name)
37+
parser.add_argument(
38+
'--long',
39+
action='store_true',
40+
default=False,
41+
help='List additional fields in output')
42+
parser.add_argument(
43+
'--identity',
44+
action='store_true',
45+
default=False,
46+
help='List extensions for the Identity API')
47+
return parser
48+
49+
def take_action(self, parsed_args):
50+
self.log.debug('take_action(%s)' % parsed_args)
51+
52+
if parsed_args.long:
53+
columns = ('Name', 'Namespace', 'Description',
54+
'Alias', 'Updated', 'Links')
55+
else:
56+
columns = ('Name', 'Alias', 'Description')
57+
58+
data = []
59+
60+
# by default we want to show everything, unless the
61+
# user specifies one or more of the APIs to show
62+
# for now, only identity is supported
63+
show_all = (not parsed_args.identity)
64+
65+
if parsed_args.identity or show_all:
66+
identity_client = self.app.client_manager.identity
67+
try:
68+
data += identity_client.extensions.list()
69+
except Exception:
70+
raise exc.CommandError(
71+
"Extensions list not supported by"
72+
" identity API")
73+
74+
return (columns,
75+
(utils.get_item_properties(
76+
s, columns,
77+
formatters={},
78+
) for s in data))
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
import copy
15+
16+
from openstackclient.common import extension
17+
from openstackclient.tests import fakes
18+
from openstackclient.tests import utils
19+
20+
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
21+
22+
23+
class TestExtension(utils.TestCommand):
24+
25+
def setUp(self):
26+
super(TestExtension, self).setUp()
27+
28+
self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client(
29+
endpoint=fakes.AUTH_URL,
30+
token=fakes.AUTH_TOKEN,
31+
)
32+
33+
# Get shortcuts to the ExtensionManager Mocks
34+
self.identity_extensions_mock = (
35+
self.app.client_manager.identity.extensions)
36+
self.identity_extensions_mock.reset_mock()
37+
38+
39+
class TestExtensionList(TestExtension):
40+
41+
def setUp(self):
42+
super(TestExtensionList, self).setUp()
43+
44+
self.identity_extensions_mock.list.return_value = [
45+
fakes.FakeResource(
46+
None,
47+
copy.deepcopy(identity_fakes.EXTENSION),
48+
loaded=True,
49+
),
50+
]
51+
52+
# Get the command object to test
53+
self.cmd = extension.ListExtension(self.app, None)
54+
55+
def test_extension_list_no_options(self):
56+
arglist = []
57+
verifylist = []
58+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
59+
60+
# DisplayCommandBase.take_action() returns two tuples
61+
columns, data = self.cmd.take_action(parsed_args)
62+
63+
# no args should output from all services
64+
self.identity_extensions_mock.list.assert_called_with()
65+
66+
collist = ('Name', 'Alias', 'Description')
67+
self.assertEqual(columns, collist)
68+
datalist = (
69+
(
70+
identity_fakes.extension_name,
71+
identity_fakes.extension_alias,
72+
identity_fakes.extension_description,
73+
),
74+
)
75+
self.assertEqual(tuple(data), datalist)
76+
77+
def test_extension_list_long(self):
78+
arglist = [
79+
'--long',
80+
]
81+
verifylist = [
82+
('long', True),
83+
]
84+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
85+
86+
# DisplayCommandBase.take_action() returns two tuples
87+
columns, data = self.cmd.take_action(parsed_args)
88+
89+
# no args should output from all services
90+
self.identity_extensions_mock.list.assert_called_with()
91+
92+
collist = ('Name', 'Namespace', 'Description', 'Alias', 'Updated',
93+
'Links')
94+
self.assertEqual(columns, collist)
95+
datalist = (
96+
(
97+
identity_fakes.extension_name,
98+
identity_fakes.extension_namespace,
99+
identity_fakes.extension_description,
100+
identity_fakes.extension_alias,
101+
identity_fakes.extension_updated,
102+
identity_fakes.extension_links,
103+
),
104+
)
105+
self.assertEqual(tuple(data), datalist)
106+
107+
def test_extension_list_identity(self):
108+
arglist = [
109+
'--identity',
110+
]
111+
verifylist = [
112+
('identity', True),
113+
]
114+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
115+
116+
# DisplayCommandBase.take_action() returns two tuples
117+
columns, data = self.cmd.take_action(parsed_args)
118+
119+
self.identity_extensions_mock.list.assert_called_with()
120+
121+
collist = ('Name', 'Alias', 'Description')
122+
self.assertEqual(columns, collist)
123+
datalist = ((
124+
identity_fakes.extension_name,
125+
identity_fakes.extension_alias,
126+
identity_fakes.extension_description,
127+
), )
128+
self.assertEqual(tuple(data), datalist)

‎openstackclient/tests/compute/v2/fakes.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,34 @@
2828
'name': server_name,
2929
}
3030

31+
extension_name = 'Multinic'
32+
extension_namespace = 'http://docs.openstack.org/compute/ext/'\
33+
'multinic/api/v1.1'
34+
extension_description = 'Multiple network support'
35+
extension_updated = '2014-01-07T12:00:0-00:00'
36+
extension_alias = 'NMN'
37+
extension_links = '[{"href":'\
38+
'"https://github.com/openstack/compute-api", "type":'\
39+
' "text/html", "rel": "describedby"}]'
40+
41+
EXTENSION = {
42+
'name': extension_name,
43+
'namespace': extension_namespace,
44+
'description': extension_description,
45+
'updated': extension_updated,
46+
'alias': extension_alias,
47+
'links': extension_links,
48+
}
49+
3150

3251
class FakeComputev2Client(object):
3352
def __init__(self, **kwargs):
3453
self.images = mock.Mock()
3554
self.images.resource_class = fakes.FakeResource(None, {})
3655
self.servers = mock.Mock()
3756
self.servers.resource_class = fakes.FakeResource(None, {})
57+
self.extensions = mock.Mock()
58+
self.extensions.resource_class = fakes.FakeResource(None, {})
3859
self.auth_token = kwargs['token']
3960
self.management_url = kwargs['endpoint']
4061

‎openstackclient/tests/identity/v2_0/fakes.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,26 @@
100100
'service_id': endpoint_service_id,
101101
}
102102

103+
extension_name = 'OpenStack Keystone User CRUD'
104+
extension_namespace = 'http://docs.openstack.org/identity/'\
105+
'api/ext/OS-KSCRUD/v1.0'
106+
extension_description = 'OpenStack extensions to Keystone v2.0 API'\
107+
' enabling User Operations.'
108+
extension_updated = '2013-07-07T12:00:0-00:00'
109+
extension_alias = 'OS-KSCRUD'
110+
extension_links = '[{"href":'\
111+
'"https://github.com/openstack/identity-api", "type":'\
112+
' "text/html", "rel": "describedby"}]'
113+
114+
EXTENSION = {
115+
'name': extension_name,
116+
'namespace': extension_namespace,
117+
'description': extension_description,
118+
'updated': extension_updated,
119+
'alias': extension_alias,
120+
'links': extension_links,
121+
}
122+
103123

104124
class FakeIdentityv2Client(object):
105125
def __init__(self, **kwargs):
@@ -116,6 +136,8 @@ def __init__(self, **kwargs):
116136
self.ec2.resource_class = fakes.FakeResource(None, {})
117137
self.endpoints = mock.Mock()
118138
self.endpoints.resource_class = fakes.FakeResource(None, {})
139+
self.extensions = mock.Mock()
140+
self.extensions.resource_class = fakes.FakeResource(None, {})
119141
self.auth_token = kwargs['token']
120142
self.management_url = kwargs['endpoint']
121143

‎openstackclient/tests/volume/v1/fakes.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,35 @@
4545
'metadata': volume_metadata,
4646
}
4747

48+
extension_name = 'SchedulerHints'
49+
extension_namespace = 'http://docs.openstack.org/'\
50+
'block-service/ext/scheduler-hints/api/v2'
51+
extension_description = 'Pass arbitrary key/value'\
52+
'pairs to the scheduler.'
53+
extension_updated = '2014-02-07T12:00:0-00:00'
54+
extension_alias = 'OS-SCH-HNT'
55+
extension_links = '[{"href":'\
56+
'"https://github.com/openstack/block-api", "type":'\
57+
' "text/html", "rel": "describedby"}]'
58+
59+
EXTENSION = {
60+
'name': extension_name,
61+
'namespace': extension_namespace,
62+
'description': extension_description,
63+
'updated': extension_updated,
64+
'alias': extension_alias,
65+
'links': extension_links,
66+
}
67+
4868

4969
class FakeVolumev1Client(object):
5070
def __init__(self, **kwargs):
5171
self.volumes = mock.Mock()
5272
self.volumes.resource_class = fakes.FakeResource(None, {})
5373
self.services = mock.Mock()
5474
self.services.resource_class = fakes.FakeResource(None, {})
75+
self.extensions = mock.Mock()
76+
self.extensions.resource_class = fakes.FakeResource(None, {})
5577
self.auth_token = kwargs['token']
5678
self.management_url = kwargs['endpoint']
5779

‎setup.cfg‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ openstack.common =
3838
limits_show = openstackclient.common.limits:ShowLimits
3939
quota_set = openstackclient.common.quota:SetQuota
4040
quota_show = openstackclient.common.quota:ShowQuota
41+
extension_list = openstackclient.common.extension:ListExtension
4142

4243
openstack.compute.v2 =
4344
compute_agent_create = openstackclient.compute.v2.agent:CreateAgent

0 commit comments

Comments
 (0)