Add support for v2.37 and auto-allocated networking
This adds support for the v2.37 microversion. The networks part of the server create request is required in this microversion so if nothing is specified for --nic arguments on the command line we default to 'auto' for backward compatibility in the CLI. Part of blueprint get-me-a-network Change-Id: I6636ddcd3be7bf393d2d69cc6c1ba5c7d65ff674
This commit is contained in:
parent
84d86d3f3c
commit
030ce53d4e
@ -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.36")
|
API_MAX_VERSION = api_versions.APIVersion("2.37")
|
||||||
|
@ -172,3 +172,59 @@ class TestServersTagsV226(base.ClientTestBase):
|
|||||||
uuid = self._boot_server_with_tags()
|
uuid = self._boot_server_with_tags()
|
||||||
self.nova("server-tag-delete-all %s" % uuid)
|
self.nova("server-tag-delete-all %s" % uuid)
|
||||||
self.assertEqual([], self.client.servers.tag_list(uuid))
|
self.assertEqual([], self.client.servers.tag_list(uuid))
|
||||||
|
|
||||||
|
|
||||||
|
class TestServersAutoAllocateNetworkCLI(base.ClientTestBase):
|
||||||
|
|
||||||
|
COMPUTE_API_VERSION = '2.37'
|
||||||
|
|
||||||
|
def _find_network_in_table(self, table):
|
||||||
|
# Example:
|
||||||
|
# +-----------------+-----------------------------------+
|
||||||
|
# | Property | Value |
|
||||||
|
# +-----------------+-----------------------------------+
|
||||||
|
# | private network | 192.168.154.128 |
|
||||||
|
# +-----------------+-----------------------------------+
|
||||||
|
for line in table.split('\n'):
|
||||||
|
if '|' in line:
|
||||||
|
l_property, l_value = line.split('|')[1:3]
|
||||||
|
if ' network' in l_property.strip():
|
||||||
|
return ' '.join(l_property.strip().split()[:-1])
|
||||||
|
|
||||||
|
def test_boot_server_with_auto_network(self):
|
||||||
|
"""Tests that the CLI defaults to 'auto' when --nic isn't specified.
|
||||||
|
"""
|
||||||
|
server_info = self.nova('boot', params=(
|
||||||
|
'%(name)s --flavor %(flavor)s --poll '
|
||||||
|
'--image %(image)s ' % {'name': self.name_generate('server'),
|
||||||
|
'flavor': self.flavor.id,
|
||||||
|
'image': self.image.id}))
|
||||||
|
server_id = self._get_value_from_the_table(server_info, 'id')
|
||||||
|
self.addCleanup(self.wait_for_resource_delete,
|
||||||
|
server_id, self.client.servers)
|
||||||
|
self.addCleanup(self.client.servers.delete, server_id)
|
||||||
|
# get the server details to verify there is a network, we don't care
|
||||||
|
# what the network name is, we just want to see an entry show up
|
||||||
|
server_info = self.nova('show', params=server_id)
|
||||||
|
network = self._find_network_in_table(server_info)
|
||||||
|
self.assertIsNotNone(
|
||||||
|
network, 'Auto-allocated network not found: %s' % server_info)
|
||||||
|
|
||||||
|
def test_boot_server_with_no_network(self):
|
||||||
|
"""Tests that '--nic none' is honored.
|
||||||
|
"""
|
||||||
|
server_info = self.nova('boot', params=(
|
||||||
|
'%(name)s --flavor %(flavor)s --poll '
|
||||||
|
'--image %(image)s --nic none' %
|
||||||
|
{'name': self.name_generate('server'),
|
||||||
|
'flavor': self.flavor.id,
|
||||||
|
'image': self.image.id}))
|
||||||
|
server_id = self._get_value_from_the_table(server_info, 'id')
|
||||||
|
self.addCleanup(self.wait_for_resource_delete,
|
||||||
|
server_id, self.client.servers)
|
||||||
|
self.addCleanup(self.client.servers.delete, server_id)
|
||||||
|
# get the server details to verify there is not a network
|
||||||
|
server_info = self.nova('show', params=server_id)
|
||||||
|
network = self._find_network_in_table(server_info)
|
||||||
|
self.assertIsNone(
|
||||||
|
network, 'Unexpected network allocation: %s' % server_info)
|
||||||
|
@ -42,6 +42,11 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
if self.api_version:
|
if self.api_version:
|
||||||
self.cs.api_version = api_versions.APIVersion(self.api_version)
|
self.cs.api_version = api_versions.APIVersion(self.api_version)
|
||||||
|
|
||||||
|
def _get_server_create_default_nics(self):
|
||||||
|
"""Callback for default nics kwarg when creating a server.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
def test_list_servers(self):
|
def test_list_servers(self):
|
||||||
sl = self.cs.servers.list()
|
sl = self.cs.servers.list()
|
||||||
self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
@ -132,7 +137,8 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
files={
|
files={
|
||||||
'/etc/passwd': 'some data', # a file
|
'/etc/passwd': 'some data', # a file
|
||||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||||
}
|
},
|
||||||
|
nics=self._get_server_create_default_nics()
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -191,7 +197,8 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
meta={'foo': 'bar'},
|
meta={'foo': 'bar'},
|
||||||
userdata="hello moto",
|
userdata="hello moto",
|
||||||
key_name="fakekey",
|
key_name="fakekey",
|
||||||
block_device_mapping_v2=bdm
|
block_device_mapping_v2=bdm,
|
||||||
|
nics=self._get_server_create_default_nics()
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/os-volumes_boot')
|
self.assert_called('POST', '/os-volumes_boot')
|
||||||
@ -239,7 +246,8 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
userdata="hello moto",
|
userdata="hello moto",
|
||||||
key_name="fakekey",
|
key_name="fakekey",
|
||||||
access_ip_v6=access_ip_v6,
|
access_ip_v6=access_ip_v6,
|
||||||
access_ip_v4=access_ip_v4
|
access_ip_v4=access_ip_v4,
|
||||||
|
nics=self._get_server_create_default_nics()
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -256,6 +264,7 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
'/etc/passwd': 'some data', # a file
|
'/etc/passwd': 'some data', # a file
|
||||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||||
},
|
},
|
||||||
|
nics=self._get_server_create_default_nics(),
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -273,6 +282,7 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
'/etc/passwd': 'some data', # a file
|
'/etc/passwd': 'some data', # a file
|
||||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||||
},
|
},
|
||||||
|
nics=self._get_server_create_default_nics(),
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -290,6 +300,7 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
'/etc/passwd': 'some data', # a file
|
'/etc/passwd': 'some data', # a file
|
||||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||||
},
|
},
|
||||||
|
nics=self._get_server_create_default_nics(),
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -303,7 +314,8 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
image=1,
|
image=1,
|
||||||
flavor=1,
|
flavor=1,
|
||||||
admin_pass=test_password,
|
admin_pass=test_password,
|
||||||
key_name=test_key
|
key_name=test_key,
|
||||||
|
nics=self._get_server_create_default_nics()
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -328,6 +340,7 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
'/etc/passwd': 'some data', # a file
|
'/etc/passwd': 'some data', # a file
|
||||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||||
},
|
},
|
||||||
|
nics=self._get_server_create_default_nics(),
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -343,7 +356,8 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
name="My server",
|
name="My server",
|
||||||
image=1,
|
image=1,
|
||||||
flavor=1,
|
flavor=1,
|
||||||
disk_config=disk_config
|
disk_config=disk_config,
|
||||||
|
nics=self._get_server_create_default_nics(),
|
||||||
)
|
)
|
||||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
@ -976,6 +990,16 @@ class ServersTest(utils.FixturedTestCase):
|
|||||||
key_name="fakekey"
|
key_name="fakekey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_create_server_with_nics_auto(self):
|
||||||
|
"""Negative test for specifying nics='auto' before 2.37
|
||||||
|
"""
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
self.cs.servers.create,
|
||||||
|
name='test',
|
||||||
|
image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a',
|
||||||
|
flavor='1',
|
||||||
|
nics='auto')
|
||||||
|
|
||||||
|
|
||||||
class ServersV26Test(ServersTest):
|
class ServersV26Test(ServersTest):
|
||||||
|
|
||||||
@ -1074,7 +1098,8 @@ class ServersV219Test(ServersV217Test):
|
|||||||
flavor=1,
|
flavor=1,
|
||||||
meta={'foo': 'bar'},
|
meta={'foo': 'bar'},
|
||||||
userdata="hello moto",
|
userdata="hello moto",
|
||||||
key_name="fakekey"
|
key_name="fakekey",
|
||||||
|
nics=self._get_server_create_default_nics()
|
||||||
)
|
)
|
||||||
self.assert_called('POST', '/servers')
|
self.assert_called('POST', '/servers')
|
||||||
|
|
||||||
@ -1260,3 +1285,39 @@ class ServersV232Test(ServersV226Test):
|
|||||||
image=1, flavor=1, meta={'foo': 'bar'},
|
image=1, flavor=1, meta={'foo': 'bar'},
|
||||||
userdata="hello moto", key_name="fakekey",
|
userdata="hello moto", key_name="fakekey",
|
||||||
block_device_mapping_v2=bdm)
|
block_device_mapping_v2=bdm)
|
||||||
|
|
||||||
|
|
||||||
|
class ServersV2_37Test(ServersV226Test):
|
||||||
|
|
||||||
|
api_version = "2.37"
|
||||||
|
|
||||||
|
def _get_server_create_default_nics(self):
|
||||||
|
return 'auto'
|
||||||
|
|
||||||
|
def test_create_server_no_nics(self):
|
||||||
|
"""Tests that nics are required in microversion 2.37+
|
||||||
|
"""
|
||||||
|
self.assertRaises(ValueError, self.cs.servers.create,
|
||||||
|
name='test',
|
||||||
|
image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a',
|
||||||
|
flavor='1')
|
||||||
|
|
||||||
|
def test_create_server_with_nics_auto(self):
|
||||||
|
s = self.cs.servers.create(
|
||||||
|
name='test', image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a',
|
||||||
|
flavor='1', nics=self._get_server_create_default_nics())
|
||||||
|
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
self.assert_called('POST', '/servers')
|
||||||
|
self.assertIsInstance(s, servers.Server)
|
||||||
|
|
||||||
|
def test_add_floating_ip(self):
|
||||||
|
# self.cs.floating_ips.list() is not available after 2.35
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_add_floating_ip_to_fixed(self):
|
||||||
|
# self.cs.floating_ips.list() is not available after 2.35
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_remove_floating_ip(self):
|
||||||
|
# self.cs.floating_ips.list() is not available after 2.35
|
||||||
|
pass
|
||||||
|
@ -606,6 +606,64 @@ class ShellTest(utils.TestCase):
|
|||||||
cmd, api_version='2.32')
|
cmd, api_version='2.32')
|
||||||
self.assertIn('tag=tag', six.text_type(ex))
|
self.assertIn('tag=tag', six.text_type(ex))
|
||||||
|
|
||||||
|
def test_boot_invalid_nics_v2_36_auto(self):
|
||||||
|
"""This is a negative test to make sure we fail with the correct
|
||||||
|
message. --nic auto isn't allowed before 2.37.
|
||||||
|
"""
|
||||||
|
cmd = ('boot --image %s --flavor 1 --nic auto test' % FAKE_UUID_1)
|
||||||
|
ex = self.assertRaises(exceptions.CommandError, self.run_command,
|
||||||
|
cmd, api_version='2.36')
|
||||||
|
self.assertNotIn('auto,none', six.text_type(ex))
|
||||||
|
|
||||||
|
def test_boot_invalid_nics_v2_37(self):
|
||||||
|
"""This is a negative test to make sure we fail with the correct
|
||||||
|
message.
|
||||||
|
"""
|
||||||
|
cmd = ('boot --image %s --flavor 1 '
|
||||||
|
'--nic net-id=1 --nic auto some-server' % FAKE_UUID_1)
|
||||||
|
ex = self.assertRaises(exceptions.CommandError, self.run_command,
|
||||||
|
cmd, api_version='2.37')
|
||||||
|
self.assertIn('auto,none', six.text_type(ex))
|
||||||
|
|
||||||
|
def test_boot_nics_auto_allocate_default(self):
|
||||||
|
"""Tests that if microversion>=2.37 is specified and no --nics are
|
||||||
|
specified that a single --nic with net-id=auto is used.
|
||||||
|
"""
|
||||||
|
cmd = 'boot --image %s --flavor 1 some-server' % FAKE_UUID_1
|
||||||
|
self.run_command(cmd, api_version='2.37')
|
||||||
|
self.assert_called_anytime(
|
||||||
|
'POST', '/servers',
|
||||||
|
{
|
||||||
|
'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'name': 'some-server',
|
||||||
|
'imageRef': FAKE_UUID_1,
|
||||||
|
'min_count': 1,
|
||||||
|
'max_count': 1,
|
||||||
|
'networks': 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_boot_nics_auto_allocate_none(self):
|
||||||
|
"""Tests specifying '--nic none' with microversion 2.37
|
||||||
|
"""
|
||||||
|
cmd = 'boot --image %s --flavor 1 --nic none some-server' % FAKE_UUID_1
|
||||||
|
self.run_command(cmd, api_version='2.37')
|
||||||
|
self.assert_called_anytime(
|
||||||
|
'POST', '/servers',
|
||||||
|
{
|
||||||
|
'server': {
|
||||||
|
'flavorRef': '1',
|
||||||
|
'name': 'some-server',
|
||||||
|
'imageRef': FAKE_UUID_1,
|
||||||
|
'min_count': 1,
|
||||||
|
'max_count': 1,
|
||||||
|
'networks': 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_boot_nics_ipv6(self):
|
def test_boot_nics_ipv6(self):
|
||||||
cmd = ('boot --image %s --flavor 1 '
|
cmd = ('boot --image %s --flavor 1 '
|
||||||
'--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server' %
|
'--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server' %
|
||||||
@ -3062,6 +3120,7 @@ class ShellTest(utils.TestCase):
|
|||||||
32, # doesn't require separate version-wrapped methods in
|
32, # doesn't require separate version-wrapped methods in
|
||||||
# novaclient
|
# novaclient
|
||||||
34, # doesn't require any changes in novaclient
|
34, # doesn't require any changes in novaclient
|
||||||
|
37, # There are no versioned 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))
|
||||||
|
@ -709,6 +709,11 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
body['server']['block_device_mapping_v2'] = block_device_mapping_v2
|
body['server']['block_device_mapping_v2'] = block_device_mapping_v2
|
||||||
|
|
||||||
if nics is not None:
|
if nics is not None:
|
||||||
|
# With microversion 2.37+ nics can be an enum of 'auto' or 'none'
|
||||||
|
# or a list of dicts.
|
||||||
|
if isinstance(nics, six.string_types):
|
||||||
|
all_net_data = nics
|
||||||
|
else:
|
||||||
# NOTE(tr3buchet): nics can be an empty list
|
# NOTE(tr3buchet): nics can be an empty list
|
||||||
all_net_data = []
|
all_net_data = []
|
||||||
for nic_info in nics:
|
for nic_info in nics:
|
||||||
@ -719,8 +724,8 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
if (nic_info.get('v4-fixed-ip') and
|
if (nic_info.get('v4-fixed-ip') and
|
||||||
nic_info.get('v6-fixed-ip')):
|
nic_info.get('v6-fixed-ip')):
|
||||||
raise base.exceptions.CommandError(_(
|
raise base.exceptions.CommandError(_(
|
||||||
"Only one of 'v4-fixed-ip' and 'v6-fixed-ip' may be"
|
"Only one of 'v4-fixed-ip' and 'v6-fixed-ip' "
|
||||||
" provided."))
|
"may be provided."))
|
||||||
elif nic_info.get('v4-fixed-ip'):
|
elif nic_info.get('v4-fixed-ip'):
|
||||||
net_data['fixed_ip'] = nic_info['v4-fixed-ip']
|
net_data['fixed_ip'] = nic_info['v4-fixed-ip']
|
||||||
elif nic_info.get('v6-fixed-ip'):
|
elif nic_info.get('v6-fixed-ip'):
|
||||||
@ -1219,6 +1224,14 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
base.getid(server))
|
base.getid(server))
|
||||||
return base.TupleWithMeta((resp, body), resp)
|
return base.TupleWithMeta((resp, body), resp)
|
||||||
|
|
||||||
|
def _validate_create_nics(self, nics):
|
||||||
|
# nics are required with microversion 2.37+ and can be a string or list
|
||||||
|
if self.api_version > api_versions.APIVersion('2.36'):
|
||||||
|
if not nics:
|
||||||
|
raise ValueError('nics are required after microversion 2.36')
|
||||||
|
elif nics and not isinstance(nics, list):
|
||||||
|
raise ValueError('nics must be a list')
|
||||||
|
|
||||||
def create(self, name, image, flavor, meta=None, files=None,
|
def create(self, name, image, flavor, meta=None, files=None,
|
||||||
reservation_id=None, min_count=None,
|
reservation_id=None, min_count=None,
|
||||||
max_count=None, security_groups=None, userdata=None,
|
max_count=None, security_groups=None, userdata=None,
|
||||||
@ -1259,9 +1272,17 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
device mappings for this server.
|
device mappings for this server.
|
||||||
:param block_device_mapping_v2: (optional extension) A dict of block
|
:param block_device_mapping_v2: (optional extension) A dict of block
|
||||||
device mappings for this server.
|
device mappings for this server.
|
||||||
:param nics: (optional extension) an ordered list of nics to be
|
:param nics: An ordered list of nics (dicts) to be added to this
|
||||||
added to this server, with information about
|
server, with information about connected networks,
|
||||||
connected networks, fixed IPs, port etc.
|
fixed IPs, port etc.
|
||||||
|
Beginning in microversion 2.37 this field is required and
|
||||||
|
also supports a single string value of 'auto' or 'none'.
|
||||||
|
The 'auto' value means the Compute service will
|
||||||
|
automatically allocate a network for the project if one
|
||||||
|
is not available. This is the same behavior as not
|
||||||
|
passing anything for nics before microversion 2.37. The
|
||||||
|
'none' value tells the Compute service to not allocate
|
||||||
|
any networking for the server.
|
||||||
:param scheduler_hints: (optional extension) arbitrary key-value pairs
|
:param scheduler_hints: (optional extension) arbitrary key-value pairs
|
||||||
specified by the client to help boot an instance
|
specified by the client to help boot an instance
|
||||||
:param config_drive: (optional extension) value for config drive
|
:param config_drive: (optional extension) value for config drive
|
||||||
@ -1289,6 +1310,8 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
if "description" in kwargs and self.api_version < descr_microversion:
|
if "description" in kwargs and self.api_version < descr_microversion:
|
||||||
raise exceptions.UnsupportedAttribute("description", "2.19")
|
raise exceptions.UnsupportedAttribute("description", "2.19")
|
||||||
|
|
||||||
|
self._validate_create_nics(nics)
|
||||||
|
|
||||||
tags_microversion = api_versions.APIVersion("2.32")
|
tags_microversion = api_versions.APIVersion("2.32")
|
||||||
if self.api_version < tags_microversion:
|
if self.api_version < tags_microversion:
|
||||||
if nics:
|
if nics:
|
||||||
|
@ -188,7 +188,16 @@ def _parse_block_device_mapping_v2(args, image):
|
|||||||
|
|
||||||
|
|
||||||
def _parse_nics(cs, args):
|
def _parse_nics(cs, args):
|
||||||
if cs.api_version >= api_versions.APIVersion('2.32'):
|
supports_auto_alloc = cs.api_version >= api_versions.APIVersion('2.37')
|
||||||
|
if supports_auto_alloc:
|
||||||
|
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
|
||||||
|
"the form --nic <auto,none,net-id=net-uuid,"
|
||||||
|
"net-name=network-name,v4-fixed-ip=ip-addr,"
|
||||||
|
"v6-fixed-ip=ip-addr,port-id=port-uuid,tag=tag>, "
|
||||||
|
"with only one of net-id, net-name or port-id "
|
||||||
|
"specified. Specifying a --nic of auto or none cannot "
|
||||||
|
"be used with any other --nic value."))
|
||||||
|
elif cs.api_version >= api_versions.APIVersion('2.32'):
|
||||||
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
|
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
|
||||||
"the form --nic <net-id=net-uuid,"
|
"the form --nic <net-id=net-uuid,"
|
||||||
"net-name=network-name,v4-fixed-ip=ip-addr,"
|
"net-name=network-name,v4-fixed-ip=ip-addr,"
|
||||||
@ -202,6 +211,7 @@ def _parse_nics(cs, args):
|
|||||||
"v6-fixed-ip=ip-addr,port-id=port-uuid>, "
|
"v6-fixed-ip=ip-addr,port-id=port-uuid>, "
|
||||||
"with only one of net-id, net-name or port-id "
|
"with only one of net-id, net-name or port-id "
|
||||||
"specified."))
|
"specified."))
|
||||||
|
auto_or_none = False
|
||||||
nics = []
|
nics = []
|
||||||
for nic_str in args.nics:
|
for nic_str in args.nics:
|
||||||
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
|
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
|
||||||
@ -209,6 +219,13 @@ def _parse_nics(cs, args):
|
|||||||
|
|
||||||
for kv_str in nic_str.split(","):
|
for kv_str in nic_str.split(","):
|
||||||
try:
|
try:
|
||||||
|
# handle the special auto/none cases
|
||||||
|
if kv_str in ('auto', 'none'):
|
||||||
|
if not supports_auto_alloc:
|
||||||
|
raise exceptions.CommandError(err_msg % nic_str)
|
||||||
|
nics.append(kv_str)
|
||||||
|
auto_or_none = True
|
||||||
|
continue
|
||||||
k, v = kv_str.split("=", 1)
|
k, v = kv_str.split("=", 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise exceptions.CommandError(err_msg % nic_str)
|
raise exceptions.CommandError(err_msg % nic_str)
|
||||||
@ -225,6 +242,9 @@ def _parse_nics(cs, args):
|
|||||||
else:
|
else:
|
||||||
raise exceptions.CommandError(err_msg % nic_str)
|
raise exceptions.CommandError(err_msg % nic_str)
|
||||||
|
|
||||||
|
if auto_or_none:
|
||||||
|
continue
|
||||||
|
|
||||||
if nic_info['v4-fixed-ip'] and not netutils.is_valid_ipv4(
|
if nic_info['v4-fixed-ip'] and not netutils.is_valid_ipv4(
|
||||||
nic_info['v4-fixed-ip']):
|
nic_info['v4-fixed-ip']):
|
||||||
raise exceptions.CommandError(_("Invalid ipv4 address."))
|
raise exceptions.CommandError(_("Invalid ipv4 address."))
|
||||||
@ -238,6 +258,17 @@ def _parse_nics(cs, args):
|
|||||||
|
|
||||||
nics.append(nic_info)
|
nics.append(nic_info)
|
||||||
|
|
||||||
|
if nics:
|
||||||
|
if auto_or_none:
|
||||||
|
if len(nics) > 1:
|
||||||
|
raise exceptions.CommandError(err_msg % nic_str)
|
||||||
|
# change the single list entry to a string
|
||||||
|
nics = nics[0]
|
||||||
|
else:
|
||||||
|
# Default to 'auto' if API version >= 2.37 and nothing was specified
|
||||||
|
if supports_auto_alloc:
|
||||||
|
nics = 'auto'
|
||||||
|
|
||||||
return nics
|
return nics
|
||||||
|
|
||||||
|
|
||||||
@ -577,6 +608,7 @@ def _boot(cs, args):
|
|||||||
dest='nics',
|
dest='nics',
|
||||||
default=[],
|
default=[],
|
||||||
start_version='2.32',
|
start_version='2.32',
|
||||||
|
end_version='2.36',
|
||||||
help=_("Create a NIC on the server. "
|
help=_("Create a NIC on the server. "
|
||||||
"Specify option multiple times to create multiple nics. "
|
"Specify option multiple times to create multiple nics. "
|
||||||
"net-id: attach NIC to network with this UUID "
|
"net-id: attach NIC to network with this UUID "
|
||||||
@ -587,6 +619,31 @@ def _boot(cs, args):
|
|||||||
"port-id: attach NIC to port with this UUID "
|
"port-id: attach NIC to port with this UUID "
|
||||||
"tag: interface metadata tag (optional) "
|
"tag: interface metadata tag (optional) "
|
||||||
"(either port-id or net-id must be provided)."))
|
"(either port-id or net-id must be provided)."))
|
||||||
|
@utils.arg(
|
||||||
|
'--nic',
|
||||||
|
metavar="<auto,none,"
|
||||||
|
"net-id=net-uuid,net-name=network-name,port-id=port-uuid,"
|
||||||
|
"v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,tag=tag>",
|
||||||
|
action='append',
|
||||||
|
dest='nics',
|
||||||
|
default=[],
|
||||||
|
start_version='2.37',
|
||||||
|
help=_("Create a NIC on the server. "
|
||||||
|
"Specify option multiple times to create multiple nics unless "
|
||||||
|
"using the special 'auto' or 'none' values. "
|
||||||
|
"auto: automatically allocate network resources if none are "
|
||||||
|
"available. This cannot be specified with any other nic value and "
|
||||||
|
"cannot be specified multiple times. "
|
||||||
|
"none: do not attach a NIC at all. This cannot be specified "
|
||||||
|
"with any other nic value and cannot be specified multiple times. "
|
||||||
|
"net-id: attach NIC to network with a specific UUID. "
|
||||||
|
"net-name: attach NIC to network with this name "
|
||||||
|
"(either port-id or net-id or net-name must be provided), "
|
||||||
|
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
|
||||||
|
"v6-fixed-ip: IPv6 fixed address for NIC (optional), "
|
||||||
|
"port-id: attach NIC to port with this UUID "
|
||||||
|
"tag: interface metadata tag (optional) "
|
||||||
|
"(either port-id or net-id must be provided)."))
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'--config-drive',
|
'--config-drive',
|
||||||
metavar="<value>",
|
metavar="<value>",
|
||||||
|
27
releasenotes/notes/microversion-2.37-d03da96406a45e67.yaml
Normal file
27
releasenotes/notes/microversion-2.37-d03da96406a45e67.yaml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The 2.37 microversion is now supported. This introduces the following
|
||||||
|
changes:
|
||||||
|
|
||||||
|
* CLI: The **--nic** value for the **nova boot** command now takes two
|
||||||
|
special values, 'auto' and 'none'. If --nic is not specified, the
|
||||||
|
CLI defaults to 'auto'.
|
||||||
|
* Python API: The **nics** kwarg is required when creating a server using
|
||||||
|
the *novaclient.v2.servers.ServerManager.create* API. The **nics**
|
||||||
|
value can be a list of dicts or a string with value 'auto' or
|
||||||
|
'none'.
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
With the 2.37 microversion, the **nics** kwarg is required when creating
|
||||||
|
a server using the *novaclient.v2.servers.ServerManager.create* API. The
|
||||||
|
**nics** value can be a list of dicts or an enum string with one of the
|
||||||
|
following values:
|
||||||
|
|
||||||
|
* **auto**: This tells the Compute service to automatically allocate a
|
||||||
|
network for the project if one is not available and then associate
|
||||||
|
an IP from that network with the server. This is the same behavior as
|
||||||
|
passing nics=None before the 2.37 microversion.
|
||||||
|
* **none**: This tells the Compute service to not allocate any networking
|
||||||
|
for the server.
|
Loading…
Reference in New Issue
Block a user