Add rich network features to server profile

Add 'security_groups', 'floating_network', 'floating_ip'
network properties to server profile.
This patch change the behavior of creating network ports,
it will create ports before creating a server, then store
ports information to server.data.

Change-Id: I6859f7f4dee0c987b2caef0754747555b9d24ff6
This commit is contained in:
Ethan Lynn 2017-05-23 18:08:10 +08:00 committed by Ethan Lynn
parent 495299c87f
commit 16a5b95c7b
5 changed files with 677 additions and 343 deletions

View File

@ -1170,7 +1170,7 @@ class ServerProfile(base.Profile):
continue
network = net.get(self.NETWORK, None)
if network:
net_id = self.network(obj).network_get(network)
net_id = self.network(obj).network_get(network).id
if p['network_id'] != net_id:
continue
selected_ports.append(p)

View File

@ -64,9 +64,11 @@ class ServerProfile(base.Profile):
)
NETWORK_KEYS = (
PORT, FIXED_IP, NETWORK,
PORT, FIXED_IP, NETWORK, PORT_SECURITY_GROUPS,
FLOATING_NETWORK, FLOATING_IP,
) = (
'port', 'fixed_ip', 'network',
'port', 'fixed_ip', 'network', 'security_groups',
'floating_network', 'floating_ip',
)
PERSONALITY_KEYS = (
@ -183,6 +185,21 @@ class ServerProfile(base.Profile):
FIXED_IP: schema.String(
_('Fixed IP to be used by the network.'),
),
PORT_SECURITY_GROUPS: schema.List(
_('A list of security groups to be attached to '
'this port.'),
schema=schema.String(
_('Name of a security group'),
required=True,
),
),
FLOATING_NETWORK: schema.String(
_('The nework on which to create a floating IP'),
),
FLOATING_IP: schema.String(
_('The floating IP address to be associated with '
'this port.'),
),
},
),
updatable=True,
@ -438,53 +455,109 @@ class ServerProfile(base.Profile):
del bd[key]
return bdm
def _validate_network(self, obj, network, reason=None):
result = {}
error = None
# check network
net_ident = network.get(self.NETWORK)
if net_ident:
def _check_security_groups(self, nc, net_spec, result):
"""Check security groups.
:param nc: network driver connection.
:param net_spec: the specification to check.
:param result: the result that is used as return value.
:returns: None if succeeded or an error message if things go wrong.
"""
sgs = net_spec.get(self.PORT_SECURITY_GROUPS)
if not sgs:
return
res = []
try:
for sg in sgs:
sg_obj = nc.security_group_find(sg)
res.append(sg_obj.id)
except exc.InternalError as ex:
return six.text_type(ex)
result[self.PORT_SECURITY_GROUPS] = res
return
def _check_network(self, nc, net, result):
"""Check the specified network.
:param nc: network driver connection.
:param net: the name or ID of network to check.
:param result: the result that is used as return value.
:returns: None if succeeded or an error message if things go wrong.
"""
if net is None:
return
try:
net_obj = nc.network_get(net)
result[self.NETWORK] = net_obj.id
except exc.InternalError as ex:
return six.text_type(ex)
def _check_port(self, nc, port, result):
"""Check the specified port.
:param nc: network driver connection.
:param port: the name or ID of port to check.
:param result: the result that is used as return value.
:returns: None if succeeded or an error message if things go wrong.
"""
if port is None:
return
try:
port_obj = nc.port_find(port)
if port_obj.status != 'DOWN':
return _("The status of the port %(p)s must be DOWN"
) % {'p': port}
result[self.PORT] = port_obj.id
return
except exc.InternalError as ex:
return six.text_type(ex)
def _check_floating_ip(self, nc, net_spec, result):
"""Check floating IP and network, if specified.
:param nc: network driver connection.
:param net_spec: the specification to check.
:param result: the result that is used as return value.
:returns: None if succeeded or an error message if things go wrong.
"""
net = net_spec.get(self.FLOATING_NETWORK)
if net:
try:
net = self.network(obj).network_get(net_ident)
if reason == 'update':
result['net_id'] = net.id
else:
result['uuid'] = net.id
net_obj = nc.network_get(net)
result[self.FLOATING_NETWORK] = net_obj.id
except exc.InternalError as ex:
error = six.text_type(ex)
return six.text_type(ex)
# check port
port_ident = network.get(self.PORT)
if not error and port_ident:
try:
port = self.network(obj).port_find(port_ident)
if port.status != 'DOWN':
error = _("The status of the port %(port)s must be DOWN"
) % {'port': port_ident}
flt_ip = net_spec.get(self.FLOATING_IP)
if not flt_ip:
return
if reason == 'update':
result['port_id'] = port.id
else:
result['port'] = port.id
except exc.InternalError as ex:
error = six.text_type(ex)
elif port_ident is None and net_ident is None:
error = _("'%(port)s' is required if '%(net)s' is omitted"
) % {'port': self.PORT, 'net': self.NETWORK}
try:
# Find floating ip with this address
fip = nc.floatingip_find(flt_ip)
if fip:
if fip.status == 'ACTIVE':
return _('the floating IP %s has been used.') % flt_ip
result['floating_ip_id'] = fip.id
fixed_ip = network.get(self.FIXED_IP)
if not error and fixed_ip:
if port_ident is not None:
error = _("The '%(port)s' property and the '%(fixed_ip)s' "
"property cannot be specified at the same time"
) % {'port': self.PORT, 'fixed_ip': self.FIXED_IP}
else:
if reason == 'update':
result['fixed_ips'] = [{'ip_address': fixed_ip}]
else:
result['fixed_ip'] = fixed_ip
# Create a floating IP with address if floating ip unspecified
if not net:
return _('Must specify a network to create floating IP')
result[self.FLOATING_IP] = flt_ip
return
except exc.InternalError as ex:
return six.text_type(ex)
def _validate_network(self, obj, net_spec, reason=None):
def _verify(error):
if error is None:
return
if error:
if reason == 'create':
raise exc.EResourceCreation(type='server', message=error)
elif reason == 'update':
@ -493,8 +566,182 @@ class ServerProfile(base.Profile):
else:
raise exc.InvalidSpec(message=error)
nc = self.network(obj)
result = {}
# check network
net = net_spec.get(self.NETWORK)
error = self._check_network(nc, net, result)
_verify(error)
# check port
port = net_spec.get(self.PORT)
error = self._check_port(nc, port, result)
_verify(error)
if port is None and net is None:
_verify(_("One of '%(p)s' and '%(n)s' must be provided"
) % {'p': self.PORT, 'n': self.NETWORK})
fixed_ip = net_spec.get(self.FIXED_IP)
if fixed_ip:
if port is not None:
_verify(_("The '%(p)s' property and the '%(fip)s' property "
"cannot be specified at the same time"
) % {'p': self.PORT, 'fip': self.FIXED_IP})
result[self.FIXED_IP] = fixed_ip
# Check security_groups
error = self._check_security_groups(nc, net_spec, result)
_verify(error)
# Check floating IP
error = self._check_floating_ip(nc, net_spec, result)
_verify(error)
return result
def _get_port(self, obj, net_spec):
"""Fetch or create a port.
:param obj: The node object.
:param net_spec: The parameters to create a port.
:returns: Created port object and error message.
"""
port_id = net_spec.get(self.PORT, None)
if port_id:
try:
port = self.network(obj).port_find(port_id)
return port, None
except exc.InternalError as ex:
return None, ex
port_attr = {
'network_id': net_spec.get(self.NETWORK),
}
fixed_ip = net_spec.get(self.FIXED_IP, None)
if fixed_ip:
port_attr['fixed_ips'] = [fixed_ip]
security_groups = net_spec.get(self.PORT_SECURITY_GROUPS, [])
if security_groups:
port_attr['security_groups'] = security_groups
try:
port = self.network(obj).port_create(**port_attr)
return port, None
except exc.InternalError as ex:
return None, ex
def _delete_ports(self, obj, ports):
"""Delete ports.
:param obj: The node object
:param ports: A list of internal ports.
:returns: None for succeed or error for failure.
"""
pp = copy.deepcopy(ports)
for port in pp:
# remove port created by senlin
if port.get('remove', False):
try:
# remove floating IP created by senlin
if port.get('floating', None) and port[
'floating'].get('remove', False):
self.network(obj).floatingip_delete(
port['floating']['id'])
self.network(obj).port_delete(port['id'])
except exc.InternalError as ex:
return ex
ports.remove(port)
node_data = obj.data
node_data['internal_ports'] = ports
node_obj.Node.update(self.context, obj.id, {'data': node_data})
def _get_floating_ip(self, obj, fip_spec, port_id):
"""Find or Create a floating IP.
:param obj: The node object.
:param fip_spec: The parameters to create a floating ip
:param port_id: The port ID to associate with
:returns: A floating IP object and error message.
"""
floating_ip_id = fip_spec.get('floating_ip_id', None)
if floating_ip_id:
try:
fip = self.network(obj).floatingip_find(floating_ip_id)
if fip.port_id is None:
attr = {'port_id': port_id}
fip = self.network(obj).floatingip_update(fip, **attr)
return fip, None
except exc.InternalError as ex:
return None, ex
net_id = fip_spec.get(self.FLOATING_NETWORK)
fip_addr = fip_spec.get(self.FLOATING_IP)
attr = {
'port_id': port_id,
'floating_network_id': net_id,
}
if fip_addr:
attr.update({'floating_ip_address': fip_addr})
try:
fip = self.network(obj).floatingip_create(**attr)
return fip, None
except exc.InternalError as ex:
return None, ex
def _create_ports_from_properties(self, obj, networks, action_type):
"""Create or find ports based on networks property.
:param obj: The node object.
:param networks: The networks property used for node.
:param action_type: Either 'create' or 'update'.
:returns: A list of created port's attributes.
"""
internal_ports = obj.data.get('internal_ports', [])
if not networks:
return []
for net_spec in networks:
net = self._validate_network(obj, net_spec, action_type)
# Create port
port, ex = self._get_port(obj, net)
# Delete created ports before raise error
if ex:
d_ex = self._delete_ports(obj, internal_ports)
if d_ex:
raise d_ex
else:
raise ex
port_attrs = {
'id': port.id,
'network_id': port.network_id,
'security_group_ids': port.security_group_ids,
'fixed_ips': port.fixed_ips
}
if self.PORT not in net:
port_attrs.update({'remove': True})
# Create floating ip
if 'floating_ip_id' in net or self.FLOATING_NETWORK in net:
fip, ex = self._get_floating_ip(obj, net, port_attrs['id'])
if ex:
d_ex = self._delete_ports(obj, internal_ports)
if d_ex:
raise d_ex
else:
raise ex
port_attrs['floating'] = {
'id': fip.id,
'floating_ip_address': fip.floating_ip_address,
'floating_network_id': fip.floating_network_id,
}
if self.FLOATING_NETWORK in net:
port_attrs['floating'].update({'remove': True})
internal_ports.append(port_attrs)
if internal_ports:
node_data = obj.data
node_data.update(internal_ports=internal_ports)
node_obj.Node.update(self.context, obj.id, {'data': node_data})
return internal_ports
def _build_metadata(self, obj, usermeta):
"""Build custom metadata for server.
@ -582,10 +829,10 @@ class ServerProfile(base.Profile):
networks = self.properties[self.NETWORKS]
if networks is not None:
kwargs['networks'] = []
for net_spec in networks:
net = self._validate_network(obj, net_spec, 'create')
kwargs['networks'].append(net)
ports = self._create_ports_from_properties(
obj, networks, 'create')
kwargs['networks'] = [
{'port': port['id']} for port in ports]
secgroups = self.properties[self.SECURITY_GROUPS]
if secgroups:
@ -631,6 +878,7 @@ class ServerProfile(base.Profile):
server_id = obj.physical_id
ignore_missing = params.get('ignore_missing', True)
internal_ports = obj.data.get('internal_ports', [])
force = params.get('force', False)
try:
@ -640,6 +888,10 @@ class ServerProfile(base.Profile):
else:
driver.server_delete(server_id, ignore_missing)
driver.wait_for_server_delete(server_id)
if internal_ports:
ex = self._delete_ports(obj, internal_ports)
if ex:
raise ex
return True
except exc.InternalError as ex:
raise exc.EResourceDeletion(type='server', id=server_id,
@ -812,7 +1064,7 @@ class ServerProfile(base.Profile):
message=six.text_type(ex))
return True
def _create_interfaces(self, obj, networks):
def _update_network_add_port(self, obj, networks):
"""Create new interfaces for the server node.
:param obj: The node object to operate.
@ -828,17 +1080,60 @@ class ServerProfile(base.Profile):
raise exc.EResourceUpdate(type='server', id=obj.physical_id,
message=six.text_type(ex))
for net_spec in networks:
net_attrs = self._validate_network(obj, net_spec, 'update')
if net_attrs:
try:
cc.server_interface_create(server, **net_attrs)
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server',
id=obj.physical_id,
message=six.text_type(ex))
ports = self._create_ports_from_properties(
obj, networks, 'update')
for port in ports:
params = {'port': port['id']}
try:
cc.server_interface_create(server, **params)
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server',
id=obj.physical_id,
message=six.text_type(ex))
def _delete_interfaces(self, obj, networks):
def _find_port_by_net_spec(self, obj, net_spec, ports):
"""Find existing ports match with specific network properties.
:param obj: The node object.
:param net_spec: Network property of this profile.
:param ports: A list of ports which attached to this server.
:returns: A list of candidate ports matching this network spec.
"""
# TODO(anyone): handle security_groups
net = self._validate_network(obj, net_spec, 'update')
selected_ports = []
for p in ports:
floating = p.get('floating', {})
floating_network = net.get(self.FLOATING_NETWORK, None)
if floating_network and floating.get(
'floating_network_id') != floating_network:
continue
floating_ip_address = net.get(self.FLOATING_IP, None)
if floating_ip_address and floating.get(
'floating_ip_address') != floating_ip_address:
continue
# If network properties didn't contain floating ip,
# then we should better not make a port with floating ip
# as candidate.
if (floating and not floating_network and not floating_ip_address):
continue
port_id = net.get(self.PORT, None)
if port_id and p['id'] != port_id:
continue
fixed_ip = net.get(self.FIXED_IP, None)
if fixed_ip:
fixed_ips = [ff['ip_address'] for ff in p['fixed_ips']]
if fixed_ip not in fixed_ips:
continue
network = net.get(self.NETWORK, None)
if network:
net_id = self.network(obj).network_get(network).id
if p['network_id'] != net_id:
continue
selected_ports.append(p)
return selected_ports
def _update_network_remove_port(self, obj, networks):
"""Delete existing interfaces from the node.
:param obj: The node object to operate.
@ -847,59 +1142,31 @@ class ServerProfile(base.Profile):
:returns: ``None``
:raises: ``EResourceUpdate``
"""
def _get_network(nc, net_id, server_id):
try:
net = nc.network_get(net_id)
return net.id
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server', id=server_id,
message=six.text_type(ex))
def _do_delete(port_id, server_id):
try:
cc.server_interface_delete(port_id, server_id)
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server', id=server_id,
message=six.text_type(ex))
cc = self.compute(obj)
nc = self.network(obj)
try:
existing = list(cc.server_interface_list(obj.physical_id))
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server', id=obj.physical_id,
message=six.text_type(ex))
ports = []
for intf in existing:
fixed_ips = [addr['ip_address'] for addr in intf.fixed_ips]
ports.append({
'id': intf.port_id,
'net': intf.net_id,
'ips': fixed_ips
})
internal_ports = obj.data.get('internal_ports', [])
for n in networks:
network = n.get('network', None)
port = n.get('port', None)
fixed_ip = n.get('fixed_ip', None)
if port:
for p in ports:
if p['id'] == port:
ports.remove(p)
_do_delete(port, obj.physical_id)
elif fixed_ip:
net_id = _get_network(nc, network, obj.physical_id)
for p in ports:
if (fixed_ip in p['ips'] and net_id == p['net']):
ports.remove(p)
_do_delete(p['id'], obj.physical_id)
elif port is None and fixed_ip is None:
net_id = _get_network(nc, network, obj.physical_id)
for p in ports:
if p['net'] == net_id:
ports.remove(p)
_do_delete(p['id'], obj.physical_id)
candidate_ports = self._find_port_by_net_spec(
obj, n, internal_ports)
port = candidate_ports[0]
try:
# Detach port from server
cc.server_interface_delete(port['id'], obj.physical_id)
# delete port if created by senlin
if port.get('remove', False):
nc.port_delete(port['id'], ignore_missing=True)
# delete floating IP if created by senlin
if (port.get('floating', None) and
port['floating'].get('remove', False)):
nc.floatingip_delete(port['floating']['id'],
ignore_missing=True)
except exc.InternalError as ex:
raise exc.EResourceUpdate(type='server', id=obj.physical_id,
message=six.text_type(ex))
internal_ports.remove(port)
obj.data['internal_ports'] = internal_ports
node_obj.Node.update(self.context, obj.id, {'data': obj.data})
def _update_network(self, obj, new_profile):
"""Updating server network interfaces.
@ -920,11 +1187,11 @@ class ServerProfile(base.Profile):
# Detach some existing interfaces
if networks_delete:
self._delete_interfaces(obj, networks_delete)
self._update_network_remove_port(obj, networks_delete)
# Attach new interfaces
if networks_create:
self._create_interfaces(obj, networks_create)
self._update_network_add_port(obj, networks_create)
return
def do_update(self, obj, new_profile=None, **params):

View File

@ -17,6 +17,7 @@ from oslo_utils import encodeutils
import six
from senlin.common import exception as exc
from senlin.objects import node as node_ob
from senlin.profiles import base as profiles_base
from senlin.profiles.os.nova import server
from senlin.tests.unit.common import base
@ -44,9 +45,9 @@ class TestNovaServerBasic(base.SenlinTestCase):
"metadata": {"meta var": "meta val"},
'name': 'FAKE_SERVER_NAME',
'networks': [{
'port': 'FAKE_PORT',
'fixed_ip': 'FAKE_IP',
'network': 'FAKE_NET',
'floating_network': 'FAKE_PUBLIC_NET',
}],
'personality': [{
'path': '/etc/motd',
@ -116,9 +117,15 @@ class TestNovaServerBasic(base.SenlinTestCase):
'fixed_ip': 'FAKE_IP',
'port': 'FAKE_PORT',
'uuid': 'FAKE_NETWORK_ID',
'floating_network': 'FAKE_PUBLIC_NET_ID',
}
self.patchobject(profile, '_validate_network',
return_value=fake_net)
fake_ports = [{
'id': 'FAKE_PORT'
}]
self.patchobject(profile, '_create_ports_from_properties',
return_value=fake_ports)
def test_do_create(self):
cc = mock.Mock()
@ -161,9 +168,7 @@ class TestNovaServerBasic(base.SenlinTestCase):
},
name='FAKE_SERVER_NAME',
networks=[{
'fixed_ip': 'FAKE_IP',
'port': 'FAKE_PORT',
'uuid': 'FAKE_NETWORK_ID',
}],
personality=[{
'path': '/etc/motd',
@ -252,9 +257,16 @@ class TestNovaServerBasic(base.SenlinTestCase):
self.assertRaises(exc.EResourceCreation,
profile.do_create,
node_obj)
expect_params = {
'floating_network': None,
'network': 'FAKE_NET',
'fixed_ip': None,
'floating_ip': None,
'port': None,
'security_groups': None
}
mock_net.assert_called_once_with(
node_obj, {'network': 'FAKE_NET', 'port': None, 'fixed_ip': None},
'create')
node_obj, expect_params, 'create')
def test_do_create_server_attrs_not_defined(self):
cc = mock.Mock()
@ -456,8 +468,10 @@ class TestNovaServerBasic(base.SenlinTestCase):
def test_do_create_wait_server_timeout(self):
cc = mock.Mock()
nc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
self._stubout_profile(profile, mock_image=True, mock_flavor=True,
mock_keypair=True, mock_net=True)
@ -519,6 +533,7 @@ class TestNovaServerBasic(base.SenlinTestCase):
profile._computeclient = cc
test_server = mock.Mock(physical_id='FAKE_ID')
test_server.data = {}
res = profile.do_delete(test_server)
@ -526,6 +541,39 @@ class TestNovaServerBasic(base.SenlinTestCase):
cc.server_delete.assert_called_once_with('FAKE_ID', True)
cc.wait_for_server_delete.assert_called_once_with('FAKE_ID')
@mock.patch.object(node_ob.Node, 'update')
def test_do_delete_ports_ok(self, mock_node_obj):
profile = server.ServerProfile('t', self.spec)
cc = mock.Mock()
cc.server_delete.return_value = None
nc = mock.Mock()
nc.port_delete.return_value = None
nc.floatingip_delete.return_value = None
profile._computeclient = cc
profile._networkclient = nc
test_server = mock.Mock(physical_id='FAKE_ID')
test_server.Node = mock.Mock()
test_server.data = {'internal_ports': [{
'floating': {
'remove': True,
'id': 'FAKE_FLOATING_ID',
},
'id': 'FAKE_PORT_ID',
'remove': True
}]}
res = profile.do_delete(test_server)
self.assertTrue(res)
mock_node_obj.assert_called_once_with(
mock.ANY, test_server.id, {'data': {'internal_ports': []}})
nc.floatingip_delete.assert_called_once_with('FAKE_FLOATING_ID')
nc.port_delete.assert_called_once_with('FAKE_PORT_ID')
cc.server_delete.assert_called_once_with('FAKE_ID', True)
cc.wait_for_server_delete.assert_called_once_with('FAKE_ID')
def test_do_delete_ignore_missing_force(self):
profile = server.ServerProfile('t', self.spec)
@ -533,6 +581,7 @@ class TestNovaServerBasic(base.SenlinTestCase):
profile._computeclient = cc
test_server = mock.Mock(physical_id='FAKE_ID')
test_server.data = {}
res = profile.do_delete(test_server, ignore_missing=False, force=True)

View File

@ -16,6 +16,7 @@ import mock
import six
from senlin.common import exception as exc
from senlin.objects import node as node_obj
from senlin.profiles.os.nova import server
from senlin.tests.unit.common import base
from senlin.tests.unit.common import utils
@ -156,6 +157,7 @@ class TestNovaServerUpdate(base.SenlinTestCase):
'user_data': 'FAKE_USER_DATA',
}
}
self.patchobject(node_obj.Node, 'update')
def test__update_name(self):
profile = server.ServerProfile('t', self.spec)
@ -743,23 +745,39 @@ class TestNovaServerUpdate(base.SenlinTestCase):
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
validation_results = [
{'net_id': 'net1', 'fixed_ips': [{'ip_address': 'ip2'}]},
{'net_id': 'net2'},
{'port_id': 'port4'}
{'network': 'net1_id', 'fixed_ip': 'ip2'},
{'network': 'net2_id'},
{'port': 'port4'}
]
mock_validate = self.patchobject(profile, '_validate_network',
side_effect=validation_results)
ports_results = [
(mock.Mock(
id='port1_id', network_id='net1_id',
fixed_ips=[{'ip_address': 'ip2'}], security_group_ids=[]),
None),
(mock.Mock(
id='port2_id', network_id='net2_id',
fixed_ips=[{'ip_address': 'ip3'}], security_group_ids=[]),
None),
(mock.Mock(
id='port4_id', network_id='net3_id',
fixed_ips=[{'ip_address': 'ip4'}], security_group_ids=[]),
None)
]
mock_get_port = self.patchobject(profile, '_get_port',
side_effect=ports_results)
networks = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip2'},
{'network': 'net2', 'port': None, 'fixed_ip': None},
{'network': None, 'port': 'port4', 'fixed_ip': None}
]
obj = mock.Mock(physical_id='NOVA_ID')
obj = mock.Mock(physical_id='NOVA_ID', data={})
res = profile._create_interfaces(obj, networks)
res = profile._update_network_add_port(obj, networks)
self.assertIsNone(res)
cc.server_get.assert_called_once_with('NOVA_ID')
cc.server_get.assert_called_with('NOVA_ID')
validation_calls = [
mock.call(obj,
{'network': 'net1', 'port': None, 'fixed_ip': 'ip2'},
@ -772,11 +790,11 @@ class TestNovaServerUpdate(base.SenlinTestCase):
'update')
]
mock_validate.assert_has_calls(validation_calls)
mock_get_port.assert_called_with(obj, {'port': 'port4'})
create_calls = [
mock.call(server_obj, net_id='net1',
fixed_ips=[{'ip_address': 'ip2'}]),
mock.call(server_obj, net_id='net2'),
mock.call(server_obj, port_id='port4'),
mock.call(server_obj, port='port1_id'),
mock.call(server_obj, port='port2_id'),
mock.call(server_obj, port='port4_id'),
]
cc.server_interface_create.assert_has_calls(create_calls)
@ -785,18 +803,19 @@ class TestNovaServerUpdate(base.SenlinTestCase):
cc.server_get.side_effect = exc.InternalError(message='Not valid')
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
self.patchobject(profile, '_create_ports_from_properties')
obj = mock.Mock(physical_id='NOVA_ID')
networks = [{'foo': 'bar'}] # not used
ex = self.assertRaises(exc.EResourceUpdate,
profile._create_interfaces,
profile._update_network_add_port,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': Not valid.",
six.text_type(ex))
cc.server_get.assert_called_once_with('NOVA_ID')
self.assertEqual(0, cc.server_interface_create.call_count)
self.assertEqual(0, profile._create_ports_from_properties.call_count)
def test__create_interfaces_failed_validation(self):
cc = mock.Mock()
@ -812,7 +831,7 @@ class TestNovaServerUpdate(base.SenlinTestCase):
obj = mock.Mock(physical_id='NOVA_ID')
ex = self.assertRaises(exc.EResourceUpdate,
profile._create_interfaces,
profile._update_network_add_port,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': Driver error.",
@ -825,37 +844,28 @@ class TestNovaServerUpdate(base.SenlinTestCase):
cc = mock.Mock()
nc = mock.Mock()
net1 = mock.Mock(id='net1')
interfaces = [
mock.Mock(port_id='port1', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip1'
}]),
mock.Mock(port_id='port2', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip-random2'
}]),
mock.Mock(port_id='port3', net_id='net2',
fixed_ips=[{
'subnet_id': 'subnet2', 'ip_address': 'ip3'
}])
]
cc.server_interface_list.return_value = interfaces
nc.network_get.side_effect = [net1, net1]
nc.network_get.return_value = net1
nc.port_find.return_value = mock.Mock(id='port3', status='DOWN')
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
obj = mock.Mock(physical_id='NOVA_ID')
obj = mock.Mock(physical_id='NOVA_ID', data={'internal_ports': [
{'id': 'port1', 'network_id': 'net1', 'remove': True,
'fixed_ips': [{'ip_address': 'ip1'}]},
{'id': 'port2', 'network_id': 'net1', 'remove': True,
'fixed_ips': [{'ip_address': 'ip-random2'}]},
{'id': 'port3', 'network_id': 'net1', 'remove': True,
'fixed_ips': [{'ip_address': 'ip3'}]}]})
networks = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip1'},
{'network': 'net1', 'port': None, 'fixed_ip': None},
{'network': None, 'port': 'port3', 'fixed_ip': None}
]
res = profile._delete_interfaces(obj, networks)
res = profile._update_network_remove_port(obj, networks)
self.assertIsNone(res)
cc.server_interface_list.assert_called_once_with('NOVA_ID')
nc.network_get.assert_has_calls([
mock.call('net1'), mock.call('net1')
])
@ -864,196 +874,44 @@ class TestNovaServerUpdate(base.SenlinTestCase):
mock.call('port2', 'NOVA_ID'),
mock.call('port3', 'NOVA_ID'),
])
nc.port_delete.assert_has_calls([
mock.call('port1', ignore_missing=True),
mock.call('port2', ignore_missing=True),
mock.call('port3', ignore_missing=True),
])
def test__delete_interfaces_failed_listing_interfaces(self):
def test__delete_interfaces_failed_delete(self):
cc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = mock.Mock()
err = exc.InternalError(message='BANG')
cc.server_interface_list.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
obj, mock.Mock())
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
def test__delete_interfaces_failed_1st_delete(self):
cc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = mock.Mock()
interfaces = [
mock.Mock(port_id='port1', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip1'
}]),
mock.Mock(port_id='port2', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip-random2'
}]),
mock.Mock(port_id='port3', net_id='net2',
fixed_ips=[{
'subnet_id': 'subnet2', 'ip_address': 'ip3'
}])
candidate_ports = [
[{'id': 'port1', 'network_id': 'net1',
'fixed_ips': [{'ip_address': 'ip1'}]}],
]
cc.server_interface_list.return_value = interfaces
self.patchobject(profile, '_find_port_by_net_spec',
side_effect=candidate_ports)
err = exc.InternalError(message='BANG')
cc.server_interface_delete.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
networks = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip1'},
{'network': 'net1', 'port': None, 'fixed_ip': None},
{'network': None, 'port': 'port3', 'fixed_ip': None}
internal_ports = [
{'id': 'port1', 'remove': True}
]
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
cc.server_interface_delete.assert_called_once_with('port3', 'NOVA_ID')
def test__delete_interfaces_failed_1st_get_network(self):
cc = mock.Mock()
nc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
interfaces = [
mock.Mock(port_id='port1', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip1'
}]),
]
cc.server_interface_list.return_value = interfaces
err = exc.InternalError(message='BANG')
nc.network_get.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
obj = mock.Mock(physical_id='NOVA_ID',
data={'internal_ports': internal_ports})
networks = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip1'},
]
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
profile._update_network_remove_port,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
nc.network_get.assert_called_once_with('net1')
def test__delete_interfaces_failed_2nd_delete(self):
cc = mock.Mock()
nc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
interfaces = [
mock.Mock(port_id='port3', net_id='net2',
fixed_ips=[{
'subnet_id': 'subnet2', 'ip_address': 'ip3'
}])
]
cc.server_interface_list.return_value = interfaces
net2 = mock.Mock(id='net2')
nc.network_get.return_value = net2
err = exc.InternalError(message='BANG')
cc.server_interface_delete.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
networks = [
{'network': 'net2', 'port': None, 'fixed_ip': None},
]
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
nc.network_get.assert_called_once_with('net2')
def test__delete_interfaces_failed_2nd_get_network(self):
cc = mock.Mock()
nc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
interfaces = [
mock.Mock(port_id='port1', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip1'
}]),
]
cc.server_interface_list.return_value = interfaces
net1 = mock.Mock(id='net1')
nc.network_get.return_value = net1
err = exc.InternalError(message='BANG')
cc.server_interface_delete.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
networks = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip1'},
]
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
nc.network_get.assert_called_once_with('net1')
cc.server_interface_delete.assert_called_once_with('port1', 'NOVA_ID')
def test__delete_interfaces_failed_3rd_delete(self):
cc = mock.Mock()
nc = mock.Mock()
profile = server.ServerProfile('t', self.spec)
profile._computeclient = cc
profile._networkclient = nc
interfaces = [
mock.Mock(port_id='port1', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip1'
}]),
mock.Mock(port_id='port2', net_id='net1',
fixed_ips=[{
'subnet_id': 'subnet1', 'ip_address': 'ip-random2'
}]),
mock.Mock(port_id='port3', net_id='net2',
fixed_ips=[{
'subnet_id': 'subnet2', 'ip_address': 'ip3'
}])
]
cc.server_interface_list.return_value = interfaces
net2 = mock.Mock(id='net2')
nc.network_get.return_value = net2
err = exc.InternalError(message='BANG')
cc.server_interface_delete.side_effect = err
obj = mock.Mock(physical_id='NOVA_ID')
networks = [
{'network': 'net2', 'port': None, 'fixed_ip': None},
]
ex = self.assertRaises(exc.EResourceUpdate,
profile._delete_interfaces,
obj, networks)
self.assertEqual("Failed in updating server 'NOVA_ID': BANG.",
six.text_type(ex))
cc.server_interface_list.assert_called_once_with('NOVA_ID')
nc.network_get.assert_called_once_with('net2')
cc.server_interface_delete.assert_called_once_with('port3', 'NOVA_ID')
@mock.patch.object(server.ServerProfile, '_delete_interfaces')
@mock.patch.object(server.ServerProfile, '_create_interfaces')
@mock.patch.object(server.ServerProfile, '_update_network_remove_port')
@mock.patch.object(server.ServerProfile, '_update_network_add_port')
def test__update_network(self, mock_create, mock_delete):
obj = mock.Mock(physical_id='FAKE_ID')
@ -1061,14 +919,14 @@ class TestNovaServerUpdate(base.SenlinTestCase):
old_spec['properties']['networks'] = [
{'network': 'net1', 'fixed_ip': 'ip1'},
{'network': 'net1'},
{'port': 'port3'}
{'port': 'port3'},
]
profile = server.ServerProfile('t', old_spec)
new_spec = copy.deepcopy(self.spec)
new_spec['properties']['networks'] = [
{'network': 'net1', 'fixed_ip': 'ip2'},
{'network': 'net2'},
{'port': 'port4'}
{'port': 'port4'},
]
new_profile = server.ServerProfile('t1', new_spec)
@ -1077,15 +935,21 @@ class TestNovaServerUpdate(base.SenlinTestCase):
self.assertIsNone(res)
networks_create = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip2'},
{'network': 'net2', 'port': None, 'fixed_ip': None},
{'network': None, 'port': 'port4', 'fixed_ip': None}
{'floating_network': None, 'network': 'net1', 'fixed_ip': 'ip2',
'floating_ip': None, 'port': None, 'security_groups': None},
{'floating_network': None, 'network': 'net2', 'fixed_ip': None,
'floating_ip': None, 'port': None, 'security_groups': None},
{'floating_network': None, 'network': None, 'fixed_ip': None,
'floating_ip': None, 'port': 'port4', 'security_groups': None}
]
mock_create.assert_called_once_with(obj, networks_create)
networks_delete = [
{'network': 'net1', 'port': None, 'fixed_ip': 'ip1'},
{'network': 'net1', 'port': None, 'fixed_ip': None},
{'network': None, 'port': 'port3', 'fixed_ip': None}
{'floating_network': None, 'network': 'net1', 'fixed_ip': 'ip1',
'floating_ip': None, 'port': None, 'security_groups': None},
{'floating_network': None, 'network': 'net1', 'fixed_ip': None,
'floating_ip': None, 'port': None, 'security_groups': None},
{'floating_network': None, 'network': None, 'fixed_ip': None,
'floating_ip': None, 'port': 'port3', 'security_groups': None}
]
mock_delete.assert_called_once_with(obj, networks_delete)

View File

@ -35,6 +35,9 @@ spec = {
"metadata": {"meta var": "meta val"},
'name': 'FAKE_SERVER_NAME',
'networks': [{
'floating_ip': 'FAKE_FLOATING_IP',
'floating_network': 'FAKE_FLOATING_NET',
'security_groups': ['FAKE_SECURITY_GROUP'],
'port': 'FAKE_PORT',
'fixed_ip': 'FAKE_IP',
'network': 'FAKE_NET',
@ -424,30 +427,72 @@ class TestKeypairValidation(base.SenlinTestCase):
class TestNetworkValidation(base.SenlinTestCase):
scenarios = [
('validate:net-y:port-y:fixed_ip-n', dict(
('validate:net-n:port-n:fixed_ip-n:sgroups-n', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[mock.Mock(id='NET_ID')],
inputs={'port': 'PORT'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
result={'uuid': 'NET_ID', 'port': 'PORT_ID'},
sg_result=[],
floating_result=[],
result={'port': 'PORT_ID'},
exception=None,
message='')),
('validate:net-y:port-n:fixed_ip-y', dict(
('validate:net-y:port-n:fixed_ip-n:sgroups-y', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'security_groups': ['default']},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[mock.Mock(id='SG_ID')],
floating_result=[],
result={'network': 'NET_ID', 'security_groups': ['SG_ID']},
exception=None,
message='')),
('validate:net-y:port-n:fixed_ip-n:sgroups-n:floating_net-y', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'floating_network': 'NET'},
net_result=[mock.Mock(id='NET_ID'), mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'floating_network': 'NET_ID'},
exception=None,
message='')),
('validate:net-y:port-n:fixed_ip-n:floating_net-y:floating_ip-y', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'floating_network': 'NET',
'floating_ip': 'FLOATINGIP'},
net_result=[mock.Mock(id='NET_ID'), mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[],
floating_result=[mock.Mock(id='FLOATINGIP_ID', status='INACTIVE')],
result={'network': 'NET_ID', 'floating_network': 'NET_ID',
'floating_ip_id': 'FLOATINGIP_ID',
'floating_ip': 'FLOATINGIP'},
exception=None,
message='')),
('validate:net-y:port-n:fixed_ip-y:sgroups-n', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'fixed_ip': 'FIXED_IP'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
result={'uuid': 'NET_ID', 'fixed_ip': 'FIXED_IP'},
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'fixed_ip': 'FIXED_IP'},
exception=None,
message='')),
('validate:net-f:port-y:fixed_ip-n', dict(
('validate:net-f:port-y:fixed_ip-n:sgroups-n', dict(
reason=None,
success=False,
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[exc.InternalError(message='NET Failure')],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.InvalidSpec,
message='NET Failure')),
@ -457,6 +502,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[exc.InternalError(message='PORT Failure')],
sg_result=[],
floating_result=[],
result={},
exception=exc.InvalidSpec,
message='PORT Failure')),
@ -466,24 +513,53 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='ACTIVE')],
sg_result=[],
floating_result=[],
result={},
exception=exc.InvalidSpec,
message='The status of the port PORT must be DOWN')),
('validate:net-n:port-y:fixed_ip-n:floating_net-n:floating_ip-y', dict(
reason=None,
success=False,
inputs={'port': 'PORT', 'floating_ip': 'FLOATINGIP'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
sg_result=[],
floating_result=[mock.Mock(id='FLOATINGIP_ID', status='INACTIVE')],
result={},
exception=exc.InvalidSpec,
message='Must specify a network to create floating IP')),
('validate:net-n:port-y:fixed_ip-n:floating_ip-active', dict(
reason=None,
success=False,
inputs={'port': 'PORT', 'floating_network': 'NET',
'floating_ip': 'FLOATINGIP'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
sg_result=[],
floating_result=[mock.Mock(id='FLOATINGIP_ID', status='ACTIVE')],
result={},
exception=exc.InvalidSpec,
message='the floating IP FLOATINGIP has been used.')),
('validate:net-n:port-n:fixed_ip-n', dict(
reason=None,
success=False,
inputs={'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.InvalidSpec,
message="'port' is required if 'network' is omitted")),
message="One of 'port' and 'network' must be provided")),
('validate:net-n:port-y:fixed_ip-y', dict(
reason=None,
success=False,
inputs={'port': 'PORT', 'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
sg_result=[],
floating_result=[],
result={},
exception=exc.InvalidSpec,
message=("The 'port' property and the 'fixed_ip' property cannot "
@ -494,7 +570,9 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
result={'uuid': 'NET_ID', 'port': 'PORT_ID'},
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'port': 'PORT_ID'},
exception=None,
message='')),
('create:net-y:port-n:fixed_ip-y', dict(
@ -503,7 +581,31 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'fixed_ip': 'FIXED_IP'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
result={'uuid': 'NET_ID', 'fixed_ip': 'FIXED_IP'},
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'fixed_ip': 'FIXED_IP'},
exception=None,
message='')),
('create:net-y:port-n:fixed_ip-n:sgroups-y', dict(
reason='create',
success=True,
inputs={'network': 'NET', 'security_groups': ['default']},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[mock.Mock(id='SG_ID')],
floating_result=[],
result={'network': 'NET_ID', 'security_groups': ['SG_ID']},
exception=None,
message='')),
('create:net-y:port-n:fixed_ip-n:sgroups-n:floating_net-y', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'floating_network': 'NET'},
net_result=[mock.Mock(id='NET_ID'), mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'floating_network': 'NET_ID'},
exception=None,
message='')),
('create:net-f:port-y:fixed_ip-n', dict(
@ -512,6 +614,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[exc.InternalError(message='NET Failure')],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceCreation,
message='Failed in creating server: NET Failure.')),
@ -521,6 +625,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[exc.InternalError(message='PORT Failure')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceCreation,
message='Failed in creating server: PORT Failure.')),
@ -530,6 +636,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='ACTIVE')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceCreation,
message=('Failed in creating server: The status of the port PORT '
@ -540,16 +648,20 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceCreation,
message=("Failed in creating server: 'port' is required if "
"'network' is omitted."))),
message=("Failed in creating server: One of 'port' "
"and 'network' must be provided."))),
('create:net-n:port-y:fixed_ip-y', dict(
reason='create',
success=False,
inputs={'port': 'PORT', 'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceCreation,
message=("Failed in creating server: The 'port' property and the "
@ -561,7 +673,9 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
result={'net_id': 'NET_ID', 'port_id': 'PORT_ID'},
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'port': 'PORT_ID'},
exception=None,
message='')),
('update:net-y:port-n:fixed_ip-y', dict(
@ -570,8 +684,32 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'fixed_ip': 'FIXED_IP'},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
result={'net_id': 'NET_ID',
'fixed_ips': [{'ip_address': 'FIXED_IP'}]},
sg_result=[],
floating_result=[],
result={'network': 'NET_ID',
'fixed_ip': 'FIXED_IP'},
exception=None,
message='')),
('update:net-y:port-n:fixed_ip-n:sgroups-y', dict(
reason='create',
success=True,
inputs={'network': 'NET', 'security_groups': ['default']},
net_result=[mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[mock.Mock(id='SG_ID')],
floating_result=[],
result={'network': 'NET_ID', 'security_groups': ['SG_ID']},
exception=None,
message='')),
('update:net-y:port-n:fixed_ip-n:sgroups-n:floating_net-y', dict(
reason=None,
success=True,
inputs={'network': 'NET', 'floating_network': 'NET'},
net_result=[mock.Mock(id='NET_ID'), mock.Mock(id='NET_ID')],
port_result=[],
sg_result=[],
floating_result=[],
result={'network': 'NET_ID', 'floating_network': 'NET_ID'},
exception=None,
message='')),
('update:net-f:port-y:fixed_ip-n', dict(
@ -580,6 +718,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'network': 'NET', 'port': 'PORT'},
net_result=[exc.InternalError(message='NET Failure')],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceUpdate,
message="Failed in updating server 'NOVA_ID': NET Failure.")),
@ -589,6 +729,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[exc.InternalError(message='PORT Failure')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceUpdate,
message="Failed in updating server 'NOVA_ID': PORT Failure.")),
@ -598,6 +740,8 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'port': 'PORT'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='ACTIVE')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceUpdate,
message=("Failed in updating server 'NOVA_ID': The status of the "
@ -608,16 +752,20 @@ class TestNetworkValidation(base.SenlinTestCase):
inputs={'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceUpdate,
message=("Failed in updating server 'NOVA_ID': 'port' is required "
"if 'network' is omitted."))),
message=("Failed in updating server 'NOVA_ID': One of 'port' "
"and 'network' must be provided."))),
('update:net-n:port-y:fixed_ip-y', dict(
reason='update',
success=False,
inputs={'port': 'PORT', 'fixed_ip': 'FIXED_IP'},
net_result=[],
port_result=[mock.Mock(id='PORT_ID', status='DOWN')],
sg_result=[],
floating_result=[],
result={},
exception=exc.EResourceUpdate,
message=("Failed in updating server 'NOVA_ID': The 'port' "
@ -635,6 +783,8 @@ class TestNetworkValidation(base.SenlinTestCase):
def test_validation(self):
self.nc.network_get.side_effect = self.net_result
self.nc.port_find.side_effect = self.port_result
self.nc.security_group_find.side_effect = self.sg_result
self.nc.floatingip_find.side_effect = self.floating_result
obj = mock.Mock(physical_id='NOVA_ID')
if self.success:
@ -647,9 +797,13 @@ class TestNetworkValidation(base.SenlinTestCase):
self.assertEqual(self.message, six.text_type(ex))
if self.net_result:
self.nc.network_get.assert_called_once_with('NET')
self.nc.network_get.assert_called_with('NET')
if self.port_result:
self.nc.port_find.assert_called_once_with('PORT')
if self.sg_result:
self.nc.security_group_find.assert_called_once_with('default')
if self.floating_result:
self.nc.floatingip_find.assert_called_once_with('FLOATINGIP')
class TestNovaServerValidate(base.SenlinTestCase):