Merge "Supports string values 'none' and 'auto' for networks"
This commit is contained in:
commit
a8484dd44d
@ -530,3 +530,7 @@ class InvalidServiceVersion(HeatException):
|
||||
class InvalidTemplateVersions(HeatException):
|
||||
msg_fmt = _('A template version alias %(version)s was added for a '
|
||||
'template class that has no official YYYY-MM-DD version.')
|
||||
|
||||
|
||||
class UnableToAutoAllocateNetwork(HeatException):
|
||||
msg_fmt = _('Unable to automatically allocate a network: %(message)s')
|
||||
|
@ -59,9 +59,9 @@ class NovaClientPlugin(client_plugin.ClientPlugin):
|
||||
NOVA_API_VERSION = '2.1'
|
||||
|
||||
validate_versions = [
|
||||
V2_2, V2_8, V2_10, V2_15, V2_26
|
||||
V2_2, V2_8, V2_10, V2_15, V2_26, V2_37
|
||||
] = [
|
||||
'2.2', '2.8', '2.10', '2.15', '2.26'
|
||||
'2.2', '2.8', '2.10', '2.15', '2.26', '2.37'
|
||||
]
|
||||
|
||||
supported_versions = [NOVA_API_VERSION] + validate_versions
|
||||
|
@ -109,10 +109,12 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
|
||||
_NETWORK_KEYS = (
|
||||
NETWORK_UUID, NETWORK_ID, NETWORK_FIXED_IP, NETWORK_PORT,
|
||||
NETWORK_SUBNET, NETWORK_PORT_EXTRA, NETWORK_FLOATING_IP
|
||||
NETWORK_SUBNET, NETWORK_PORT_EXTRA, NETWORK_FLOATING_IP,
|
||||
ALLOCATE_NETWORK,
|
||||
) = (
|
||||
'uuid', 'network', 'fixed_ip', 'port',
|
||||
'subnet', 'port_extra_properties', 'floating_ip'
|
||||
'subnet', 'port_extra_properties', 'floating_ip',
|
||||
'allocate_network',
|
||||
)
|
||||
|
||||
_SOFTWARE_CONFIG_FORMATS = (
|
||||
@ -127,6 +129,12 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
'POLL_SERVER_CFN', 'POLL_SERVER_HEAT', 'POLL_TEMP_URL', 'ZAQAR_MESSAGE'
|
||||
)
|
||||
|
||||
_ALLOCATE_TYPES = (
|
||||
NETWORK_NONE, NETWORK_AUTO,
|
||||
) = (
|
||||
'none', 'auto',
|
||||
)
|
||||
|
||||
ATTRIBUTES = (
|
||||
NAME_ATTR, ADDRESSES, NETWORKS_ATTR, FIRST_ADDRESS,
|
||||
INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS, TAGS_ATTR
|
||||
@ -405,6 +413,23 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
constraints.CustomConstraint('neutron.network')
|
||||
]
|
||||
),
|
||||
ALLOCATE_NETWORK: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The special string values of network, '
|
||||
'auto: means either a network that is already '
|
||||
'available to the project will be used, or if one '
|
||||
'does not exist, will be automatically created for '
|
||||
'the project; none: means no networking will be '
|
||||
'allocated for the created server. Supported by '
|
||||
'Nova API since version "2.37". This property can '
|
||||
'not be used with other network keys.'),
|
||||
support_status=support.SupportStatus(version='9.0.0'),
|
||||
constraints=[
|
||||
constraints.AllowedValues(
|
||||
[NETWORK_NONE, NETWORK_AUTO])
|
||||
],
|
||||
update_allowed=True,
|
||||
),
|
||||
NETWORK_FIXED_IP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fixed IP address to specify for the port '
|
||||
@ -754,7 +779,15 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
|
||||
server = None
|
||||
try:
|
||||
server = self.client().servers.create(
|
||||
# if 'auto' or 'none' is specified, we get the string type
|
||||
# nics after self._build_nics(), and the string network
|
||||
# is supported since nova microversion 2.37
|
||||
if isinstance(nics, six.string_types):
|
||||
nc = self.client(version=self.client_plugin().V2_37)
|
||||
else:
|
||||
nc = self.client()
|
||||
|
||||
server = nc.servers.create(
|
||||
name=self._server_name(),
|
||||
image=image,
|
||||
flavor=flavor,
|
||||
@ -1370,16 +1403,38 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
if image:
|
||||
self._validate_image_flavor(image, flavor)
|
||||
|
||||
# network properties 'uuid' and 'network' shouldn't be used
|
||||
# both at once for all networks
|
||||
networks = self.properties[self.NETWORKS] or []
|
||||
# record if any networks include explicit ports
|
||||
networks_with_port = False
|
||||
for network in networks:
|
||||
networks_with_port = (networks_with_port or
|
||||
network.get(self.NETWORK_PORT) is not None)
|
||||
self._validate_network(network)
|
||||
|
||||
has_str_net = self._str_network(networks) is not None
|
||||
if has_str_net:
|
||||
if len(networks) != 1:
|
||||
msg = _('Property "%s" can not be specified if '
|
||||
'multiple network interfaces set for '
|
||||
'server.') % self.ALLOCATE_NETWORK
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
# Check if str_network is allowed to use
|
||||
try:
|
||||
self.client(
|
||||
version=self.client_plugin().V2_37)
|
||||
except exception.InvalidServiceVersion as ex:
|
||||
msg = (_('Cannot use "%(prop)s" property - compute service '
|
||||
'does not support the required api '
|
||||
'microversion: %(ex)s')
|
||||
% {'prop': self.ALLOCATE_NETWORK,
|
||||
'ex': six.text_type(ex)})
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
# record if any networks include explicit ports
|
||||
has_port = any(n[self.NETWORK_PORT] is not None for n in networks)
|
||||
# if 'security_groups' present for the server and explicit 'port'
|
||||
# in one or more entries in 'networks', raise validation error
|
||||
if has_port and self.properties[self.SECURITY_GROUPS]:
|
||||
raise exception.ResourcePropertyConflict(
|
||||
self.SECURITY_GROUPS,
|
||||
"/".join([self.NETWORKS, self.NETWORK_PORT]))
|
||||
|
||||
# Check if tags is allowed to use
|
||||
if self.properties[self.TAGS]:
|
||||
try:
|
||||
@ -1396,13 +1451,6 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
|
||||
if metadata or personality:
|
||||
limits = self.client_plugin().absolute_limits()
|
||||
|
||||
# if 'security_groups' present for the server and explicit 'port'
|
||||
# in one or more entries in 'networks', raise validation error
|
||||
if networks_with_port and self.properties[self.SECURITY_GROUPS]:
|
||||
raise exception.ResourcePropertyConflict(
|
||||
self.SECURITY_GROUPS,
|
||||
"/".join([self.NETWORKS, self.NETWORK_PORT]))
|
||||
|
||||
# verify that the number of metadata entries is not greater
|
||||
# than the maximum number allowed in the provider's absolute
|
||||
# limits
|
||||
|
@ -35,16 +35,28 @@ class ServerNetworkMixin(object):
|
||||
subnet = network.get(self.NETWORK_SUBNET)
|
||||
fixed_ip = network.get(self.NETWORK_FIXED_IP)
|
||||
floating_ip = network.get(self.NETWORK_FLOATING_IP)
|
||||
str_network = network.get(self.ALLOCATE_NETWORK)
|
||||
|
||||
if net_id is None and port is None and subnet is None:
|
||||
msg = _('One of the properties "%(id)s", "%(port_id)s" '
|
||||
'or "%(subnet)s" should be set for the '
|
||||
if (net_id is None and
|
||||
port is None and
|
||||
subnet is None and
|
||||
not str_network):
|
||||
msg = _('One of the properties "%(id)s", "%(port_id)s", '
|
||||
'"%(str_network)s" or "%(subnet)s" should be set for the '
|
||||
'specified network of server "%(server)s".'
|
||||
'') % dict(id=self.NETWORK_ID,
|
||||
port_id=self.NETWORK_PORT,
|
||||
subnet=self.NETWORK_SUBNET,
|
||||
str_network=self.ALLOCATE_NETWORK,
|
||||
server=self.name)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
# can not specify str_network with other keys of networks
|
||||
# at the same time
|
||||
has_value_keys = [k for k, v in network.items() if v is not None]
|
||||
if str_network and len(has_value_keys) != 1:
|
||||
msg = _('Can not specify "%s" with other keys of networks '
|
||||
'at the same time.') % self.ALLOCATE_NETWORK
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
if port is not None and not self.is_using_neutron():
|
||||
msg = _('Property "%s" is supported only for '
|
||||
@ -214,6 +226,11 @@ class ServerNetworkMixin(object):
|
||||
def _build_nics(self, networks, security_groups=None):
|
||||
if not networks:
|
||||
return None
|
||||
|
||||
str_network = self._str_network(networks)
|
||||
if str_network:
|
||||
return str_network
|
||||
|
||||
nics = []
|
||||
|
||||
for idx, net in enumerate(networks):
|
||||
@ -329,8 +346,42 @@ class ServerNetworkMixin(object):
|
||||
if net is not None:
|
||||
net['port'] = props['port']
|
||||
|
||||
def calculate_networks(self, old_nets, new_nets, ifaces,
|
||||
security_groups=None):
|
||||
def _get_available_networks(self):
|
||||
# first we get the private networks owned by the tenant
|
||||
search_opts = {'tenant_id': self.context.tenant_id, 'shared': False,
|
||||
'admin_state_up': True, }
|
||||
nc = self.client('neutron')
|
||||
nets = nc.list_networks(**search_opts).get('networks', [])
|
||||
# second we get the public shared networks
|
||||
search_opts = {'shared': True}
|
||||
nets += nc.list_networks(**search_opts).get('networks', [])
|
||||
|
||||
ids = [net['id'] for net in nets]
|
||||
|
||||
return ids
|
||||
|
||||
def _auto_allocate_network(self):
|
||||
topology = self.client('neutron').get_auto_allocated_topology(
|
||||
self.context.tenant_id)['auto_allocated_topology']
|
||||
|
||||
return topology['id']
|
||||
|
||||
def _calculate_using_str_network(self, ifaces, str_net):
|
||||
add_nets = []
|
||||
remove_ports = [iface.port_id for iface in ifaces or []]
|
||||
if str_net == self.NETWORK_AUTO:
|
||||
nets = self._get_available_networks()
|
||||
if not nets:
|
||||
nets = [self._auto_allocate_network()]
|
||||
if len(nets) > 1:
|
||||
msg = 'Multiple possible networks found.'
|
||||
raise exception.UnableToAutoAllocateNetwork(message=msg)
|
||||
|
||||
add_nets.append({'port_id': None, 'net_id': nets[0], 'fip': None})
|
||||
return remove_ports, add_nets
|
||||
|
||||
def _calculate_using_list_networks(self, old_nets, new_nets, ifaces,
|
||||
security_groups):
|
||||
remove_ports = []
|
||||
add_nets = []
|
||||
attach_first_free_port = False
|
||||
@ -351,33 +402,38 @@ class ServerNetworkMixin(object):
|
||||
# 3. detach unmatched networks, which were present in old_nets
|
||||
# 4. attach unmatched networks, which were present in new_nets
|
||||
else:
|
||||
# remove not updated networks from old and new networks lists,
|
||||
# also get list these networks
|
||||
not_updated_nets = self._exclude_not_updated_networks(old_nets,
|
||||
new_nets)
|
||||
# if old net is string net, remove the interfaces
|
||||
if self._str_network(old_nets):
|
||||
remove_ports = [iface.port_id for iface in ifaces or []]
|
||||
else:
|
||||
# remove not updated networks from old and new networks lists,
|
||||
# also get list these networks
|
||||
not_updated_nets = self._exclude_not_updated_networks(
|
||||
old_nets,
|
||||
new_nets)
|
||||
|
||||
self.update_networks_matching_iface_port(
|
||||
old_nets + not_updated_nets, ifaces)
|
||||
self.update_networks_matching_iface_port(
|
||||
old_nets + not_updated_nets, ifaces)
|
||||
|
||||
# according to nova interface-detach command detached port
|
||||
# will be deleted
|
||||
inter_port_data = self._data_get_ports()
|
||||
inter_port_ids = [p['id'] for p in inter_port_data]
|
||||
for net in old_nets:
|
||||
port_id = net.get(self.NETWORK_PORT)
|
||||
# we can't match the port for some user case, like:
|
||||
# the internal port was detached in nova first, then
|
||||
# user update template to detach this nic. The internal
|
||||
# port will remains till we delete the server resource.
|
||||
if port_id:
|
||||
remove_ports.append(port_id)
|
||||
if port_id in inter_port_ids:
|
||||
# if we have internal port with such id, remove it
|
||||
# instantly.
|
||||
self._delete_internal_port(port_id)
|
||||
if net.get(self.NETWORK_FLOATING_IP):
|
||||
self._floating_ip_disassociate(
|
||||
net.get(self.NETWORK_FLOATING_IP))
|
||||
# according to nova interface-detach command detached port
|
||||
# will be deleted
|
||||
inter_port_data = self._data_get_ports()
|
||||
inter_port_ids = [p['id'] for p in inter_port_data]
|
||||
for net in old_nets:
|
||||
port_id = net.get(self.NETWORK_PORT)
|
||||
# we can't match the port for some user case, like:
|
||||
# the internal port was detached in nova first, then
|
||||
# user update template to detach this nic. The internal
|
||||
# port will remains till we delete the server resource.
|
||||
if port_id:
|
||||
remove_ports.append(port_id)
|
||||
if port_id in inter_port_ids:
|
||||
# if we have internal port with such id, remove it
|
||||
# instantly.
|
||||
self._delete_internal_port(port_id)
|
||||
if net.get(self.NETWORK_FLOATING_IP):
|
||||
self._floating_ip_disassociate(
|
||||
net.get(self.NETWORK_FLOATING_IP))
|
||||
|
||||
handler_kwargs = {'port_id': None, 'net_id': None, 'fip': None}
|
||||
# if new_nets is None, we should attach first free port,
|
||||
@ -415,6 +471,23 @@ class ServerNetworkMixin(object):
|
||||
|
||||
return remove_ports, add_nets
|
||||
|
||||
def _str_network(self, networks):
|
||||
# if user specify 'allocate_network', return it
|
||||
# otherwise we return None
|
||||
for net in networks or []:
|
||||
str_net = net.get(self.ALLOCATE_NETWORK)
|
||||
if str_net:
|
||||
return str_net
|
||||
|
||||
def calculate_networks(self, old_nets, new_nets, ifaces,
|
||||
security_groups=None):
|
||||
new_str_net = self._str_network(new_nets)
|
||||
if new_str_net:
|
||||
return self._calculate_using_str_network(ifaces, new_str_net)
|
||||
else:
|
||||
return self._calculate_using_list_networks(
|
||||
old_nets, new_nets, ifaces, security_groups)
|
||||
|
||||
def update_floating_ip_association(self, floating_ip, flip_associate):
|
||||
if self.is_using_neutron() and flip_associate.get('port_id'):
|
||||
self._floating_ip_neutron_associate(floating_ip, flip_associate)
|
||||
|
@ -507,6 +507,31 @@ class ServersTest(common.HeatTestCase):
|
||||
args, kwargs = mock_create.call_args
|
||||
self.assertEqual({}, kwargs['meta'])
|
||||
|
||||
def test_server_create_with_str_network(self):
|
||||
stack_name = 'server_with_str_network'
|
||||
return_server = self.fc.servers.list()[1]
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
mock_nc = self.patchobject(nova.NovaClientPlugin, '_create',
|
||||
return_value=self.fc)
|
||||
self.patchobject(glance.GlanceClientPlugin, 'get_image',
|
||||
return_value=self.mock_image)
|
||||
self.patchobject(nova.NovaClientPlugin, 'get_flavor',
|
||||
return_value=self.mock_flavor)
|
||||
self.patchobject(neutron.NeutronClientPlugin,
|
||||
'find_resourceid_by_name_or_id')
|
||||
|
||||
props = tmpl['Resources']['WebServer']['Properties']
|
||||
props['networks'] = [{'allocate_network': 'none'}]
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = servers.Server('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
self.patchobject(server, 'store_external_ports')
|
||||
create_mock = self.patchobject(self.fc.servers, 'create',
|
||||
return_value=return_server)
|
||||
scheduler.TaskRunner(server.create)()
|
||||
mock_nc.assert_called_with(version='2.37')
|
||||
self.assertEqual('none', create_mock.call_args[1]['nics'])
|
||||
|
||||
def test_server_create_with_image_id(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.id = '5678'
|
||||
@ -1298,8 +1323,9 @@ class ServersTest(common.HeatTestCase):
|
||||
'find_resourceid_by_name_or_id')
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
server.validate)
|
||||
self.assertIn(_('One of the properties "network", "port" or "subnet" '
|
||||
'should be set for the specified network of '
|
||||
self.assertIn(_('One of the properties "network", "port", '
|
||||
'"allocate_network" or "subnet" should be set '
|
||||
'for the specified network of '
|
||||
'server "%s".') % server.name,
|
||||
six.text_type(ex))
|
||||
|
||||
@ -1328,6 +1354,30 @@ class ServersTest(common.HeatTestCase):
|
||||
'corresponding port can not be retrieved.'),
|
||||
six.text_type(ex))
|
||||
|
||||
def test_server_validate_with_networks_str_net(self):
|
||||
stack_name = 'srv_networks_str_nets'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
# create a server with 'uuid' and 'network' properties
|
||||
tmpl['Resources']['WebServer']['Properties']['networks'] = (
|
||||
[{'network': '6b1688bb-18a0-4754-ab05-19daaedc5871',
|
||||
'allocate_network': 'auto'}])
|
||||
self.patchobject(nova.NovaClientPlugin, '_create',
|
||||
return_value=self.fc)
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = servers.Server('server_validate_net_list_str',
|
||||
resource_defns['WebServer'], stack)
|
||||
self.patchobject(glance.GlanceClientPlugin, 'get_image',
|
||||
return_value=self.mock_image)
|
||||
self.patchobject(nova.NovaClientPlugin, 'get_flavor',
|
||||
return_value=self.mock_flavor)
|
||||
self.patchobject(neutron.NeutronClientPlugin,
|
||||
'find_resourceid_by_name_or_id')
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
server.validate)
|
||||
self.assertIn(_('Can not specify "allocate_network" with '
|
||||
'other keys of networks at the same time.'),
|
||||
six.text_type(ex))
|
||||
|
||||
def test_server_validate_port_fixed_ip(self):
|
||||
stack_name = 'port_with_fixed_ip'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name,
|
||||
@ -3139,10 +3189,12 @@ class ServersTest(common.HeatTestCase):
|
||||
|
||||
def create_old_net(self, port=None, net=None,
|
||||
ip=None, uuid=None, subnet=None,
|
||||
port_extra_properties=None, floating_ip=None):
|
||||
port_extra_properties=None, floating_ip=None,
|
||||
str_network=None):
|
||||
return {'port': port, 'network': net, 'fixed_ip': ip, 'uuid': uuid,
|
||||
'subnet': subnet, 'floating_ip': floating_ip,
|
||||
'port_extra_properties': port_extra_properties}
|
||||
'port_extra_properties': port_extra_properties,
|
||||
'allocate_network': str_network}
|
||||
|
||||
def create_fake_iface(self, port, net, ip):
|
||||
class fake_interface(object):
|
||||
@ -3212,7 +3264,8 @@ class ServersTest(common.HeatTestCase):
|
||||
old_nets_copy = copy.deepcopy(old_nets)
|
||||
for net in new_nets_copy:
|
||||
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet',
|
||||
'port_extra_properties', 'floating_ip'):
|
||||
'port_extra_properties', 'floating_ip',
|
||||
'allocate_network'):
|
||||
net.setdefault(key)
|
||||
|
||||
matched_nets = server._exclude_not_updated_networks(old_nets,
|
||||
@ -3243,7 +3296,8 @@ class ServersTest(common.HeatTestCase):
|
||||
old_nets_copy = copy.deepcopy(old_nets)
|
||||
for net in new_nets_copy:
|
||||
for key in ('port', 'network', 'fixed_ip', 'uuid', 'subnet',
|
||||
'port_extra_properties', 'floating_ip'):
|
||||
'port_extra_properties', 'floating_ip',
|
||||
'allocate_network'):
|
||||
net.setdefault(key)
|
||||
|
||||
matched_nets = server._exclude_not_updated_networks(old_nets, new_nets)
|
||||
@ -3267,7 +3321,8 @@ class ServersTest(common.HeatTestCase):
|
||||
'subnet': None,
|
||||
'uuid': None,
|
||||
'port_extra_properties': None,
|
||||
'floating_ip': None}]
|
||||
'floating_ip': None,
|
||||
'allocate_network': None}]
|
||||
new_nets_copy = copy.deepcopy(new_nets)
|
||||
|
||||
matched_nets = server._exclude_not_updated_networks(old_nets, new_nets)
|
||||
@ -3310,35 +3365,40 @@ class ServersTest(common.HeatTestCase):
|
||||
'subnet': None,
|
||||
'floating_ip': None,
|
||||
'port_extra_properties': None,
|
||||
'uuid': None},
|
||||
'uuid': None,
|
||||
'allocate_network': None},
|
||||
{'port': 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
||||
'network': 'gggggggg-1111-1111-1111-gggggggggggg',
|
||||
'fixed_ip': '1.2.3.4',
|
||||
'subnet': None,
|
||||
'port_extra_properties': None,
|
||||
'floating_ip': None,
|
||||
'uuid': None},
|
||||
'uuid': None,
|
||||
'allocate_network': None},
|
||||
{'port': 'cccccccc-cccc-cccc-cccc-cccccccccccc',
|
||||
'network': 'gggggggg-1111-1111-1111-gggggggggggg',
|
||||
'fixed_ip': None,
|
||||
'subnet': None,
|
||||
'port_extra_properties': None,
|
||||
'floating_ip': None,
|
||||
'uuid': None},
|
||||
'uuid': None,
|
||||
'allocate_network': None},
|
||||
{'port': 'dddddddd-dddd-dddd-dddd-dddddddddddd',
|
||||
'network': None,
|
||||
'fixed_ip': None,
|
||||
'subnet': None,
|
||||
'port_extra_properties': None,
|
||||
'floating_ip': None,
|
||||
'uuid': None},
|
||||
'uuid': None,
|
||||
'allocate_network': None},
|
||||
{'port': 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
|
||||
'uuid': 'gggggggg-1111-1111-1111-gggggggggggg',
|
||||
'fixed_ip': '5.6.7.8',
|
||||
'subnet': None,
|
||||
'port_extra_properties': None,
|
||||
'floating_ip': None,
|
||||
'network': None}]
|
||||
'network': None,
|
||||
'allocate_network': None}]
|
||||
|
||||
self.patchobject(neutron.NeutronClientPlugin,
|
||||
'find_resourceid_by_name_or_id',
|
||||
@ -3513,6 +3573,110 @@ class ServersTest(common.HeatTestCase):
|
||||
self.assertEqual(1, mock_detach_check.call_count)
|
||||
self.assertEqual(1, mock_attach_check.call_count)
|
||||
|
||||
def _test_server_update_to_auto(self, available_multi_nets=None):
|
||||
multi_nets = available_multi_nets or []
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.id = '5678'
|
||||
server = self._create_test_server(return_server, 'networks_update')
|
||||
|
||||
old_networks = [
|
||||
{'port': '95e25541-d26a-478d-8f36-ae1c8f6b74dc'}]
|
||||
|
||||
before_props = self.server_props.copy()
|
||||
before_props['networks'] = old_networks
|
||||
update_props = self.server_props.copy()
|
||||
update_props['networks'] = [{'allocate_network': 'auto'}]
|
||||
update_template = server.t.freeze(properties=update_props)
|
||||
server.t = server.t.freeze(properties=before_props)
|
||||
|
||||
self.patchobject(self.fc.servers, 'get', return_value=return_server)
|
||||
poor_interfaces = [
|
||||
self.create_fake_iface('95e25541-d26a-478d-8f36-ae1c8f6b74dc',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'11.12.13.14')
|
||||
]
|
||||
|
||||
self.patchobject(return_server, 'interface_list',
|
||||
return_value=poor_interfaces)
|
||||
self.patchobject(server, '_get_available_networks',
|
||||
return_value=multi_nets)
|
||||
mock_detach = self.patchobject(return_server, 'interface_detach')
|
||||
mock_attach = self.patchobject(return_server, 'interface_attach')
|
||||
updater = scheduler.TaskRunner(server.update, update_template)
|
||||
if not multi_nets:
|
||||
self.patchobject(nova.NovaClientPlugin, 'check_interface_detach',
|
||||
return_value=True)
|
||||
self.patchobject(nova.NovaClientPlugin,
|
||||
'check_interface_attach',
|
||||
return_value=True)
|
||||
|
||||
auto_allocate_net = '9cfe6c74-c105-4906-9a1f-81d9064e9bca'
|
||||
self.patchobject(server, '_auto_allocate_network',
|
||||
return_value=[auto_allocate_net])
|
||||
updater()
|
||||
self.assertEqual((server.UPDATE, server.COMPLETE), server.state)
|
||||
self.assertEqual(1, mock_detach.call_count)
|
||||
self.assertEqual(1, mock_attach.call_count)
|
||||
mock_attach.called_once_with(
|
||||
{'port_id': None,
|
||||
'net_id': auto_allocate_net,
|
||||
'fip': None})
|
||||
else:
|
||||
self.assertRaises(exception.ResourceFailure, updater)
|
||||
self.assertEqual(0, mock_detach.call_count)
|
||||
self.assertEqual(0, mock_attach.call_count)
|
||||
|
||||
def test_server_update_str_networks_auto(self):
|
||||
self._test_server_update_to_auto()
|
||||
|
||||
def test_server_update_str_networks_auto_multi_nets(self):
|
||||
available_nets = ['net_1', 'net_2']
|
||||
self._test_server_update_to_auto(available_nets)
|
||||
|
||||
def test_server_update_str_networks_none(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.id = '5678'
|
||||
server = self._create_test_server(return_server, 'networks_update')
|
||||
|
||||
old_networks = [
|
||||
{'port': '95e25541-d26a-478d-8f36-ae1c8f6b74dc'},
|
||||
{'port': '4121f61a-1b2e-4ab0-901e-eade9b1cb09d'},
|
||||
{'network': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'fixed_ip': '31.32.33.34'}]
|
||||
|
||||
before_props = self.server_props.copy()
|
||||
before_props['networks'] = old_networks
|
||||
update_props = self.server_props.copy()
|
||||
update_props['networks'] = [{'allocate_network': 'none'}]
|
||||
update_template = server.t.freeze(properties=update_props)
|
||||
server.t = server.t.freeze(properties=before_props)
|
||||
|
||||
self.patchobject(self.fc.servers, 'get', return_value=return_server)
|
||||
port_interfaces = [
|
||||
self.create_fake_iface('95e25541-d26a-478d-8f36-ae1c8f6b74dc',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'11.12.13.14'),
|
||||
self.create_fake_iface('4121f61a-1b2e-4ab0-901e-eade9b1cb09d',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'21.22.23.24'),
|
||||
self.create_fake_iface('0907fa82-a024-43c2-9fc5-efa1bccaa74a',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'31.32.33.34')
|
||||
]
|
||||
|
||||
self.patchobject(return_server, 'interface_list',
|
||||
return_value=port_interfaces)
|
||||
mock_detach = self.patchobject(return_server, 'interface_detach')
|
||||
self.patchobject(nova.NovaClientPlugin,
|
||||
'check_interface_detach',
|
||||
return_value=True)
|
||||
mock_attach = self.patchobject(return_server, 'interface_attach')
|
||||
|
||||
scheduler.TaskRunner(server.update, update_template)()
|
||||
self.assertEqual((server.UPDATE, server.COMPLETE), server.state)
|
||||
self.assertEqual(3, mock_detach.call_count)
|
||||
self.assertEqual(0, mock_attach.call_count)
|
||||
|
||||
def test_server_update_networks_with_complex_parameters(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.id = '5678'
|
||||
|
Loading…
Reference in New Issue
Block a user