Add host and hypervisor_hostname to create servers

Adds the --host and --hypervisor-hostname options to
the nova boot command and related python API bindings.

Depends-On: https://review.opendev.org/#/c/645520/

Change-Id: If16d00b75f4d5f2b96aa6e3f32a973108049d928
Blueprint: add-host-and-hypervisor-hostname-flag-to-create-server
This commit is contained in:
zhu.boxiang 2019-03-26 15:48:46 +08:00 committed by Matt Riedemann
parent 4bfcc1a9fa
commit 41c25881e6
7 changed files with 242 additions and 4 deletions

View File

@ -952,6 +952,8 @@ nova boot
[--description <description>] [--tags <tags>] [--description <description>] [--tags <tags>]
[--return-reservation-id] [--return-reservation-id]
[--trusted-image-certificate-id <trusted-image-certificate-id>] [--trusted-image-certificate-id <trusted-image-certificate-id>]
[--host <host>]
[--hypervisor-hostname <hypervisor-hostname>]
<name> <name>
Boot a new server. Boot a new server.
@ -1117,6 +1119,14 @@ quality of service support, microversion ``2.72`` is required.
May be specified multiple times to pass multiple trusted image May be specified multiple times to pass multiple trusted image
certificate IDs. (Supported by API versions '2.63' - '2.latest') certificate IDs. (Supported by API versions '2.63' - '2.latest')
``--host <host>``
Requested host to create servers. Admin only by default.
(Supported by API versions '2.74' - '2.latest')
``--hypervisor-hostname <hypervisor-hostname>``
Requested hypervisor hostname to create servers. Admin only by default.
(Supported by API versions '2.74' - '2.latest')
.. _nova_cell-capacities: .. _nova_cell-capacities:
nova cell-capacities nova cell-capacities

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.73") API_MAX_VERSION = api_versions.APIVersion("2.74")

View File

@ -1743,3 +1743,99 @@ class ServersV273Test(ServersV268Test):
self.assert_called('GET', '/servers/detail?locked=False') self.assert_called('GET', '/servers/detail?locked=False')
for s in sl: for s in sl:
self.assertIsInstance(s, servers.Server) self.assertIsInstance(s, servers.Server)
class ServersV274Test(ServersV273Test):
api_version = "2.74"
def test_create_server_with_host(self):
self.cs.servers.create(
name="My server",
image=1,
flavor=1,
nics="auto",
host="new-host"
)
self.assert_called('POST', '/servers',
{'server': {
'flavorRef': '1',
'imageRef': '1',
'max_count': 1,
'min_count': 1,
'name': 'My server',
'networks': 'auto',
'host': 'new-host'
}}
)
def test_create_server_with_hypervisor_hostname(self):
self.cs.servers.create(
name="My server",
image=1,
flavor=1,
nics="auto",
hypervisor_hostname="new-host"
)
self.assert_called('POST', '/servers',
{'server': {
'flavorRef': '1',
'imageRef': '1',
'max_count': 1,
'min_count': 1,
'name': 'My server',
'networks': 'auto',
'hypervisor_hostname': 'new-host'
}}
)
def test_create_server_with_host_and_hypervisor_hostname(self):
self.cs.servers.create(
name="My server",
image=1,
flavor=1,
nics="auto",
host="new-host",
hypervisor_hostname="new-host"
)
self.assert_called('POST', '/servers',
{'server': {
'flavorRef': '1',
'imageRef': '1',
'max_count': 1,
'min_count': 1,
'name': 'My server',
'networks': 'auto',
'host': 'new-host',
'hypervisor_hostname': 'new-host'
}}
)
def test_create_server_with_host_pre_274_fails(self):
self.cs.api_version = api_versions.APIVersion('2.73')
ex = self.assertRaises(exceptions.UnsupportedAttribute,
self.cs.servers.create,
name="My server", image=1, flavor=1,
nics='auto', host="new-host")
self.assertIn("'host' argument is only allowed since microversion "
"2.74", six.text_type(ex))
def test_create_server_with_hypervisor_hostname_pre_274_fails(self):
self.cs.api_version = api_versions.APIVersion('2.73')
ex = self.assertRaises(exceptions.UnsupportedAttribute,
self.cs.servers.create,
name="My server", image=1, flavor=1,
nics='auto', hypervisor_hostname="new-host")
self.assertIn("'hypervisor_hostname' argument is only allowed since "
"microversion 2.74", six.text_type(ex))
def test_create_server_with_host_and_hypervisor_hostname_pre_274_fails(
self):
self.cs.api_version = api_versions.APIVersion('2.73')
ex = self.assertRaises(exceptions.UnsupportedAttribute,
self.cs.servers.create,
name="My server", image=1, flavor=1,
nics='auto', host="new-host",
hypervisor_hostname="new-host")
self.assertIn("'host' argument is only allowed since microversion "
"2.74", six.text_type(ex))

View File

@ -1374,6 +1374,83 @@ class ShellTest(utils.TestCase):
self.assertIn('Instance %s could not be found.' % FAKE_UUID_1, self.assertIn('Instance %s could not be found.' % FAKE_UUID_1,
six.text_type(ex)) six.text_type(ex))
def test_boot_with_host_v274(self):
self.run_command('boot --flavor 1 --image %s '
'--host new-host --nic auto '
'some-server' % FAKE_UUID_1,
api_version='2.74')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'host': 'new-host',
}},
)
def test_boot_with_hypervisor_hostname_v274(self):
self.run_command('boot --flavor 1 --image %s --nic auto '
'--hypervisor-hostname new-host '
'some-server' % FAKE_UUID_1,
api_version='2.74')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'hypervisor_hostname': 'new-host',
}},
)
def test_boot_with_host_and_hypervisor_hostname_v274(self):
self.run_command('boot --flavor 1 --image %s '
'--host new-host --nic auto '
'--hypervisor-hostname new-host '
'some-server' % FAKE_UUID_1,
api_version='2.74')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'host': 'new-host',
'hypervisor_hostname': 'new-host',
}},
)
def test_boot_with_host_pre_v274(self):
cmd = ('boot --flavor 1 --image %s --nic auto '
'--host new-host some-server'
% FAKE_UUID_1)
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.73')
def test_boot_with_hypervisor_hostname_pre_v274(self):
cmd = ('boot --flavor 1 --image %s --nic auto '
'--hypervisor-hostname new-host some-server'
% FAKE_UUID_1)
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.73')
def test_boot_with_host_and_hypervisor_hostname_pre_v274(self):
cmd = ('boot --flavor 1 --image %s --nic auto '
'--host new-host --hypervisor-hostname new-host some-server'
% FAKE_UUID_1)
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.73')
def test_flavor_list(self): def test_flavor_list(self):
out, _ = self.run_command('flavor-list') out, _ = self.run_command('flavor-list')
self.assert_called_anytime('GET', '/flavors/detail') self.assert_called_anytime('GET', '/flavors/detail')
@ -4185,6 +4262,7 @@ class ShellTest(utils.TestCase):
70, # There are no version-wrapped shell method changes for this. 70, # There are no version-wrapped shell method changes for this.
71, # There are no version-wrapped shell method changes for this. 71, # There are no version-wrapped shell method changes for this.
72, # There are no version-wrapped shell method changes for this. 72, # There are no version-wrapped shell method changes for this.
74, # There are no version-wrapped shell method changes for this.
]) ])
versions_supported = set(range(0, versions_supported = set(range(0,
novaclient.API_MAX_VERSION.ver_minor + 1)) novaclient.API_MAX_VERSION.ver_minor + 1))

View File

@ -694,7 +694,8 @@ class ServerManager(base.BootingManagerWithFind):
block_device_mapping_v2=None, nics=None, scheduler_hints=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None,
config_drive=None, admin_pass=None, disk_config=None, config_drive=None, admin_pass=None, disk_config=None,
access_ip_v4=None, access_ip_v6=None, description=None, access_ip_v4=None, access_ip_v6=None, description=None,
tags=None, trusted_image_certificates=None, **kwargs): tags=None, trusted_image_certificates=None,
host=None, hypervisor_hostname=None, **kwargs):
""" """
Create (boot) a new server. Create (boot) a new server.
""" """
@ -817,6 +818,12 @@ class ServerManager(base.BootingManagerWithFind):
body['server']['trusted_image_certificates'] = ( body['server']['trusted_image_certificates'] = (
trusted_image_certificates) trusted_image_certificates)
if host:
body['server']['host'] = host
if hypervisor_hostname:
body['server']['hypervisor_hostname'] = hypervisor_hostname
return self._create('/servers', body, response_key, return self._create('/servers', body, response_key,
return_raw=return_raw, **kwargs) return_raw=return_raw, **kwargs)
@ -1267,7 +1274,9 @@ class ServerManager(base.BootingManagerWithFind):
nics=None, scheduler_hints=None, nics=None, scheduler_hints=None,
config_drive=None, disk_config=None, admin_pass=None, config_drive=None, disk_config=None, admin_pass=None,
access_ip_v4=None, access_ip_v6=None, access_ip_v4=None, access_ip_v6=None,
trusted_image_certificates=None, **kwargs): trusted_image_certificates=None,
host=None, hypervisor_hostname=None,
**kwargs):
# TODO(anthony): indicate in doc string if param is an extension # TODO(anthony): indicate in doc string if param is an extension
# and/or optional # and/or optional
""" """
@ -1334,6 +1343,10 @@ class ServerManager(base.BootingManagerWithFind):
server as tags (allowed since microversion 2.52) server as tags (allowed since microversion 2.52)
:param trusted_image_certificates: A list of trusted certificate IDs :param trusted_image_certificates: A list of trusted certificate IDs
(allowed since microversion 2.63) (allowed since microversion 2.63)
:param host: requested host to create servers
(allowed since microversion 2.74)
:param hypervisor_hostname: requested hypervisor hostname to create
servers (allowed since microversion 2.74)
""" """
if not min_count: if not min_count:
min_count = 1 min_count = 1
@ -1388,6 +1401,15 @@ class ServerManager(base.BootingManagerWithFind):
"Block device volume_type is not supported before " "Block device volume_type is not supported before "
"microversion 2.67") "microversion 2.67")
host_microversion = api_versions.APIVersion("2.74")
if host and self.api_version < host_microversion:
raise exceptions.UnsupportedAttribute("host", "2.74")
hypervisor_hostname_microversion = api_versions.APIVersion("2.74")
if (hypervisor_hostname and
self.api_version < hypervisor_hostname_microversion):
raise exceptions.UnsupportedAttribute(
"hypervisor_hostname", "2.74")
boot_kwargs = dict( boot_kwargs = dict(
meta=meta, files=files, userdata=userdata, meta=meta, files=files, userdata=userdata,
reservation_id=reservation_id, min_count=min_count, reservation_id=reservation_id, min_count=min_count,
@ -1396,7 +1418,9 @@ class ServerManager(base.BootingManagerWithFind):
scheduler_hints=scheduler_hints, config_drive=config_drive, scheduler_hints=scheduler_hints, config_drive=config_drive,
disk_config=disk_config, admin_pass=admin_pass, disk_config=disk_config, admin_pass=admin_pass,
access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6, access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6,
trusted_image_certificates=trusted_image_certificates, **kwargs) trusted_image_certificates=trusted_image_certificates,
host=host, hypervisor_hostname=hypervisor_hostname,
**kwargs)
if block_device_mapping: if block_device_mapping:
boot_kwargs['block_device_mapping'] = block_device_mapping boot_kwargs['block_device_mapping'] = block_device_mapping

View File

@ -517,6 +517,12 @@ def _boot(cs, args):
if 'tags' in args and args.tags: if 'tags' in args and args.tags:
boot_kwargs["tags"] = args.tags.split(',') boot_kwargs["tags"] = args.tags.split(',')
if 'host' in args and args.host:
boot_kwargs["host"] = args.host
if 'hypervisor_hostname' in args and args.hypervisor_hostname:
boot_kwargs["hypervisor_hostname"] = args.hypervisor_hostname
if include_files: if include_files:
boot_kwargs['files'] = files boot_kwargs['files'] = files
@ -942,6 +948,21 @@ def _boot(cs, args):
'May be specified multiple times to pass multiple trusted image ' 'May be specified multiple times to pass multiple trusted image '
'certificate IDs.'), 'certificate IDs.'),
start_version="2.63") start_version="2.63")
@utils.arg(
'--host',
metavar='<host>',
dest='host',
default=None,
help=_('Requested host to create servers. Admin only by default.'),
start_version="2.74")
@utils.arg(
'--hypervisor-hostname',
metavar='<hypervisor-hostname>',
dest='hypervisor_hostname',
default=None,
help=_('Requested hypervisor hostname to create servers. Admin only by '
'default.'),
start_version="2.74")
def do_boot(cs, args): def do_boot(cs, args):
"""Boot a new server.""" """Boot a new server."""
boot_args, boot_kwargs = _boot(cs, args) boot_args, boot_kwargs = _boot(cs, args)

View File

@ -0,0 +1,9 @@
---
features:
- |
Support is added for the `2.74 microversion`_ which allows specifying the
``--host`` and ``--hypervisor-hostname`` options on the ``nova boot``
command. The ``novaclient.v2.servers.ServerManager.create()`` method now
also supports ``host`` and ``hypervisor_hostname`` parameters.
.. _2.74 microversion: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id66