Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

virt_net: idempotency of create/stop actions #53276

Merged
merged 14 commits into from
Mar 28, 2019
Merged
61 changes: 32 additions & 29 deletions lib/ansible/modules/cloud/misc/virt_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,28 +200,16 @@ def __init__(self, uri, module):
self.conn = conn

def find_entry(self, entryid):
# entryid = -1 returns a list of everything
if entryid == -1: # Get active entries
names = self.conn.listNetworks() + self.conn.listDefinedNetworks()
return [self.conn.networkLookupByName(n) for n in names]

results = []

# Get active entries
for name in self.conn.listNetworks():
entry = self.conn.networkLookupByName(name)
results.append(entry)

# Get inactive entries
for name in self.conn.listDefinedNetworks():
entry = self.conn.networkLookupByName(name)
results.append(entry)

if entryid == -1:
return results

for entry in results:
if entry.name() == entryid:
return entry

raise EntryNotFound("network %s not found" % entryid)
try:
return self.conn.networkLookupByName(entryid)
except libvirt.libvirtError as e:
if e.get_error_code() == libvirt.VIR_ERR_NO_NETWORK:
raise EntryNotFound("network %s not found" % entryid)
raise

def create(self, entryid):
if not self.module.check_mode:
Expand Down Expand Up @@ -284,11 +272,18 @@ def destroy(self, entryid):
return self.module.exit_json(changed=True)

def undefine(self, entryid):
if not self.module.check_mode:
entry = None
try:
entry = self.find_entry(entryid)
found = True
except EntryNotFound:
found = False

if found:
return self.find_entry(entryid).undefine()
else:
if not self.find_entry(entryid):
return self.module.exit_json(changed=True)

if self.module.check_mode:
return self.module.exit_json(changed=found)

def get_status2(self, entry):
state = entry.isActive()
Expand Down Expand Up @@ -417,19 +412,27 @@ def set_autostart(self, entryid, state):
return self.conn.set_autostart(entryid, state)

def create(self, entryid):
return self.conn.create(entryid)
if self.conn.get_status(entryid) == "active":
return
try:
return self.conn.create(entryid)
except libvirt.libvirtError as e:
if e.get_error_code() == libvirt.VIR_ERR_NETWORK_EXIST:
return None
goneri marked this conversation as resolved.
Show resolved Hide resolved
raise

def modify(self, entryid, xml):
return self.conn.modify(entryid, xml)

def start(self, entryid):
return self.conn.create(entryid)
return self.create(entryid)

def stop(self, entryid):
return self.conn.destroy(entryid)
if self.conn.get_status(entryid) == "active":
return self.conn.destroy(entryid)

def destroy(self, entryid):
return self.conn.destroy(entryid)
return self.stop(entryid)

def undefine(self, entryid):
return self.conn.undefine(entryid)
Expand Down
5 changes: 5 additions & 0 deletions test/integration/targets/virt_net/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
shippable/posix/group1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this test works under docker without privileged mode, remove this and use unsupported.

Tests that require privileged mode under docker are not run on Shippable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks for the clarification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I gave you bad instructions.

I forgot that this would run on our RHEL VMs, so having it in shippable/posix/group1 is appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np, I will push an update. I understand I also need to remove unsupported.

skip/freebsd
skip/osx
needs/privileged
goneri marked this conversation as resolved.
Show resolved Hide resolved
destructive
9 changes: 9 additions & 0 deletions test/integration/targets/virt_net/files/foobar.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<network>
<name>foobar</name>
<forward mode='nat'/>
<ip address='192.168.125.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.125.2' end='192.168.125.254'/>
</dhcp>
</ip>
</network>
78 changes: 78 additions & 0 deletions test/integration/targets/virt_net/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
- include_vars: '{{ item }}'
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_version}}.yml"
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
- "{{ ansible_distribution }}.yml"
- "default.yml"

- block:
- name: Install libvirt packages
package:
name: "{{ virt_net_packages }}"

- name: Start libvirt
service:
name: libvirtd
state: started

- name: Define the foobar network
virt_net:
command: define
name: foobar
xml: '{{ lookup("file", "foobar.xml") }}'

- name: Define the foobar network (again)
virt_net:
command: define
name: foobar
xml: '{{ lookup("file", "foobar.xml") }}'
register: second_virt_net_define

- name: Start the default network
virt_net:
uri: qemu:///system
command: start
name: foobar

- name: Start the default network (again)
virt_net:
uri: qemu:///system
command: start
name: foobar
register: second_virt_net_start

- name: Destroy the foobar network
virt_net:
command: destroy
name: foobar

- name: Undefine the foobar network
virt_net:
command: undefine
name: foobar
register: second_virt_net_define

- name: Undefine the foobar network (again)
virt_net:
command: undefine
name: foobar
register: second_virt_net_undefine

- name: Ensure the second calls return "unchanged"
assert:
that:
- "second_virt_net_start is not changed"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can reduce test output by combining the two asserts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

- "second_virt_net_define is not changed"
- "second_virt_net_undefine is not changed"

always:
- name: Stop libvirt
service:
name: libvirtd
state: stopped

- name: Remove only the libvirt packages
package:
name: "{{ virt_net_packages|select('match', '.*libvirt.*')|list }}"
state: absent
goneri marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does removing the libvirt packages also remove the network configuration created earlier? If not, it should also be removed as part of the test -- which would also cover more module functionality in the tests as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service is stopped automatically when the packages are removed. But yes, it's a good idea to validate the removal of the network with virt_net. I will do that.

6 changes: 6 additions & 0 deletions test/integration/targets/virt_net/vars/Debian.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
virt_net_packages:
- libvirt-daemon
- libvirt-daemon-system
- python-libvirt
- python-lxml
6 changes: 6 additions & 0 deletions test/integration/targets/virt_net/vars/Fedora-29.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
virt_net_packages:
- libvirt
- libvirt-daemon
- python3-libvirt
- python3-lxml
6 changes: 6 additions & 0 deletions test/integration/targets/virt_net/vars/RedHat-7.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
virt_net_packages:
- libvirt
- libvirt-daemon
- libvirt-python
- python-lxml
6 changes: 6 additions & 0 deletions test/integration/targets/virt_net/vars/RedHat-8.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
virt_net_packages:
- libvirt
- libvirt-daemon
- python3-libvirt
- python3-lxml
5 changes: 5 additions & 0 deletions test/integration/targets/virt_net/vars/Ubuntu-16.04.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
virt_net_packages:
- libvirt-daemon
- python-libvirt
- python-lxml
5 changes: 5 additions & 0 deletions test/integration/targets/virt_net/vars/Ubuntu-18.04.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
virt_net_packages:
- libvirt-daemon
- python-libvirt
- python-lxml
6 changes: 6 additions & 0 deletions test/integration/targets/virt_net/vars/Ubuntu-18.10.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
virt_net_packages:
- libvirt-daemon
- libvirt-daemon-system
- python-libvirt
- python-lxml
5 changes: 5 additions & 0 deletions test/integration/targets/virt_net/vars/default.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
virt_net_packages:
- libvirt-daemon
- python-libvirt
- python-lxml
Empty file.
Empty file.
69 changes: 69 additions & 0 deletions test/units/modules/cloud/misc/virt_net/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import pytest

from ansible.modules.cloud.misc import virt_net

from units.compat import mock


virt_net.libvirt = None
virt_net.HAS_VIRT = True


class DummyNetwork():
def __init__(self, name, isActive=True):
self._name = name
self._isActive = isActive

def name(self):
return self._name

def isActive(self):
return self._isActive


class DummyLibvirtConn():
def __init__(self):
self._network = [
DummyNetwork("inactive_net", isActive=False),
DummyNetwork("active_net", isActive=True)]

def listNetworks(self):
return [i.name() for i in self._network]

def networkLookupByName(self, name):
for i in self._network:
if i.name() == name:
return i

def listDefinedNetworks(self):
return []


class DummyLibvirt():
VIR_ERR_NETWORK_EXIST = 'VIR_ERR_NETWORK_EXIST'

@classmethod
def open(cls, uri):
return DummyLibvirtConn()

class libvirtError(Exception):
def __init__(self):
self.error_code

def get_error_code(self):
return self.error_code


@pytest.fixture
def dummy_libvirt(monkeypatch):
monkeypatch.setattr(virt_net, 'libvirt', DummyLibvirt)
return DummyLibvirt


@pytest.fixture
def virt_net_obj(dummy_libvirt):
return virt_net.VirtNetwork('qemu:///nowhere', mock.MagicMock())
30 changes: 30 additions & 0 deletions test/units/modules/cloud/misc/virt_net/test_virt_net.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from units.compat import mock


def test_virt_net_create_already_active(virt_net_obj, dummy_libvirt):
virt_net_obj.conn.create = mock.Mock()
assert virt_net_obj.create("active_net") is None
virt_net_obj.conn.create.assert_not_called()


def test_virt_net_recreate(virt_net_obj, dummy_libvirt):
virt_net_obj.conn.create = mock.Mock()
dummy_libvirt.libvirtError.error_code = 'VIR_ERR_NETWORK_EXIST'
virt_net_obj.conn.create.side_effect = dummy_libvirt.libvirtError
assert virt_net_obj.create("active_net") is None


def test_virt_stop_ignore_inactive(virt_net_obj):
virt_net_obj.conn.destroy = mock.Mock()
virt_net_obj.stop('inactive_net')
virt_net_obj.conn.destroy.assert_not_called()


def test_virt_stop_active(virt_net_obj, monkeypatch):
virt_net_obj.conn.destroy = mock.Mock()
virt_net_obj.stop('active_net')
virt_net_obj.conn.destroy.assert_called_with('active_net')