Support programmatic use of disk config extension

Allow servers to be created, resized, and rebuilt with the disk_config
option:

http://api.openstack.org/api-ref.html#ext-os-disk-config

There is a separate extension that exists for disk config already, but
it only works via the novaclient CLI interface, not via programmatic use
of python-novaclient as a library.

assert changes in tests/v1_1/fakes allow for other data in rebuild
and resize requests.

Change-Id: I8051ffb8747cf5c67b0199d22fbbe80306b01499
This commit is contained in:
Brian Elliott 2013-07-31 15:53:26 -05:00
parent 4ab2b60850
commit 0d454089c9
4 changed files with 79 additions and 7 deletions

View File

@ -231,7 +231,7 @@ class BootingManagerWithFind(ManagerWithFind):
max_count=None, security_groups=None, key_name=None,
availability_zone=None, block_device_mapping=None, nics=None,
scheduler_hints=None, config_drive=None, admin_pass=None,
**kwargs):
disk_config=None, **kwargs):
"""
Create (boot) a new server.
@ -264,6 +264,8 @@ class BootingManagerWithFind(ManagerWithFind):
:param config_drive: (optional extension) value for config drive
either boolean, or volume-id
:param admin_pass: admin password for the server.
:param disk_config: (optional extension) control how the disk is
partitioned when the server is created.
"""
body = {"server": {
"name": name,
@ -360,6 +362,9 @@ class BootingManagerWithFind(ManagerWithFind):
all_net_data.append(net_data)
body['server']['networks'] = all_net_data
if disk_config is not None:
body['server']['OS-DCF:diskConfig'] = disk_config
return self._create(resource_url, body, response_key,
return_raw=return_raw, **kwargs)

View File

@ -473,10 +473,11 @@ class FakeHTTPClient(base_client.HTTPClient):
keys = body[action].keys()
if 'adminPass' in keys:
keys.remove('adminPass')
assert keys == ['imageRef']
assert 'imageRef' in keys
_body = self.get_servers_1234()[2]
elif action == 'resize':
assert body[action].keys() == ['flavorRef']
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize':
assert body[action] is None
# This one method returns a different response code

View File

@ -135,6 +135,29 @@ class ServersTest(utils.TestCase):
cs.assert_called('POST', '/servers')
self.assertTrue(isinstance(s, servers.Server))
def _create_disk_config(self, disk_config):
s = cs.servers.create(
name="My server",
image=1,
flavor=1,
disk_config=disk_config
)
cs.assert_called('POST', '/servers')
self.assertTrue(isinstance(s, servers.Server))
# verify disk config param was used in the request:
last_request = cs.client.callstack[-1]
body = last_request[-1]
server = body['server']
self.assertTrue('OS-DCF:diskConfig' in server)
self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
def test_create_server_disk_config_auto(self):
self._create_disk_config('AUTO')
def test_create_server_disk_config_manual(self):
self._create_disk_config('MANUAL')
def test_update_server(self):
s = cs.servers.get(1234)
@ -199,6 +222,29 @@ class ServersTest(utils.TestCase):
cs.servers.rebuild(s, image=1, password='5678')
cs.assert_called('POST', '/servers/1234/action')
def _rebuild_resize_disk_config(self, disk_config, operation="rebuild"):
s = cs.servers.get(1234)
if operation == "rebuild":
s.rebuild(image=1, disk_config=disk_config)
elif operation == "resize":
s.resize(flavor=1, disk_config=disk_config)
cs.assert_called('POST', '/servers/1234/action')
# verify disk config param was used in the request:
last_request = cs.client.callstack[-1]
body = last_request[-1]
d = body[operation]
self.assertTrue('OS-DCF:diskConfig' in d)
self.assertEqual(disk_config, d['OS-DCF:diskConfig'])
def test_rebuild_server_disk_config_auto(self):
self._rebuild_resize_disk_config('AUTO')
def test_rebuild_server_disk_config_manual(self):
self._rebuild_resize_disk_config('MANUAL')
def test_resize_server(self):
s = cs.servers.get(1234)
s.resize(flavor=1)
@ -206,6 +252,12 @@ class ServersTest(utils.TestCase):
cs.servers.resize(s, flavor=1)
cs.assert_called('POST', '/servers/1234/action')
def test_resize_server_disk_config_auto(self):
self._rebuild_resize_disk_config('AUTO', 'resize')
def test_resize_server_disk_config_manual(self):
self._rebuild_resize_disk_config('MANUAL', 'resize')
def test_confirm_resized_server(self):
s = cs.servers.get(1234)
s.confirm_resize()

View File

@ -570,7 +570,7 @@ class ServerManager(base.BootingManagerWithFind):
max_count=None, security_groups=None, userdata=None,
key_name=None, availability_zone=None,
block_device_mapping=None, nics=None, scheduler_hints=None,
config_drive=None, **kwargs):
config_drive=None, disk_config=None, **kwargs):
# TODO(anthony): indicate in doc string if param is an extension
# and/or optional
"""
@ -604,6 +604,9 @@ class ServerManager(base.BootingManagerWithFind):
specified by the client to help boot an instance
:param config_drive: (optional extension) value for config drive
either boolean, or volume-id
:param disk_config: (optional extension) control how the disk is
partitioned when the server is created. possible
values are 'AUTO' or 'MANUAL'.
"""
if not min_count:
min_count = 1
@ -620,7 +623,7 @@ class ServerManager(base.BootingManagerWithFind):
max_count=max_count, security_groups=security_groups,
key_name=key_name, availability_zone=availability_zone,
scheduler_hints=scheduler_hints, config_drive=config_drive,
**kwargs)
disk_config=disk_config, **kwargs)
if block_device_mapping:
resource_url = "/os-volumes_boot"
@ -674,17 +677,23 @@ class ServerManager(base.BootingManagerWithFind):
"""
self._action('reboot', server, {'type': reboot_type})
def rebuild(self, server, image, password=None, **kwargs):
def rebuild(self, server, image, password=None, disk_config=None,
**kwargs):
"""
Rebuild -- shut down and then re-image -- a server.
:param server: The :class:`Server` (or its ID) to share onto.
:param image: the :class:`Image` (or its ID) to re-image with.
:param password: string to set as password on the rebuilt server.
:param disk_config: partitioning mode to use on the rebuilt server.
Valid values are 'AUTO' or 'MANUAL'
"""
body = {'imageRef': base.getid(image)}
if password is not None:
body['adminPass'] = password
if disk_config is not None:
body['OS-DCF:diskConfig'] = disk_config
_resp, body = self._action('rebuild', server, body, **kwargs)
return Server(self, body['server'])
@ -696,12 +705,14 @@ class ServerManager(base.BootingManagerWithFind):
"""
self._action('migrate', server)
def resize(self, server, flavor, **kwargs):
def resize(self, server, flavor, disk_config=None, **kwargs):
"""
Resize a server's resources.
:param server: The :class:`Server` (or its ID) to share onto.
:param flavor: the :class:`Flavor` (or its ID) to resize to.
:param disk_config: partitioning mode to use on the rebuilt server.
Valid values are 'AUTO' or 'MANUAL'
Until a resize event is confirmed with :meth:`confirm_resize`, the old
server will be kept around and you'll be able to roll back to the old
@ -709,6 +720,9 @@ class ServerManager(base.BootingManagerWithFind):
automatically confirmed after 24 hours.
"""
info = {'flavorRef': base.getid(flavor)}
if disk_config is not None:
info['OS-DCF:diskConfig'] = disk_config
self._action('resize', server, info=info, **kwargs)
def confirm_resize(self, server):