diff --git a/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels.py b/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels.py
new file mode 100644
index 00000000000..3e79b1a3e69
--- /dev/null
+++ b/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels.py
@@ -0,0 +1,203 @@
+#!/usr/bin/pythonapi/
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Red Hat, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+import traceback
+
+try:
+ import ovirtsdk4.types as otypes
+except ImportError:
+ pass
+
+from collections import defaultdict
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.ovirt import (
+ BaseModule,
+ check_sdk,
+ create_connection,
+ ovirt_full_argument_spec,
+)
+
+
+DOCUMENTATION = '''
+---
+module: ovirt_affinity_labels
+short_description: Module to affinity labels in oVirt
+version_added: "2.3"
+author: "Ondra Machacek (@machacekondra)"
+description:
+ - "This module manage affinity labels in oVirt. It can also manage assignments
+ of those labels to hosts and VMs."
+options:
+ name:
+ description:
+ - "Name of the the affinity label to manage."
+ required: true
+ state:
+ description:
+ - "Should the affinity label be present or absent."
+ choices: ['present', 'absent']
+ default: present
+ cluster:
+ description:
+ - "Name of the cluster where vms and hosts resides."
+ vms:
+ description:
+ - "List of the VMs names, which should have assigned this affinity label."
+ hosts:
+ description:
+ - "List of the hosts names, which should have assigned this affinity label."
+extends_documentation_fragment: ovirt
+'''
+
+EXAMPLES = '''
+# Examples don't contain auth parameter for simplicity,
+# look at ovirt_auth module to see how to reuse authentication:
+
+# Create(if not exists) and assign affinity label to vms vm1 and vm2 and host host1
+- ovirt_affinity_labels:
+ name: mylabel
+ cluster: mycluster
+ vms:
+ - vm1
+ - vm2
+ hosts:
+ - host1
+
+# To detach all VMs from label
+- ovirt_affinity_labels:
+ name: mylabel
+ cluster: mycluster
+ vms: []
+
+# Remove affinity label
+- ovirt_affinity_labels:
+ state: absent
+ name: mylabel
+'''
+
+RETURN = '''
+id:
+ description: ID of the affinity label which is managed
+ returned: On success if affinity label is found.
+ type: str
+ sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
+template:
+ description: "Dictionary of all the affinity label attributes. Affinity label attributes can be found on your oVirt instance
+ at following url: https://ovirt.example.com/ovirt-engine/api/model#types/affinity_label."
+ returned: On success if affinity label is found.
+'''
+
+
+class AffinityLabelsModule(BaseModule):
+
+ def build_entity(self):
+ return otypes.AffinityLabel(name=self._module.params['name'])
+
+ def post_create(self, entity):
+ self.update_check(entity)
+
+ def pre_remove(self, entity):
+ self._module.params['vms'] = []
+ self._module.params['hosts'] = []
+ self.update_check(entity)
+
+ def _update_label_assignments(self, entity, name, label_obj_type):
+ objs_service = getattr(self._connection.system_service(), '%s_service' % name)()
+ if self._module.params[name] is not None:
+ objs = self._connection.follow_link(getattr(entity, name))
+ objs_names = defaultdict(list)
+ for obj in objs:
+ labeled_entity = objs_service.service(obj.id).get()
+ if self._module.params['cluster'] is None:
+ objs_names[labeled_entity.name].append(obj.id)
+ elif self._connection.follow_link(labeled_entity.cluster).name == self._module.params['cluster']:
+ objs_names[labeled_entity.name].append(obj.id)
+
+ for obj in self._module.params[name]:
+ if obj not in objs_names:
+ for obj_id in objs_service.list(
+ search='name=%s and cluster=%s' % (obj, self._module.params['cluster'])
+ ):
+ label_service = getattr(self._service.service(entity.id), '%s_service' % name)()
+ if not self._module.check_mode:
+ label_service.add(**{
+ name[:-1]: label_obj_type(id=obj_id.id)
+ })
+ self.changed = True
+
+ for obj in objs_names:
+ if obj not in self._module.params[name]:
+ label_service = getattr(self._service.service(entity.id), '%s_service' % name)()
+ if not self._module.check_mode:
+ for obj_id in objs_names[obj]:
+ label_service.service(obj_id).remove()
+ self.changed = True
+
+ def update_check(self, entity):
+ self._update_label_assignments(entity, 'vms', otypes.Vm)
+ self._update_label_assignments(entity, 'hosts', otypes.Host)
+ return True
+
+
+def main():
+ argument_spec = ovirt_full_argument_spec(
+ state=dict(
+ choices=['present', 'absent'],
+ default='present',
+ ),
+ cluster=dict(default=None),
+ name=dict(default=None, required=True),
+ vms=dict(default=None, type='list'),
+ hosts=dict(default=None, type='list'),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ('state', 'present', ['cluster']),
+ ],
+ )
+ check_sdk(module)
+
+ try:
+ connection = create_connection(module.params.pop('auth'))
+ affinity_labels_service = connection.system_service().affinity_labels_service()
+ affinity_labels_module = AffinityLabelsModule(
+ connection=connection,
+ module=module,
+ service=affinity_labels_service,
+ )
+
+ state = module.params['state']
+ if state == 'present':
+ ret = affinity_labels_module.create()
+ elif state == 'absent':
+ ret = affinity_labels_module.remove()
+
+ module.exit_json(**ret)
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+ finally:
+ connection.close(logout=False)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels_facts.py b/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels_facts.py
new file mode 100644
index 00000000000..9d13c4cb3d6
--- /dev/null
+++ b/lib/ansible/modules/extras/cloud/ovirt/ovirt_affinity_labels_facts.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Red Hat, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+import fnmatch
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.ovirt import (
+ check_sdk,
+ create_connection,
+ get_dict_of_struct,
+ ovirt_full_argument_spec,
+)
+
+
+DOCUMENTATION = '''
+---
+module: ovirt_affinity_labels_facts
+short_description: Retrieve facts about one or more oVirt affinity labels
+author: "Ondra Machacek (@machacekondra)"
+version_added: "2.3"
+description:
+ - "Retrieve facts about one or more oVirt affinity labels."
+notes:
+ - "This module creates a new top-level C(affinity_labels) fact, which
+ contains a list of affinity labels."
+options:
+ name:
+ description:
+ - "Name of the affinity labels which should be listed."
+ vm:
+ description:
+ - "Name of the VM, which affinity labels should be listed."
+ host:
+ description:
+ - "Name of the host, which affinity labels should be listed."
+extends_documentation_fragment: ovirt
+'''
+
+EXAMPLES = '''
+# Examples don't contain auth parameter for simplicity,
+# look at ovirt_auth module to see how to reuse authentication:
+
+# Gather facts about all affinity labels, which names start with C(label):
+- ovirt_affinity_labels_facts:
+ name: label*
+- debug:
+ var: affinity_labels
+
+# Gather facts about all affinity labels, which are assigned to VMs
+# which names start with C(postgres):
+- ovirt_affinity_labels_facts:
+ vm: postgres*
+- debug:
+ var: affinity_labels
+
+# Gather facts about all affinity labels, which are assigned to hosts
+# which names start with C(west):
+- ovirt_affinity_labels_facts:
+ host: west*
+- debug:
+ var: affinity_labels
+
+# Gather facts about all affinity labels, which are assigned to hosts
+# which names start with C(west) or VMs which names start with C(postgres):
+- ovirt_affinity_labels_facts:
+ host: west*
+ vm: postgres*
+- debug:
+ var: affinity_labels
+'''
+
+RETURN = '''
+ovirt_vms:
+ description: "List of dictionaries describing the affinity labels. Affinity labels attribues are mapped to dictionary keys,
+ all affinity labels attributes can be found at following url: https://ovirt.example.com/ovirt-engine/api/model#types/affinity_label."
+ returned: On success.
+ type: list
+'''
+
+
+def main():
+ argument_spec = ovirt_full_argument_spec(
+ name=dict(default=None),
+ host=dict(default=None),
+ vm=dict(default=None),
+ )
+ module = AnsibleModule(argument_spec)
+ check_sdk(module)
+
+ try:
+ connection = create_connection(module.params.pop('auth'))
+ affinity_labels_service = connection.system_service().affinity_labels_service()
+ labels = []
+ all_labels = affinity_labels_service.list()
+ if module.params['name']:
+ labels.extend([
+ l for l in all_labels
+ if fnmatch.fnmatch(l.name, module.params['name'])
+ ])
+ if module.params['host']:
+ hosts_service = connection.system_service().hosts_service()
+ labels.extend([
+ label
+ for label in all_labels
+ for host in connection.follow_link(label.hosts)
+ if fnmatch.fnmatch(hosts_service.service(host.id).get().name, module.params['host'])
+ ])
+ if module.params['vm']:
+ vms_service = connection.system_service().vms_service()
+ labels.extend([
+ label
+ for label in all_labels
+ for vm in connection.follow_link(label.vms)
+ if fnmatch.fnmatch(vms_service.service(vm.id).get().name, module.params['vm'])
+ ])
+
+ if not (module.params['vm'] or module.params['host'] or module.params['name']):
+ labels = all_labels
+
+ module.exit_json(
+ changed=False,
+ ansible_facts=dict(
+ affinity_labels=[
+ get_dict_of_struct(l) for l in labels
+ ],
+ ),
+ )
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+ finally:
+ connection.close(logout=False)
+
+
+if __name__ == '__main__':
+ main()