Skip to content

Commit 4289ddd

Browse files
author
Dean Troyer
committed
Low-level Compute v2 API: security group
api.compute.APIv2 starts with security group functions. novaclient 8.0 is now released without support for the previously deprecated nova-net functions, so include a new low-level REST implementation of the removed APIs. Change-Id: Id007535f0598226a8202716232313e37fe6247f9
1 parent 09286ad commit 4289ddd

12 files changed

Lines changed: 729 additions & 265 deletions

File tree

openstackclient/api/compute_v2.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
"""Compute v2 API Library"""
15+
16+
from keystoneauth1 import exceptions as ksa_exceptions
17+
from osc_lib.api import api
18+
from osc_lib import exceptions
19+
from osc_lib.i18n import _
20+
21+
22+
class APIv2(api.BaseAPI):
23+
"""Compute v2 API"""
24+
25+
def __init__(self, **kwargs):
26+
super(APIv2, self).__init__(**kwargs)
27+
28+
# Overrides
29+
30+
# TODO(dtroyer): Override find() until these fixes get into an osc-lib
31+
# minimum release
32+
def find(
33+
self,
34+
path,
35+
value=None,
36+
attr=None,
37+
):
38+
"""Find a single resource by name or ID
39+
40+
:param string path:
41+
The API-specific portion of the URL path
42+
:param string value:
43+
search expression (required, really)
44+
:param string attr:
45+
name of attribute for secondary search
46+
"""
47+
48+
try:
49+
ret = self._request('GET', "/%s/%s" % (path, value)).json()
50+
if isinstance(ret, dict):
51+
# strip off the enclosing dict
52+
key = list(ret.keys())[0]
53+
ret = ret[key]
54+
except (
55+
ksa_exceptions.NotFound,
56+
ksa_exceptions.BadRequest,
57+
):
58+
kwargs = {attr: value}
59+
try:
60+
ret = self.find_one(path, **kwargs)
61+
except ksa_exceptions.NotFound:
62+
msg = _("%s not found") % value
63+
raise exceptions.NotFound(msg)
64+
65+
return ret
66+
67+
# Security Groups
68+
69+
def security_group_create(
70+
self,
71+
name=None,
72+
description=None,
73+
):
74+
"""Create a new security group
75+
76+
https://developer.openstack.org/api-ref/compute/#create-security-group
77+
78+
:param string name:
79+
Security group name
80+
:param integer description:
81+
Security group description
82+
"""
83+
84+
url = "/os-security-groups"
85+
86+
params = {
87+
'name': name,
88+
'description': description,
89+
}
90+
91+
return self.create(
92+
url,
93+
json={'security_group': params},
94+
)['security_group']
95+
96+
def security_group_delete(
97+
self,
98+
security_group=None,
99+
):
100+
"""Delete a security group
101+
102+
https://developer.openstack.org/api-ref/compute/#delete-security-group
103+
104+
:param string security_group:
105+
Security group name or ID
106+
"""
107+
108+
url = "/os-security-groups"
109+
110+
security_group = self.find(
111+
url,
112+
attr='name',
113+
value=security_group,
114+
)['id']
115+
if security_group is not None:
116+
return self.delete('/%s/%s' % (url, security_group))
117+
118+
return None
119+
120+
def security_group_find(
121+
self,
122+
security_group=None,
123+
):
124+
"""Return a security group given name or ID
125+
126+
https://developer.openstack.org/api-ref/compute/#show-security-group-details
127+
128+
:param string security_group:
129+
Security group name or ID
130+
:returns: A dict of the security group attributes
131+
"""
132+
133+
url = "/os-security-groups"
134+
135+
return self.find(
136+
url,
137+
attr='name',
138+
value=security_group,
139+
)
140+
141+
def security_group_list(
142+
self,
143+
limit=None,
144+
marker=None,
145+
search_opts=None,
146+
):
147+
"""Get security groups
148+
149+
https://developer.openstack.org/api-ref/compute/#list-security-groups
150+
151+
:param integer limit:
152+
query return count limit
153+
:param string marker:
154+
query marker
155+
:param search_opts:
156+
(undocumented) Search filter dict
157+
all_tenants: True|False - return all projects
158+
:returns:
159+
list of security groups names
160+
"""
161+
162+
params = {}
163+
if search_opts is not None:
164+
params = dict((k, v) for (k, v) in search_opts.items() if v)
165+
if limit:
166+
params['limit'] = limit
167+
if marker:
168+
params['offset'] = marker
169+
170+
url = "/os-security-groups"
171+
return self.list(url, **params)["security_groups"]
172+
173+
def security_group_set(
174+
self,
175+
security_group=None,
176+
# name=None,
177+
# description=None,
178+
**params
179+
):
180+
"""Update a security group
181+
182+
https://developer.openstack.org/api-ref/compute/#update-security-group
183+
184+
:param string security_group:
185+
Security group name or ID
186+
187+
TODO(dtroyer): Create an update method in osc-lib
188+
"""
189+
190+
# Short-circuit no-op
191+
if params is None:
192+
return None
193+
194+
url = "/os-security-groups"
195+
196+
security_group = self.find(
197+
url,
198+
attr='name',
199+
value=security_group,
200+
)
201+
if security_group is not None:
202+
for (k, v) in params.items():
203+
# Only set a value if it is already present
204+
if k in security_group:
205+
security_group[k] = v
206+
return self._request(
207+
"PUT",
208+
"/%s/%s" % (url, security_group['id']),
209+
json={'security_group': security_group},
210+
).json()['security_group']
211+
return None

openstackclient/compute/client.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
"2.1": "novaclient.client",
3232
}
3333

34+
COMPUTE_API_TYPE = 'compute'
35+
COMPUTE_API_VERSIONS = {
36+
'2': 'openstackclient.api.compute_v2.APIv2',
37+
}
38+
3439
# Save the microversion if in use
3540
_compute_api_version = None
3641

@@ -58,6 +63,13 @@ def make_client(instance):
5863

5964
LOG.debug('Instantiating compute client for %s', version)
6065

66+
compute_api = utils.get_client_class(
67+
API_NAME,
68+
version.ver_major,
69+
COMPUTE_API_VERSIONS,
70+
)
71+
LOG.debug('Instantiating compute api: %s', compute_api)
72+
6173
# Set client http_log_debug to True if verbosity level is high enough
6274
http_log_debug = utils.get_effective_log_level() <= logging.DEBUG
6375

@@ -77,6 +89,16 @@ def make_client(instance):
7789
**kwargs
7890
)
7991

92+
client.api = compute_api(
93+
session=instance.session,
94+
service_type=COMPUTE_API_TYPE,
95+
endpoint=instance.get_endpoint_for_service_type(
96+
COMPUTE_API_TYPE,
97+
region_name=instance.region_name,
98+
interface=instance.interface,
99+
)
100+
)
101+
80102
return client
81103

82104

openstackclient/compute/v2/server.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,14 @@
2222
import os
2323
import sys
2424

25+
from novaclient.v2 import servers
2526
from osc_lib.cli import parseractions
2627
from osc_lib.command import command
2728
from osc_lib import exceptions
2829
from osc_lib import utils
2930
from oslo_utils import timeutils
3031
import six
3132

32-
try:
33-
from novaclient.v2 import servers
34-
except ImportError:
35-
from novaclient.v1_1 import servers
36-
3733
from openstackclient.i18n import _
3834
from openstackclient.identity import common as identity_common
3935

@@ -316,12 +312,11 @@ def take_action(self, parsed_args):
316312
compute_client.servers,
317313
parsed_args.server,
318314
)
319-
security_group = utils.find_resource(
320-
compute_client.security_groups,
315+
security_group = compute_client.api.security_group_find(
321316
parsed_args.group,
322317
)
323318

324-
server.add_security_group(security_group.id)
319+
server.add_security_group(security_group['id'])
325320

326321

327322
class AddServerVolume(command.Command):
@@ -1437,12 +1432,11 @@ def take_action(self, parsed_args):
14371432
compute_client.servers,
14381433
parsed_args.server,
14391434
)
1440-
security_group = utils.find_resource(
1441-
compute_client.security_groups,
1435+
security_group = compute_client.api.security_group_find(
14421436
parsed_args.group,
14431437
)
14441438

1445-
server.remove_security_group(security_group.id)
1439+
server.remove_security_group(security_group['id'])
14461440

14471441

14481442
class RemoveServerVolume(command.Command):

openstackclient/network/v2/security_group.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@ def take_action_network(self, client, parsed_args):
140140

141141
def take_action_compute(self, client, parsed_args):
142142
description = self._get_description(parsed_args)
143-
obj = client.security_groups.create(
143+
obj = client.api.security_group_create(
144144
parsed_args.name,
145145
description,
146146
)
147147
display_columns, property_columns = _get_columns(obj)
148148
data = utils.get_dict_properties(
149-
obj._info,
149+
obj,
150150
property_columns,
151151
formatters=_formatters_compute
152152
)
@@ -174,8 +174,7 @@ def take_action_network(self, client, parsed_args):
174174
client.delete_security_group(obj)
175175

176176
def take_action_compute(self, client, parsed_args):
177-
data = utils.find_resource(client.security_groups, self.r)
178-
client.security_groups.delete(data.id)
177+
client.api.security_group_delete(self.r)
179178

180179

181180
# TODO(rauta): Use the SDK resource mapped attribute names once
@@ -242,7 +241,10 @@ def take_action_network(self, client, parsed_args):
242241

243242
def take_action_compute(self, client, parsed_args):
244243
search = {'all_tenants': parsed_args.all_projects}
245-
data = client.security_groups.list(search_opts=search)
244+
data = client.api.security_group_list(
245+
# TODO(dtroyer): add limit, marker
246+
search_opts=search,
247+
)
246248

247249
columns = (
248250
"ID",
@@ -254,7 +256,7 @@ def take_action_compute(self, client, parsed_args):
254256
columns = columns + ('Tenant ID',)
255257
column_headers = column_headers + ('Project',)
256258
return (column_headers,
257-
(utils.get_item_properties(
259+
(utils.get_dict_properties(
258260
s, columns,
259261
) for s in data))
260262

@@ -294,23 +296,20 @@ def take_action_network(self, client, parsed_args):
294296
client.update_security_group(obj, **attrs)
295297

296298
def take_action_compute(self, client, parsed_args):
297-
data = utils.find_resource(
298-
client.security_groups,
299-
parsed_args.group,
300-
)
299+
data = client.api.security_group_find(parsed_args.group)
301300

302301
if parsed_args.name is not None:
303-
data.name = parsed_args.name
302+
data['name'] = parsed_args.name
304303
if parsed_args.description is not None:
305-
data.description = parsed_args.description
304+
data['description'] = parsed_args.description
306305

307306
# NOTE(rtheis): Previous behavior did not raise a CommandError
308307
# if there were no updates. Maintain this behavior and issue
309308
# the update.
310-
client.security_groups.update(
309+
client.api.security_group_set(
311310
data,
312-
data.name,
313-
data.description,
311+
data['name'],
312+
data['description'],
314313
)
315314

316315

@@ -337,13 +336,10 @@ def take_action_network(self, client, parsed_args):
337336
return (display_columns, data)
338337

339338
def take_action_compute(self, client, parsed_args):
340-
obj = utils.find_resource(
341-
client.security_groups,
342-
parsed_args.group,
343-
)
339+
obj = client.api.security_group_find(parsed_args.group)
344340
display_columns, property_columns = _get_columns(obj)
345341
data = utils.get_dict_properties(
346-
obj._info,
342+
obj,
347343
property_columns,
348344
formatters=_formatters_compute
349345
)

0 commit comments

Comments
 (0)