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:
parent
495299c87f
commit
16a5b95c7b
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue