File opensuse-3000-virt-defined-states-222.patch of Package salt

From e5d42c6313ba051f22f83cbde3da9410fd7fc3b9 Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cbosdonnat@suse.com>
Date: Fri, 13 Mar 2020 16:38:08 +0100
Subject: [PATCH] openSUSE-3000 virt-defined-states (#222)

* Create virt.pool_defined state out of virt.pool_running

Users may want to use states to ensure a virtual storage pool is defined
and not enforce it to be running. Extract the code that performs the
pool definition / update from virt.pool_running state into a
virt.pool_defined.

Obviously the virt.pool_running state calls the virt.pool_defined one.
In such a case no additionnal test is needed for virt.pool_defined since
this is already tested with virt.pool_running.

* Add virt.update test parameter

In order to allow running dry-runs of virt.update module add a test
parameter. This will later be used by the virt states.

* Extract virt.defined state from virt.running

In order to ensure a virtual guest is defined independently of its
status, extract the corresponding code from the virt.running state.

This commit also handles the __opts__['test'] for the running state.
Since the update call only performs changes if needed, deprecate the
update parameter.

* Extract virt.network_defined from virt.network_running

Just like domains and storage pools, users may want to ensure a network
is defined without influencing it's status. Extract the code from
network_running state defining the network into a network_defined state.

While at it, support __opt__['test'] == True in these states. Updating
the network definition in the pool_defined state will come in a future
PR.

* Fix virt.update to handle None mem and cpu

virt.running state now may call virt.update with None mem and cpu
parameters. This was not handled in _gen_xml(). Also add some more tests
cases matching this for virt.update.
---
 salt/modules/virt.py            |   16 +-
 salt/states/virt.py             |  673 +++++++++++++++-----
 tests/unit/modules/test_virt.py |   26 +
 tests/unit/states/test_virt.py  | 1346 ++++++++++++++++++++++++++++++++-------
 4 files changed, 1665 insertions(+), 396 deletions(-)

diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index 3889238ecd..f0820e8825 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -1783,6 +1783,7 @@ def update(name,
            graphics=None,
            live=True,
            boot=None,
+           test=False,
            **kwargs):
     '''
     Update the definition of an existing domain.
@@ -1835,6 +1836,10 @@ def update(name,
 
         .. versionadded:: 3000
 
+    :param test: run in dry-run mode if set to True
+
+        .. versionadded:: sodium
+
     :return:
 
         Returns a dictionary indicating the status of what has been done. It is structured in
@@ -1880,8 +1885,8 @@ def update(name,
         boot = _handle_remote_boot_params(boot)
 
     new_desc = ElementTree.fromstring(_gen_xml(name,
-                                               cpu,
-                                               mem,
+                                               cpu or 0,
+                                               mem or 0,
                                                all_disks,
                                                _get_merged_nics(hypervisor, nic_profile, interfaces),
                                                hypervisor,
@@ -1973,11 +1978,12 @@ def update(name,
         if changes['disk']:
             for idx, item in enumerate(changes['disk']['sorted']):
                 source_file = all_disks[idx]['source_file']
-                if item in changes['disk']['new'] and source_file and not os.path.isfile(source_file):
+                if item in changes['disk']['new'] and source_file and not os.path.isfile(source_file) and not test:
                     _qemu_image_create(all_disks[idx])
 
         try:
-            conn.defineXML(salt.utils.stringutils.to_str(ElementTree.tostring(desc)))
+            if not test:
+                conn.defineXML(salt.utils.stringutils.to_str(ElementTree.tostring(desc)))
             status['definition'] = True
         except libvirt.libvirtError as err:
             conn.close()
@@ -2010,7 +2016,7 @@ def update(name,
 
         for cmd in commands:
             try:
-                ret = getattr(domain, cmd['cmd'])(*cmd['args'])
+                ret = getattr(domain, cmd['cmd'])(*cmd['args']) if not test else 0
                 device_type = cmd['device']
                 if device_type in ['cpu', 'mem']:
                     status[device_type] = not bool(ret)
diff --git a/salt/states/virt.py b/salt/states/virt.py
index 55a9ad2616..819776d707 100644
--- a/salt/states/virt.py
+++ b/salt/states/virt.py
@@ -14,6 +14,7 @@ for the generation and signing of certificates for systems running libvirt:
 
 # Import Python libs
 from __future__ import absolute_import, print_function, unicode_literals
+import copy
 import fnmatch
 import os
 
@@ -245,6 +246,187 @@ def powered_off(name, connection=None, username=None, password=None):
                       connection=connection, username=username, password=password)
 
 
+def defined(name,
+            cpu=None,
+            mem=None,
+            vm_type=None,
+            disk_profile=None,
+            disks=None,
+            nic_profile=None,
+            interfaces=None,
+            graphics=None,
+            seed=True,
+            install=True,
+            pub_key=None,
+            priv_key=None,
+            connection=None,
+            username=None,
+            password=None,
+            os_type=None,
+            arch=None,
+            boot=None,
+            update=True):
+    '''
+    Starts an existing guest, or defines and starts a new VM with specified arguments.
+
+    .. versionadded:: sodium
+
+    :param name: name of the virtual machine to run
+    :param cpu: number of CPUs for the virtual machine to create
+    :param mem: amount of memory in MiB for the new virtual machine
+    :param vm_type: force virtual machine type for the new VM. The default value is taken from
+        the host capabilities. This could be useful for example to use ``'qemu'`` type instead
+        of the ``'kvm'`` one.
+    :param disk_profile:
+        Name of the disk profile to use for the new virtual machine
+    :param disks:
+        List of disk to create for the new virtual machine.
+        See :ref:`init-disk-def` for more details on the items on this list.
+    :param nic_profile:
+        Name of the network interfaces profile to use for the new virtual machine
+    :param interfaces:
+        List of network interfaces to create for the new virtual machine.
+        See :ref:`init-nic-def` for more details on the items on this list.
+    :param graphics:
+        Graphics device to create for the new virtual machine.
+        See :ref:`init-graphics-def` for more details on this dictionary
+    :param saltenv:
+        Fileserver environment (Default: ``'base'``).
+        See :mod:`cp module for more details <salt.modules.cp>`
+    :param seed: ``True`` to seed the disk image. Only used when the ``image`` parameter is provided.
+                 (Default: ``True``)
+    :param install: install salt minion if absent (Default: ``True``)
+    :param pub_key: public key to seed with (Default: ``None``)
+    :param priv_key: public key to seed with (Default: ``None``)
+    :param seed_cmd: Salt command to execute to seed the image. (Default: ``'seed.apply'``)
+    :param connection: libvirt connection URI, overriding defaults
+    :param username: username to connect with, overriding defaults
+    :param password: password to connect with, overriding defaults
+    :param os_type:
+        type of virtualization as found in the ``//os/type`` element of the libvirt definition.
+        The default value is taken from the host capabilities, with a preference for ``hvm``.
+        Only used when creating a new virtual machine.
+    :param arch:
+        architecture of the virtual machine. The default value is taken from the host capabilities,
+        but ``x86_64`` is prefed over ``i686``. Only used when creating a new virtual machine.
+
+    :param boot:
+        Specifies kernel for the virtual machine, as well as boot parameters
+        for the virtual machine. This is an optionl parameter, and all of the
+        keys are optional within the dictionary. If a remote path is provided
+        to kernel or initrd, salt will handle the downloading of the specified
+        remote fild, and will modify the XML accordingly.
+
+        .. code-block:: python
+
+            {
+                'kernel': '/root/f8-i386-vmlinuz',
+                'initrd': '/root/f8-i386-initrd',
+                'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/'
+            }
+
+    :param update: set to ``False`` to prevent updating a defined domain. (Default: ``True``)
+
+        .. deprecated:: sodium
+
+    .. rubric:: Example States
+
+    Make sure a virtual machine called ``domain_name`` is defined:
+
+    .. code-block:: yaml
+
+        domain_name:
+          virt.defined:
+            - cpu: 2
+            - mem: 2048
+            - disk_profile: prod
+            - disks:
+              - name: system
+                size: 8192
+                overlay_image: True
+                pool: default
+                image: /path/to/image.qcow2
+              - name: data
+                size: 16834
+            - nic_profile: prod
+            - interfaces:
+              - name: eth0
+                mac: 01:23:45:67:89:AB
+              - name: eth1
+                type: network
+                source: admin
+            - graphics:
+                type: spice
+                listen:
+                    type: address
+                    address: 192.168.0.125
+
+    '''
+
+    ret = {'name': name,
+           'changes': {},
+           'result': True if not __opts__['test'] else None,
+           'comment': ''
+           }
+
+    try:
+        if name in __salt__['virt.list_domains'](connection=connection, username=username, password=password):
+            status = {}
+            if update:
+                status = __salt__['virt.update'](name,
+                                                 cpu=cpu,
+                                                 mem=mem,
+                                                 disk_profile=disk_profile,
+                                                 disks=disks,
+                                                 nic_profile=nic_profile,
+                                                 interfaces=interfaces,
+                                                 graphics=graphics,
+                                                 live=True,
+                                                 connection=connection,
+                                                 username=username,
+                                                 password=password,
+                                                 boot=boot,
+                                                 test=__opts__['test'])
+            ret['changes'][name] = status
+            if not status.get('definition'):
+                ret['comment'] = 'Domain {0} unchanged'.format(name)
+                ret['result'] = True
+            elif status.get('errors'):
+                ret['comment'] = 'Domain {0} updated with live update(s) failures'.format(name)
+            else:
+                ret['comment'] = 'Domain {0} updated'.format(name)
+        else:
+            if not __opts__['test']:
+                __salt__['virt.init'](name,
+                                      cpu=cpu,
+                                      mem=mem,
+                                      os_type=os_type,
+                                      arch=arch,
+                                      hypervisor=vm_type,
+                                      disk=disk_profile,
+                                      disks=disks,
+                                      nic=nic_profile,
+                                      interfaces=interfaces,
+                                      graphics=graphics,
+                                      seed=seed,
+                                      install=install,
+                                      pub_key=pub_key,
+                                      priv_key=priv_key,
+                                      connection=connection,
+                                      username=username,
+                                      password=password,
+                                      boot=boot,
+                                      start=False)
+            ret['changes'][name] = {'definition': True}
+            ret['comment'] = 'Domain {0} defined'.format(name)
+    except libvirt.libvirtError as err:
+        # Something bad happened when defining / updating the VM, report it
+        ret['comment'] = six.text_type(err)
+        ret['result'] = False
+
+    return ret
+
+
 def running(name,
             cpu=None,
             mem=None,
@@ -326,9 +508,10 @@ def running(name,
     :param seed_cmd: Salt command to execute to seed the image. (Default: ``'seed.apply'``)
 
         .. versionadded:: 2019.2.0
-    :param update: set to ``True`` to update a defined module. (Default: ``False``)
+    :param update: set to ``True`` to update a defined domain. (Default: ``False``)
 
         .. versionadded:: 2019.2.0
+        .. deprecated:: sodium
     :param connection: libvirt connection URI, overriding defaults
 
         .. versionadded:: 2019.2.0
@@ -424,93 +607,74 @@ def running(name,
                     address: 192.168.0.125
 
     '''
-
-    ret = {'name': name,
-           'changes': {},
-           'result': True,
-           'comment': '{0} is running'.format(name)
-           }
-
-    try:
+    merged_disks = disks
+    if image:
+        default_disks = [{'system': {}}]
+        disknames = ['system']
+        if disk_profile:
+            disklist = copy.deepcopy(
+                    __salt__['config.get']('virt:disk', {}).get(disk_profile, default_disks))
+            disknames = disklist.keys()
+        disk = {'name': disknames[0], 'image': image}
+        if merged_disks:
+            first_disk = [d for d in merged_disks if d.get('name') == disknames[0]]
+            if first_disk and 'image' not in first_disk[0]:
+                first_disk[0]['image'] = image
+            else:
+                merged_disks.append(disk)
+        else:
+            merged_disks = [disk]
+        salt.utils.versions.warn_until(
+            'Sodium',
+            '\'image\' parameter has been deprecated. Rather use the \'disks\' parameter '
+            'to override or define the image. \'image\' will be removed in {version}.'
+        )
+
+    if not update:
+        salt.utils.versions.warn_until('Magnesium',
+                '\'update\' parameter has been deprecated. Future behavior will be the one of update=True'
+                'It will be removed in {version}.')
+    ret = defined(name,
+                  cpu=cpu,
+                  mem=mem,
+                  vm_type=vm_type,
+                  disk_profile=disk_profile,
+                  disks=merged_disks,
+                  nic_profile=nic_profile,
+                  interfaces=interfaces,
+                  graphics=graphics,
+                  seed=seed,
+                  install=install,
+                  pub_key=pub_key,
+                  priv_key=priv_key,
+                  os_type=os_type,
+                  arch=arch,
+                  boot=boot,
+                  update=update,
+                  connection=connection,
+                  username=username,
+                  password=password)
+
+    result = True if not __opts__['test'] else None
+    if ret['result'] is None or ret['result']:
+        changed = ret['changes'][name].get('definition', False)
         try:
             domain_state = __salt__['virt.vm_state'](name)
             if domain_state.get(name) != 'running':
-                action_msg = 'started'
-                if update:
-                    status = __salt__['virt.update'](name,
-                                                     cpu=cpu,
-                                                     mem=mem,
-                                                     disk_profile=disk_profile,
-                                                     disks=disks,
-                                                     nic_profile=nic_profile,
-                                                     interfaces=interfaces,
-                                                     graphics=graphics,
-                                                     live=False,
-                                                     connection=connection,
-                                                     username=username,
-                                                     password=password,
-                                                     boot=boot)
-                    if status['definition']:
-                        action_msg = 'updated and started'
-                __salt__['virt.start'](name)
-                ret['changes'][name] = 'Domain {0}'.format(action_msg)
-                ret['comment'] = 'Domain {0} {1}'.format(name, action_msg)
-            else:
-                if update:
-                    status = __salt__['virt.update'](name,
-                                                     cpu=cpu,
-                                                     mem=mem,
-                                                     disk_profile=disk_profile,
-                                                     disks=disks,
-                                                     nic_profile=nic_profile,
-                                                     interfaces=interfaces,
-                                                     graphics=graphics,
-                                                     connection=connection,
-                                                     username=username,
-                                                     password=password,
-                                                     boot=boot)
-                    ret['changes'][name] = status
-                    if status.get('errors', None):
-                        ret['comment'] = 'Domain {0} updated, but some live update(s) failed'.format(name)
-                    elif not status['definition']:
-                        ret['comment'] = 'Domain {0} exists and is running'.format(name)
-                    else:
-                        ret['comment'] = 'Domain {0} updated, restart to fully apply the changes'.format(name)
-                else:
-                    ret['comment'] = 'Domain {0} exists and is running'.format(name)
-        except CommandExecutionError:
-            if image:
-                salt.utils.versions.warn_until(
-                    'Sodium',
-                    '\'image\' parameter has been deprecated. Rather use the \'disks\' parameter '
-                    'to override or define the image. \'image\' will be removed in {version}.'
-                )
-            __salt__['virt.init'](name,
-                                  cpu=cpu,
-                                  mem=mem,
-                                  os_type=os_type,
-                                  arch=arch,
-                                  image=image,
-                                  hypervisor=vm_type,
-                                  disk=disk_profile,
-                                  disks=disks,
-                                  nic=nic_profile,
-                                  interfaces=interfaces,
-                                  graphics=graphics,
-                                  seed=seed,
-                                  install=install,
-                                  pub_key=pub_key,
-                                  priv_key=priv_key,
-                                  connection=connection,
-                                  username=username,
-                                  password=password,
-                                  boot=boot)
-            ret['changes'][name] = 'Domain defined and started'
-            ret['comment'] = 'Domain {0} defined and started'.format(name)
-    except libvirt.libvirtError as err:
-        # Something bad happened when starting / updating the VM, report it
-        ret['comment'] = six.text_type(err)
-        ret['result'] = False
+                if not __opts__['test']:
+                    __salt__['virt.start'](name, connection=connection, username=username, password=password)
+                comment = 'Domain {} started'.format(name)
+                if not ret['comment'].endswith('unchanged'):
+                    comment = '{} and started'.format(ret['comment'])
+                ret['comment'] = comment
+                ret['changes'][name]['started'] = True
+            elif not changed:
+                ret['comment'] = 'Domain {0} exists and is running'.format(name)
+
+        except libvirt.libvirtError as err:
+            # Something bad happened when starting / updating the VM, report it
+            ret['comment'] = six.text_type(err)
+            ret['result'] = False
 
     return ret
 
@@ -670,6 +834,106 @@ def reverted(name, snapshot=None, cleanup=False):  # pylint: disable=redefined-o
     return ret
 
 
+def network_defined(name,
+                    bridge,
+                    forward,
+                    vport=None,
+                    tag=None,
+                    ipv4_config=None,
+                    ipv6_config=None,
+                    autostart=True,
+                    connection=None,
+                    username=None,
+                    password=None):
+    '''
+    Defines a new network with specified arguments.
+
+    :param bridge: Bridge name
+    :param forward: Forward mode(bridge, router, nat)
+    :param vport: Virtualport type (Default: ``'None'``)
+    :param tag: Vlan tag (Default: ``'None'``)
+    :param ipv4_config:
+        IPv4 network configuration. See the :py:func`virt.network_define
+        <salt.modules.virt.network_define>` function corresponding parameter documentation
+        for more details on this dictionary.
+        (Default: None).
+    :param ipv6_config:
+        IPv6 network configuration. See the :py:func`virt.network_define
+        <salt.modules.virt.network_define>` function corresponding parameter documentation
+        for more details on this dictionary.
+        (Default: None).
+    :param autostart: Network autostart (default ``'True'``)
+    :param connection: libvirt connection URI, overriding defaults
+    :param username: username to connect with, overriding defaults
+    :param password: password to connect with, overriding defaults
+
+    .. versionadded:: sodium
+
+    .. code-block:: yaml
+
+        network_name:
+          virt.network_defined
+
+    .. code-block:: yaml
+
+        network_name:
+          virt.network_defined:
+            - bridge: main
+            - forward: bridge
+            - vport: openvswitch
+            - tag: 180
+            - autostart: True
+
+    .. code-block:: yaml
+
+        network_name:
+          virt.network_defined:
+            - bridge: natted
+            - forward: nat
+            - ipv4_config:
+                cidr: 192.168.42.0/24
+                dhcp_ranges:
+                  - start: 192.168.42.10
+                    end: 192.168.42.25
+                  - start: 192.168.42.100
+                    end: 192.168.42.150
+            - autostart: True
+
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True if not __opts__['test'] else None,
+           'comment': ''
+           }
+
+    try:
+        info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password)
+        if info and info[name]:
+            ret['comment'] = 'Network {0} exists'.format(name)
+            ret['result'] = True
+        else:
+            if not __opts__['test']:
+                __salt__['virt.network_define'](name,
+                                                bridge,
+                                                forward,
+                                                vport=vport,
+                                                tag=tag,
+                                                ipv4_config=ipv4_config,
+                                                ipv6_config=ipv6_config,
+                                                autostart=autostart,
+                                                start=False,
+                                                connection=connection,
+                                                username=username,
+                                                password=password)
+            ret['changes'][name] = 'Network defined'
+            ret['comment'] = 'Network {0} defined'.format(name)
+    except libvirt.libvirtError as err:
+        ret['result'] = False
+        ret['comment'] = err.get_error_message()
+
+    return ret
+
+
 def network_running(name,
                     bridge,
                     forward,
@@ -715,13 +979,13 @@ def network_running(name,
 
     .. code-block:: yaml
 
-        domain_name:
-          virt.network_define
+        network_name:
+          virt.network_running
 
     .. code-block:: yaml
 
         network_name:
-          virt.network_define:
+          virt.network_running:
             - bridge: main
             - forward: bridge
             - vport: openvswitch
@@ -731,7 +995,7 @@ def network_running(name,
     .. code-block:: yaml
 
         network_name:
-          virt.network_define:
+          virt.network_running:
             - bridge: natted
             - forward: nat
             - ipv4_config:
@@ -744,44 +1008,46 @@ def network_running(name,
             - autostart: True
 
     '''
-    ret = {'name': name,
-           'changes': {},
-           'result': True,
-           'comment': ''
-           }
-
-    try:
-        info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password)
-        if info:
-            if info[name]['active']:
-                ret['comment'] = 'Network {0} exists and is running'.format(name)
+    ret = network_defined(name,
+                          bridge,
+                          forward,
+                          vport=vport,
+                          tag=tag,
+                          ipv4_config=ipv4_config,
+                          ipv6_config=ipv6_config,
+                          autostart=autostart,
+                          connection=connection,
+                          username=username,
+                          password=password)
+
+    defined = name in ret['changes'] and ret['changes'][name].startswith('Network defined')
+
+    result = True if not __opts__['test'] else None
+    if ret['result'] is None or ret['result']:
+        try:
+            info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password)
+            # In the corner case where test=True and the network wasn't defined
+            # we may not get the network in the info dict and that is normal.
+            if info.get(name, {}).get('active', False):
+                ret['comment'] = '{} and is running'.format(ret['comment'])
             else:
-                __salt__['virt.network_start'](name, connection=connection, username=username, password=password)
-                ret['changes'][name] = 'Network started'
-                ret['comment'] = 'Network {0} started'.format(name)
-        else:
-            __salt__['virt.network_define'](name,
-                                            bridge,
-                                            forward,
-                                            vport=vport,
-                                            tag=tag,
-                                            ipv4_config=ipv4_config,
-                                            ipv6_config=ipv6_config,
-                                            autostart=autostart,
-                                            start=True,
-                                            connection=connection,
-                                            username=username,
-                                            password=password)
-            ret['changes'][name] = 'Network defined and started'
-            ret['comment'] = 'Network {0} defined and started'.format(name)
-    except libvirt.libvirtError as err:
-        ret['result'] = False
-        ret['comment'] = err.get_error_message()
+                if not __opts__['test']:
+                    __salt__['virt.network_start'](name, connection=connection, username=username, password=password)
+                change = 'Network started'
+                if name in ret['changes']:
+                    change = '{} and started'.format(ret['changes'][name])
+                ret['changes'][name] = change
+                ret['comment'] = '{} and started'.format(ret['comment'])
+            ret['result'] = result
+
+        except libvirt.libvirtError as err:
+            ret['result'] = False
+            ret['comment'] = err.get_error_message()
 
     return ret
 
 
-def pool_running(name,
+def pool_defined(name,
                  ptype=None,
                  target=None,
                  permissions=None,
@@ -792,9 +1058,9 @@ def pool_running(name,
                  username=None,
                  password=None):
     '''
-    Defines and starts a new pool with specified arguments.
+    Defines a new pool with specified arguments.
 
-    .. versionadded:: 2019.2.0
+    .. versionadded:: sodium
 
     :param ptype: libvirt pool type
     :param target: full path to the target device or folder. (Default: ``None``)
@@ -816,12 +1082,7 @@ def pool_running(name,
     .. code-block:: yaml
 
         pool_name:
-          virt.pool_define
-
-    .. code-block:: yaml
-
-        pool_name:
-          virt.pool_define:
+          virt.pool_defined:
             - ptype: netfs
             - target: /mnt/cifs
             - permissions:
@@ -884,29 +1145,19 @@ def pool_running(name,
                                                  username=username,
                                                  password=password)
 
-                action = "started"
-                if info[name]['state'] == 'running':
-                    action = "restarted"
+                action = ''
+                if info[name]['state'] != 'running':
                     if not __opts__['test']:
-                        __salt__['virt.pool_stop'](name, connection=connection, username=username, password=password)
-
-                if not __opts__['test']:
-                    __salt__['virt.pool_build'](name, connection=connection, username=username, password=password)
-                    __salt__['virt.pool_start'](name, connection=connection, username=username, password=password)
+                        __salt__['virt.pool_build'](name, connection=connection, username=username, password=password)
+                    action = ', built'
 
-                autostart_str = ', autostart flag changed' if needs_autostart else ''
-                ret['changes'][name] = 'Pool updated, built{0} and {1}'.format(autostart_str, action)
-                ret['comment'] = 'Pool {0} updated, built{1} and {2}'.format(name, autostart_str, action)
+                action = '{}, autostart flag changed'.format(action) if needs_autostart else action
+                ret['changes'][name] = 'Pool updated{0}'.format(action)
+                ret['comment'] = 'Pool {0} updated{1}'.format(name, action)
 
             else:
-                if info[name]['state'] == 'running':
-                    ret['comment'] = 'Pool {0} unchanged and is running'.format(name)
-                    ret['result'] = True
-                else:
-                    ret['changes'][name] = 'Pool started'
-                    ret['comment'] = 'Pool {0} started'.format(name)
-                    if not __opts__['test']:
-                        __salt__['virt.pool_start'](name, connection=connection, username=username, password=password)
+                ret['comment'] = 'Pool {0} unchanged'.format(name)
+                ret['result'] = True
         else:
             needs_autostart = autostart
             if not __opts__['test']:
@@ -932,17 +1183,12 @@ def pool_running(name,
                                             connection=connection,
                                             username=username,
                                             password=password)
-
-                __salt__['virt.pool_start'](name,
-                                            connection=connection,
-                                            username=username,
-                                            password=password)
             if needs_autostart:
-                ret['changes'][name] = 'Pool defined, started and marked for autostart'
-                ret['comment'] = 'Pool {0} defined, started and marked for autostart'.format(name)
+                ret['changes'][name] = 'Pool defined, marked for autostart'
+                ret['comment'] = 'Pool {0} defined, marked for autostart'.format(name)
             else:
-                ret['changes'][name] = 'Pool defined and started'
-                ret['comment'] = 'Pool {0} defined and started'.format(name)
+                ret['changes'][name] = 'Pool defined'
+                ret['comment'] = 'Pool {0} defined'.format(name)
 
         if needs_autostart:
             if not __opts__['test']:
@@ -958,6 +1204,117 @@ def pool_running(name,
     return ret
 
 
+def pool_running(name,
+                 ptype=None,
+                 target=None,
+                 permissions=None,
+                 source=None,
+                 transient=False,
+                 autostart=True,
+                 connection=None,
+                 username=None,
+                 password=None):
+    '''
+    Defines and starts a new pool with specified arguments.
+
+    .. versionadded:: 2019.2.0
+
+    :param ptype: libvirt pool type
+    :param target: full path to the target device or folder. (Default: ``None``)
+    :param permissions:
+        target permissions. See :ref:`pool-define-permissions` for more details on this structure.
+    :param source:
+        dictionary containing keys matching the ``source_*`` parameters in function
+        :func:`salt.modules.virt.pool_define`.
+    :param transient:
+        when set to ``True``, the pool will be automatically undefined after being stopped. (Default: ``False``)
+    :param autostart:
+        Whether to start the pool when booting the host. (Default: ``True``)
+    :param start:
+        When ``True``, define and start the pool, otherwise the pool will be left stopped.
+    :param connection: libvirt connection URI, overriding defaults
+    :param username: username to connect with, overriding defaults
+    :param password: password to connect with, overriding defaults
+
+    .. code-block:: yaml
+
+        pool_name:
+          virt.pool_running
+
+    .. code-block:: yaml
+
+        pool_name:
+          virt.pool_running:
+            - ptype: netfs
+            - target: /mnt/cifs
+            - permissions:
+                - mode: 0770
+                - owner: 1000
+                - group: 100
+            - source:
+                dir: samba_share
+                hosts:
+                  - one.example.com
+                  - two.example.com
+                format: cifs
+            - autostart: True
+
+    '''
+    ret = pool_defined(name,
+                       ptype=ptype,
+                       target=target,
+                       permissions=permissions,
+                       source=source,
+                       transient=transient,
+                       autostart=autostart,
+                       connection=connection,
+                       username=username,
+                       password=password)
+    defined = name in ret['changes'] and ret['changes'][name].startswith('Pool defined')
+    updated = name in ret['changes'] and ret['changes'][name].startswith('Pool updated')
+
+    result = True if not __opts__['test'] else None
+    if ret['result'] is None or ret['result']:
+        try:
+            info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password)
+            action = 'started'
+            # In the corner case where test=True and the pool wasn't defined
+            # we may get not get our pool in the info dict and that is normal.
+            is_running = info.get(name, {}).get('state', 'stopped') == 'running'
+            if is_running:
+                if updated:
+                    action = 'built, restarted'
+                    if not __opts__['test']:
+                        __salt__['virt.pool_stop'](name, connection=connection, username=username, password=password)
+                    if not __opts__['test']:
+                        __salt__['virt.pool_build'](name, connection=connection, username=username, password=password)
+                else:
+                    action = 'already running'
+                    result = True
+
+            if not is_running or updated or defined:
+                if not __opts__['test']:
+                    __salt__['virt.pool_start'](name, connection=connection, username=username, password=password)
+
+            comment = 'Pool {0}'.format(name)
+            change = 'Pool'
+            if name in ret['changes']:
+                comment = '{0},'.format(ret['comment'])
+                change = '{0},'.format(ret['changes'][name])
+
+            if action != 'already running':
+                ret['changes'][name] = '{0} {1}'.format(change, action)
+
+            ret['comment'] = '{0} {1}'.format(comment, action)
+            ret['result'] = result
+
+        except libvirt.libvirtError as err:
+            ret['comment'] = err.get_error_message()
+            ret['result'] = False
+
+    return ret
+
+
 def pool_deleted(name,
                  purge=False,
                  connection=None,
diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py
index d762dcc479..8690154662 100644
--- a/tests/unit/modules/test_virt.py
+++ b/tests/unit/modules/test_virt.py
@@ -1272,6 +1272,32 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
         define_mock = MagicMock(return_value=True)
         self.mock_conn.defineXML = define_mock
 
+        # No parameter passed case
+        self.assertEqual({
+                'definition': False,
+                'disk': {'attached': [], 'detached': []},
+                'interface': {'attached': [], 'detached': []}
+            }, virt.update('my vm'))
+
+        # Same parameters passed than in default virt.defined state case
+        self.assertEqual({
+                'definition': False,
+                'disk': {'attached': [], 'detached': []},
+                'interface': {'attached': [], 'detached': []}
+            }, virt.update('my vm',
+                           cpu=None,
+                           mem=None,
+                           disk_profile=None,
+                           disks=None,
+                           nic_profile=None,
+                           interfaces=None,
+                           graphics=None,
+                           live=True,
+                           connection=None,
+                           username=None,
+                           password=None,
+                           boot=None))
+
         # Update vcpus case
         setvcpus_mock = MagicMock(return_value=0)
         domain_mock.setVcpusFlags = setvcpus_mock
diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py
index c50c04b8ab..6727704494 100644
--- a/tests/unit/states/test_virt.py
+++ b/tests/unit/states/test_virt.py
@@ -217,6 +217,243 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                                                        organization='SaltStack',
                                                        expiration_days=700), ret)
 
+    def test_defined(self):
+        '''
+        defined state test cases.
+        '''
+        ret = {'name': 'myvm',
+               'changes': {},
+               'result': True,
+               'comment': 'myvm is running'}
+        with patch.dict(virt.__opts__, {'test': False}):
+            # no change test
+            init_mock = MagicMock(return_value=True)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': MagicMock(return_value={'definition': False}),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': False}},
+                            'comment': 'Domain myvm unchanged'})
+                self.assertDictEqual(virt.defined('myvm'), ret)
+
+            # Test defining a guest with connection details
+            init_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=[]),
+                        'virt.init': init_mock,
+                        'virt.update': MagicMock(side_effect=CommandExecutionError('not found')),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True}},
+                            'comment': 'Domain myvm defined'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                            'image': '/path/to/image.qcow2'
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                ifaces = [{
+                             'name': 'eth0',
+                             'mac': '01:23:45:67:89:AB'
+                          },
+                          {
+                             'name': 'eth1',
+                             'type': 'network',
+                             'source': 'admin'
+                          }]
+                graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
+                self.assertDictEqual(virt.defined('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  os_type='linux',
+                                                  arch='i686',
+                                                  vm_type='qemu',
+                                                  disk_profile='prod',
+                                                  disks=disks,
+                                                  nic_profile='prod',
+                                                  interfaces=ifaces,
+                                                  graphics=graphics,
+                                                  seed=False,
+                                                  install=False,
+                                                  pub_key='/path/to/key.pub',
+                                                  priv_key='/path/to/key',
+                                                  connection='someconnection',
+                                                  username='libvirtuser',
+                                                  password='supersecret'), ret)
+                init_mock.assert_called_with('myvm',
+                                             cpu=2,
+                                             mem=2048,
+                                             os_type='linux',
+                                             arch='i686',
+                                             disk='prod',
+                                             disks=disks,
+                                             nic='prod',
+                                             interfaces=ifaces,
+                                             graphics=graphics,
+                                             hypervisor='qemu',
+                                             seed=False,
+                                             boot=None,
+                                             install=False,
+                                             start=False,
+                                             pub_key='/path/to/key.pub',
+                                             priv_key='/path/to/key',
+                                             connection='someconnection',
+                                             username='libvirtuser',
+                                             password='supersecret')
+
+            # Working update case when running
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.defined('myvm', cpu=2), ret)
+
+            # Working update case when running with boot params
+            boot = {
+                'kernel': '/root/f8-i386-vmlinuz',
+                'initrd': '/root/f8-i386-initrd',
+                'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/'
+            }
+
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.defined('myvm', boot=boot), ret)
+
+            # Working update case when stopped
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': MagicMock(return_value={'definition': True})
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.defined('myvm', cpu=2), ret)
+
+            # Failed live update case
+            update_mock = MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': update_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
+                            'result': True,
+                            'comment': 'Domain myvm updated with live update(s) failures'})
+                self.assertDictEqual(virt.defined('myvm', cpu=2), ret)
+                update_mock.assert_called_with('myvm', cpu=2, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=False)
+
+            # Failed definition update case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')])
+                    }):
+                ret.update({'changes': {},
+                            'result': False,
+                            'comment': 'error message'})
+                self.assertDictEqual(virt.defined('myvm', cpu=2), ret)
+
+        # Test dry-run mode
+        with patch.dict(virt.__opts__, {'test': True}):
+            # Guest defined case
+            init_mock = MagicMock(return_value=True)
+            update_mock = MagicMock(side_effect=CommandExecutionError('not found'))
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=[]),
+                        'virt.init': init_mock,
+                        'virt.update': update_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True}},
+                            'result': None,
+                            'comment': 'Domain myvm defined'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                            'image': '/path/to/image.qcow2'
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                ifaces = [{
+                             'name': 'eth0',
+                             'mac': '01:23:45:67:89:AB'
+                          },
+                          {
+                             'name': 'eth1',
+                             'type': 'network',
+                             'source': 'admin'
+                          }]
+                graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
+                self.assertDictEqual(virt.defined('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  os_type='linux',
+                                                  arch='i686',
+                                                  vm_type='qemu',
+                                                  disk_profile='prod',
+                                                  disks=disks,
+                                                  nic_profile='prod',
+                                                  interfaces=ifaces,
+                                                  graphics=graphics,
+                                                  seed=False,
+                                                  install=False,
+                                                  pub_key='/path/to/key.pub',
+                                                  priv_key='/path/to/key',
+                                                  connection='someconnection',
+                                                  username='libvirtuser',
+                                                  password='supersecret'), ret)
+                init_mock.assert_not_called()
+                update_mock.assert_not_called()
+
+            # Guest update case
+            update_mock = MagicMock(return_value={'definition': True})
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': update_mock
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True}},
+                            'result': None,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.defined('myvm', cpu=2), ret)
+                update_mock.assert_called_with('myvm', cpu=2, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=True)
+
+            # No changes case
+            update_mock = MagicMock(return_value={'definition': False})
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                        'virt.update': update_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': False}},
+                            'result': True,
+                            'comment': 'Domain myvm unchanged'})
+                self.assertDictEqual(virt.defined('myvm'), ret)
+                update_mock.assert_called_with('myvm', cpu=None, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=True)
+
     def test_running(self):
         '''
         running state test cases.
@@ -225,163 +462,369 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                'changes': {},
                'result': True,
                'comment': 'myvm is running'}
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
-                    'virt.start': MagicMock(return_value=0),
-                }):
-            ret.update({'changes': {'myvm': 'Domain started'},
-                        'comment': 'Domain myvm started'})
-            self.assertDictEqual(virt.running('myvm'), ret)
-
-        init_mock = MagicMock(return_value=True)
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
-                    'virt.init': init_mock,
-                    'virt.start': MagicMock(return_value=0)
-                }):
-            ret.update({'changes': {'myvm': 'Domain defined and started'},
-                        'comment': 'Domain myvm defined and started'})
-            self.assertDictEqual(virt.running('myvm',
-                                              cpu=2,
-                                              mem=2048,
-                                              image='/path/to/img.qcow2'), ret)
-            init_mock.assert_called_with('myvm', cpu=2, mem=2048, image='/path/to/img.qcow2',
-                                         os_type=None, arch=None, boot=None,
-                                         disk=None, disks=None, nic=None, interfaces=None,
-                                         graphics=None, hypervisor=None,
-                                         seed=True, install=True, pub_key=None, priv_key=None,
-                                         connection=None, username=None, password=None)
+        with patch.dict(virt.__opts__, {'test': False}):
+            # Test starting an existing guest without changing it
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.start': MagicMock(return_value=0),
+                        'virt.update': MagicMock(return_value={'definition': False}),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {'started': True}},
+                            'comment': 'Domain myvm started'})
+                self.assertDictEqual(virt.running('myvm'), ret)
 
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
-                    'virt.init': init_mock,
-                    'virt.start': MagicMock(return_value=0)
-                }):
-            ret.update({'changes': {'myvm': 'Domain defined and started'},
-                        'comment': 'Domain myvm defined and started'})
-            disks = [{
-                        'name': 'system',
-                        'size': 8192,
-                        'overlay_image': True,
-                        'pool': 'default',
-                        'image': '/path/to/image.qcow2'
-                     },
-                     {
-                        'name': 'data',
-                        'size': 16834
-                     }]
-            ifaces = [{
-                         'name': 'eth0',
-                         'mac': '01:23:45:67:89:AB'
-                      },
-                      {
-                         'name': 'eth1',
-                         'type': 'network',
-                         'source': 'admin'
-                      }]
-            graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
-            self.assertDictEqual(virt.running('myvm',
-                                              cpu=2,
-                                              mem=2048,
-                                              os_type='linux',
-                                              arch='i686',
-                                              vm_type='qemu',
-                                              disk_profile='prod',
-                                              disks=disks,
-                                              nic_profile='prod',
-                                              interfaces=ifaces,
-                                              graphics=graphics,
-                                              seed=False,
-                                              install=False,
-                                              pub_key='/path/to/key.pub',
-                                              priv_key='/path/to/key',
+            # Test defining and starting a guest the old way
+            init_mock = MagicMock(return_value=True)
+            start_mock = MagicMock(return_value=0)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.init': init_mock,
+                        'virt.start': start_mock,
+                        'virt.list_domains': MagicMock(return_value=[]),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'comment': 'Domain myvm defined and started'})
+                self.assertDictEqual(virt.running('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  image='/path/to/img.qcow2'), ret)
+                init_mock.assert_called_with('myvm', cpu=2, mem=2048,
+                                             os_type=None, arch=None, boot=None,
+                                             disk=None, disks=[{'name': 'system', 'image': '/path/to/img.qcow2'}], nic=None, interfaces=None,
+                                             graphics=None, hypervisor=None, start=False,
+                                             seed=True, install=True, pub_key=None, priv_key=None,
+                                             connection=None, username=None, password=None,)
+                start_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+            # Test image parameter with disks with defined image
+            init_mock = MagicMock(return_value=True)
+            start_mock = MagicMock(return_value=0)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.init': init_mock,
+                        'virt.start': start_mock,
+                        'virt.list_domains': MagicMock(return_value=[]),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'comment': 'Domain myvm defined and started'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                            'image': '/path/to/image.qcow2'
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                self.assertDictEqual(virt.running('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  disks=disks,
+                                                  image='/path/to/img.qcow2'), ret)
+                init_mock.assert_called_with('myvm', cpu=2, mem=2048,
+                                             os_type=None, arch=None, boot=None,
+                                             disk=None, disks=disks, nic=None, interfaces=None,
+                                             graphics=None, hypervisor=None, start=False,
+                                             seed=True, install=True, pub_key=None, priv_key=None,
+                                             connection=None, username=None, password=None,)
+                start_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+            # Test image parameter with disks without defined image
+            init_mock = MagicMock(return_value=True)
+            start_mock = MagicMock(return_value=0)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.init': init_mock,
+                        'virt.start': start_mock,
+                        'virt.list_domains': MagicMock(return_value=[]),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'comment': 'Domain myvm defined and started'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                self.assertDictEqual(virt.running('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  disks=disks,
+                                                  image='/path/to/img.qcow2'), ret)
+                init_mock.assert_called_with('myvm', cpu=2, mem=2048,
+                                             os_type=None, arch=None, boot=None,
+                                             disk=None,
+                                             disks=[{
+                                                'name': 'system',
+                                                'size': 8192,
+                                                'overlay_image': True,
+                                                'pool': 'default',
+                                                'image': '/path/to/img.qcow2',
+                                             },
+                                             {
+                                                'name': 'data',
+                                                'size': 16834
+                                             }],
+                                             nic=None, interfaces=None,
+                                             graphics=None, hypervisor=None, start=False,
+                                             seed=True, install=True, pub_key=None, priv_key=None,
+                                             connection=None, username=None, password=None,)
+                start_mock.assert_called_with('myvm', connection=None, username=None, password=None)
+
+            # Test defining and starting a guest the new way with connection details
+            init_mock.reset_mock()
+            start_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.init': init_mock,
+                        'virt.start': start_mock,
+                        'virt.list_domains': MagicMock(return_value=[]),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'comment': 'Domain myvm defined and started'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                            'image': '/path/to/image.qcow2'
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                ifaces = [{
+                             'name': 'eth0',
+                             'mac': '01:23:45:67:89:AB'
+                          },
+                          {
+                             'name': 'eth1',
+                             'type': 'network',
+                             'source': 'admin'
+                          }]
+                graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
+                self.assertDictEqual(virt.running('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  os_type='linux',
+                                                  arch='i686',
+                                                  vm_type='qemu',
+                                                  disk_profile='prod',
+                                                  disks=disks,
+                                                  nic_profile='prod',
+                                                  interfaces=ifaces,
+                                                  graphics=graphics,
+                                                  seed=False,
+                                                  install=False,
+                                                  pub_key='/path/to/key.pub',
+                                                  priv_key='/path/to/key',
+                                                  connection='someconnection',
+                                                  username='libvirtuser',
+                                                  password='supersecret'), ret)
+                init_mock.assert_called_with('myvm',
+                                             cpu=2,
+                                             mem=2048,
+                                             os_type='linux',
+                                             arch='i686',
+                                             disk='prod',
+                                             disks=disks,
+                                             nic='prod',
+                                             interfaces=ifaces,
+                                             graphics=graphics,
+                                             hypervisor='qemu',
+                                             seed=False,
+                                             boot=None,
+                                             install=False,
+                                             start=False,
+                                             pub_key='/path/to/key.pub',
+                                             priv_key='/path/to/key',
+                                             connection='someconnection',
+                                             username='libvirtuser',
+                                             password='supersecret')
+                start_mock.assert_called_with('myvm',
                                               connection='someconnection',
                                               username='libvirtuser',
-                                              password='supersecret'), ret)
-            init_mock.assert_called_with('myvm',
-                                         cpu=2,
-                                         mem=2048,
-                                         os_type='linux',
-                                         arch='i686',
-                                         image=None,
-                                         disk='prod',
-                                         disks=disks,
-                                         nic='prod',
-                                         interfaces=ifaces,
-                                         graphics=graphics,
-                                         hypervisor='qemu',
-                                         seed=False,
-                                         boot=None,
-                                         install=False,
-                                         pub_key='/path/to/key.pub',
-                                         priv_key='/path/to/key',
-                                         connection='someconnection',
-                                         username='libvirtuser',
-                                         password='supersecret')
+                                              password='supersecret')
 
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
-                    'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')])
-                }):
-            ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'})
-            self.assertDictEqual(virt.running('myvm'), ret)
+            # Test with existing guest, but start raising an error
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.update': MagicMock(return_value={'definition': False}),
+                        'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')]),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {}}, 'result': False, 'comment': 'libvirt error msg'})
+                self.assertDictEqual(virt.running('myvm'), ret)
 
-        # Working update case when running
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
-                    'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
-                }):
-            ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
-                        'result': True,
-                        'comment': 'Domain myvm updated, restart to fully apply the changes'})
-            self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
-
-        # Working update case when running with boot params
-        boot = {
-            'kernel': '/root/f8-i386-vmlinuz',
-            'initrd': '/root/f8-i386-initrd',
-            'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/'
-        }
+            # Working update case when running
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
+                        'virt.update': MagicMock(return_value={'definition': True, 'cpu': True}),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.running('myvm', cpu=2, update=True), ret)
+
+            # Working update case when running with boot params
+            boot = {
+                'kernel': '/root/f8-i386-vmlinuz',
+                'initrd': '/root/f8-i386-initrd',
+                'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/'
+            }
 
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
-                    'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
-                }):
-            ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
-                        'result': True,
-                        'comment': 'Domain myvm updated, restart to fully apply the changes'})
-            self.assertDictEqual(virt.running('myvm', update=True, boot=boot), ret)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
+                        'virt.update': MagicMock(return_value={'definition': True, 'cpu': True}),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated'})
+                self.assertDictEqual(virt.running('myvm', boot=boot, update=True), ret)
 
-        # Working update case when stopped
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
-                    'virt.start': MagicMock(return_value=0),
-                    'virt.update': MagicMock(return_value={'definition': True})
-                }):
-            ret.update({'changes': {'myvm': 'Domain updated and started'},
-                        'result': True,
-                        'comment': 'Domain myvm updated and started'})
-            self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+            # Working update case when stopped
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.start': MagicMock(return_value=0),
+                        'virt.update': MagicMock(return_value={'definition': True}),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'result': True,
+                            'comment': 'Domain myvm updated and started'})
+                self.assertDictEqual(virt.running('myvm', cpu=2, update=True), ret)
 
-        # Failed live update case
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
-                    'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
-                }):
-            ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
-                        'result': True,
-                        'comment': 'Domain myvm updated, but some live update(s) failed'})
-            self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+            # Failed live update case
+            update_mock = MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
+                        'virt.update': update_mock,
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
+                            'result': True,
+                            'comment': 'Domain myvm updated with live update(s) failures'})
+                self.assertDictEqual(virt.running('myvm', cpu=2, update=True), ret)
+                update_mock.assert_called_with('myvm', cpu=2, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=False)
+
+            # Failed definition update case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
+                        'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')]),
+                        'virt.list_domains': MagicMock(return_value=['myvm']),
+                    }):
+                ret.update({'changes': {},
+                            'result': False,
+                            'comment': 'error message'})
+                self.assertDictEqual(virt.running('myvm', cpu=2, update=True), ret)
 
-        # Failed definition update case
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
-                    'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')])
-                }):
-            ret.update({'changes': {},
-                        'result': False,
-                        'comment': 'error message'})
-            self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
+        # Test dry-run mode
+        with patch.dict(virt.__opts__, {'test': True}):
+            # Guest defined case
+            init_mock = MagicMock(return_value=True)
+            start_mock = MagicMock(return_value=0)
+            list_mock = MagicMock(return_value=[])
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.init': init_mock,
+                        'virt.start': start_mock,
+                        'virt.list_domains': list_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'result': None,
+                            'comment': 'Domain myvm defined and started'})
+                disks = [{
+                            'name': 'system',
+                            'size': 8192,
+                            'overlay_image': True,
+                            'pool': 'default',
+                            'image': '/path/to/image.qcow2'
+                         },
+                         {
+                            'name': 'data',
+                            'size': 16834
+                         }]
+                ifaces = [{
+                             'name': 'eth0',
+                             'mac': '01:23:45:67:89:AB'
+                          },
+                          {
+                             'name': 'eth1',
+                             'type': 'network',
+                             'source': 'admin'
+                          }]
+                graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
+                self.assertDictEqual(virt.running('myvm',
+                                                  cpu=2,
+                                                  mem=2048,
+                                                  os_type='linux',
+                                                  arch='i686',
+                                                  vm_type='qemu',
+                                                  disk_profile='prod',
+                                                  disks=disks,
+                                                  nic_profile='prod',
+                                                  interfaces=ifaces,
+                                                  graphics=graphics,
+                                                  seed=False,
+                                                  install=False,
+                                                  pub_key='/path/to/key.pub',
+                                                  priv_key='/path/to/key',
+                                                  connection='someconnection',
+                                                  username='libvirtuser',
+                                                  password='supersecret'), ret)
+                init_mock.assert_not_called()
+                start_mock.assert_not_called()
+
+            # Guest update case
+            update_mock = MagicMock(return_value={'definition': True})
+            start_mock = MagicMock(return_value=0)
+            list_mock = MagicMock(return_value=['myvm'])
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
+                        'virt.start': start_mock,
+                        'virt.update': update_mock,
+                        'virt.list_domains': list_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': True, 'started': True}},
+                            'result': None,
+                            'comment': 'Domain myvm updated and started'})
+                self.assertDictEqual(virt.running('myvm', cpu=2, update=True), ret)
+                update_mock.assert_called_with('myvm', cpu=2, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=True)
+                start_mock.assert_not_called()
+
+            # No changes case
+            update_mock = MagicMock(return_value={'definition': False})
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
+                        'virt.update': update_mock,
+                        'virt.list_domains': list_mock,
+                    }):
+                ret.update({'changes': {'myvm': {'definition': False}},
+                            'result': True,
+                            'comment': 'Domain myvm exists and is running'})
+                self.assertDictEqual(virt.running('myvm', update=True), ret)
+                update_mock.assert_called_with('myvm', cpu=None, mem=None,
+                                               disk_profile=None, disks=None, nic_profile=None, interfaces=None,
+                                               graphics=None, live=True,
+                                               connection=None, username=None, password=None,
+                                               boot=None, test=True)
 
     def test_stopped(self):
         '''
@@ -599,92 +1042,506 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
             ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
             self.assertDictEqual(virt.rebooted('myvm'), ret)
 
+    def test_network_defined(self):
+        '''
+        network_defined state test cases.
+        '''
+        ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''}
+        with patch.dict(virt.__opts__, {'test': False}):
+            define_mock = MagicMock(return_value=True)
+            # Non-existing network case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(side_effect=[{}, {'mynet': {'active': False}}]),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network defined'},
+                            'comment': 'Network mynet defined'})
+                self.assertDictEqual(virt.network_defined('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          vport='openvswitch',
+                                                          tag=180,
+                                                          ipv4_config={
+                                                            'cidr': '192.168.2.0/24',
+                                                            'dhcp_ranges': [
+                                                                {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                                {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                            ]
+                                                          },
+                                                          ipv6_config={
+                                                            'cidr': '2001:db8:ca2:2::1/64',
+                                                            'dhcp_ranges': [
+                                                                {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                            ]
+                                                          },
+                                                          autostart=False,
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                define_mock.assert_called_with('mynet',
+                                               'br2',
+                                               'bridge',
+                                               vport='openvswitch',
+                                               tag=180,
+                                               autostart=False,
+                                               start=False,
+                                               ipv4_config={
+                                                 'cidr': '192.168.2.0/24',
+                                                 'dhcp_ranges': [
+                                                     {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                     {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                 ]
+                                               },
+                                               ipv6_config={
+                                                 'cidr': '2001:db8:ca2:2::1/64',
+                                                 'dhcp_ranges': [
+                                                     {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                 ]
+                                               },
+                                               connection='myconnection',
+                                               username='user',
+                                               password='secret')
+
+            # Case where there is nothing to be done
+            define_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {}, 'comment': 'Network mynet exists'})
+                self.assertDictEqual(virt.network_defined('mynet', 'br2', 'bridge'), ret)
+
+            # Error case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={}),
+                        'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+                    }):
+                ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+                self.assertDictEqual(virt.network_defined('mynet', 'br2', 'bridge'), ret)
+
+        # Test cases with __opt__['test'] set to True
+        with patch.dict(virt.__opts__, {'test': True}):
+            ret.update({'result': None})
+
+            # Non-existing network case
+            define_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={}),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network defined'},
+                            'comment': 'Network mynet defined'})
+                self.assertDictEqual(virt.network_defined('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          vport='openvswitch',
+                                                          tag=180,
+                                                          ipv4_config={
+                                                            'cidr': '192.168.2.0/24',
+                                                            'dhcp_ranges': [
+                                                                {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                                {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                            ]
+                                                          },
+                                                          ipv6_config={
+                                                            'cidr': '2001:db8:ca2:2::1/64',
+                                                            'dhcp_ranges': [
+                                                                {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                            ]
+                                                          },
+                                                          autostart=False,
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                define_mock.assert_not_called()
+
+            # Case where there is nothing to be done
+            define_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {}, 'comment': 'Network mynet exists', 'result': True})
+                self.assertDictEqual(virt.network_defined('mynet', 'br2', 'bridge'), ret)
+
+            # Error case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+                    }):
+                ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+                self.assertDictEqual(virt.network_defined('mynet', 'br2', 'bridge'), ret)
+
     def test_network_running(self):
         '''
         network_running state test cases.
         '''
         ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''}
-        define_mock = MagicMock(return_value=True)
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.network_info': MagicMock(return_value={}),
-                    'virt.network_define': define_mock
-                }):
-            ret.update({'changes': {'mynet': 'Network defined and started'},
-                        'comment': 'Network mynet defined and started'})
-            self.assertDictEqual(virt.network_running('mynet',
-                                                      'br2',
-                                                      'bridge',
-                                                      vport='openvswitch',
-                                                      tag=180,
-                                                      ipv4_config={
-                                                        'cidr': '192.168.2.0/24',
-                                                        'dhcp_ranges': [
-                                                            {'start': '192.168.2.10', 'end': '192.168.2.25'},
-                                                            {'start': '192.168.2.110', 'end': '192.168.2.125'},
-                                                        ]
-                                                      },
-                                                      ipv6_config={
-                                                        'cidr': '2001:db8:ca2:2::1/64',
-                                                        'dhcp_ranges': [
-                                                            {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
-                                                        ]
-                                                      },
-                                                      autostart=False,
-                                                      connection='myconnection',
-                                                      username='user',
-                                                      password='secret'), ret)
-            define_mock.assert_called_with('mynet',
-                                           'br2',
-                                           'bridge',
-                                           vport='openvswitch',
-                                           tag=180,
-                                           autostart=False,
-                                           start=True,
-                                           ipv4_config={
-                                             'cidr': '192.168.2.0/24',
-                                             'dhcp_ranges': [
-                                                 {'start': '192.168.2.10', 'end': '192.168.2.25'},
-                                                 {'start': '192.168.2.110', 'end': '192.168.2.125'},
-                                             ]
-                                           },
-                                           ipv6_config={
-                                             'cidr': '2001:db8:ca2:2::1/64',
-                                             'dhcp_ranges': [
-                                                 {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
-                                             ]
-                                           },
-                                           connection='myconnection',
-                                           username='user',
-                                           password='secret')
+        with patch.dict(virt.__opts__, {'test': False}):
+            define_mock = MagicMock(return_value=True)
+            start_mock = MagicMock(return_value=True)
+            # Non-existing network case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(side_effect=[{}, {'mynet': {'active': False}}]),
+                        'virt.network_define': define_mock,
+                        'virt.network_start': start_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network defined and started'},
+                            'comment': 'Network mynet defined and started'})
+                self.assertDictEqual(virt.network_running('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          vport='openvswitch',
+                                                          tag=180,
+                                                          ipv4_config={
+                                                            'cidr': '192.168.2.0/24',
+                                                            'dhcp_ranges': [
+                                                                {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                                {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                            ]
+                                                          },
+                                                          ipv6_config={
+                                                            'cidr': '2001:db8:ca2:2::1/64',
+                                                            'dhcp_ranges': [
+                                                                {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                            ]
+                                                          },
+                                                          autostart=False,
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                define_mock.assert_called_with('mynet',
+                                               'br2',
+                                               'bridge',
+                                               vport='openvswitch',
+                                               tag=180,
+                                               autostart=False,
+                                               start=False,
+                                               ipv4_config={
+                                                 'cidr': '192.168.2.0/24',
+                                                 'dhcp_ranges': [
+                                                     {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                     {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                 ]
+                                               },
+                                               ipv6_config={
+                                                 'cidr': '2001:db8:ca2:2::1/64',
+                                                 'dhcp_ranges': [
+                                                     {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                 ]
+                                               },
+                                               connection='myconnection',
+                                               username='user',
+                                               password='secret')
+                start_mock.assert_called_with('mynet',
+                                              connection='myconnection',
+                                              username='user',
+                                              password='secret')
 
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
-                    'virt.network_define': define_mock,
-                }):
-            ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
-            self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+            # Case where there is nothing to be done
+            define_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
+                self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
 
-        start_mock = MagicMock(return_value=True)
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}),
-                    'virt.network_start': start_mock,
-                    'virt.network_define': define_mock,
-                }):
-            ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet started'})
-            self.assertDictEqual(virt.network_running('mynet',
-                                                      'br2',
-                                                      'bridge',
+            # Network existing and stopped case
+            start_mock = MagicMock(return_value=True)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}),
+                        'virt.network_start': start_mock,
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet exists and started'})
+                self.assertDictEqual(virt.network_running('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret')
+
+            # Error case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={}),
+                        'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+                    }):
+                ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+                self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+
+        # Test cases with __opt__['test'] set to True
+        with patch.dict(virt.__opts__, {'test': True}):
+            ret.update({'result': None})
+
+            # Non-existing network case
+            define_mock.reset_mock()
+            start_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={}),
+                        'virt.network_define': define_mock,
+                        'virt.network_start': start_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network defined and started'},
+                            'comment': 'Network mynet defined and started'})
+                self.assertDictEqual(virt.network_running('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          vport='openvswitch',
+                                                          tag=180,
+                                                          ipv4_config={
+                                                            'cidr': '192.168.2.0/24',
+                                                            'dhcp_ranges': [
+                                                                {'start': '192.168.2.10', 'end': '192.168.2.25'},
+                                                                {'start': '192.168.2.110', 'end': '192.168.2.125'},
+                                                            ]
+                                                          },
+                                                          ipv6_config={
+                                                            'cidr': '2001:db8:ca2:2::1/64',
+                                                            'dhcp_ranges': [
+                                                                {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
+                                                            ]
+                                                          },
+                                                          autostart=False,
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                define_mock.assert_not_called()
+                start_mock.assert_not_called()
+
+            # Case where there is nothing to be done
+            define_mock.reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
+                self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+
+            # Network existing and stopped case
+            start_mock = MagicMock(return_value=True)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}),
+                        'virt.network_start': start_mock,
+                        'virt.network_define': define_mock,
+                    }):
+                ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet exists and started'})
+                self.assertDictEqual(virt.network_running('mynet',
+                                                          'br2',
+                                                          'bridge',
+                                                          connection='myconnection',
+                                                          username='user',
+                                                          password='secret'), ret)
+                start_mock.assert_not_called()
+
+            # Error case
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.network_info': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+                    }):
+                ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+                self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+
+    def test_pool_defined(self):
+        '''
+        pool_defined state test cases.
+        '''
+        ret = {'name': 'mypool', 'changes': {}, 'result': True, 'comment': ''}
+        mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build']}
+        with patch.dict(virt.__opts__, {'test': False}):
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(side_effect=[{}, {'mypool': {'state': 'stopped', 'autostart': True}}]),
+                        'virt.pool_define': mocks['define'],
+                        'virt.pool_build': mocks['build'],
+                        'virt.pool_set_autostart': mocks['autostart']
+                    }):
+                ret.update({'changes': {'mypool': 'Pool defined, marked for autostart'},
+                            'comment': 'Pool mypool defined, marked for autostart'})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       permissions={'mode': '0770',
+                                                                    'owner': 1000,
+                                                                    'group': 100,
+                                                                    'label': 'seclabel'},
+                                                       source={'devices': [{'path': '/dev/sda'}]},
+                                                       transient=True,
+                                                       autostart=True,
+                                                       connection='myconnection',
+                                                       username='user',
+                                                       password='secret'), ret)
+                mocks['define'].assert_called_with('mypool',
+                                                   ptype='logical',
+                                                   target='/dev/base',
+                                                   permissions={'mode': '0770',
+                                                                'owner': 1000,
+                                                                'group': 100,
+                                                                'label': 'seclabel'},
+                                                   source_devices=[{'path': '/dev/sda'}],
+                                                   source_dir=None,
+                                                   source_adapter=None,
+                                                   source_hosts=None,
+                                                   source_auth=None,
+                                                   source_name=None,
+                                                   source_format=None,
+                                                   source_initiator=None,
+                                                   transient=True,
+                                                   start=False,
+                                                   connection='myconnection',
+                                                   username='user',
+                                                   password='secret')
+                mocks['autostart'].assert_called_with('mypool',
+                                                      state='on',
                                                       connection='myconnection',
                                                       username='user',
-                                                      password='secret'), ret)
-            start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret')
+                                                      password='secret')
+                mocks['build'].assert_called_with('mypool',
+                                                  connection='myconnection',
+                                                  username='user',
+                                                  password='secret')
 
-        with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                    'virt.network_info': MagicMock(return_value={}),
-                    'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
-                }):
-            ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
-            self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
+            mocks['update'] = MagicMock(return_value=False)
+            for mock in mocks:
+                mocks[mock].reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}),
+                        'virt.pool_update': mocks['update'],
+                        'virt.pool_build': mocks['build'],
+                    }):
+                ret.update({'changes': {}, 'comment': 'Pool mypool unchanged'})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       source={'devices': [{'path': '/dev/sda'}]}), ret)
+                mocks['build'].assert_not_called()
+
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={}),
+                        'virt.pool_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
+                    }):
+                ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       source={'devices': [{'path': '/dev/sda'}]}), ret)
+
+            # Test case with update and autostart change on stopped pool
+            for mock in mocks:
+                mocks[mock].reset_mock()
+            mocks['update'] = MagicMock(return_value=True)
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}),
+                        'virt.pool_update': mocks['update'],
+                        'virt.pool_set_autostart': mocks['autostart'],
+                        'virt.pool_build': mocks['build'],
+                    }):
+                ret.update({'changes': {'mypool': 'Pool updated, built, autostart flag changed'},
+                            'comment': 'Pool mypool updated, built, autostart flag changed',
+                            'result': True})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       autostart=False,
+                                                       permissions={'mode': '0770',
+                                                                    'owner': 1000,
+                                                                    'group': 100,
+                                                                    'label': 'seclabel'},
+                                                       source={'devices': [{'path': '/dev/sda'}]}), ret)
+                mocks['build'].assert_called_with('mypool', connection=None, username=None, password=None)
+                mocks['autostart'].assert_called_with('mypool', state='off',
+                                                      connection=None, username=None, password=None)
+                mocks['update'].assert_called_with('mypool',
+                                                   ptype='logical',
+                                                   target='/dev/base',
+                                                   permissions={'mode': '0770',
+                                                                'owner': 1000,
+                                                                'group': 100,
+                                                                'label': 'seclabel'},
+                                                   source_devices=[{'path': '/dev/sda'}],
+                                                   source_dir=None,
+                                                   source_adapter=None,
+                                                   source_hosts=None,
+                                                   source_auth=None,
+                                                   source_name=None,
+                                                   source_format=None,
+                                                   source_initiator=None,
+                                                   connection=None,
+                                                   username=None,
+                                                   password=None)
+
+            # test case with update and no autostart change on running pool
+            for mock in mocks:
+                mocks[mock].reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': False}}),
+                        'virt.pool_update': mocks['update'],
+                        'virt.pool_build': mocks['build'],
+                    }):
+                ret.update({'changes': {'mypool': 'Pool updated'},
+                            'comment': 'Pool mypool updated',
+                            'result': True})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       autostart=False,
+                                                       permissions={'mode': '0770',
+                                                                    'owner': 1000,
+                                                                    'group': 100,
+                                                                    'label': 'seclabel'},
+                                                       source={'devices': [{'path': '/dev/sda'}]}), ret)
+                mocks['update'].assert_called_with('mypool',
+                                                   ptype='logical',
+                                                   target='/dev/base',
+                                                   permissions={'mode': '0770',
+                                                                'owner': 1000,
+                                                                'group': 100,
+                                                                'label': 'seclabel'},
+                                                   source_devices=[{'path': '/dev/sda'}],
+                                                   source_dir=None,
+                                                   source_adapter=None,
+                                                   source_hosts=None,
+                                                   source_auth=None,
+                                                   source_name=None,
+                                                   source_format=None,
+                                                   source_initiator=None,
+                                                   connection=None,
+                                                   username=None,
+                                                   password=None)
+
+        with patch.dict(virt.__opts__, {'test': True}):
+            # test case with test=True and no change
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}),
+                        'virt.pool_update': MagicMock(return_value=False),
+                    }):
+                ret.update({'changes': {}, 'comment': 'Pool mypool unchanged',
+                            'result': True})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       source={'devices': [{'path': '/dev/sda'}]}), ret)
+
+            # test case with test=True and pool to be defined
+            for mock in mocks:
+                mocks[mock].reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={}),
+                    }):
+                ret.update({'changes': {'mypool': 'Pool defined, marked for autostart'},
+                            'comment': 'Pool mypool defined, marked for autostart',
+                            'result': None})
+                self.assertDictEqual(virt.pool_defined('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       permissions={'mode': '0770',
+                                                                    'owner': 1000,
+                                                                    'group': 100,
+                                                                    'label': 'seclabel'},
+                                                       source={'devices': [{'path': '/dev/sda'}]},
+                                                       transient=True,
+                                                       autostart=True,
+                                                       connection='myconnection',
+                                                       username='user',
+                                                       password='secret'), ret)
 
     def test_pool_running(self):
         '''
@@ -694,14 +1551,14 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
         mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build', 'start', 'stop']}
         with patch.dict(virt.__opts__, {'test': False}):
             with patch.dict(virt.__salt__, {  # pylint: disable=no-member
-                        'virt.pool_info': MagicMock(return_value={}),
+                        'virt.pool_info': MagicMock(side_effect=[{}, {'mypool': {'state': 'stopped', 'autostart': True}}]),
                         'virt.pool_define': mocks['define'],
                         'virt.pool_build': mocks['build'],
                         'virt.pool_start': mocks['start'],
                         'virt.pool_set_autostart': mocks['autostart']
                     }):
-                ret.update({'changes': {'mypool': 'Pool defined, started and marked for autostart'},
-                            'comment': 'Pool mypool defined, started and marked for autostart'})
+                ret.update({'changes': {'mypool': 'Pool defined, marked for autostart, started'},
+                            'comment': 'Pool mypool defined, marked for autostart, started'})
                 self.assertDictEqual(virt.pool_running('mypool',
                                                        ptype='logical',
                                                        target='/dev/base',
@@ -754,7 +1611,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                         'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}),
                         'virt.pool_update': MagicMock(return_value=False),
                     }):
-                ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running'})
+                ret.update({'changes': {}, 'comment': 'Pool mypool already running'})
                 self.assertDictEqual(virt.pool_running('mypool',
                                                        ptype='logical',
                                                        target='/dev/base',
@@ -797,8 +1654,8 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                         'virt.pool_build': mocks['build'],
                         'virt.pool_start': mocks['start']
                     }):
-                ret.update({'changes': {'mypool': 'Pool updated, built, autostart flag changed and started'},
-                            'comment': 'Pool mypool updated, built, autostart flag changed and started',
+                ret.update({'changes': {'mypool': 'Pool updated, built, autostart flag changed, started'},
+                            'comment': 'Pool mypool updated, built, autostart flag changed, started',
                             'result': True})
                 self.assertDictEqual(virt.pool_running('mypool',
                                                        ptype='logical',
@@ -842,8 +1699,8 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                         'virt.pool_start': mocks['start'],
                         'virt.pool_stop': mocks['stop']
                     }):
-                ret.update({'changes': {'mypool': 'Pool updated, built and restarted'},
-                            'comment': 'Pool mypool updated, built and restarted',
+                ret.update({'changes': {'mypool': 'Pool updated, built, restarted'},
+                            'comment': 'Pool mypool updated, built, restarted',
                             'result': True})
                 self.assertDictEqual(virt.pool_running('mypool',
                                                        ptype='logical',
@@ -882,7 +1739,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                         'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}),
                         'virt.pool_update': MagicMock(return_value=False),
                     }):
-                ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running',
+                ret.update({'changes': {}, 'comment': 'Pool mypool already running',
                             'result': True})
                 self.assertDictEqual(virt.pool_running('mypool',
                                                        ptype='logical',
@@ -905,6 +1762,29 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
                                                        target='/dev/base',
                                                        source={'devices': [{'path': '/dev/sda'}]}), ret)
 
+            # test case with test=True and pool to be defined
+            for mock in mocks:
+                mocks[mock].reset_mock()
+            with patch.dict(virt.__salt__, {  # pylint: disable=no-member
+                        'virt.pool_info': MagicMock(return_value={}),
+                    }):
+                ret.update({'changes': {'mypool': 'Pool defined, marked for autostart, started'},
+                            'comment': 'Pool mypool defined, marked for autostart, started',
+                            'result': None})
+                self.assertDictEqual(virt.pool_running('mypool',
+                                                       ptype='logical',
+                                                       target='/dev/base',
+                                                       permissions={'mode': '0770',
+                                                                    'owner': 1000,
+                                                                    'group': 100,
+                                                                    'label': 'seclabel'},
+                                                       source={'devices': [{'path': '/dev/sda'}]},
+                                                       transient=True,
+                                                       autostart=True,
+                                                       connection='myconnection',
+                                                       username='user',
+                                                       password='secret'), ret)
+
     def test_pool_deleted(self):
         '''
         Test the pool_deleted state
-- 
2.16.4


openSUSE Build Service is sponsored by