Rework novaclient floating ips actions

Methods for association floating ips and dissociation were deprecated in
novaclient a year ago and latest major release (python-novaclient 10)
doesn't include them[*].
These actions should be performed via neutronclient now. It requires more
work, but there is no other options.

[*] https://github.com/openstack/python-novaclient/blob/master/releasenotes/notes/remove-virt-interfaces-add-rm-fixed-floating-398c905d9c91cca8.yaml

Change-Id: I6c019fe84fb3d6dfcef5dfadbbccff01bde4a102
This commit is contained in:
Andrey Kurilin 2018-03-06 20:50:25 +02:00
parent 7174452c13
commit d89eb07a02
6 changed files with 347 additions and 41 deletions

View File

@ -287,6 +287,40 @@
users:
tenants: 2
users_per_tenant: 1
-
title: NovaServers.boot_and_associate_floating_ip tests
scenario:
NovaServers.boot_and_associate_floating_ip:
flavor:
name: {{flavor_name}}
image:
name: {{image_name}}
runner:
constant:
times: 2
concurrency: 2
contexts:
users:
tenants: 2
users_per_tenant: 1
network: {}
-
title: NovaServers.boot_server_associate_and_dissociate_floating_ip tests
scenario:
NovaServers.boot_server_associate_and_dissociate_floating_ip:
flavor:
name: {{flavor_name}}
image:
name: {{image_name}}
runner:
constant:
times: 2
concurrency: 2
contexts:
users:
tenants: 2
users_per_tenant: 2
network: {}
-
title: NovaServers.list_servers tests
scenario:

View File

@ -682,3 +682,136 @@
sla:
failure_rate:
max: 0
VMTasks.boot_runcommand_delete:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
command:
script_file: "~/.rally/extra/instance_test.sh"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: {{smoke or 2}}
concurrency: {{smoke or 2}}
context:
users:
tenants: {{smoke or 2}}
users_per_tenant: {{smoke or 2}}
network: {}
sla:
failure_rate:
max: 0
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
command:
script_file: "~/.rally/extra/instance_test.sh"
interpreter: "/bin/sh"
username: "cirros"
volume_args:
size: 2
runner:
type: "constant"
times: {{smoke or 2}}
concurrency: {{smoke or 2}}
context:
users:
tenants: {{smoke or 2}}
users_per_tenant: {{smoke or 1}}
network: {}
sla:
failure_rate:
max: 0
-
args:
flavor:
name: {{flavor_name}}
image:
name: {{image_name}}
floating_network: "public"
command:
script_inline: |
time_seconds(){ (time -p $1 ) 2>&1 |awk '/real/{print $2}'; }
file=/tmp/test.img
c=100 #100M
write_seq=$(time_seconds "dd if=/dev/zero of=$file bs=1M count=$c")
read_seq=$(time_seconds "dd if=$file of=/dev/null bs=1M count=$c")
[ -f $file ] && rm $file
echo "{
\"write_seq\": $write_seq,
\"read_seq\": $read_seq
}"
interpreter: "/bin/sh"
username: "cirros"
runner:
type: "constant"
times: 2
concurrency: 2
context:
users:
tenants: 1
users_per_tenant: 1
network: {}
sla:
failure_rate:
max: 0
-
args:
command:
# The `image_command_customizer` context prepares an image and
# executes `rally-jobs/extra/install_benchmark.sh` script. The script
# itself creates a new file inside the image "dd_test.sh" which is
# called in the scenario. It doesn't have anything related to
# VMTask.dd_load_test scenario
remote_path: "./dd_test.sh"
flavor:
name: "m1.tiny"
username: "cirros"
runner:
type: "constant"
times: 1
concurrency: 1
context:
image_command_customizer:
command:
local_path: "~/.rally/extra/install_benchmark.sh"
remote_path: "./install_benchmark.sh"
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
username: "cirros"
users:
tenants: 1
users_per_tenant: 1
network:
dns_nameservers: []
VMTasks.dd_load_test:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
floating_network: "public"
force_delete: false
username: "cirros"
runner:
type: "constant"
times: 2
concurrency: 2
context:
users:
tenants: 2
users_per_tenant: 1
network: {}

View File

@ -606,13 +606,42 @@ class NovaScenario(scenario.OpenStackScenario):
"""Add floating IP to an instance
:param server: The :class:`Server` to add an IP to.
:param address: The ip address or FloatingIP to add to the instance
:param address: The dict-like representation of FloatingIP to add
to the instance
:param fixed_address: The fixedIP address the FloatingIP is to be
associated with (optional)
"""
server.add_floating_ip(address, fixed_address=fixed_address)
with atomic.ActionTimer(self, "neutron.list_ports"):
ports = self.clients("neutron").list_ports(device_id=server.id)
port = ports["ports"][0]
fip = address
if not isinstance(address, dict):
LOG.warning(
"The argument 'address' of "
"NovaScenario._associate_floating_ip method accepts a "
"dict-like representation of floating ip. Transmitting a "
"string with just an IP is deprecated.")
with atomic.ActionTimer(self, "neutron.list_floating_ips"):
all_fips = self.clients("neutron").list_floatingips(
tenant_id=self.context["tenant"]["id"])
filtered_fip = [f for f in all_fips["floatingips"]
if f["floating_ip_address"] == address]
if not filtered_fip:
raise exceptions.NotFoundException(
"There is no floating ip with '%s' address." % address)
fip = filtered_fip[0]
# the first case: fip object is returned from network wrapper
# the second case: from neutronclient directly
fip_ip = fip.get("ip", fip.get("floating_ip_address", None))
fip_update_dict = {"port_id": port["id"]}
if fixed_address:
fip_update_dict["fixed_ip_address"] = fixed_address
self.clients("neutron").update_floatingip(
fip["id"], {"floatingip": fip_update_dict}
)
utils.wait_for(server,
is_ready=self.check_ip_address(address),
is_ready=self.check_ip_address(fip_ip),
update_resource=utils.get_from_manager())
# Update server data
server.addresses = server.manager.get(server.id).addresses
@ -622,12 +651,34 @@ class NovaScenario(scenario.OpenStackScenario):
"""Remove floating IP from an instance
:param server: The :class:`Server` to add an IP to.
:param address: The ip address or FloatingIP to remove
:param address: The dict-like representation of FloatingIP to remove
"""
server.remove_floating_ip(address)
fip = address
if not isinstance(fip, dict):
LOG.warning(
"The argument 'address' of "
"NovaScenario._dissociate_floating_ip method accepts a "
"dict-like representation of floating ip. Transmitting a "
"string with just an IP is deprecated.")
with atomic.ActionTimer(self, "neutron.list_floating_ips"):
all_fips = self.clients("neutron").list_floatingips(
tenant_id=self.context["tenant"]["id"]
)
filtered_fip = [f for f in all_fips["floatingips"]
if f["floating_ip_address"] == address]
if not filtered_fip:
raise exceptions.NotFoundException(
"There is no floating ip with '%s' address." % address)
fip = filtered_fip[0]
self.clients("neutron").update_floatingip(
fip["id"], {"floatingip": {"port_id": None}}
)
# the first case: fip object is returned from network wrapper
# the second case: from neutronclient directly
fip_ip = fip.get("ip", fip.get("floating_ip_address", None))
utils.wait_for(
server,
is_ready=self.check_ip_address(address, must_exist=False),
is_ready=self.check_ip_address(fip_ip, must_exist=False),
update_resource=utils.get_from_manager()
)
# Update server data
@ -640,8 +691,8 @@ class NovaScenario(scenario.OpenStackScenario):
def _check_addr(resource):
for network, addr_list in resource.addresses.items():
for addr in addr_list:
if ip_to_check == addr["addr"]:
return must_exist
if ip_to_check == addr["addr"]:
return must_exist
return not must_exist
return _check_addr

View File

@ -170,7 +170,7 @@ class VMScenario(nova_utils.NovaScenario):
ext_network=floating_network,
tenant_id=server.tenant_id, fixed_ip=fixed_ip)
self._associate_floating_ip(server, fip["ip"], fixed_address=fixed_ip)
self._associate_floating_ip(server, fip, fixed_address=fixed_ip)
return fip
@ -179,7 +179,7 @@ class VMScenario(nova_utils.NovaScenario):
with logging.ExceptionLogger(
LOG, "Unable to delete IP: %s" % fip["ip"]):
if self.check_ip_address(fip["ip"])(server):
self._dissociate_floating_ip(server, fip["ip"])
self._dissociate_floating_ip(server, fip)
with atomic.ActionTimer(self, "neutron.delete_floating_ip"):
network_wrapper.wrap(self.clients,
self).delete_floating_ip(

View File

@ -38,12 +38,8 @@ class NovaScenarioTestCase(test.ScenarioTestCase):
self.floating_ip = mock.Mock()
self.image = mock.Mock()
self.context.update(
{"user": {"id": "fake_user_id", "credential": mock.MagicMock()}})
def _context_with_networks(self, networks):
retval = {"tenant": {"networks": networks}}
retval.update(self.context)
return retval
{"user": {"id": "fake_user_id", "credential": mock.MagicMock()},
"tenant": {"id": "fake_tenant"}})
def _context_with_secgroup(self, secgroup):
retval = {"user": {"secgroup": secgroup,
@ -140,9 +136,12 @@ class NovaScenarioTestCase(test.ScenarioTestCase):
"nova.boot_server")
def test__boot_server_with_network_exception(self):
self.context.update({"tenant": {"networks": None}})
self.clients("nova").servers.create.return_value = self.server
nova_scenario = utils.NovaScenario(
context=self._context_with_networks(None))
context=self.context)
self.assertRaises(TypeError, nova_scenario._boot_server,
"image_id", "flavor_id",
auto_assign_nic=True)
@ -506,32 +505,121 @@ class NovaScenarioTestCase(test.ScenarioTestCase):
def test__associate_floating_ip(self):
nova_scenario = utils.NovaScenario(context=self.context)
nova_scenario._associate_floating_ip(self.server, self.floating_ip)
self.server.add_floating_ip.assert_called_once_with(self.floating_ip,
fixed_address=None)
neutronclient = nova_scenario.clients("neutron")
neutronclient.list_ports.return_value = {"ports": [{"id": "p1"},
{"id": "p2"}]}
fip_ip = "172.168.0.1"
fip_id = "some"
# case #1- an object from neutronclient
floating_ip = {"floating_ip_address": fip_ip, "id": fip_id}
nova_scenario._associate_floating_ip(self.server, floating_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": "p1"}}
)
# case #2 - an object from network wrapper
neutronclient.update_floatingip.reset_mock()
floating_ip = {"ip": fip_ip, "id": fip_id}
nova_scenario._associate_floating_ip(self.server, floating_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": "p1"}}
)
# these should not be called in both cases
self.assertFalse(neutronclient.list_floatingips.called)
# it is an old behavior. let's check that it was not called
self.assertFalse(self.server.add_floating_ip.called)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
"nova.associate_floating_ip", count=2)
def test__associate_floating_ip_deprecated_behavior(self):
nova_scenario = utils.NovaScenario(context=self.context)
neutronclient = nova_scenario.clients("neutron")
neutronclient.list_ports.return_value = {"ports": [{"id": "p1"},
{"id": "p2"}]}
fip_id = "fip1"
fip_ip = "172.168.0.1"
neutronclient.list_floatingips.return_value = {
"floatingips": [
{"id": fip_id, "floating_ip_address": fip_ip},
{"id": "fip2", "floating_ip_address": "127.0.0.1"}]}
nova_scenario._associate_floating_ip(self.server, fip_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": "p1"}}
)
neutronclient.list_floatingips.assert_called_once_with(
tenant_id="fake_tenant")
# it is an old behavior. let's check that it was not called
self.assertFalse(self.server.add_floating_ip.called)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
"nova.associate_floating_ip")
def test__associate_floating_ip_with_no_atomic_action(self):
nova_scenario = utils.NovaScenario(context=self.context)
nova_scenario._associate_floating_ip(self.server, self.floating_ip)
self.server.add_floating_ip.assert_called_once_with(self.floating_ip,
fixed_address=None)
def test__dissociate_floating_ip(self):
nova_scenario = utils.NovaScenario(context=self.context)
nova_scenario._dissociate_floating_ip(self.server, self.floating_ip)
self.server.remove_floating_ip.assert_called_once_with(
self.floating_ip)
neutronclient = nova_scenario.clients("neutron")
fip_ip = "172.168.0.1"
fip_id = "some"
# case #1- an object from neutronclient
floating_ip = {"floating_ip_address": fip_ip, "id": fip_id}
nova_scenario._dissociate_floating_ip(self.server, floating_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": None}}
)
# case #2 - an object from network wrapper
neutronclient.update_floatingip.reset_mock()
floating_ip = {"ip": fip_ip, "id": fip_id}
nova_scenario._dissociate_floating_ip(self.server, floating_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": None}}
)
# these should not be called in both cases
self.assertFalse(neutronclient.list_floatingips.called)
# it is an old behavior. let's check that it was not called
self.assertFalse(self.server.add_floating_ip.called)
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
"nova.dissociate_floating_ip", count=2)
def test__disassociate_floating_ip_deprecated_behavior(self):
nova_scenario = utils.NovaScenario(context=self.context)
neutronclient = nova_scenario.clients("neutron")
fip_id = "fip1"
fip_ip = "172.168.0.1"
neutronclient.list_floatingips.return_value = {
"floatingips": [
{"id": fip_id, "floating_ip_address": fip_ip},
{"id": "fip2", "floating_ip_address": "127.0.0.1"}]}
nova_scenario._dissociate_floating_ip(self.server, fip_ip)
neutronclient.update_floatingip.assert_called_once_with(
fip_id, {"floatingip": {"port_id": None}}
)
neutronclient.list_floatingips.assert_called_once_with(
tenant_id="fake_tenant")
self._test_atomic_action_timer(nova_scenario.atomic_actions(),
"nova.dissociate_floating_ip")
def test__dissociate_floating_ip_with_no_atomic_action(self):
nova_scenario = utils.NovaScenario(context=self.context)
nova_scenario._dissociate_floating_ip(self.server, self.floating_ip)
self.server.remove_floating_ip.assert_called_once_with(
self.floating_ip)
def test__check_ip_address(self):
nova_scenario = utils.NovaScenario(context=self.context)
fake_server = fakes.FakeServerManager().create("test_server",

View File

@ -209,8 +209,8 @@ class VMScenarioTestCase(test.ScenarioTestCase):
scenario, server = self.get_scenario()
netwrap = mock_wrap.return_value
netwrap.create_floating_ip.return_value = {
"id": "foo_id", "ip": "foo_ip"}
fip = {"id": "foo_id", "ip": "foo_ip"}
netwrap.create_floating_ip.return_value = fip
scenario._attach_floating_ip(
server, floating_network="bar_network")
@ -221,7 +221,7 @@ class VMScenarioTestCase(test.ScenarioTestCase):
tenant_id="foo_tenant", fixed_ip="foo_ip")
scenario._associate_floating_ip.assert_called_once_with(
server, "foo_ip", fixed_address="foo_ip")
server, fip, fixed_address="foo_ip")
@mock.patch(VMTASKS_UTILS + ".network_wrapper.wrap")
def test__delete_floating_ip(self, mock_wrap):
@ -231,14 +231,14 @@ class VMScenarioTestCase(test.ScenarioTestCase):
scenario.check_ip_address = mock.Mock(return_value=_check_addr)
scenario._dissociate_floating_ip = mock.Mock()
scenario._delete_floating_ip(
server, fip={"id": "foo_id", "ip": "foo_ip"})
fip = {"id": "foo_id", "ip": "foo_ip"}
scenario._delete_floating_ip(server, fip=fip)
scenario.check_ip_address.assert_called_once_with(
"foo_ip")
_check_addr.assert_called_once_with(server)
scenario._dissociate_floating_ip.assert_called_once_with(
server, "foo_ip")
server, fip)
mock_wrap.assert_called_once_with(scenario.clients, scenario)
mock_wrap.return_value.delete_floating_ip.assert_called_once_with(
"foo_id", wait=True)