Better support for metadata in Compute service
Metadata for both Servers and Images as seen from the Compute service is less than optimal. This approach uses a mixin class and allows both the Server and Image resources to leverage the same code, as they both work with metadata in the same way. Change-Id: I1d6b1f4c840d1127257244472243e4fa170baa8d Closes-Bug: #1467732
This commit is contained in:
parent
004bad9f1f
commit
5f69213a78
|
@ -188,6 +188,102 @@ class Proxy(proxy.BaseProxy):
|
|||
img = _image.ImageDetail if details else _image.Image
|
||||
return self._list(img, paginated=True, **query)
|
||||
|
||||
def _get_base_resource(self, res, base):
|
||||
# Metadata calls for Image and Server can work for both those
|
||||
# resources but also ImageDetail and ServerDetail. If we get
|
||||
# either class, use it, otherwise create an instance of the base.
|
||||
if isinstance(res, base):
|
||||
return res
|
||||
else:
|
||||
return base({"id": res})
|
||||
|
||||
def get_image_metadata(self, image, key=None):
|
||||
"""Return a dictionary of metadata for an image
|
||||
|
||||
:param server: Either the id of an image or a
|
||||
:class:`~openstack.compute.v2.image.Image` or
|
||||
:class:`~openstack.compute.v2.image.ImageDetail`
|
||||
instance.
|
||||
:param key: An optional key to retrieve from the image's metadata.
|
||||
When no ``key`` is specified, all metadata is retrieved.
|
||||
|
||||
:returns: A dictionary of the image's metadata. All keys and values
|
||||
are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(image, _image.Image)
|
||||
return res.get_metadata(self.session, key)
|
||||
|
||||
def create_image_metadata(self, image, **metadata):
|
||||
"""Create metadata for an image
|
||||
|
||||
:param server: Either the id of an image or a
|
||||
:class:`~openstack.compute.v2.image.Image` or
|
||||
:class:`~openstack.compute.v2.image.ImageDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be added as metadata
|
||||
on the image. All keys and values
|
||||
are stored as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata that was created on the image.
|
||||
All keys and values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(image, _image.Image)
|
||||
return res.create_metadata(self.session, **metadata)
|
||||
|
||||
def replace_image_metadata(self, image, **metadata):
|
||||
"""Replace metadata for an image
|
||||
|
||||
:param server: Either the id of a image or a
|
||||
:class:`~openstack.compute.v2.image.Image` or
|
||||
:class:`~openstack.compute.v2.image.ImageDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be added as metadata
|
||||
on the image. Any other existing metadata
|
||||
is removed. All keys and values are stored
|
||||
as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata for the image. All keys and
|
||||
values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(image, _image.Image)
|
||||
return res.replace_metadata(self.session, **metadata)
|
||||
|
||||
def update_image_metadata(self, image, **metadata):
|
||||
"""Update metadata for an image
|
||||
|
||||
:param server: Either the id of an image or a
|
||||
:class:`~openstack.compute.v2.image.Image` or
|
||||
:class:`~openstack.compute.v2.image.ImageDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be updated in the image's
|
||||
metadata. No other metadata is modified
|
||||
by this call. All keys and values are stored
|
||||
as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata for the image. All keys and
|
||||
values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(image, _image.Image)
|
||||
return res.update_metadata(self.session, **metadata)
|
||||
|
||||
def delete_image_metadata(self, image, key):
|
||||
"""Delete metadata for an image
|
||||
|
||||
:param server: Either the id of an image or a
|
||||
:class:`~openstack.compute.v2.image.Image` or
|
||||
:class:`~openstack.compute.v2.image.ImageDetail`
|
||||
instance.
|
||||
:param key: The key to delete
|
||||
|
||||
:rtype: ``None``
|
||||
"""
|
||||
res = self._get_base_resource(image, _image.Image)
|
||||
return res.delete_metadata(self.session, key)
|
||||
|
||||
def create_keypair(self, **attrs):
|
||||
"""Create a new keypair from attributes
|
||||
|
||||
|
@ -583,3 +679,90 @@ class Proxy(proxy.BaseProxy):
|
|||
"""
|
||||
return self._list(availability_zone.AvailabilityZone,
|
||||
paginated=False, **query)
|
||||
|
||||
def get_server_metadata(self, server, key=None):
|
||||
"""Return a dictionary of metadata for a server
|
||||
|
||||
:param server: Either the id of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` or
|
||||
:class:`~openstack.compute.v2.server.ServerDetail`
|
||||
instance.
|
||||
:param key: An optional key to retrieve from the server's metadata.
|
||||
When no ``key`` is specified, all metadata is retrieved.
|
||||
|
||||
:returns: A dictionary of the server's metadata. All keys and values
|
||||
are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(server, _server.Server)
|
||||
return res.get_metadata(self.session, key)
|
||||
|
||||
def create_server_metadata(self, server, **metadata):
|
||||
"""Create metadata for a server
|
||||
|
||||
:param server: Either the id of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` or
|
||||
:class:`~openstack.compute.v2.server.ServerDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be added as metadata
|
||||
on the server. All keys and values
|
||||
are stored as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata that was created on the server.
|
||||
All keys and values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(server, _server.Server)
|
||||
return res.create_metadata(self.session, **metadata)
|
||||
|
||||
def replace_server_metadata(self, server, **metadata):
|
||||
"""Replace metadata for a server
|
||||
|
||||
:param server: Either the id of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` or
|
||||
:class:`~openstack.compute.v2.server.ServerDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be added as metadata
|
||||
on the server. Any other existing metadata
|
||||
is removed. All keys and values are stored
|
||||
as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata for the server. All keys and
|
||||
values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(server, _server.Server)
|
||||
return res.replace_metadata(self.session, **metadata)
|
||||
|
||||
def update_server_metadata(self, server, **metadata):
|
||||
"""Update metadata for a server
|
||||
|
||||
:param server: Either the id of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` or
|
||||
:class:`~openstack.compute.v2.server.ServerDetail`
|
||||
instance.
|
||||
:param kwargs metadata: Key/value pairs to be updated in the server's
|
||||
metadata. No other metadata is modified
|
||||
by this call. All keys and values are stored
|
||||
as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata for the server. All keys and
|
||||
values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
res = self._get_base_resource(server, _server.Server)
|
||||
return res.update_metadata(self.session, **metadata)
|
||||
|
||||
def delete_server_metadata(self, server, key):
|
||||
"""Delete metadata for a server
|
||||
|
||||
:param server: Either the id of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` or
|
||||
:class:`~openstack.compute.v2.server.ServerDetail`
|
||||
instance.
|
||||
:param key: The key to delete
|
||||
|
||||
:rtype: ``None``
|
||||
"""
|
||||
res = self._get_base_resource(server, _server.Server)
|
||||
return res.delete_metadata(self.session, key)
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
# under the License.
|
||||
|
||||
from openstack.compute import compute_service
|
||||
from openstack.compute.v2 import metadata
|
||||
from openstack import resource
|
||||
|
||||
|
||||
class Image(resource.Resource):
|
||||
class Image(resource.Resource, metadata.MetadataMixin):
|
||||
resource_key = 'image'
|
||||
resources_key = 'images'
|
||||
base_path = '/images'
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from openstack import utils
|
||||
|
||||
|
||||
class MetadataMixin(object):
|
||||
|
||||
def _metadata(self, method, key=None, clear=False, delete=False,
|
||||
**metadata):
|
||||
for k, v in metadata.items():
|
||||
if not isinstance(v, six.string_types):
|
||||
raise ValueError("The value for %s (%s) must be "
|
||||
"a text string" % (k, v))
|
||||
|
||||
# If we're in a ServerDetail, we need to pop the "detail" portion
|
||||
# of the URL off and then everything else will work the same.
|
||||
pos = self.base_path.find("detail")
|
||||
if pos != -1:
|
||||
base = self.base_path[:pos]
|
||||
else:
|
||||
base = self.base_path
|
||||
|
||||
if key is not None:
|
||||
url = utils.urljoin(base, self.id, "metadata", key)
|
||||
else:
|
||||
url = utils.urljoin(base, self.id, "metadata")
|
||||
|
||||
kwargs = {"endpoint_filter": self.service}
|
||||
if metadata or clear:
|
||||
# 'meta' is the key for singlular modifications.
|
||||
# 'metadata' is the key for mass modifications.
|
||||
key = "meta" if key is not None else "metadata"
|
||||
kwargs["json"] = {key: metadata}
|
||||
|
||||
headers = {"Accept": ""} if delete else {}
|
||||
|
||||
response = method(url, headers=headers, **kwargs)
|
||||
|
||||
# DELETE doesn't return a JSON body while everything else does.
|
||||
return response.json() if not delete else None
|
||||
|
||||
def get_metadata(self, session, key=None):
|
||||
"""Retrieve metadata
|
||||
|
||||
:param session: The session to use for this request.
|
||||
:param key: If specified, retrieve metadata only for this key.
|
||||
If not specified, or ``None`` (the default),
|
||||
retrieve all available metadata.
|
||||
|
||||
:returns: A dictionary of the requested metadata. All keys and values
|
||||
are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
result = self._metadata(session.get, key=key)
|
||||
return result["metadata"] if key is None else result["meta"]
|
||||
|
||||
def create_metadata(self, session, **metadata):
|
||||
"""Create metadata
|
||||
|
||||
NOTE: One PUT call will be made for each key/value pair specified.
|
||||
|
||||
:param session: The session to use for this request.
|
||||
:param kwargs metadata: key/value metadata pairs to be created on
|
||||
this server instance. All keys and values
|
||||
are stored as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata that was created. All keys and
|
||||
values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
results = {}
|
||||
# A PUT to /metadata will entirely replace any existing metadata,
|
||||
# so we need to PUT each individual key/value to /metadata/key
|
||||
# in order to preserve anything existing and only add new keys.
|
||||
for key, value in metadata.items():
|
||||
result = self._metadata(session.put, key=key, **{key: value})
|
||||
results[key] = result["meta"][key]
|
||||
return results
|
||||
|
||||
def replace_metadata(self, session, **metadata):
|
||||
"""Replace metadata
|
||||
|
||||
This call will replace any existing metadata with the key/value pairs
|
||||
given here.
|
||||
|
||||
:param session: The session to use for this request.
|
||||
:param kwargs metadata: key/value metadata pairs to be created on
|
||||
this server instance. Any other existing
|
||||
metadata is removed.
|
||||
When metadata is not set, it is effectively
|
||||
cleared out, replacing the metadata
|
||||
with nothing.
|
||||
All keys and values are stored as Unicode.
|
||||
|
||||
|
||||
:returns: A dictionary of the metadata after being replaced.
|
||||
All keys and values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
# A PUT with empty metadata will clear anything out.
|
||||
clear = True if not metadata else False
|
||||
result = self._metadata(session.put, clear=clear, **metadata)
|
||||
return result["metadata"]
|
||||
|
||||
def update_metadata(self, session, **metadata):
|
||||
"""Update metadata
|
||||
|
||||
This call will replace only the metadata with the same keys
|
||||
given here. Metadata with other keys will not be modified.
|
||||
|
||||
:param session: The session to use for this request.
|
||||
:param kwargs metadata: key/value metadata pairs to be update on
|
||||
this server instance. All keys and values
|
||||
are stored as Unicode.
|
||||
|
||||
:returns: A dictionary of the metadata after being updated.
|
||||
All keys and values are Unicode text.
|
||||
:rtype: dict
|
||||
"""
|
||||
result = self._metadata(session.post, **metadata)
|
||||
return result["metadata"]
|
||||
|
||||
def delete_metadata(self, session, key):
|
||||
"""Delete metadata
|
||||
|
||||
:param session: The session to use for this request.
|
||||
:param string key: The key to delete.
|
||||
|
||||
:rtype: ``None``
|
||||
"""
|
||||
self._metadata(session.delete, key=key, delete=True)
|
|
@ -13,11 +13,12 @@
|
|||
from openstack.compute import compute_service
|
||||
from openstack.compute.v2 import flavor
|
||||
from openstack.compute.v2 import image
|
||||
from openstack.compute.v2 import metadata
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
|
||||
|
||||
class Server(resource.Resource):
|
||||
class Server(resource.Resource, metadata.MetadataMixin):
|
||||
resource_key = 'server'
|
||||
resources_key = 'servers'
|
||||
base_path = '/servers'
|
||||
|
|
|
@ -52,3 +52,43 @@ class TestImage(base.BaseFunctionalTest):
|
|||
self.assertIn('metadata', image)
|
||||
self.assertIn('progress', image)
|
||||
self.assertIn('status', image)
|
||||
|
||||
def test_image_metadata(self):
|
||||
image = self._get_non_test_image()
|
||||
sot = self.conn.compute.get_image(image.id)
|
||||
|
||||
# Start by clearing out any other metadata.
|
||||
self.assertDictEqual(self.conn.compute.replace_image_metadata(sot),
|
||||
{})
|
||||
|
||||
# Insert first and last name metadata
|
||||
meta = {"first": "Matthew", "last": "Dellavedova"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.create_image_metadata(sot, **meta), meta)
|
||||
|
||||
# Update only the first name
|
||||
short = {"first": "Matt", "last": "Dellavedova"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.update_image_metadata(sot,
|
||||
first=short["first"]),
|
||||
short)
|
||||
|
||||
# Get all metadata and then only the last name
|
||||
self.assertDictEqual(self.conn.compute.get_image_metadata(sot),
|
||||
short)
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.get_image_metadata(sot, "last"),
|
||||
{"last": short["last"]})
|
||||
|
||||
# Replace everything with just a nickname
|
||||
nick = {"nickname": "Delly"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.replace_image_metadata(sot, **nick),
|
||||
nick)
|
||||
self.assertDictEqual(self.conn.compute.get_image_metadata(sot),
|
||||
nick)
|
||||
|
||||
# Delete the only remaining key, make sure we're empty
|
||||
self.assertIsNone(
|
||||
self.conn.compute.delete_image_metadata(sot, "nickname"))
|
||||
self.assertDictEqual(self.conn.compute.get_image_metadata(sot), {})
|
||||
|
|
|
@ -41,6 +41,7 @@ class TestServer(base.BaseFunctionalTest):
|
|||
args = {}
|
||||
sot = cls.conn.compute.create_server(
|
||||
name=cls.NAME, flavor=flavor, image=image.id, **args)
|
||||
cls.conn.compute.wait_for_server(sot)
|
||||
assert isinstance(sot, server.Server)
|
||||
cls.assertIs(cls.NAME, sot.name)
|
||||
cls.server = sot
|
||||
|
@ -66,3 +67,42 @@ class TestServer(base.BaseFunctionalTest):
|
|||
def test_list(self):
|
||||
names = [o.name for o in self.conn.compute.servers()]
|
||||
self.assertIn(self.NAME, names)
|
||||
|
||||
def test_server_metadata(self):
|
||||
sot = self.conn.compute.get_server(self.server.id)
|
||||
|
||||
# Start by clearing out any other metadata.
|
||||
self.assertDictEqual(self.conn.compute.replace_server_metadata(sot),
|
||||
{})
|
||||
|
||||
# Insert first and last name metadata
|
||||
meta = {"first": "Matthew", "last": "Dellavedova"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.create_server_metadata(sot, **meta), meta)
|
||||
|
||||
# Update only the first name
|
||||
short = {"first": "Matt", "last": "Dellavedova"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.update_server_metadata(sot,
|
||||
first=short["first"]),
|
||||
short)
|
||||
|
||||
# Get all metadata and then only the last name
|
||||
self.assertDictEqual(self.conn.compute.get_server_metadata(sot),
|
||||
short)
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.get_server_metadata(sot, "last"),
|
||||
{"last": short["last"]})
|
||||
|
||||
# Replace everything with just a nickname
|
||||
nick = {"nickname": "Delly"}
|
||||
self.assertDictEqual(
|
||||
self.conn.compute.replace_server_metadata(sot, **nick),
|
||||
nick)
|
||||
self.assertDictEqual(self.conn.compute.get_server_metadata(sot),
|
||||
nick)
|
||||
|
||||
# Delete the only remaining key, make sure we're empty
|
||||
self.assertIsNone(
|
||||
self.conn.compute.delete_server_metadata(sot, "nickname"))
|
||||
self.assertDictEqual(self.conn.compute.get_server_metadata(sot), {})
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
# 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.
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from openstack.compute.v2 import server
|
||||
|
||||
IDENTIFIER = 'IDENTIFIER'
|
||||
|
||||
# NOTE: The implementation for metadata is done via a mixin class that both
|
||||
# the server and image resources inherit from. Currently this test class
|
||||
# uses the Server resource to test it. Ideally it would be parameterized
|
||||
# to run with both Server and Image when the tooling for subtests starts
|
||||
# working.
|
||||
|
||||
|
||||
class TestMetadata(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMetadata, self).setUp()
|
||||
self.metadata_result = {"metadata": {"go": "cubs", "boo": "sox"}}
|
||||
self.meta_result = {"meta": {"oh": "yeah"}}
|
||||
|
||||
def test_get_all_metadata_Server(self):
|
||||
self._test_get_all_metadata(server.Server({"id": IDENTIFIER}))
|
||||
|
||||
def test_get_all_metadata_ServerDetail(self):
|
||||
# This is tested explicitly so we know ServerDetail items are
|
||||
# properly having /detail stripped out of their base_path.
|
||||
self._test_get_all_metadata(server.ServerDetail({"id": IDENTIFIER}))
|
||||
|
||||
def _test_get_all_metadata(self, sot):
|
||||
response = mock.Mock()
|
||||
response.json.return_value = self.metadata_result
|
||||
sess = mock.Mock()
|
||||
sess.get.return_value = response
|
||||
|
||||
result = sot.get_metadata(sess)
|
||||
|
||||
self.assertEqual(result, self.metadata_result["metadata"])
|
||||
sess.get.assert_called_once_with("servers/IDENTIFIER/metadata",
|
||||
headers={},
|
||||
endpoint_filter=sot.service)
|
||||
|
||||
def test_get_one_metadata(self):
|
||||
response = mock.Mock()
|
||||
response.json.return_value = self.meta_result
|
||||
sess = mock.Mock()
|
||||
sess.get.return_value = response
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
key = "lol"
|
||||
result = sot.get_metadata(sess, key)
|
||||
|
||||
self.assertEqual(result, self.meta_result["meta"])
|
||||
sess.get.assert_called_once_with("servers/IDENTIFIER/metadata/" + key,
|
||||
headers={},
|
||||
endpoint_filter=sot.service)
|
||||
|
||||
def test_create_metadata_bad_type(self):
|
||||
sess = mock.Mock()
|
||||
sess.put = mock.Mock()
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
self.assertRaises(ValueError,
|
||||
sot.create_metadata, sess, some_key=True)
|
||||
|
||||
def test_create_metadata(self):
|
||||
metadata = {"first": "1", "second": "2"}
|
||||
responses = []
|
||||
for key, value in metadata.items():
|
||||
response = mock.Mock()
|
||||
response.json.return_value = {"meta": {key: value}}
|
||||
responses.append(response)
|
||||
|
||||
sess = mock.Mock()
|
||||
sess.put.side_effect = responses
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
result = sot.create_metadata(sess, **metadata)
|
||||
|
||||
self.assertEqual(result, dict([(k, v) for k, v in metadata.items()]))
|
||||
|
||||
# assert_called_with depends on sequence, which doesn't work nicely
|
||||
# with all of the dictionaries we're working with here. Build up
|
||||
# our own list of calls and check that they've happend
|
||||
calls = []
|
||||
for key in metadata.keys():
|
||||
calls.append(mock.call("servers/IDENTIFIER/metadata/" + key,
|
||||
endpoint_filter=sot.service,
|
||||
headers={},
|
||||
json={"meta": {key: metadata[key]}}))
|
||||
|
||||
sess.put.assert_has_calls(calls, any_order=True)
|
||||
|
||||
def test_replace_metadata(self):
|
||||
response = mock.Mock()
|
||||
response.json.return_value = self.metadata_result
|
||||
sess = mock.Mock()
|
||||
sess.put.return_value = response
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
new_meta = {"lol": "rofl"}
|
||||
|
||||
result = sot.replace_metadata(sess, **new_meta)
|
||||
|
||||
self.assertEqual(result, self.metadata_result["metadata"])
|
||||
sess.put.assert_called_once_with("servers/IDENTIFIER/metadata",
|
||||
endpoint_filter=sot.service,
|
||||
headers={},
|
||||
json={"metadata": new_meta})
|
||||
|
||||
def test_replace_metadata_clear(self):
|
||||
empty = {}
|
||||
|
||||
response = mock.Mock()
|
||||
response.json.return_value = {"metadata": empty}
|
||||
sess = mock.Mock()
|
||||
sess.put.return_value = response
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
result = sot.replace_metadata(sess)
|
||||
|
||||
self.assertEqual(result, empty)
|
||||
sess.put.assert_called_once_with("servers/IDENTIFIER/metadata",
|
||||
endpoint_filter=sot.service,
|
||||
headers={},
|
||||
json={"metadata": empty})
|
||||
|
||||
def test_update_metadata(self):
|
||||
response = mock.Mock()
|
||||
response.json.return_value = self.metadata_result
|
||||
sess = mock.Mock()
|
||||
sess.post.return_value = response
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
updated_meta = {"lol": "rofl"}
|
||||
|
||||
result = sot.update_metadata(sess, **updated_meta)
|
||||
|
||||
self.assertEqual(result, self.metadata_result["metadata"])
|
||||
sess.post.assert_called_once_with("servers/IDENTIFIER/metadata",
|
||||
endpoint_filter=sot.service,
|
||||
headers={},
|
||||
json={"metadata": updated_meta})
|
||||
|
||||
def test_delete_metadata(self):
|
||||
sess = mock.Mock()
|
||||
sess.delete.return_value = None
|
||||
|
||||
sot = server.Server({"id": IDENTIFIER})
|
||||
|
||||
key = "hey"
|
||||
|
||||
result = sot.delete_metadata(sess, key)
|
||||
|
||||
self.assertIsNone(result)
|
||||
sess.delete.assert_called_once_with(
|
||||
"servers/IDENTIFIER/metadata/" + key,
|
||||
headers={"Accept": ""},
|
||||
endpoint_filter=sot.service)
|
|
@ -223,6 +223,13 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
|
|||
def test_server_update(self):
|
||||
self.verify_update(self.proxy.update_server, server.Server)
|
||||
|
||||
def test_server_wait_for(self):
|
||||
value = server.Server(attrs={'id': '1234'})
|
||||
self.verify_wait_for_status(
|
||||
self.proxy.wait_for_server,
|
||||
method_args=[value],
|
||||
expected_args=[value, 'ACTIVE', ['ERROR'], 2, 120])
|
||||
|
||||
def test_server_resize(self):
|
||||
self._verify("openstack.compute.v2.server.Server.resize",
|
||||
self.proxy.resize_server,
|
||||
|
@ -277,3 +284,55 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
|
|||
def test_availability_zones(self):
|
||||
self.verify_list(self.proxy.availability_zones, az.AvailabilityZone,
|
||||
paginated=False)
|
||||
|
||||
def test_get_all_server_metadata(self):
|
||||
self._verify2("openstack.compute.v2.server.Server.get_metadata",
|
||||
self.proxy.get_server_metadata,
|
||||
expected_result={},
|
||||
method_args=["value"],
|
||||
expected_args=[self.session, None])
|
||||
|
||||
def test_get_one_server_metadata(self):
|
||||
self._verify2("openstack.compute.v2.server.Server.get_metadata",
|
||||
self.proxy.get_server_metadata,
|
||||
expected_result={},
|
||||
method_args=["value"],
|
||||
method_kwargs={"key": "key"},
|
||||
expected_args=[self.session, "key"])
|
||||
|
||||
def test_create_server_metadata(self):
|
||||
kwargs = {"a": "1", "b": "2"}
|
||||
self._verify2("openstack.compute.v2.server.Server.create_metadata",
|
||||
self.proxy.create_server_metadata,
|
||||
expected_result={},
|
||||
method_args=["value"],
|
||||
method_kwargs=kwargs,
|
||||
expected_args=[self.session],
|
||||
expected_kwargs=kwargs)
|
||||
|
||||
def test_replace_server_metadata(self):
|
||||
kwargs = {"a": "1", "b": "2"}
|
||||
self._verify2("openstack.compute.v2.server.Server.replace_metadata",
|
||||
self.proxy.replace_server_metadata,
|
||||
expected_result={},
|
||||
method_args=["value"],
|
||||
method_kwargs=kwargs,
|
||||
expected_args=[self.session],
|
||||
expected_kwargs=kwargs)
|
||||
|
||||
def test_update_server_metadata(self):
|
||||
kwargs = {"a": "1", "b": "2"}
|
||||
self._verify2("openstack.compute.v2.server.Server.update_metadata",
|
||||
self.proxy.update_server_metadata,
|
||||
expected_result={},
|
||||
method_args=["value"],
|
||||
method_kwargs=kwargs,
|
||||
expected_args=[self.session],
|
||||
expected_kwargs=kwargs)
|
||||
|
||||
def test_delete_server_metadata(self):
|
||||
self._verify2("openstack.compute.v2.server.Server.delete_metadata",
|
||||
self.proxy.delete_server_metadata,
|
||||
expected_result=None,
|
||||
method_args=["value", "key"],
|
||||
expected_args=[self.session, "key"])
|
||||
|
|
Loading…
Reference in New Issue