Complete compute aggregate functions

In order to switch next part of OSC towards SDK we need to add some
missing bits to the aggregates.

Since this is another example when API returns 400 if we try accessing
resource with name and it doesn't support - modify general behavior for
also skipping 400 in the find method

Change-Id: Ia6711b1c27514d0698fec1efedaefeeb93722b9d
This commit is contained in:
Artem Goncharov 2020-11-10 13:44:08 +01:00
parent 599ef07775
commit b60915aab3
6 changed files with 141 additions and 16 deletions

View File

@ -261,6 +261,20 @@ class Proxy(proxy.Proxy):
"""
return self._get(_aggregate.Aggregate, aggregate)
def find_aggregate(self, name_or_id, ignore_missing=True):
"""Find a single aggregate
:param name_or_id: The name or ID of an aggregate.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:returns: One :class:`~openstack.compute.v2.aggregate.Aggregate`
or None
"""
return self._find(_aggregate.Aggregate, name_or_id,
ignore_missing=ignore_missing)
def create_aggregate(self, **attrs):
"""Create a new host aggregate from attributes
@ -345,6 +359,26 @@ class Proxy(proxy.Proxy):
aggregate = self._get_resource(_aggregate.Aggregate, aggregate)
return aggregate.set_metadata(self, metadata)
def aggregate_precache_images(self, aggregate, images):
"""Requests image precaching on an aggregate
:param aggregate: Either the ID of a aggregate or a
:class:`~openstack.compute.v2.aggregate.Aggregate` instance.
:param images: Single image id or list of image ids.
:returns: ``None``
"""
aggregate = self._get_resource(_aggregate.Aggregate, aggregate)
# We need to ensure we pass list of image IDs
if isinstance(images, str):
images = [images]
image_data = []
for img in images:
image_data.append({'id': img})
return aggregate.precache_images(self, image_data)
# ========== Images ==========
def delete_image(self, image, ignore_missing=True):
"""Delete an image

View File

@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack import exceptions
from openstack import resource
from openstack import utils
@ -30,25 +30,31 @@ class Aggregate(resource.Resource):
# Properties
#: Availability zone of aggregate
availability_zone = resource.Body('availability_zone')
#: The date and time when the resource was created.
created_at = resource.Body('created_at')
#: The date and time when the resource was deleted.
deleted_at = resource.Body('deleted_at')
#: Deleted?
deleted = resource.Body('deleted')
is_deleted = resource.Body('deleted', type=bool)
#: Name of aggregate
name = resource.Body('name')
#: Hosts
hosts = resource.Body('hosts')
hosts = resource.Body('hosts', type=list)
#: Metadata
metadata = resource.Body('metadata')
metadata = resource.Body('metadata', type=dict)
#: The date and time when the resource was updated
updated_at = resource.Body('updated_at')
#: UUID
uuid = resource.Body('uuid')
# uuid introduced in 2.41
_max_microversion = '2.41'
# Image pre-caching introduced in 2.81
_max_microversion = '2.81'
def _action(self, session, body, microversion=None):
"""Preform aggregate actions given the message body."""
url = utils.urljoin(self.base_path, self.id, 'action')
headers = {'Accept': ''}
response = session.post(
url, json=body, headers=headers, microversion=microversion)
url, json=body, microversion=microversion)
exceptions.raise_from_response(response)
aggregate = Aggregate()
aggregate._translate_response(response=response)
return aggregate
@ -67,3 +73,12 @@ class Aggregate(resource.Resource):
"""Creates or replaces metadata for an aggregate."""
body = {'set_metadata': {'metadata': metadata}}
return self._action(session, body)
def precache_images(self, session, images):
"""Requests image pre-caching"""
body = {'cache': images}
url = utils.urljoin(self.base_path, self.id, 'images')
response = session.post(
url, json=body, microversion=self._max_microversion)
exceptions.raise_from_response(response)
# This API has no result

View File

@ -1895,7 +1895,9 @@ class Resource(dict):
connection=session._get_connection(),
**params)
return match.fetch(session, **params)
except exceptions.NotFoundException:
except (exceptions.NotFoundException, exceptions.BadRequestException):
# NOTE(gtema): There are few places around openstack that return
# 400 if we try to GET resource and it doesn't exist.
pass
if ('name' in cls._query_mapping._mapping.keys()

View File

@ -60,7 +60,10 @@ class TestAggregate(base.TestCase):
sot = aggregate.Aggregate(**EXAMPLE)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['availability_zone'], sot.availability_zone)
self.assertEqual(EXAMPLE['deleted'], sot.deleted)
self.assertEqual(EXAMPLE['deleted'], sot.is_deleted)
self.assertEqual(EXAMPLE['deleted_at'], sot.deleted_at)
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)
self.assertEqual(EXAMPLE['hosts'], sot.hosts)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['uuid'], sot.uuid)
@ -73,9 +76,8 @@ class TestAggregate(base.TestCase):
url = 'os-aggregates/4/action'
body = {"add_host": {"host": "host1"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, json=body, headers=headers, microversion=None)
url, json=body, microversion=None)
def test_remove_host(self):
sot = aggregate.Aggregate(**EXAMPLE)
@ -84,9 +86,8 @@ class TestAggregate(base.TestCase):
url = 'os-aggregates/4/action'
body = {"remove_host": {"host": "host1"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, json=body, headers=headers, microversion=None)
url, json=body, microversion=None)
def test_set_metadata(self):
sot = aggregate.Aggregate(**EXAMPLE)
@ -95,6 +96,15 @@ class TestAggregate(base.TestCase):
url = 'os-aggregates/4/action'
body = {"set_metadata": {"metadata": {"key: value"}}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, json=body, headers=headers, microversion=None)
url, json=body, microversion=None)
def test_precache_image(self):
sot = aggregate.Aggregate(**EXAMPLE)
sot.precache_images(self.sess, ['1'])
url = 'os-aggregates/4/images'
body = {"cache": ['1']}
self.sess.post.assert_called_with(
url, json=body, microversion=sot._max_microversion)

View File

@ -12,6 +12,7 @@
from unittest import mock
from openstack.compute.v2 import _proxy
from openstack.compute.v2 import aggregate
from openstack.compute.v2 import availability_zone as az
from openstack.compute.v2 import extension
from openstack.compute.v2 import flavor
@ -245,6 +246,63 @@ class TestKeyPair(TestComputeProxy):
)
class TestAggregate(TestComputeProxy):
def test_aggregate_create(self):
self.verify_create(self.proxy.create_aggregate, aggregate.Aggregate)
def test_aggregate_delete(self):
self.verify_delete(
self.proxy.delete_aggregate, aggregate.Aggregate, False)
def test_aggregate_delete_ignore(self):
self.verify_delete(
self.proxy.delete_aggregate, aggregate.Aggregate, True)
def test_aggregate_find(self):
self.verify_find(self.proxy.find_aggregate, aggregate.Aggregate)
def test_aggregates(self):
self.verify_list_no_kwargs(self.proxy.aggregates, aggregate.Aggregate)
def test_aggregate_get(self):
self.verify_get(self.proxy.get_aggregate, aggregate.Aggregate)
def test_aggregate_update(self):
self.verify_update(self.proxy.update_aggregate, aggregate.Aggregate)
def test_aggregate_add_host(self):
self._verify("openstack.compute.v2.aggregate.Aggregate.add_host",
self.proxy.add_host_to_aggregate,
method_args=["value", "host"],
expected_args=["host"])
def test_aggregate_remove_host(self):
self._verify("openstack.compute.v2.aggregate.Aggregate.remove_host",
self.proxy.remove_host_from_aggregate,
method_args=["value", "host"],
expected_args=["host"])
def test_aggregate_set_metadata(self):
self._verify("openstack.compute.v2.aggregate.Aggregate.set_metadata",
self.proxy.set_aggregate_metadata,
method_args=["value", {'a': 'b'}],
expected_args=[{'a': 'b'}])
def test_aggregate_precache_image(self):
self._verify(
"openstack.compute.v2.aggregate.Aggregate.precache_images",
self.proxy.aggregate_precache_images,
method_args=["value", '1'],
expected_args=[[{'id': '1'}]])
def test_aggregate_precache_images(self):
self._verify(
"openstack.compute.v2.aggregate.Aggregate.precache_images",
self.proxy.aggregate_precache_images,
method_args=["value", ['1', '2']],
expected_args=[[{'id': '1'}, {'id': '2'}]])
class TestCompute(TestComputeProxy):
def test_extension_find(self):
self.verify_find(self.proxy.find_extension, extension.Extension)

View File

@ -0,0 +1,6 @@
---
features:
- Complete compute.aggregate functions to the latest state
fixes:
- aggregate.deleted property is renamed to 'is_deleted' to comply with the
naming convention