Add support for changing metadata of compute instances
Change-Id: I2301e0ab2024c51e7482ce0f05b706e8a395d676
This commit is contained in:
parent
a1ab68cd2b
commit
e67b306c10
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add new APIs, OpenStackCloud.set_server_metadata() and OpenStackCloud.delete_server_metadata() to manage metadata of existing nova compute instances
|
@ -172,6 +172,16 @@ class ServerRebuild(task_manager.Task):
|
|||||||
return client.nova_client.servers.rebuild(**self.args)
|
return client.nova_client.servers.rebuild(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerSetMetadata(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.nova_client.servers.set_meta(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerDeleteMetadata(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.nova_client.servers.delete_meta(**self.args)
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupList(task_manager.Task):
|
class ServerGroupList(task_manager.Task):
|
||||||
def main(self, client):
|
def main(self, client):
|
||||||
return client.nova_client.server_groups.list(**self.args)
|
return client.nova_client.server_groups.list(**self.args)
|
||||||
|
@ -4302,6 +4302,47 @@ class OpenStackCloud(object):
|
|||||||
extra_data=dict(server=server))
|
extra_data=dict(server=server))
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
def set_server_metadata(self, name_or_id, metadata):
|
||||||
|
"""Set metadata in a server instance.
|
||||||
|
|
||||||
|
:param str name_or_id: The name or id of the server instance
|
||||||
|
to update.
|
||||||
|
:param dict metadata: A dictionary with the key=value pairs
|
||||||
|
to set in the server instance. It only updates the key=value
|
||||||
|
pairs provided. Existing ones will remain untouched.
|
||||||
|
|
||||||
|
:raises: OpenStackCloudException on operation error.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.manager.submitTask(
|
||||||
|
_tasks.ServerSetMetadata(server=self.get_server(name_or_id),
|
||||||
|
metadata=metadata))
|
||||||
|
except OpenStackCloudException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"Error updating metadata: {0}".format(e))
|
||||||
|
|
||||||
|
def delete_server_metadata(self, name_or_id, metadata_keys):
|
||||||
|
"""Delete metadata from a server instance.
|
||||||
|
|
||||||
|
:param str name_or_id: The name or id of the server instance
|
||||||
|
to update.
|
||||||
|
:param list metadata_keys: A list with the keys to be deleted
|
||||||
|
from the server instance.
|
||||||
|
|
||||||
|
:raises: OpenStackCloudException on operation error.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.manager.submitTask(
|
||||||
|
_tasks.ServerDeleteMetadata(server=self.get_server(name_or_id),
|
||||||
|
keys=metadata_keys))
|
||||||
|
except OpenStackCloudException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"Error deleting metadata: {0}".format(e))
|
||||||
|
|
||||||
def delete_server(
|
def delete_server(
|
||||||
self, name_or_id, wait=False, timeout=180, delete_ips=False,
|
self, name_or_id, wait=False, timeout=180, delete_ips=False,
|
||||||
delete_ip_retry=1):
|
delete_ip_retry=1):
|
||||||
|
@ -19,6 +19,7 @@ test_compute
|
|||||||
Functional tests for `shade` compute methods.
|
Functional tests for `shade` compute methods.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from shade import exc
|
||||||
from shade.tests.functional import base
|
from shade.tests.functional import base
|
||||||
from shade.tests.functional.util import pick_flavor, pick_image
|
from shade.tests.functional.util import pick_flavor, pick_image
|
||||||
|
|
||||||
@ -215,3 +216,37 @@ class TestCompute(base.BaseFunctionalTestCase):
|
|||||||
wait=True)
|
wait=True)
|
||||||
self.addCleanup(self.demo_cloud.delete_image, image['id'])
|
self.addCleanup(self.demo_cloud.delete_image, image['id'])
|
||||||
self.assertEqual('active', image['status'])
|
self.assertEqual('active', image['status'])
|
||||||
|
|
||||||
|
def test_set_and_delete_metadata(self):
|
||||||
|
self.addCleanup(self._cleanup_servers_and_volumes, self.server_name)
|
||||||
|
self.demo_cloud.create_server(
|
||||||
|
name=self.server_name,
|
||||||
|
image=self.image,
|
||||||
|
flavor=self.flavor,
|
||||||
|
wait=True)
|
||||||
|
self.demo_cloud.set_server_metadata(self.server_name,
|
||||||
|
{'key1': 'value1',
|
||||||
|
'key2': 'value2'})
|
||||||
|
updated_server = self.demo_cloud.get_server(self.server_name)
|
||||||
|
self.assertEqual(set(updated_server.metadata.items()),
|
||||||
|
set({'key1': 'value1', 'key2': 'value2'}.items()))
|
||||||
|
|
||||||
|
self.demo_cloud.set_server_metadata(self.server_name,
|
||||||
|
{'key2': 'value3'})
|
||||||
|
updated_server = self.demo_cloud.get_server(self.server_name)
|
||||||
|
self.assertEqual(set(updated_server.metadata.items()),
|
||||||
|
set({'key1': 'value1', 'key2': 'value3'}.items()))
|
||||||
|
|
||||||
|
self.demo_cloud.delete_server_metadata(self.server_name, ['key2'])
|
||||||
|
updated_server = self.demo_cloud.get_server(self.server_name)
|
||||||
|
self.assertEqual(set(updated_server.metadata.items()),
|
||||||
|
set({'key1': 'value1'}.items()))
|
||||||
|
|
||||||
|
self.demo_cloud.delete_server_metadata(self.server_name, ['key1'])
|
||||||
|
updated_server = self.demo_cloud.get_server(self.server_name)
|
||||||
|
self.assertEqual(set(updated_server.metadata.items()), set([]))
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exc.OpenStackCloudException,
|
||||||
|
self.demo_cloud.delete_server_metadata,
|
||||||
|
self.server_name, ['key1'])
|
||||||
|
66
shade/tests/unit/test_server_delete_metadata.py
Normal file
66
shade/tests/unit/test_server_delete_metadata.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_server_delete_metadata
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Tests for the `delete_server_metadata` command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mock import patch, Mock
|
||||||
|
import os_client_config
|
||||||
|
from shade import OpenStackCloud
|
||||||
|
from shade.exc import OpenStackCloudException
|
||||||
|
from shade.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestServerDeleteMetadata(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestServerDeleteMetadata, self).setUp()
|
||||||
|
config = os_client_config.OpenStackConfig()
|
||||||
|
self.client = OpenStackCloud(
|
||||||
|
cloud_config=config.get_one_cloud(validate=False))
|
||||||
|
self.client._SERVER_AGE = 0
|
||||||
|
|
||||||
|
def test_server_delete_metadata_with_delete_meta_exception(self):
|
||||||
|
"""
|
||||||
|
Test that a generic exception in the novaclient delete_meta raises
|
||||||
|
an exception in delete_server_metadata.
|
||||||
|
"""
|
||||||
|
with patch("shade.OpenStackCloud"):
|
||||||
|
config = {
|
||||||
|
"servers.delete_meta.side_effect": Exception("exception"),
|
||||||
|
}
|
||||||
|
OpenStackCloud.nova_client = Mock(**config)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.delete_server_metadata,
|
||||||
|
{'id': 'server-id'}, ['key'])
|
||||||
|
|
||||||
|
def test_server_delete_metadata_with_exception_reraise(self):
|
||||||
|
"""
|
||||||
|
Test that an OpenStackCloudException exception gets re-raised
|
||||||
|
in delete_server_metadata.
|
||||||
|
"""
|
||||||
|
with patch("shade.OpenStackCloud"):
|
||||||
|
config = {
|
||||||
|
"servers.delete_meta.side_effect":
|
||||||
|
OpenStackCloudException("exception"),
|
||||||
|
}
|
||||||
|
OpenStackCloud.nova_client = Mock(**config)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.delete_server_metadata,
|
||||||
|
'server-id', ['key'])
|
67
shade/tests/unit/test_server_set_metadata.py
Normal file
67
shade/tests/unit/test_server_set_metadata.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_server_set_metadata
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Tests for the `set_server_metadata` command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mock import patch, Mock
|
||||||
|
import os_client_config
|
||||||
|
from shade import OpenStackCloud
|
||||||
|
from shade.exc import OpenStackCloudException
|
||||||
|
from shade.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestServerSetMetadata(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestServerSetMetadata, self).setUp()
|
||||||
|
config = os_client_config.OpenStackConfig()
|
||||||
|
self.client = OpenStackCloud(
|
||||||
|
cloud_config=config.get_one_cloud(validate=False))
|
||||||
|
self.client._SERVER_AGE = 0
|
||||||
|
|
||||||
|
def test_server_set_metadata_with_set_meta_exception(self):
|
||||||
|
"""
|
||||||
|
Test that a generic exception in the novaclient set_meta raises
|
||||||
|
an exception in set_server_metadata.
|
||||||
|
"""
|
||||||
|
with patch("shade.OpenStackCloud"):
|
||||||
|
config = {
|
||||||
|
"servers.set_meta.side_effect": Exception("exception"),
|
||||||
|
}
|
||||||
|
OpenStackCloud.nova_client = Mock(**config)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.set_server_metadata,
|
||||||
|
{'id': 'server-id'}, {'meta': 'data'})
|
||||||
|
|
||||||
|
def test_server_set_metadata_with_exception_reraise(self):
|
||||||
|
"""
|
||||||
|
Test that an OpenStackCloudException exception gets re-raised
|
||||||
|
in set_server_metadata.
|
||||||
|
"""
|
||||||
|
with patch("shade.OpenStackCloud"):
|
||||||
|
config = {
|
||||||
|
"servers.set_meta.side_effect":
|
||||||
|
OpenStackCloudException("exception"),
|
||||||
|
}
|
||||||
|
OpenStackCloud.nova_client = Mock(**config)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.set_server_metadata,
|
||||||
|
'server-id', {'meta': 'data'})
|
Loading…
Reference in New Issue
Block a user