Stop leaking server objects

We should not be returning raw client objects when creating or
rebuilding a server.

The usage document is updated to indicate that access to resource
values via attribute is deprecated, and the examples in the README
now reflect dict-style access.

Change-Id: Iac38d4c0b29f867cc3cefaccf48c1c3fcd17a3d9
This commit is contained in:
David Shrewsbury 2015-05-08 15:23:55 -04:00
parent 3328cc77da
commit e71bee318c
6 changed files with 43 additions and 31 deletions

View File

@ -37,9 +37,9 @@ Sometimes an example is nice.
# But you can also access the underlying python-*client objects # But you can also access the underlying python-*client objects
cinder = cloud.cinder_client cinder = cloud.cinder_client
volumes = cinder.volumes.list() volumes = cinder.volumes.list()
volume_id = [v for v in volumes if v.status == 'available'][0].id volume_id = [v for v in volumes if v['status'] == 'available'][0]['id']
nova = cloud.nova_client nova = cloud.nova_client
print nova.volumes.create_server_volume(s.id, volume_id, None) print nova.volumes.create_server_volume(s['id'], volume_id, None)
attachments = [] attachments = []
print volume_id print volume_id
while not attachments: while not attachments:

View File

@ -1,10 +1,17 @@
======== =====
Usage Usage
======== =====
To use shade in a project:: To use shade in a project::
import shade import shade
.. warning::
Several of the API methods return a ``dict`` that describe a resource.
It is possible to access keys of the dict as an attribute (e.g.,
``server.id`` instead of ``server['id']``) to maintain some backward
compatibility, but attribute access is deprecated. New code should
assume a normal dictionary and access values via key.
.. automodule:: shade .. automodule:: shade
:members: :members:

View File

@ -1677,7 +1677,11 @@ class OpenStackCloud(object):
def create_server(self, auto_ip=True, ips=None, ip_pool=None, def create_server(self, auto_ip=True, ips=None, ip_pool=None,
root_volume=None, terminate_volume=False, root_volume=None, terminate_volume=False,
wait=False, timeout=180, **bootkwargs): wait=False, timeout=180, **bootkwargs):
"""Create a virtual server instance.
:returns: A dict representing the created server.
:raises: OpenStackCloudException on operation error.
"""
if root_volume: if root_volume:
if terminate_volume: if terminate_volume:
suffix = ':::1' suffix = ':::1'
@ -1703,21 +1707,22 @@ class OpenStackCloud(object):
timeout, timeout,
"Timeout waiting for the server to come up."): "Timeout waiting for the server to come up."):
try: try:
server = self.manager.submitTask( server = meta.obj_to_dict(
self.manager.submitTask(
_tasks.ServerGet(server=server)) _tasks.ServerGet(server=server))
)
except Exception: except Exception:
continue continue
if server.status == 'ACTIVE': if server['status'] == 'ACTIVE':
return self.add_ips_to_server( return self.add_ips_to_server(
server, auto_ip, ips, ip_pool) server, auto_ip, ips, ip_pool)
if server.status == 'ERROR': if server['status'] == 'ERROR':
raise OpenStackCloudException( raise OpenStackCloudException(
"Error in creating the server", "Error in creating the server",
extra_data=dict( extra_data=dict(server=server))
server=meta.obj_to_dict(server))) return meta.obj_to_dict(server)
return server
def rebuild_server(self, server_id, image_id, wait=False, timeout=180): def rebuild_server(self, server_id, image_id, wait=False, timeout=180):
try: try:
@ -1733,20 +1738,21 @@ class OpenStackCloud(object):
"Timeout waiting for server {0} to " "Timeout waiting for server {0} to "
"rebuild.".format(server_id)): "rebuild.".format(server_id)):
try: try:
server = self.manager.submitTask( server = meta.obj_to_dict(
self.manager.submitTask(
_tasks.ServerGet(server=server)) _tasks.ServerGet(server=server))
)
except Exception: except Exception:
continue continue
if server.status == 'ACTIVE': if server['status'] == 'ACTIVE':
break return server
if server.status == 'ERROR': if server['status'] == 'ERROR':
raise OpenStackCloudException( raise OpenStackCloudException(
"Error in rebuilding the server", "Error in rebuilding the server",
extra_data=dict( extra_data=dict(server=server))
server=meta.obj_to_dict(server))) return meta.obj_to_dict(server)
return server
def delete_server(self, name, wait=False, timeout=180): def delete_server(self, name, wait=False, timeout=180):
server = self.get_server(name) server = self.get_server(name)

View File

@ -19,7 +19,6 @@ test_compute
Functional tests for `shade` compute methods. Functional tests for `shade` compute methods.
""" """
from novaclient.v2.servers import Server
from shade import openstack_cloud from shade import openstack_cloud
from shade.tests import base from shade.tests import base
from shade.tests.functional.util import pick_flavor, pick_image from shade.tests.functional.util import pick_flavor, pick_image
@ -47,10 +46,9 @@ class TestCompute(base.TestCase):
self.addCleanup(self._cleanup_servers) self.addCleanup(self._cleanup_servers)
server = self.cloud.create_server(name='test_create_server', server = self.cloud.create_server(name='test_create_server',
image=self.image, flavor=self.flavor) image=self.image, flavor=self.flavor)
self.assertIsInstance(server, Server) self.assertEquals(server['name'], 'test_create_server')
self.assertEquals(server.name, 'test_create_server') self.assertEquals(server['image']['id'], self.image.id)
self.assertEquals(server.image['id'], self.image.id) self.assertEquals(server['flavor']['id'], self.flavor.id)
self.assertEquals(server.flavor['id'], self.flavor.id)
def test_delete_server(self): def test_delete_server(self):
self.cloud.create_server(name='test_delete_server', self.cloud.create_server(name='test_delete_server',

View File

@ -20,6 +20,7 @@ Tests for the `create_server` command.
""" """
from mock import patch, Mock from mock import patch, Mock
from shade import meta
from shade import OpenStackCloud from shade import OpenStackCloud
from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout) from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout)
from shade.tests import base, fakes from shade.tests import base, fakes
@ -115,8 +116,8 @@ class TestCreateServer(base.TestCase):
"servers.get.return_value": fake_server "servers.get.return_value": fake_server
} }
OpenStackCloud.nova_client = Mock(**config) OpenStackCloud.nova_client = Mock(**config)
self.assertEqual( self.assertEqual(meta.obj_to_dict(fake_server),
self.client.create_server(), fake_server) self.client.create_server())
def test_create_server_wait(self): def test_create_server_wait(self):
""" """

View File

@ -20,6 +20,7 @@ Tests for the `rebuild_server` command.
""" """
from mock import patch, Mock from mock import patch, Mock
from shade import meta
from shade import OpenStackCloud from shade import OpenStackCloud
from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout) from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout)
from shade.tests.unit import base from shade.tests.unit import base
@ -85,8 +86,8 @@ class TestRebuildServer(base.TestCase):
"servers.rebuild.return_value": mock_server "servers.rebuild.return_value": mock_server
} }
OpenStackCloud.nova_client = Mock(**config) OpenStackCloud.nova_client = Mock(**config)
self.assertEqual( self.assertEqual(meta.obj_to_dict(mock_server),
self.client.rebuild_server("a", "b"), mock_server) self.client.rebuild_server("a", "b"))
def test_rebuild_server_wait(self): def test_rebuild_server_wait(self):
""" """
@ -100,6 +101,5 @@ class TestRebuildServer(base.TestCase):
"servers.get.return_value": mock_server "servers.get.return_value": mock_server
} }
OpenStackCloud.nova_client = Mock(**config) OpenStackCloud.nova_client = Mock(**config)
self.assertEqual( self.assertEqual(meta.obj_to_dict(mock_server),
self.client.rebuild_server("a", "b", wait=True), self.client.rebuild_server("a", "b", wait=True))
mock_server)