Make metadata handling consistent in Compute

Make metadata handling consistent across all Compute resources.
The consistent methods are get_*_metdata, set_*_metadata, and
delete_*_metadata which is similar to the __get__, __set__, and
__delete__ methods of a descriptor or the __getitem__, __setitem__,
and __delitem__ of a MutableMapping so it should be fairly natural
for Python users.

Change-Id: I977e8741bdd755e8af8c0f3c4e16e38cfba79bd5
Closes-Bug: #1546156
This commit is contained in:
Everett Toews 2016-02-28 14:07:00 -06:00
parent 4fafbbe406
commit 1f6990edbf
11 changed files with 171 additions and 727 deletions

View File

@ -198,64 +198,27 @@ class Proxy(proxy.BaseProxy):
else:
return base({"id": res})
def get_image_metadata(self, image, key=None):
def get_image_metadata(self, image):
"""Return a dictionary of metadata for an image
:param server: Either the ID of an image or a
:param image: 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
:returns: A :class:`~openstack.compute.v2.image.Image` with only the
image's metadata. All keys and values are Unicode text.
:rtype: :class:`~openstack.compute.v2.image.Image`
"""
res = self._get_base_resource(image, _image.Image)
return res.get_metadata(self.session, key)
metadata = res.get_metadata(self.session)
result = _image.Image.existing(id=res.id, metadata=metadata)
return result
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):
def set_image_metadata(self, image, **metadata):
"""Update metadata for an image
:param server: Either the ID of an image or a
:param image: Either the ID of an image or a
:class:`~openstack.compute.v2.image.Image` or
:class:`~openstack.compute.v2.image.ImageDetail`
instance.
@ -264,26 +227,30 @@ class Proxy(proxy.BaseProxy):
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
:returns: A :class:`~openstack.compute.v2.image.Image` with only the
image's metadata. All keys and values are Unicode text.
:rtype: :class:`~openstack.compute.v2.image.Image`
"""
res = self._get_base_resource(image, _image.Image)
return res.update_metadata(self.session, **metadata)
metadata = res.set_metadata(self.session, **metadata)
result = _image.Image.existing(id=res.id, metadata=metadata)
return result
def delete_image_metadata(self, image, key):
def delete_image_metadata(self, image, keys):
"""Delete metadata for an image
:param server: Either the ID of an image or a
Note: This method will do a HTTP DELETE request for every key in keys.
:param image: 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
:param list keys: The keys to delete.
:rtype: ``None``
"""
res = self._get_base_resource(image, _image.Image)
return res.delete_metadata(self.session, key)
return res.delete_metadata(self.session, keys)
def create_keypair(self, **attrs):
"""Create a new keypair from attributes
@ -681,61 +648,24 @@ class Proxy(proxy.BaseProxy):
return self._list(availability_zone.AvailabilityZone,
paginated=False, **query)
def get_server_metadata(self, server, key=None):
def get_server_metadata(self, server):
"""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
:returns: A :class:`~openstack.compute.v2.server.Server` with only the
server's metadata. All keys and values are Unicode text.
:rtype: :class:`~openstack.compute.v2.server.Server`
"""
res = self._get_base_resource(server, _server.Server)
return res.get_metadata(self.session, key)
metadata = res.get_metadata(self.session)
result = _server.Server.existing(id=res.id, metadata=metadata)
return result
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):
def set_server_metadata(self, server, **metadata):
"""Update metadata for a server
:param server: Either the ID of a server or a
@ -747,26 +677,30 @@ class Proxy(proxy.BaseProxy):
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
:returns: A :class:`~openstack.compute.v2.server.Server` with only the
server's metadata. All keys and values are Unicode text.
:rtype: :class:`~openstack.compute.v2.server.Server`
"""
res = self._get_base_resource(server, _server.Server)
return res.update_metadata(self.session, **metadata)
metadata = res.set_metadata(self.session, **metadata)
result = _server.Server.existing(id=res.id, metadata=metadata)
return result
def delete_server_metadata(self, server, key):
def delete_server_metadata(self, server, keys):
"""Delete metadata for a server
Note: This method will do a HTTP DELETE request for every key in keys.
: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
:param list keys: The keys to delete
:rtype: ``None``
"""
res = self._get_base_resource(server, _server.Server)
return res.delete_metadata(self.session, key)
return res.delete_metadata(self.session, keys)
def create_server_group(self, **attrs):
"""Create a new server group from attributes

View File

@ -39,7 +39,7 @@ class MetadataMixin(object):
kwargs = {"endpoint_filter": self.service}
if metadata or clear:
# 'meta' is the key for singlular modifications.
# 'meta' is the key for singular modifications.
# 'metadata' is the key for mass modifications.
key = "meta" if key is not None else "metadata"
kwargs["json"] = {key: metadata}
@ -51,70 +51,19 @@ class MetadataMixin(object):
# 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):
def get_metadata(self, session):
"""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)
result = self._metadata(session.get)
return result["metadata"]
def update_metadata(self, session, **metadata):
def set_metadata(self, session, **metadata):
"""Update metadata
This call will replace only the metadata with the same keys
@ -129,15 +78,21 @@ class MetadataMixin(object):
All keys and values are Unicode text.
:rtype: dict
"""
if not metadata:
return dict()
result = self._metadata(session.post, **metadata)
return result["metadata"]
def delete_metadata(self, session, key):
def delete_metadata(self, session, keys):
"""Delete metadata
Note: This method will do a HTTP DELETE request for every key in keys.
:param session: The session to use for this request.
:param string key: The key to delete.
:param list keys: The keys to delete.
:rtype: ``None``
"""
self._metadata(session.delete, key=key, delete=True)
for key in keys:
self._metadata(session.delete, key=key, delete=True)

View File

@ -1,76 +0,0 @@
# 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.compute import compute_service
from openstack import resource
class ServerMeta(resource.Resource):
resource_key = 'meta'
id_attribute = 'key'
base_path = '/servers/%(server_id)s/metadata'
service = compute_service.ComputeService()
# capabilities
allow_create = True
allow_retrieve = True
allow_update = True
allow_delete = True
allow_list = True
# Properties
#: The metadata key.
key = resource.prop('key')
#: The ID of a server.
server_id = resource.prop('server_id')
#: The metadata value.
value = resource.prop('value')
@classmethod
def create_by_id(cls, session, attrs, resource_id=None, path_args=None):
url = cls._get_url(path_args, resource_id)
body = {cls.resource_key: {attrs['key']: attrs['value']}}
resp = session.put(url, endpoint_filter=cls.service, json=body)
resp = resp.json()
return {'key': resource_id,
'value': resp[cls.resource_key][resource_id]}
@classmethod
def get_data_by_id(cls, session, resource_id, path_args=None,
include_headers=False):
url = cls._get_url(path_args, resource_id)
resp = session.get(url, endpoint_filter=cls.service)
resp = resp.json()
return {'key': resource_id,
'value': resp[cls.resource_key][resource_id]}
@classmethod
def update_by_id(cls, session, resource_id, attrs, path_args=None):
return cls.create_by_id(session, attrs, resource_id, path_args)
@classmethod
def delete_by_id(cls, session, resource_id, path_args=None):
url = cls._get_url(path_args, resource_id)
headers = {'Accept': ''}
session.delete(url, endpoint_filter=cls.service, headers=headers)
@classmethod
def list(cls, session, path_args=None, **params):
url = '/servers/%(server_id)s/metadata' % path_args
resp = session.get(url, endpoint_filter=cls.service, params=params)
resp = resp.json()
resp = resp['metadata']
return [cls.existing(server_id=path_args['server_id'], key=key,
value=value)
for key, value in six.iteritems(resp)]

View File

@ -1,53 +0,0 @@
# 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.
from openstack.compute import compute_service
from openstack import resource
class ServerMetadata(resource.Resource):
resource_key = 'metadata'
id_attribute = 'server_id'
base_path = '/servers/%(server_id)s/metadata'
service = compute_service.ComputeService()
# capabilities
allow_create = True
allow_retrieve = True
allow_update = True
# Properties
#: The ID of a server.
server_id = resource.prop('server_id')
@classmethod
def create_by_id(cls, session, attrs, resource_id=None, path_args=None):
no_id = attrs.copy()
no_id.pop('server_id')
body = {"metadata": no_id}
url = cls._get_url(path_args)
resp = session.put(url, endpoint_filter=cls.service, json=body)
resp = resp.json()
attrs = resp["metadata"].copy()
attrs['server_id'] = resource_id
return attrs
@classmethod
def get_data_by_id(cls, session, resource_id, path_args=None, args=None,
include_headers=False):
url = cls._get_url(path_args)
resp = session.get(url, endpoint_filter=cls.service)
return resp.json()[cls.resource_key]
@classmethod
def update_by_id(cls, session, resource_id, attrs, path_args=None):
return cls.create_by_id(session, attrs, resource_id, path_args)

View File

@ -55,40 +55,55 @@ class TestImage(base.BaseFunctionalTest):
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),
{})
# delete pre-existing metadata
self.conn.compute.delete_image_metadata(image, image.metadata.keys())
image = self.conn.compute.get_image_metadata(image)
self.assertFalse(image.metadata)
# Insert first and last name metadata
meta = {"first": "Matthew", "last": "Dellavedova"}
self.assertDictEqual(
self.conn.compute.create_image_metadata(sot, **meta), meta)
# get metadata
image = self.conn.compute.get_image_metadata(image)
self.assertFalse(image.metadata)
# Update only the first name
short = {"first": "Matt", "last": "Dellavedova"}
self.assertDictEqual(
self.conn.compute.update_image_metadata(sot,
first=short["first"]),
short)
# set no metadata
self.conn.compute.set_image_metadata(image)
image = self.conn.compute.get_image_metadata(image)
self.assertFalse(image.metadata)
# 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"]})
# set empty metadata
self.conn.compute.set_image_metadata(image, k0='')
image = self.conn.compute.get_image_metadata(image)
self.assertFalse(image.metadata)
# 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)
# set metadata
self.conn.compute.set_image_metadata(image, k1='v1')
image = self.conn.compute.get_image_metadata(image)
self.assertTrue(image.metadata)
self.assertEqual(1, len(image.metadata))
self.assertIn('k1', image.metadata)
self.assertEqual('v1', image.metadata['k1'])
# 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), {})
# set more metadata
self.conn.compute.set_image_metadata(image, k2='v2')
image = self.conn.compute.get_image_metadata(image)
self.assertTrue(image.metadata)
self.assertEqual(2, len(image.metadata))
self.assertIn('k1', image.metadata)
self.assertEqual('v1', image.metadata['k1'])
self.assertIn('k2', image.metadata)
self.assertEqual('v2', image.metadata['k2'])
# update metadata
self.conn.compute.set_image_metadata(image, k1='v1.1')
image = self.conn.compute.get_image_metadata(image)
self.assertTrue(image.metadata)
self.assertEqual(2, len(image.metadata))
self.assertIn('k1', image.metadata)
self.assertEqual('v1.1', image.metadata['k1'])
self.assertIn('k2', image.metadata)
self.assertEqual('v2', image.metadata['k2'])
# delete metadata
self.conn.compute.delete_image_metadata(image, image.metadata.keys())
image = self.conn.compute.get_image_metadata(image)
self.assertFalse(image.metadata)

View File

@ -69,45 +69,58 @@ class TestServer(base.BaseFunctionalTest):
self.assertIn(self.NAME, names)
def test_server_metadata(self):
sot = self.conn.compute.get_server(self.server.id)
test_server = self.conn.compute.get_server(self.server.id)
# Start by clearing out any other metadata.
self.assertDictEqual(self.conn.compute.replace_server_metadata(sot),
{})
# get metadata
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertFalse(test_server.metadata)
# Create first and last name metadata
meta = {"first": "Matthew", "last": "Dellavedova"}
self.assertDictEqual(
self.conn.compute.create_server_metadata(sot, **meta), meta)
# set no metadata
self.conn.compute.set_server_metadata(test_server)
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertFalse(test_server.metadata)
# Create something that already exists
meta = {"last": "Inman"}
self.assertDictEqual(
self.conn.compute.create_server_metadata(sot, **meta), meta)
# set empty metadata
self.conn.compute.set_server_metadata(test_server, k0='')
server = self.conn.compute.get_server_metadata(test_server)
self.assertTrue(server.metadata)
# Update only the first name
short = {"first": "Matt", "last": "Inman"}
self.assertDictEqual(
self.conn.compute.update_server_metadata(sot,
first=short["first"]),
short)
# set metadata
self.conn.compute.set_server_metadata(test_server, k1='v1')
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertTrue(test_server.metadata)
self.assertEqual(2, len(test_server.metadata))
self.assertIn('k0', test_server.metadata)
self.assertEqual('', test_server.metadata['k0'])
self.assertIn('k1', test_server.metadata)
self.assertEqual('v1', test_server.metadata['k1'])
# 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"]})
# set more metadata
self.conn.compute.set_server_metadata(test_server, k2='v2')
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertTrue(test_server.metadata)
self.assertEqual(3, len(test_server.metadata))
self.assertIn('k0', test_server.metadata)
self.assertEqual('', test_server.metadata['k0'])
self.assertIn('k1', test_server.metadata)
self.assertEqual('v1', test_server.metadata['k1'])
self.assertIn('k2', test_server.metadata)
self.assertEqual('v2', test_server.metadata['k2'])
# 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)
# update metadata
self.conn.compute.set_server_metadata(test_server, k1='v1.1')
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertTrue(test_server.metadata)
self.assertEqual(3, len(test_server.metadata))
self.assertIn('k0', test_server.metadata)
self.assertEqual('', test_server.metadata['k0'])
self.assertIn('k1', test_server.metadata)
self.assertEqual('v1.1', test_server.metadata['k1'])
self.assertIn('k2', test_server.metadata)
self.assertEqual('v2', test_server.metadata['k2'])
# 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), {})
# delete metadata
self.conn.compute.delete_server_metadata(
test_server, test_server.metadata.keys())
test_server = self.conn.compute.get_server_metadata(test_server)
self.assertFalse(test_server.metadata)

View File

@ -52,96 +52,7 @@ class TestMetadata(testtools.TestCase):
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):
def test_set_metadata(self):
response = mock.Mock()
response.json.return_value = self.metadata_result
sess = mock.Mock()
@ -149,15 +60,15 @@ class TestMetadata(testtools.TestCase):
sot = server.Server({"id": IDENTIFIER})
updated_meta = {"lol": "rofl"}
set_meta = {"lol": "rofl"}
result = sot.update_metadata(sess, **updated_meta)
result = sot.set_metadata(sess, **set_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})
json={"metadata": set_meta})
def test_delete_metadata(self):
sess = mock.Mock()
@ -167,9 +78,8 @@ class TestMetadata(testtools.TestCase):
key = "hey"
result = sot.delete_metadata(sess, key)
sot.delete_metadata(sess, [key])
self.assertIsNone(result)
sess.delete.assert_called_once_with(
"servers/IDENTIFIER/metadata/" + key,
headers={"Accept": ""},

View File

@ -289,47 +289,23 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
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])
method_result=server.Server.existing(id="value",
metadata={}),
expected_args=[self.session],
expected_result={})
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):
def test_set_server_metadata(self):
kwargs = {"a": "1", "b": "2"}
self._verify2("openstack.compute.v2.server.Server.create_metadata",
self.proxy.create_server_metadata,
expected_result={},
self._verify2("openstack.compute.v2.server.Server.set_metadata",
self.proxy.set_server_metadata,
method_args=["value"],
method_kwargs=kwargs,
method_result=server.Server.existing(id="value",
metadata=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)
expected_kwargs=kwargs,
expected_result=kwargs)
def test_delete_server_metadata(self):
self._verify2("openstack.compute.v2.server.Server.delete_metadata",

View File

@ -1,137 +0,0 @@
# 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_meta
FAKE_KEY = 'cervus'
FAKE_SERVER_ID = 'cervidae'
FAKE_VALUE = 'canadensis'
EXAMPLE = {
'key': FAKE_KEY,
'server_id': FAKE_SERVER_ID,
'value': FAKE_VALUE,
}
FAKE_RESPONSE = {"meta": {FAKE_KEY: FAKE_VALUE}}
FAKE_RESPONSES = {"metadata": {FAKE_KEY: FAKE_VALUE}}
class TestServerMeta(testtools.TestCase):
def test_basic(self):
sot = server_meta.ServerMeta()
self.assertEqual('meta', sot.resource_key)
self.assertIsNone(sot.resources_key)
self.assertEqual('/servers/%(server_id)s/metadata', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_retrieve)
self.assertTrue(sot.allow_update)
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = server_meta.ServerMeta(EXAMPLE)
self.assertEqual(EXAMPLE['key'], sot.key)
self.assertEqual(EXAMPLE['server_id'], sot.server_id)
self.assertEqual(EXAMPLE['value'], sot.value)
def test_create(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSE
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.put = mock.Mock(return_value=resp)
sot = server_meta.ServerMeta(EXAMPLE)
sot.create(sess)
url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY
body = {"meta": {FAKE_KEY: FAKE_VALUE}}
sess.put.assert_called_with(url, endpoint_filter=sot.service,
json=body)
self.assertEqual(FAKE_VALUE, sot.value)
self.assertEqual(FAKE_KEY, sot.key)
self.assertEqual(FAKE_SERVER_ID, sot.server_id)
def test_get(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSES
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.get = mock.Mock(return_value=resp)
sot = server_meta.ServerMeta()
path_args = {'server_id': FAKE_SERVER_ID}
resp = sot.list(sess, path_args=path_args)
url = '/servers/' + FAKE_SERVER_ID + '/metadata'
sess.get.assert_called_with(url, endpoint_filter=sot.service,
params={})
self.assertEqual(1, len(resp))
self.assertEqual(FAKE_SERVER_ID, resp[0].server_id)
self.assertEqual(FAKE_KEY, resp[0].key)
self.assertEqual(FAKE_VALUE, resp[0].value)
def test_update(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSE
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.put = mock.Mock(return_value=resp)
sot = server_meta.ServerMeta(EXAMPLE)
sot.update(sess)
url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY
body = {"meta": {FAKE_KEY: FAKE_VALUE}}
sess.put.assert_called_with(url, endpoint_filter=sot.service,
json=body)
self.assertEqual(FAKE_VALUE, sot.value)
self.assertEqual(FAKE_KEY, sot.key)
self.assertEqual(FAKE_SERVER_ID, sot.server_id)
def test_delete(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSES
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.delete = mock.Mock(return_value=resp)
sot = server_meta.ServerMeta(EXAMPLE)
sot.delete(sess)
url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY
headers = {'Accept': ''}
sess.delete.assert_called_with(url, endpoint_filter=sot.service,
headers=headers)
def test_list(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSES
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.get = mock.Mock(return_value=resp)
sot = server_meta.ServerMeta()
path_args = {'server_id': FAKE_SERVER_ID}
resp = sot.list(sess, path_args=path_args)
url = '/servers/' + FAKE_SERVER_ID + '/metadata'
sess.get.assert_called_with(url, endpoint_filter=sot.service,
params={})
self.assertEqual(1, len(resp))
self.assertEqual(FAKE_SERVER_ID, resp[0].server_id)
self.assertEqual(FAKE_KEY, resp[0].key)
self.assertEqual(FAKE_VALUE, resp[0].value)

View File

@ -1,97 +0,0 @@
# 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_metadata
FAKE_SERVER_ID = 'cervidae'
FAKE_KEY = 'cervus'
FAKE_VALUE = 'canadensis'
FAKE_KEY2 = 'odocoileus'
FAKE_VALUE2 = 'hemionus'
EXAMPLE = {
'server_id': FAKE_SERVER_ID,
FAKE_KEY: FAKE_VALUE,
}
FAKE_RESPONSE = {"metadata": {FAKE_KEY: FAKE_VALUE, FAKE_KEY2: FAKE_VALUE2}}
class TestServerMetadata(testtools.TestCase):
def test_basic(self):
sot = server_metadata.ServerMetadata()
self.assertEqual('metadata', sot.resource_key)
self.assertIsNone(sot.resources_key)
self.assertEqual('/servers/%(server_id)s/metadata', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_retrieve)
self.assertTrue(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = server_metadata.ServerMetadata(EXAMPLE)
self.assertEqual(EXAMPLE['server_id'], sot.server_id)
self.assertEqual(FAKE_VALUE, sot[FAKE_KEY])
def test_create(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSE
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.put = mock.Mock(return_value=resp)
sot = server_metadata.ServerMetadata(EXAMPLE.copy())
sot.create(sess)
url = '/servers/' + FAKE_SERVER_ID + '/metadata'
body = {"metadata": {FAKE_KEY: FAKE_VALUE}}
sess.put.assert_called_with(url, endpoint_filter=sot.service,
json=body)
self.assertEqual(FAKE_SERVER_ID, sot.server_id)
self.assertEqual(FAKE_VALUE, sot[FAKE_KEY])
def test_get(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSE
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.get = mock.Mock(return_value=resp)
sot = server_metadata.ServerMetadata(EXAMPLE.copy())
sot.get(sess)
url = '/servers/' + FAKE_SERVER_ID + '/metadata'
sess.get.assert_called_with(url, endpoint_filter=sot.service)
self.assertEqual(FAKE_SERVER_ID, sot.server_id)
self.assertEqual(FAKE_VALUE, sot[FAKE_KEY])
self.assertEqual(FAKE_VALUE2, sot[FAKE_KEY2])
def test_update(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSE
resp.json = mock.Mock(return_value=resp.body)
sess = mock.Mock()
sess.put = mock.Mock(return_value=resp)
sot = server_metadata.ServerMetadata(EXAMPLE.copy())
sot.update(sess)
url = '/servers/' + FAKE_SERVER_ID + '/metadata'
body = {"metadata": {FAKE_KEY: FAKE_VALUE}}
sess.put.assert_called_with(url, endpoint_filter=sot.service,
json=body)
self.assertEqual(FAKE_SERVER_ID, sot.server_id)
self.assertEqual(FAKE_VALUE, sot[FAKE_KEY])

View File

@ -50,7 +50,7 @@ class TestProxyBase(base.TestCase):
# the _verify method. It will be removed once there is one API to
# be verifying.
def _verify2(self, mock_method, test_method,
method_args=None, method_kwargs=None,
method_args=None, method_kwargs=None, method_result=None,
expected_args=None, expected_kwargs=None,
expected_result=None):
with mock.patch(mock_method) as mocked:
@ -62,8 +62,12 @@ class TestProxyBase(base.TestCase):
expected_args = expected_args or ()
expected_kwargs = expected_kwargs or {}
self.assertEqual(expected_result, test_method(*method_args,
**method_kwargs))
if method_result:
self.assertEqual(method_result, test_method(*method_args,
**method_kwargs))
else:
self.assertEqual(expected_result, test_method(*method_args,
**method_kwargs))
mocked.assert_called_with(*expected_args, **expected_kwargs)
else:
self.assertEqual(expected_result, test_method())