diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47806e264b687d7ec0acf4f6d0bac5b04979909b..b4f344433aa2662c50c301652e78c507855e1038 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 3.1.0
+
+* VMonG5k: expose `start_virtualmachines` function
+
 # 3.0.1
 
 * Doc: Add VMonG5k primer
diff --git a/docs/tutorials/ansible-integration/flent_on_vmong5k.py b/docs/tutorials/ansible-integration/flent_on_vmong5k.py
index 0dd6ee16f62330536327d5b576a739be996f5518..61ddd56fbb143dbcd89f0cfb54517eeccd362572 100644
--- a/docs/tutorials/ansible-integration/flent_on_vmong5k.py
+++ b/docs/tutorials/ansible-integration/flent_on_vmong5k.py
@@ -8,14 +8,14 @@ import logging
 logging.basicConfig(level=logging.DEBUG)
 
 conf = Configuration.from_settings(job_name="flent_on",
-                                   image="/grid5000/virt-images/debian9-x64-std.qcow2",
+                                   image="/grid5000/virt-images/debian9-x64-std-2019040916.qcow2",
                                    gateway="access.grid5000.fr",
                                    gateway_user="msimonin")\
                     .add_machine(roles=["server"],
-                                 cluster="parapluie",
+                                 cluster="grisou",
                                  number=1)\
                     .add_machine(roles=["client"],
-                                 cluster="parapluie",
+                                 cluster="grisou",
                                  number=1)\
                     .finalize()
 
diff --git a/docs/tutorials/grid5000.rst b/docs/tutorials/grid5000.rst
index e0e0659c3be936c02cfe5a069fea9c828cac47ea..2c7010ab3faae774836531131e4e6f27c66d17f7 100644
--- a/docs/tutorials/grid5000.rst
+++ b/docs/tutorials/grid5000.rst
@@ -105,13 +105,16 @@ Build the configuration programmatically
     :linenos:
 
 
-More complete example
----------------------
-
-You can found it `here
-<https://github.com/BeyondTheClouds/enoslib/tree/master/docs/tutorials/grid5000/virt>`_.
-It starts reserve non-deploy nodes and a subnet and start virtual machines on
-them.
+Start Virtual Machines on nodes
+-------------------------------
 
 .. hint::
-  Note that it is now possible to use the ``VMonG5k`` provider directly.
+  Note that it is now possible to use the ``VMonG5k`` provider directly. This
+  provider will handle the reservation and deployment of virtual machines in an
+  atomic way. The following example illustrates a mixed environment where some virtual
+  machines are started on specific nodes of your reservation.
+
+
+.. literalinclude:: grid5000/tuto_grid5000_p_virt.py
+    :language: python
+    :linenos:
diff --git a/docs/tutorials/grid5000/tuto_grid5000_p_virt.py b/docs/tutorials/grid5000/tuto_grid5000_p_virt.py
new file mode 100644
index 0000000000000000000000000000000000000000..a20761cb65c58e2be9617d64bf854ae34fca3109
--- /dev/null
+++ b/docs/tutorials/grid5000/tuto_grid5000_p_virt.py
@@ -0,0 +1,68 @@
+from enoslib.api import generate_inventory
+from enoslib.infra.enos_g5k.provider import G5k
+from enoslib.infra.enos_vmong5k.provider import start_virtualmachines
+import enoslib.infra.enos_vmong5k.configuration as vmconf
+from enoslib.infra.enos_g5k.configuration import (Configuration,
+                                                  NetworkConfiguration)
+
+import logging
+import os
+
+logging.basicConfig(level=logging.INFO)
+
+# path to the inventory
+inventory = os.path.join(os.getcwd(), "hosts")
+
+# claim the resources
+conf = Configuration.from_settings(job_type="allow_classic_ssh")
+prod_network = NetworkConfiguration(id="n1",
+                                    type="prod",
+                                    roles=["my_network"],
+                                    site="rennes")
+conf.add_network_conf(prod_network)\
+    .add_network(id="not_linked_to_any_machine",
+                 type="slash_22",
+                 roles=["my_subnet"],
+                 site="rennes")\
+    .add_machine(roles=["control"],
+                 cluster="parapluie",
+                 nodes=1,
+                 primary_network=prod_network)\
+    .add_machine(roles=["compute"],
+                 cluster="parasilo",
+                 nodes=1,
+                 primary_network=prod_network)\
+    .finalize()
+
+provider = G5k(conf)
+roles, networks = provider.init()
+
+# Retrieving subnet
+subnet = [n for n in networks if "my_subnet" in n["roles"]]
+logging.info(subnet)
+# This returns the subnet information
+# {
+#    'roles': ['my_subnet'],
+#    'start': '10.158.0.1',
+#    'dns': '131.254.203.235',
+#    'end': '10.158.3.254',
+#    'cidr': '10.158.0.0/22',
+#    'gateway': '10.159.255.254'
+#    'mac_end': '00:16:3E:9E:03:FE',
+#    'mac_start': '00:16:3E:9E:00:01',
+# }
+
+# We describe the VMs types and placement in the following
+virt_conf = vmconf.Configuration.from_settings()
+virt_conf.add_machine(roles=["vmcontrol"],
+                      number=1,
+                      undercloud=roles["control"])\
+         .add_machine(roles=["vmcompute"],
+                     number=3,
+                     undercloud=roles["compute"])\
+         .finalize()
+
+# Start them
+vmroles, networks =start_virtualmachines(virt_conf, subnet[0])
+print(vmroles)
+print(networks)
diff --git a/docs/tutorials/grid5000/virt/cloud-init-data/meta-data b/docs/tutorials/grid5000/virt/cloud-init-data/meta-data
deleted file mode 100644
index 91ad25895f9088a49570cc44033239fc27faf7fe..0000000000000000000000000000000000000000
--- a/docs/tutorials/grid5000/virt/cloud-init-data/meta-data
+++ /dev/null
@@ -1,4 +0,0 @@
-#instance-id: iid-local01
-#local-hostname: example-vm
-public-keys:
-  - "{{ pubkey }}" 
diff --git a/docs/tutorials/grid5000/virt/cloud-init-data/user-data b/docs/tutorials/grid5000/virt/cloud-init-data/user-data
deleted file mode 100644
index fefc48a74c52c0b0c4c1b50d4faa95b42177149a..0000000000000000000000000000000000000000
--- a/docs/tutorials/grid5000/virt/cloud-init-data/user-data
+++ /dev/null
@@ -1,2 +0,0 @@
-#cloud-config
-disable_root: false
diff --git a/docs/tutorials/grid5000/virt/domain.xml.j2 b/docs/tutorials/grid5000/virt/domain.xml.j2
deleted file mode 100644
index 43e08411858b3fdc25c195dc9e02772b254dfbfb..0000000000000000000000000000000000000000
--- a/docs/tutorials/grid5000/virt/domain.xml.j2
+++ /dev/null
@@ -1,38 +0,0 @@
-<domain type='kvm'>
- <name>{{ item.name }}</name>
- <memory>{{ item.mem }}</memory>
- <vcpu>{{ item.cores }}</vcpu>
- <os>
-   <type arch="x86_64">hvm</type>
- </os>
- <clock offset="localtime"/>
- <on_poweroff>destroy</on_poweroff>
- <on_reboot>restart</on_reboot>
- <on_crash>destroy</on_crash>
- <devices>
-   <emulator>/usr/bin/kvm</emulator>
-   <disk type='file' device='disk'>
-     <driver name='qemu' type='qcow2'/>
-     <source file='{{ item.backing_file }}'/>
-     <target dev='vda' bus='virtio'/>
-   </disk>
-   <disk type='file' device='cdrom'>
-     <source file='/tmp/cloud-init-data.iso'/>
-     <target dev='vdb' bus='virtio'/>
-     <readonly/>
-   </disk>
-   <interface type='bridge'>
-     <source bridge='br0'/>
-     <mac address='{{ item.mac }}'/>
-   </interface>
-   <serial type='pty'>
-     <source path='/dev/ttyS0'/>
-     <target port='0'/>
-   </serial>
-   <console type='pty'>
-     <source path='/dev/ttyS0'/>
-     <target port='0'/>
-   </console>
- </devices>
-</domain>
-
diff --git a/docs/tutorials/grid5000/virt/site.yml b/docs/tutorials/grid5000/virt/site.yml
deleted file mode 100644
index 2ac7919fca8da7b5e76d563f20f9ba2d9e45ce87..0000000000000000000000000000000000000000
--- a/docs/tutorials/grid5000/virt/site.yml
+++ /dev/null
@@ -1,58 +0,0 @@
----
-- name: This is a play
-  hosts: all
-  vars:
-    pubkey: "{{lookup('file', '~/.ssh/id_rsa.pub')}}"
-  tasks:
-
-    - name: vm -1 / 1
-      virt:
-        name: "{{ item.name }}"
-        state: destroyed
-      ignore_errors: yes
-      with_items: "{{ vms[inventory_hostname] }}"
-
-    - name: vm -0 / 1
-      virt:
-        name: "{{ item.name }}"
-        command: undefine
-      ignore_errors: yes
-      with_items: "{{ vms[inventory_hostname] }}"
-
-
-    - name: Removing previous cloud init data
-      file:
-        path: /tmp/cloud-init-data
-        state: absent
-
-    - name: Copy cloud-init data
-      copy:
-        src: cloud-init-data/
-        dest: /tmp/cloud-init-data
-        
-    - name: Copy pub key in he cloud-init data
-      template:
-        src: cloud-init-data/meta-data
-        dest: /tmp/cloud-init-data/meta-data
-
-    - name: Create the iso for cloud-init
-      shell: cd /tmp &&  genisoimage -output cloud-init-data.iso -volid cidata -joliet -rock cloud-init-data/user-data cloud-init-data/meta-data
-
-
-    - name: Copy base image
-      shell: "qemu-img create -f qcow2 -o backing_file=/grid5000/virt-images/debian9-x64-base.qcow2 {{ item.backing_file }}"
-      with_items: "{{ vms[inventory_hostname] }}"
-
-    - name: vm 0 / 1
-      virt:
-        name: "{{ item.name }}"
-        command: define
-        xml: "{{ lookup('template', 'domain.xml.j2') }}"
-      with_items: "{{ vms[inventory_hostname] }}"
-
-
-    - name: vm 1 / 1
-      virt:
-        name: "{{ item.name }}"
-        state: running
-      with_items: "{{ vms[inventory_hostname] }}"
diff --git a/docs/tutorials/grid5000/virt/tuto_grid5000_virt.py b/docs/tutorials/grid5000/virt/tuto_grid5000_virt.py
deleted file mode 100644
index 6f26728b1da832f3bd802bbb7ced0b7a2e18462b..0000000000000000000000000000000000000000
--- a/docs/tutorials/grid5000/virt/tuto_grid5000_virt.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from enoslib.api import generate_inventory, run_ansible
-from enoslib.infra.enos_g5k.provider import G5k
-from enoslib.infra.enos_g5k.configuration import (Configuration,
-                                                  NetworkConfiguration)
-
-import logging
-from netaddr import EUI
-import os
-
-logging.basicConfig(level=logging.DEBUG)
-
-VMS = 10
-PMS = 2
-
-
-def range_mac(mac_start, mac_end, step=1):
-    """Iterate over mac addresses (given as string)."""
-    start = int(EUI(mac_start))
-    end = int(EUI(mac_end))
-    for i_mac in range(start, end, step):
-        mac = EUI(int(EUI(i_mac)) + 1)
-        ip = ['10'] + [str(int(i, 2)) for i in mac.bits().split('-')[-3:]]
-        yield str(mac).replace('-', ':'), '.'.join(ip)
-
-# claim the resources
-prod = NetworkConfiguration(id="n1",
-                            type="prod",
-                            roles=["my_network"],
-                            site="rennes")
-conf = Configuration.from_settings(job_type="allow_classic_ssh",
-                                   job_name="enoslib-virt",
-                                   walltime="01:00:00")\
-                    .add_network_conf(prod)\
-                    .add_network(id="_subnet_network",
-                                 type="slash_22",
-                                 roles=["my_subnet"],
-                                 site="rennes")\
-                    .add_machine(roles=["compute"],
-                                 cluster="parasilo",
-                                 nodes=PMS,
-                                 primary_network=prod)\
-                    .finalize()
-
-provider = G5k(conf)
-roles, networks = provider.init()
-
-# path to the inventory
-inventory = os.path.join(os.getcwd(), "hosts")
-
-# generate an inventory compatible with ansible
-generate_inventory(roles, networks, inventory, check_networks=True)
-
-subnet = [n for n in networks if "my_subnet" in n["roles"]][0]
-mac_start = subnet["mac_start"]
-mac_end = subnet["mac_end"]
-
-vms = []
-
-# Distribute mac addresses to vms
-for idx, (mac, ip) in enumerate(range_mac(mac_start, mac_end)):
-    if len(vms) >= VMS:
-       break
-    name = "vm-%s" % idx
-    vms.append({
-        "name": name,
-        "cores": 1,
-        "mem": 2048000,
-        "mac": mac,
-        "backing_file": "/tmp/%s.qcow2" % name,
-        "ip": ip
-
-    })
-
-# Distribute vms to pms
-machines = roles["compute"]
-# the vms indexed by the pm hosting them
-all_vms = {}
-for index, vm in enumerate(vms):
-    # host is set to the inventory hostname
-    machine = machines[index % len(machines)].alias
-    vm["host"] = machine
-    all_vms.setdefault(machine, [])
-    all_vms[machine].append(vm)
-
-logging.info(vms)
-
-run_ansible(["site.yml"], inventory, extra_vars={"vms": all_vms})
-
-print("If everything went fine you can access one of those")
-print("+{:->16}+{:->16}+".format('', ''))
-for idx, vm in enumerate(vms):
-    print('|{:16}|{:16}|'.format(vm["name"], vm["ip"]))
-    print("+{:->16}+{:->16}+".format('', ''))
-
-
-# destroy the reservation
-# provider.destroy()
diff --git a/enoslib/host.py b/enoslib/host.py
index fb12c7770c5d14f575f16ec29aeda85907d12863..d3c52226ecee81a6433bedf05d7d1edc2ed7855d 100644
--- a/enoslib/host.py
+++ b/enoslib/host.py
@@ -24,6 +24,12 @@ class Host(object):
     def to_dict(self):
         return copy.deepcopy(self.__dict__)
 
+    @classmethod
+    def from_dict(cls, d):
+        _d = copy.deepcopy(d)
+        address = _d.pop("address")
+        return cls(address, **_d)
+
     def to_host(self):
         """Copy or coerce to a Host."""
         return Host(self.address,
diff --git a/enoslib/infra/enos_vmong5k/configuration.py b/enoslib/infra/enos_vmong5k/configuration.py
index d7656233d9138d8005fe6e114c1651194e5aa55e..7f6b712f97177171c06c0b265304478ff2025716 100644
--- a/enoslib/infra/enos_vmong5k/configuration.py
+++ b/enoslib/infra/enos_vmong5k/configuration.py
@@ -71,7 +71,8 @@ class MachineConfiguration:
                  cluster=None,
                  flavour=None,
                  flavour_desc=None,
-                 number=DEFAULT_NUMBER):
+                 number=DEFAULT_NUMBER,
+                 undercloud=None):
         self.roles = roles
 
         # Internally we keep the flavour_desc as reference not a descriptor
@@ -95,6 +96,9 @@ class MachineConfiguration:
         # this could be used to express some affinity between vms
         self.cookie = uuid.uuid4().hex
 
+        #
+        self.undercloud = undercloud if undercloud else []
+
     @classmethod
     def from_dictionnary(cls, dictionnary):
         kwargs = {}
@@ -117,10 +121,23 @@ class MachineConfiguration:
         if cluster is not None:
             kwargs.update(cluster=cluster)
 
+        undercloud = dictionnary.get("undercloud")
+        if undercloud is not None:
+            undercloud = [Host.from_dict(h) for h in undercloud]
+            kwargs.update(undercloud=undercloud)
+
         return cls(**kwargs)
 
     def to_dict(self):
         d = {}
-        d.update(roles=self.roles, flavour_desc=self.flavour_desc,
-                 number=self.number, cluster=self.cluster)
+        undercloud = self.undercloud
+        if undercloud is not None:
+            undercloud = [h.to_dict() for h in undercloud]
+            d.update(undercloud=undercloud)
+        cluster = self.cluster
+        if cluster is not None:
+            d.update(cluster=cluster)
+        d.update(roles=self.roles,
+                 flavour_desc=self.flavour_desc,
+                 number=self.number)
         return d
diff --git a/enoslib/infra/enos_vmong5k/provider.py b/enoslib/infra/enos_vmong5k/provider.py
index 63adc934c0fb027c7e4c61de233abc7114c654c3..4ccd183789b9a324a4e0a1439e4290df1002c278 100644
--- a/enoslib/infra/enos_vmong5k/provider.py
+++ b/enoslib/infra/enos_vmong5k/provider.py
@@ -19,18 +19,59 @@ from ..provider import Provider
 logger = logging.getLogger(__name__)
 
 
-def get_subnet_ip(mac):
+def start_virtualmachines(provider_conf,
+                          g5k_subnet):
+    """Starts virtualmachines on G5K.
+
+    Args:
+        provider_conf(Configuration):
+            :py:class:`enoslib.infra.enos_vmong5k.configuraton.Configuration`
+            This is the abstract description of your overcloud (VMs). Each
+            configuration has a its undercloud attributes filled with the
+            undercloud machines to use. Round Robin strategy to distribute the
+            VMs to the PMs will be used for each configuration. Mac addresses
+            will be generated according to the g5k_subnet parameter.
+        g5k_subnet(dict): The subnet to use. Serialization of
+            :py:class:`enoslib.infra.enos_vmong5k.configuraton.NetworkConfiguration`
+
+    Returns:
+        (roles, networks) tuple
+
+    """
+    def _to_hosts(roles):
+        _roles = {}
+        for role, machines in roles.items():
+            _roles[role] = [m.to_host() for m in machines]
+        return _roles
+
+    extra = {}
+    if provider_conf.gateway:
+        extra.update(gateway=provider_conf.gateway)
+    if provider_conf.gateway_user:
+        extra.update(gateway_user=provider_conf.gateway_user)
+
+    vmong5k_roles = _distribute(provider_conf.machines,
+                                g5k_subnet,
+                                extra=extra)
+
+    _start_virtualmachines(provider_conf,
+                           vmong5k_roles)
+
+    return _to_hosts(vmong5k_roles), [g5k_subnet]
+
+
+def _get_subnet_ip(mac):
     # This is the format allowed on G5K for subnets
     address = ['10'] + [str(int(i, 2)) for i in mac.bits().split('-')[-3:]]
     return IPv4Address('.'.join(address))
 
 
-def mac_range(start, stop, step=1):
+def _mac_range(start, stop, step=1):
     for item in range(int(start) + 1, int(stop), step):
         yield EUI(item, dialect=mac_unix_expanded)
 
 
-def get_host_cores(cluster):
+def _get_host_cores(cluster):
     nodes = g5k_api_utils.get_nodes(cluster)
     attributes = nodes[-1]
     processors = attributes.architecture['nb_procs']
@@ -40,8 +81,8 @@ def get_host_cores(cluster):
     return cores * processors
 
 
-def find_nodes_number(machine):
-    cores = get_host_cores(machine.cluster)
+def _find_nodes_number(machine):
+    cores = _get_host_cores(machine.cluster)
     return - ((-1 * machine.number * machine.flavour_desc["core"]) // cores)
 
 
@@ -71,7 +112,7 @@ def _do_build_g5k_conf(vmong5k_conf, site):
         roles.append(machine.cookie)
         g5k_conf.add_machine(roles=roles,
                              cluster=machine.cluster,
-                             nodes=find_nodes_number(machine),
+                             nodes=_find_nodes_number(machine),
                              primary_network=prod_network)
     return g5k_conf
 
@@ -95,12 +136,12 @@ def _build_static_hash(roles, cookie):
     return md5.hexdigest()
 
 
-def _distribute(machines, g5k_roles, g5k_subnet, extra=None):
+def _distribute(machines, g5k_subnet, extra=None):
     vmong5k_roles = defaultdict(list)
-    euis = mac_range(EUI(g5k_subnet["mac_start"]), EUI(g5k_subnet["mac_end"]))
+    euis = _mac_range(EUI(g5k_subnet["mac_start"]), EUI(g5k_subnet["mac_end"]))
     static_hashes = {}
     for machine in machines:
-        pms = g5k_roles[machine.cookie]
+        pms = machine.undercloud
         pms_it = itertools.cycle(pms)
         for idx in range(machine.number):
             static_hash = _build_static_hash(machine.roles, machine.cookie)
@@ -139,26 +180,32 @@ def _index_by_host(roles):
     return dict(vms_by_host)
 
 
-def start_virtualmachines(provider_conf, g5k_roles, vmong5k_roles):
+def _start_virtualmachines(provider_conf, vmong5k_roles):
     vms_by_host = _index_by_host(vmong5k_roles)
 
-    extra_vars = {'vms': vms_by_host,
-                  'base_image': provider_conf.image,
+    extra_vars = {"vms": vms_by_host,
+                  "base_image": provider_conf.image,
                   # push the g5k user in the env
-                  'g5k_user': os.environ.get('USER'),
-                  'working_dir': provider_conf.working_dir,
-                  'strategy': provider_conf.strategy
+                  "g5k_user": os.environ.get("USER"),
+                  "working_dir": provider_conf.working_dir,
+                  "strategy": provider_conf.strategy
                   }
     # pm_inventory_path = os.path.join(os.getcwd(), "pm_hosts")
     # generate_inventory(*g5k_init, pm_inventory_path)
     # deploy virtual machines with ansible playbook
-    run_ansible([PLAYBOOK_PATH], roles=g5k_roles, extra_vars=extra_vars)
+    all_pms = []
+    for machine in provider_conf.machines:
+        all_pms.extend(machine.undercloud)
+    all_pms = {"all": all_pms}
+
+    run_ansible([PLAYBOOK_PATH], roles=all_pms,  extra_vars=extra_vars)
 
 
 class VirtualMachine(Host):
+    """Internal data structure to manipulate virtual machines."""
 
     def __init__(self, name, eui, flavour_desc, pm, extra=None):
-        super().__init__(str(get_subnet_ip(eui)), alias=name, extra=extra)
+        super().__init__(str(_get_subnet_ip(eui)), alias=name, extra=extra)
         self.core = flavour_desc["core"]
         # libvirt uses kiB by default
         self.mem = int(flavour_desc["mem"]) * 1024
@@ -179,13 +226,6 @@ class VirtualMachine(Host):
         return int(self.eui) == int(other.eui)
 
 
-def _to_hosts(roles):
-    _roles = {}
-    for role, machines in roles.items():
-        _roles[role] = [m.to_host() for m in machines]
-    return _roles
-
-
 class VMonG5k(Provider):
     """The provider to use when deploying virtual machines on Grid'5000."""
 
@@ -195,21 +235,14 @@ class VMonG5k(Provider):
         g5k_roles, g5k_networks = g5k_provider.init()
         g5k_subnet = [n for n in g5k_networks if "__subnet__" in n["roles"]][0]
 
-        extra = {}
-        if self.provider_conf.gateway:
-            extra.update(gateway=self.provider_conf.gateway)
-        if self.provider_conf.gateway_user:
-            extra.update(gateway_user=self.provider_conf.gateway_user)
-        vmong5k_roles = _distribute(self.provider_conf.machines,
-                                    g5k_roles,
-                                    g5k_subnet,
-                                    extra=extra)
-
-        start_virtualmachines(self.provider_conf,
-                              g5k_roles,
-                              vmong5k_roles)
-
-        return _to_hosts(vmong5k_roles), [g5k_subnet]
+        # we concretize the virtualmachines
+        for machine in self.provider_conf.machines:
+            pms = g5k_roles[machine.cookie]
+            machine.undercloud = pms
+
+        roles, networks = start_virtualmachines(self.provider_conf,
+                                                g5k_subnet)
+        return roles, networks
 
     def destroy(self):
         pass
diff --git a/enoslib/infra/enos_vmong5k/schema.py b/enoslib/infra/enos_vmong5k/schema.py
index d0d5a7c4dcbc84a2a46f01c46534a1dbfdd2d65e..d882891edb4a1658db1c006bd0af9bb22e22c3a8 100644
--- a/enoslib/infra/enos_vmong5k/schema.py
+++ b/enoslib/infra/enos_vmong5k/schema.py
@@ -39,15 +39,14 @@ SCHEMA = {
         "type": "object",
         "properties": {
             "roles": {"type": "array", "items": {"type": "string"}},
-            "cluster": {"type": "string"},
             "number": {"type": "number"},
-            "oneOf": [
-                {"flavour": {"type": "string", "enum": list(FLAVOURS.keys())}},
-                {"flavour_desc": {"$ref": "#/flavour_desc"}}
-            ],
-
+            "flavour": {"type": "string", "enum": list(FLAVOURS.keys())},
+            "flavour_desc": {"$ref": "#/flavour_desc"},
+            "cluster": {"type": "string"},
+            "undercloud": {"type": "array", "items": {"type": "object"}}
         },
-        "required": ["roles", "cluster"]
+        "required": ["roles"],
+        "additionalProperties": False
     },
 
     "flavour_desc": {
diff --git a/enoslib/tests/unit/infra/enos_vmong5k/test_provider.py b/enoslib/tests/unit/infra/enos_vmong5k/test_provider.py
index 12bdd34589b8d40b589f98c3ab8e5027b5ac942a..6b069112a9c6a472fe7e0b5c68c4fa394b9a7a57 100644
--- a/enoslib/tests/unit/infra/enos_vmong5k/test_provider.py
+++ b/enoslib/tests/unit/infra/enos_vmong5k/test_provider.py
@@ -12,7 +12,7 @@ from enoslib.tests.unit import EnosTest
 
 class TestBuildG5kConf(EnosTest):
 
-    @mock.patch("enoslib.infra.enos_vmong5k.provider.find_nodes_number", return_value=2)
+    @mock.patch("enoslib.infra.enos_vmong5k.provider._find_nodes_number", return_value=2)
     def test_do_build_g5k_conf(self, mock_find_node_number):
         conf = Configuration()
         conf.add_machine(roles=["r1"],
@@ -42,53 +42,44 @@ class TestBuildG5kConf(EnosTest):
 class TestDistribute(EnosTest):
 
     def test_distribute_minimal(self):
+
+        host = Host("paravance-1")
         machine = MachineConfiguration(roles=["r1"],
-                                       flavour="tiny",
-                                       cluster="paravance",
-                                       number=1)
+                                      flavour="tiny",
+                                      undercloud=[host],
+                                      number=1)
         machines = [machine]
-        host = Host("paravance-1")
-
-        g5k_roles = {
-            "r1": [host],
-            machine.cookie: [host]
-        }
 
         g5k_subnet = {
             "mac_start": "00:16:3E:9E:44:00",
             "mac_end": "00:16:3E:9E:47:FE"
         }
 
-        vmong5k_roles = _distribute(machines, g5k_roles, g5k_subnet)
+        vmong5k_roles = _distribute(machines, g5k_subnet)
         self.assertEqual(1, len(vmong5k_roles["r1"]))
         vm = vmong5k_roles["r1"][0]
-        # we skip the first mac 
+        # we skip the first mac
         self.assertEqual(EUI(int(EUI(g5k_subnet['mac_start'])) + 1), vm.eui)
         self.assertEqual(host, vm.pm)
 
 
     def test_distribute_2_vms_1_host(self):
+        host = Host("paravance-1")
         machine = MachineConfiguration(roles=["r1"],
                                        flavour="tiny",
-                                       cluster="paravance",
+                                       undercloud=[host],
                                        number=2)
         machines = [machine]
-        host = Host("paravance-1")
-
-        g5k_roles = {
-            "r1": [host],
-            machine.cookie: [host]
-        }
 
         g5k_subnet = {
             "mac_start": "00:16:3E:9E:44:00",
             "mac_end": "00:16:3E:9E:47:FE"
         }
 
-        vmong5k_roles = _distribute(machines, g5k_roles, g5k_subnet)
+        vmong5k_roles = _distribute(machines, g5k_subnet)
         self.assertEqual(2, len(vmong5k_roles["r1"]))
         vm = vmong5k_roles["r1"][0]
-        # we skip the first mac 
+        # we skip the first mac
         self.assertEqual(EUI(int(EUI(g5k_subnet['mac_start'])) + 1), vm.eui)
         self.assertEqual(host, vm.pm)
 
@@ -97,25 +88,20 @@ class TestDistribute(EnosTest):
         self.assertEqual(host, vm.pm)
 
     def test_distribute_2_vms_2_hosts(self):
+        host0 = Host("paravance-1")
+        host1 = Host("paravance-2")
         machine = MachineConfiguration(roles=["r1"],
                                        flavour="tiny",
-                                       cluster="paravance",
+                                       undercloud=[host0, host1],
                                        number=2)
         machines = [machine]
-        host0 = Host("paravance-1")
-        host1 = Host("paravance-2")
-
-        g5k_roles = {
-            "r1": [host0, host1],
-            machine.cookie: [host0, host1]
-        }
 
         g5k_subnet = {
             "mac_start": EUI("00:16:3E:9E:44:00"),
             "mac_end": EUI("00:16:3E:9E:47:FE")
         }
 
-        vmong5k_roles = _distribute(machines, g5k_roles, g5k_subnet)
+        vmong5k_roles = _distribute(machines, g5k_subnet)
         self.assertEqual(2, len(vmong5k_roles["r1"]))
         vm = vmong5k_roles["r1"][0]
         # we skip the first mac