Merge "Complete compute aggregate functions"
This commit is contained in:
@@ -274,6 +274,20 @@ class Proxy(proxy.Proxy):
|
|||||||
"""
|
"""
|
||||||
return self._get(_aggregate.Aggregate, aggregate)
|
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):
|
def create_aggregate(self, **attrs):
|
||||||
"""Create a new host aggregate from attributes
|
"""Create a new host aggregate from attributes
|
||||||
|
|
||||||
@@ -358,6 +372,26 @@ class Proxy(proxy.Proxy):
|
|||||||
aggregate = self._get_resource(_aggregate.Aggregate, aggregate)
|
aggregate = self._get_resource(_aggregate.Aggregate, aggregate)
|
||||||
return aggregate.set_metadata(self, metadata)
|
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):
|
def delete_image(self, image, ignore_missing=True):
|
||||||
"""Delete an image
|
"""Delete an image
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from openstack import exceptions
|
||||||
from openstack import resource
|
from openstack import resource
|
||||||
from openstack import utils
|
from openstack import utils
|
||||||
|
|
||||||
@@ -30,25 +30,31 @@ class Aggregate(resource.Resource):
|
|||||||
# Properties
|
# Properties
|
||||||
#: Availability zone of aggregate
|
#: Availability zone of aggregate
|
||||||
availability_zone = resource.Body('availability_zone')
|
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?
|
||||||
deleted = resource.Body('deleted')
|
is_deleted = resource.Body('deleted', type=bool)
|
||||||
#: Name of aggregate
|
#: Name of aggregate
|
||||||
name = resource.Body('name')
|
name = resource.Body('name')
|
||||||
#: Hosts
|
#: Hosts
|
||||||
hosts = resource.Body('hosts')
|
hosts = resource.Body('hosts', type=list)
|
||||||
#: Metadata
|
#: 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
|
||||||
uuid = resource.Body('uuid')
|
uuid = resource.Body('uuid')
|
||||||
# uuid introduced in 2.41
|
# Image pre-caching introduced in 2.81
|
||||||
_max_microversion = '2.41'
|
_max_microversion = '2.81'
|
||||||
|
|
||||||
def _action(self, session, body, microversion=None):
|
def _action(self, session, body, microversion=None):
|
||||||
"""Preform aggregate actions given the message body."""
|
"""Preform aggregate actions given the message body."""
|
||||||
url = utils.urljoin(self.base_path, self.id, 'action')
|
url = utils.urljoin(self.base_path, self.id, 'action')
|
||||||
headers = {'Accept': ''}
|
|
||||||
response = session.post(
|
response = session.post(
|
||||||
url, json=body, headers=headers, microversion=microversion)
|
url, json=body, microversion=microversion)
|
||||||
|
exceptions.raise_from_response(response)
|
||||||
aggregate = Aggregate()
|
aggregate = Aggregate()
|
||||||
aggregate._translate_response(response=response)
|
aggregate._translate_response(response=response)
|
||||||
return aggregate
|
return aggregate
|
||||||
@@ -67,3 +73,12 @@ class Aggregate(resource.Resource):
|
|||||||
"""Creates or replaces metadata for an aggregate."""
|
"""Creates or replaces metadata for an aggregate."""
|
||||||
body = {'set_metadata': {'metadata': metadata}}
|
body = {'set_metadata': {'metadata': metadata}}
|
||||||
return self._action(session, body)
|
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
|
||||||
|
|||||||
@@ -1895,7 +1895,9 @@ class Resource(dict):
|
|||||||
connection=session._get_connection(),
|
connection=session._get_connection(),
|
||||||
**params)
|
**params)
|
||||||
return match.fetch(session, **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
|
pass
|
||||||
|
|
||||||
if ('name' in cls._query_mapping._mapping.keys()
|
if ('name' in cls._query_mapping._mapping.keys()
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ class TestAggregate(base.TestCase):
|
|||||||
sot = aggregate.Aggregate(**EXAMPLE)
|
sot = aggregate.Aggregate(**EXAMPLE)
|
||||||
self.assertEqual(EXAMPLE['name'], sot.name)
|
self.assertEqual(EXAMPLE['name'], sot.name)
|
||||||
self.assertEqual(EXAMPLE['availability_zone'], sot.availability_zone)
|
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['hosts'], sot.hosts)
|
||||||
self.assertEqual(EXAMPLE['id'], sot.id)
|
self.assertEqual(EXAMPLE['id'], sot.id)
|
||||||
self.assertEqual(EXAMPLE['uuid'], sot.uuid)
|
self.assertEqual(EXAMPLE['uuid'], sot.uuid)
|
||||||
@@ -73,9 +76,8 @@ class TestAggregate(base.TestCase):
|
|||||||
|
|
||||||
url = 'os-aggregates/4/action'
|
url = 'os-aggregates/4/action'
|
||||||
body = {"add_host": {"host": "host1"}}
|
body = {"add_host": {"host": "host1"}}
|
||||||
headers = {'Accept': ''}
|
|
||||||
self.sess.post.assert_called_with(
|
self.sess.post.assert_called_with(
|
||||||
url, json=body, headers=headers, microversion=None)
|
url, json=body, microversion=None)
|
||||||
|
|
||||||
def test_remove_host(self):
|
def test_remove_host(self):
|
||||||
sot = aggregate.Aggregate(**EXAMPLE)
|
sot = aggregate.Aggregate(**EXAMPLE)
|
||||||
@@ -84,9 +86,8 @@ class TestAggregate(base.TestCase):
|
|||||||
|
|
||||||
url = 'os-aggregates/4/action'
|
url = 'os-aggregates/4/action'
|
||||||
body = {"remove_host": {"host": "host1"}}
|
body = {"remove_host": {"host": "host1"}}
|
||||||
headers = {'Accept': ''}
|
|
||||||
self.sess.post.assert_called_with(
|
self.sess.post.assert_called_with(
|
||||||
url, json=body, headers=headers, microversion=None)
|
url, json=body, microversion=None)
|
||||||
|
|
||||||
def test_set_metadata(self):
|
def test_set_metadata(self):
|
||||||
sot = aggregate.Aggregate(**EXAMPLE)
|
sot = aggregate.Aggregate(**EXAMPLE)
|
||||||
@@ -95,6 +96,15 @@ class TestAggregate(base.TestCase):
|
|||||||
|
|
||||||
url = 'os-aggregates/4/action'
|
url = 'os-aggregates/4/action'
|
||||||
body = {"set_metadata": {"metadata": {"key: value"}}}
|
body = {"set_metadata": {"metadata": {"key: value"}}}
|
||||||
headers = {'Accept': ''}
|
|
||||||
self.sess.post.assert_called_with(
|
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)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from openstack.compute.v2 import _proxy
|
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 availability_zone as az
|
||||||
from openstack.compute.v2 import extension
|
from openstack.compute.v2 import extension
|
||||||
from openstack.compute.v2 import flavor
|
from openstack.compute.v2 import flavor
|
||||||
@@ -248,6 +249,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):
|
class TestCompute(TestComputeProxy):
|
||||||
def test_extension_find(self):
|
def test_extension_find(self):
|
||||||
self.verify_find(self.proxy.find_extension, extension.Extension)
|
self.verify_find(self.proxy.find_extension, extension.Extension)
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user