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: Id007535f0598226a8202716232313e37fe6247f9changes/10/454310/7
parent
09286ad858
commit
4289ddd47a
@ -0,0 +1,211 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Compute v2 API Library"""
|
||||
|
||||
from keystoneauth1 import exceptions as ksa_exceptions
|
||||
from osc_lib.api import api
|
||||
from osc_lib import exceptions
|
||||
from osc_lib.i18n import _
|
||||
|
||||
|
||||
class APIv2(api.BaseAPI):
|
||||
"""Compute v2 API"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(APIv2, self).__init__(**kwargs)
|
||||
|
||||
# Overrides
|
||||
|
||||
# TODO(dtroyer): Override find() until these fixes get into an osc-lib
|
||||
# minimum release
|
||||
def find(
|
||||
self,
|
||||
path,
|
||||
value=None,
|
||||
attr=None,
|
||||
):
|
||||
"""Find a single resource by name or ID
|
||||
|
||||
:param string path:
|
||||
The API-specific portion of the URL path
|
||||
:param string value:
|
||||
search expression (required, really)
|
||||
:param string attr:
|
||||
name of attribute for secondary search
|
||||
"""
|
||||
|
||||
try:
|
||||
ret = self._request('GET', "/%s/%s" % (path, value)).json()
|
||||
if isinstance(ret, dict):
|
||||
# strip off the enclosing dict
|
||||
key = list(ret.keys())[0]
|
||||
ret = ret[key]
|
||||
except (
|
||||
ksa_exceptions.NotFound,
|
||||
ksa_exceptions.BadRequest,
|
||||
):
|
||||
kwargs = {attr: value}
|
||||
try:
|
||||
ret = self.find_one(path, **kwargs)
|
||||
except ksa_exceptions.NotFound:
|
||||
msg = _("%s not found") % value
|
||||
raise exceptions.NotFound(msg)
|
||||
|
||||
return ret
|
||||
|
||||
# Security Groups
|
||||
|
||||
def security_group_create(
|
||||
self,
|
||||
name=None,
|
||||
description=None,
|
||||
):
|
||||
"""Create a new security group
|
||||
|
||||
https://developer.openstack.org/api-ref/compute/#create-security-group
|
||||
|
||||
:param string name:
|
||||
Security group name
|
||||
:param integer description:
|
||||
Security group description
|
||||
"""
|
||||
|
||||
url = "/os-security-groups"
|
||||
|
||||
params = {
|
||||
'name': name,
|
||||
'description': description,
|
||||
}
|
||||
|
||||
return self.create(
|
||||
url,
|
||||
json={'security_group': params},
|
||||
)['security_group']
|
||||
|
||||
def security_group_delete(
|
||||
self,
|
||||
security_group=None,
|
||||
):
|
||||
"""Delete a security group
|
||||
|
||||
https://developer.openstack.org/api-ref/compute/#delete-security-group
|
||||
|
||||
:param string security_group:
|
||||
Security group name or ID
|
||||
"""
|
||||
|
||||
url = "/os-security-groups"
|
||||
|
||||
security_group = self.find(
|
||||
url,
|
||||
attr='name',
|
||||
value=security_group,
|
||||
)['id']
|
||||
if security_group is not None:
|
||||
return self.delete('/%s/%s' % (url, security_group))
|
||||
|
||||
return None
|
||||
|
||||
def security_group_find(
|
||||
self,
|
||||
security_group=None,
|
||||
):
|
||||
"""Return a security group given name or ID
|
||||
|
||||
https://developer.openstack.org/api-ref/compute/#show-security-group-details
|
||||
|
||||
:param string security_group:
|
||||
Security group name or ID
|
||||
:returns: A dict of the security group attributes
|
||||
"""
|
||||
|
||||
url = "/os-security-groups"
|
||||
|
||||
return self.find(
|
||||
url,
|
||||
attr='name',
|
||||
value=security_group,
|
||||
)
|
||||
|
||||
def security_group_list(
|
||||
self,
|
||||
limit=None,
|
||||
marker=None,
|
||||
search_opts=None,
|
||||
):
|
||||
"""Get security groups
|
||||
|
||||
https://developer.openstack.org/api-ref/compute/#list-security-groups
|
||||
|
||||
:param integer limit:
|
||||
query return count limit
|
||||
:param string marker:
|
||||
query marker
|
||||
:param search_opts:
|
||||
(undocumented) Search filter dict
|
||||
all_tenants: True|False - return all projects
|
||||
:returns:
|
||||
list of security groups names
|
||||
"""
|
||||
|
||||
params = {}
|
||||
if search_opts is not None:
|
||||
params = dict((k, v) for (k, v) in search_opts.items() if v)
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
if marker:
|
||||
params['offset'] = marker
|
||||
|
||||
url = "/os-security-groups"
|
||||
return self.list(url, **params)["security_groups"]
|
||||
|
||||
def security_group_set(
|
||||
self,
|
||||
security_group=None,
|
||||
# name=None,
|
||||
# description=None,
|
||||
**params
|
||||
):
|
||||
"""Update a security group
|
||||
|
||||
https://developer.openstack.org/api-ref/compute/#update-security-group
|
||||
|
||||
:param string security_group:
|
||||
Security group name or ID
|
||||
|
||||
TODO(dtroyer): Create an update method in osc-lib
|
||||
"""
|
||||
|
||||
# Short-circuit no-op
|
||||
if params is None:
|
||||
return None
|
||||
|
||||
url = "/os-security-groups"
|
||||
|
||||
security_group = self.find(
|
||||
url,
|
||||
attr='name',
|
||||
value=security_group,
|
||||
)
|
||||
if security_group is not None:
|
||||
for (k, v) in params.items():
|
||||
# Only set a value if it is already present
|
||||
if k in security_group:
|
||||
security_group[k] = v
|
||||
return self._request(
|
||||
"PUT",
|
||||
"/%s/%s" % (url, security_group['id']),
|
||||
json={'security_group': security_group},
|
||||
).json()['security_group']
|
||||
return None
|
@ -0,0 +1,228 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Compute v2 API Library Tests"""
|
||||
|
||||
from requests_mock.contrib import fixture
|
||||
|
||||
from keystoneclient import session
|
||||
from openstackclient.api import compute_v2 as compute
|
||||
from openstackclient.tests.unit import utils
|
||||
from osc_lib import exceptions as osc_lib_exceptions
|
||||
|
||||
|
||||
FAKE_PROJECT = 'xyzpdq'
|
||||
FAKE_URL = 'http://gopher.com/v2'
|
||||
|
||||
|
||||
class TestComputeAPIv2(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestComputeAPIv2, self).setUp()
|
||||
sess = session.Session()
|
||||
self.api = compute.APIv2(session=sess, endpoint=FAKE_URL)
|
||||
self.requests_mock = self.useFixture(fixture.Fixture())
|
||||
|
||||
|
||||
class TestSecurityGroup(TestComputeAPIv2):
|
||||
|
||||
FAKE_SECURITY_GROUP_RESP = {
|
||||
'id': '1',
|
||||
'name': 'sg1',
|
||||
'description': 'test security group',
|
||||
'tenant_id': '0123456789',
|
||||
'rules': []
|
||||
}
|
||||
FAKE_SECURITY_GROUP_RESP_2 = {
|
||||
'id': '2',
|
||||
'name': 'sg2',
|
||||
'description': 'another test security group',
|
||||
'tenant_id': '0123456789',
|
||||
'rules': []
|
||||
}
|
||||
LIST_SECURITY_GROUP_RESP = [
|
||||
FAKE_SECURITY_GROUP_RESP_2,
|
||||
FAKE_SECURITY_GROUP_RESP,
|
||||
]
|
||||
|
||||
def test_security_group_create_default(self):
|
||||
self.requests_mock.register_uri(
|
||||
'POST',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_create('sg1')
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret)
|
||||
|
||||
def test_security_group_create_options(self):
|
||||
self.requests_mock.register_uri(
|
||||
'POST',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_create(
|
||||
name='sg1',
|
||||
description='desc',
|
||||
)
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret)
|
||||
|
||||
def test_security_group_delete_id(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'DELETE',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
status_code=202,
|
||||
)
|
||||
ret = self.api.security_group_delete('1')
|
||||
self.assertEqual(202, ret.status_code)
|
||||
self.assertEqual("", ret.text)
|
||||
|
||||
def test_security_group_delete_name(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/sg1',
|
||||
status_code=404,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'DELETE',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
status_code=202,
|
||||
)
|
||||
ret = self.api.security_group_delete('sg1')
|
||||
self.assertEqual(202, ret.status_code)
|
||||
self.assertEqual("", ret.text)
|
||||
|
||||
def test_security_group_delete_not_found(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/sg3',
|
||||
status_code=404,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.assertRaises(
|
||||
osc_lib_exceptions.NotFound,
|
||||
self.api.security_group_delete,
|
||||
'sg3',
|
||||
)
|
||||
|
||||
def test_security_group_find_id(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_find('1')
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret)
|
||||
|
||||
def test_security_group_find_name(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/sg2',
|
||||
status_code=404,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_find('sg2')
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP_2, ret)
|
||||
|
||||
def test_security_group_find_not_found(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/sg3',
|
||||
status_code=404,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.assertRaises(
|
||||
osc_lib_exceptions.NotFound,
|
||||
self.api.security_group_find,
|
||||
'sg3',
|
||||
)
|
||||
|
||||
def test_security_group_list_no_options(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_list()
|
||||
self.assertEqual(self.LIST_SECURITY_GROUP_RESP, ret)
|
||||
|
||||
def test_security_group_set_options_id(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'PUT',
|
||||
FAKE_URL + '/os-security-groups/1',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_set(
|
||||
security_group='1',
|
||||
description='desc2')
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret)
|
||||
|
||||
def test_security_group_set_options_name(self):
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups/sg2',
|
||||
status_code=404,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
FAKE_URL + '/os-security-groups',
|
||||
json={'security_groups': self.LIST_SECURITY_GROUP_RESP},
|
||||
status_code=200,
|
||||
)
|
||||
self.requests_mock.register_uri(
|
||||
'PUT',
|
||||
FAKE_URL + '/os-security-groups/2',
|
||||
json={'security_group': self.FAKE_SECURITY_GROUP_RESP_2},
|
||||
status_code=200,
|
||||
)
|
||||
ret = self.api.security_group_set(
|
||||
security_group='sg2',
|
||||
description='desc2')
|
||||
self.assertEqual(self.FAKE_SECURITY_GROUP_RESP_2, ret)
|