Add additional mapping attributes for VM/Template registration (#32835)

* Add VnicProfileMapping to register VM

Add vnic profile mappings to be supported in vm registration

* Add VnicProfileMapping to register template

Add vnic profile mappings to be supported in template registration

* Add reassign bad macs to register VM

Add reassign bad macs to be supported in vm registration.

* Add additional mappings params for VM registration

As part of the effort to support DR with oVirt
the "Register" operation is being added with a new mapping parameter
that describes the configuration of the registration.

The idea of supporting DR site to site in oVirt is to have 2 active
setups using storage replication between the primary setup and the
secondary setup.
Both setups will have active DCs, clusters, and hosts, although those
will not be identical.
The user can define a mapping which will be used to recover its setup.

Each mapping can be used to map any VM's attribute stored in the OVF
with its correlated entity.
For example, there could be a primary setup with a VM configured on cluster A.
We also keep an active secondary setup which only have cluster B.
Cluster B is compatible for that VM and in case of a DR scenario theoretically
the storage domain can be imported to the secondary setup and the use can
register the VM to cluster B.

In that case, we can automate the recovery process by defining a cluster mapping,
so once the entity will be registered its OVF will indicate it belongs to
cluster A but the mapping which will be sent will indicate that cluster B should
be valid for every thing that is configured on cluster A.
The engine should do the switch, and register the VM to cluster B in the secondary site.

Cluster mapping is just one example.
The following list describes the different mappings which were
introduced:
  LUN mapping
  Role mapping
  Permissions mapping
  Affinity group mapping
  Affinity label mapping

Each mapping will be used for its specific OVF's data once the register operation
will take place in the engine.

* Add additional mappings params for Template registration

As part of the effort to support DR with oVirt
the "Register" operation is being added with a new mapping parameter
that describes the configuration of the registration.

The idea of supporting DR site to site in oVirt is to have 2 active
setups using storage replication between the primary setup and the
secondary setup.
Both setups will have active DCs, clusters, and hosts, although those
will not be identical.
The user can define a mapping which will be used to recover its setup.

Each mapping can be used to map any Template's attribute stored in the OVF
with its correlated entity.
For example, there could be a primary setup with a Template configured on cluster A.
We also keep an active secondary setup which only have cluster B.
Cluster B is compatible for that Template and in case of a DR scenario theoretically
the storage domain can be imported to the secondary setup and the use can
register the Template to cluster B.

In that case, we can automate the recovery process by defining a cluster mapping,
so once the entity will be registered its OVF will indicate it belongs to
cluster A but the mapping which will be sent will indicate that cluster B should
be valid for every thing that is configured on cluster A.
The engine should do the switch, and register the Template to cluster B in the
secondary site.

Cluster mapping is just one example.
The following list describes the different mappings which were
introduced:
  Role mapping
  Permissions mapping

Each mapping will be used for its specific OVF's data once the register operation
will take place in the engine.

* Add support for update OVF store

Add support for task of update OVF store in a storage domain.
This commit is contained in:
maorlipchuk 2018-01-16 14:14:29 +02:00 committed by ansibot
parent 41ce1eaea9
commit 6fe0215c8f
3 changed files with 434 additions and 6 deletions

View file

@ -42,9 +42,10 @@ options:
- "Name of the storage domain to manage. (Not required when state is I(imported))" - "Name of the storage domain to manage. (Not required when state is I(imported))"
state: state:
description: description:
- "Should the storage domain be present/absent/maintenance/unattached/imported" - "Should the storage domain be present/absent/maintenance/unattached/imported/update_ovf_store"
- "I(imported) is supported since version 2.4." - "I(imported) is supported since version 2.4."
choices: ['present', 'absent', 'maintenance', 'unattached'] - "I(update_ovf_store) is supported since version 2.5, currently if C(wait) is (true), we don't wait for update."
choices: ['present', 'absent', 'maintenance', 'unattached', 'update_ovf_store']
default: present default: present
description: description:
description: description:
@ -203,6 +204,11 @@ EXAMPLES = '''
address: 10.34.63.199 address: 10.34.63.199
path: /path/export path: /path/export
# Update OVF_STORE:
- ovirt_storage_domains:
state: update_ovf_store
name: domain
# Create ISO NFS storage domain # Create ISO NFS storage domain
- ovirt_storage_domains: - ovirt_storage_domains:
name: myiso name: myiso
@ -523,7 +529,7 @@ def control_state(sd_module):
def main(): def main():
argument_spec = ovirt_full_argument_spec( argument_spec = ovirt_full_argument_spec(
state=dict( state=dict(
choices=['present', 'absent', 'maintenance', 'unattached', 'imported'], choices=['present', 'absent', 'maintenance', 'unattached', 'imported', 'update_ovf_store'],
default='present', default='present',
), ),
id=dict(default=None), id=dict(default=None),
@ -602,7 +608,10 @@ def main():
storage_domain=storage_domains_service.service(ret['id']).get() storage_domain=storage_domains_service.service(ret['id']).get()
) )
ret['changed'] = storage_domains_module.changed ret['changed'] = storage_domains_module.changed
elif state == 'update_ovf_store':
ret = storage_domains_module.action(
action='update_ovf_store'
)
module.exit_json(**ret) module.exit_json(**ret)
except Exception as e: except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc()) module.fail_json(msg=str(e), exception=traceback.format_exc())

View file

@ -64,6 +64,38 @@ options:
description: description:
- "Boolean indication whether to allow partial registration of a template when C(state) is registered." - "Boolean indication whether to allow partial registration of a template when C(state) is registered."
version_added: "2.4" version_added: "2.4"
vnic_profile_mappings:
description:
- "Mapper which maps an external virtual NIC profile to one that exists in the engine when C(state) is registered.
vnic_profile is described by the following dictionary:"
- "C(source_network_name): The network name of the source network."
- "C(source_profile_name): The prfile name related to the source network."
- "C(target_profile_id): The id of the target profile id to be mapped to in the engine."
version_added: "2.5"
cluster_mappings:
description:
- "Mapper which maps cluster name between Template's OVF and the destination cluster this Template should be registered to,
relevant when C(state) is registered.
Cluster mapping is described by the following dictionary:"
- "C(source_name): The name of the source cluster."
- "C(dest_name): The name of the destination cluster."
version_added: "2.5"
role_mappings:
description:
- "Mapper which maps role name between Template's OVF and the destination role this Template should be registered to,
relevant when C(state) is registered.
Role mapping is described by the following dictionary:"
- "C(source_name): The name of the source role."
- "C(dest_name): The name of the destination role."
version_added: "2.5"
domain_mappings:
description:
- "Mapper which maps aaa domain name between Template's OVF and the destination aaa domain this Template should be registered to,
relevant when C(state) is registered.
The aaa domain mapping is described by the following dictionary:"
- "C(source_name): The name of the source aaa domain."
- "C(dest_name): The name of the destination aaa domain."
version_added: "2.5"
exclusive: exclusive:
description: description:
- "When C(state) is I(exported) this parameter indicates if the existing templates with the - "When C(state) is I(exported) this parameter indicates if the existing templates with the
@ -154,6 +186,36 @@ EXAMPLES = '''
cluster: mycluster cluster: mycluster
id: 1111-1111-1111-1111 id: 1111-1111-1111-1111
# Register template with vnic profile mappings
- ovirt_templates:
state: registered
storage_domain: mystorage
cluster: mycluster
id: 1111-1111-1111-1111
vnic_profile_mappings:
- source_network_name: mynetwork
source_profile_name: mynetwork
target_profile_id: 3333-3333-3333-3333
- source_network_name: mynetwork2
source_profile_name: mynetwork2
target_profile_id: 4444-4444-4444-4444
# Register template with mapping
- ovirt_templates:
state: registered
storage_domain: mystorage
cluster: mycluster
id: 1111-1111-1111-1111
role_mappings:
- source_name: Role_A
dest_name: Role_B
domain_mappings:
- source_name: Domain_A
dest_name: Domain_B
cluster_mappings:
- source_name: cluster_A
dest_name: cluster_B
# Import image from Glance s a template # Import image from Glance s a template
- ovirt_templates: - ovirt_templates:
state: imported state: imported
@ -246,6 +308,74 @@ class TemplatesModule(BaseModule):
self._service = self._connection.system_service().templates_service() self._service = self._connection.system_service().templates_service()
def _get_role_mappings(module):
roleMappings = list()
for roleMapping in module.params['role_mappings']:
roleMappings.append(
otypes.RegistrationRoleMapping(
from_=otypes.Role(
name=roleMapping['source_name'],
) if roleMapping['source_name'] else None,
to=otypes.Role(
name=roleMapping['dest_name'],
) if roleMapping['dest_name'] else None,
)
)
return roleMappings
def _get_domain_mappings(module):
domainMappings = list()
for domainMapping in module.params['domain_mappings']:
domainMappings.append(
otypes.RegistrationDomainMapping(
from_=otypes.Domain(
name=domainMapping['source_name'],
) if domainMapping['source_name'] else None,
to=otypes.Domain(
name=domainMapping['dest_name'],
) if domainMapping['dest_name'] else None,
)
)
return domainMappings
def _get_cluster_mappings(module):
clusterMappings = list()
for clusterMapping in module.params['cluster_mappings']:
clusterMappings.append(
otypes.RegistrationClusterMapping(
from_=otypes.Cluster(
name=clusterMapping['source_name'],
),
to=otypes.Cluster(
name=clusterMapping['dest_name'],
),
)
)
return clusterMappings
def _get_vnic_profile_mappings(module):
vnicProfileMappings = list()
for vnicProfileMapping in module.params['vnic_profile_mappings']:
vnicProfileMappings.append(
otypes.VnicProfileMapping(
source_network_name=vnicProfileMapping['source_network_name'],
source_network_profile_name=vnicProfileMapping['source_profile_name'],
target_vnic_profile=otypes.VnicProfile(
id=vnicProfileMapping['target_profile_id'],
) if vnicProfileMapping['target_profile_id'] else None,
)
)
return vnicProfileMappings
def main(): def main():
argument_spec = ovirt_full_argument_spec( argument_spec = ovirt_full_argument_spec(
state=dict( state=dict(
@ -268,6 +398,10 @@ def main():
image_disk=dict(default=None, aliases=['glance_image_disk_name']), image_disk=dict(default=None, aliases=['glance_image_disk_name']),
template_image_disk_name=dict(default=None), template_image_disk_name=dict(default=None),
seal=dict(type='bool'), seal=dict(type='bool'),
vnic_profile_mappings=dict(default=[], type='list'),
cluster_mappings=dict(default=[], type='list'),
role_mappings=dict(default=[], type='list'),
domain_mappings=dict(default=[], type='list'),
) )
module = AnsibleModule( module = AnsibleModule(
argument_spec=argument_spec, argument_spec=argument_spec,
@ -388,7 +522,16 @@ def main():
allow_partial_import=module.params['allow_partial_import'], allow_partial_import=module.params['allow_partial_import'],
cluster=otypes.Cluster( cluster=otypes.Cluster(
name=module.params['cluster'] name=module.params['cluster']
) if module.params['cluster'] else None ) if module.params['cluster'] else None,
vnic_profile_mappings=_get_vnic_profile_mappings(module)
if module.params['vnic_profile_mappings'] else None,
registration_configuration=otypes.RegistrationConfiguration(
cluster_mappings=_get_cluster_mappings(module),
role_mappings=_get_role_mappings(module),
domain_mappings=_get_domain_mappings(module),
) if (module.params['cluster_mappings']
or module.params['role_mappings']
or module.params['domain_mappings']) else None
) )
if module.params['wait']: if module.params['wait']:

View file

@ -47,6 +47,65 @@ options:
description: description:
- Boolean indication whether to allow partial registration of Virtual Machine when C(state) is registered. - Boolean indication whether to allow partial registration of Virtual Machine when C(state) is registered.
version_added: "2.4" version_added: "2.4"
vnic_profile_mappings:
description:
- "Mapper which maps an external virtual NIC profile to one that exists in the engine when C(state) is registered.
vnic_profile is described by the following dictionary:"
- "C(source_network_name): The network name of the source network."
- "C(source_profile_name): The prfile name related to the source network."
- "C(target_profile_id): The id of the target profile id to be mapped to in the engine."
version_added: "2.5"
cluster_mappings:
description:
- "Mapper which maps cluster name between VM's OVF and the destination cluster this VM should be registered to,
relevant when C(state) is registered.
Cluster mapping is described by the following dictionary:"
- "C(source_name): The name of the source cluster."
- "C(dest_name): The name of the destination cluster."
version_added: "2.5"
role_mappings:
description:
- "Mapper which maps role name between VM's OVF and the destination role this VM should be registered to,
relevant when C(state) is registered.
Role mapping is described by the following dictionary:"
- "C(source_name): The name of the source role."
- "C(dest_name): The name of the destination role."
version_added: "2.5"
domain_mappings:
description:
- "Mapper which maps aaa domain name between VM's OVF and the destination aaa domain this VM should be registered to,
relevant when C(state) is registered.
The aaa domain mapping is described by the following dictionary:"
- "C(source_name): The name of the source aaa domain."
- "C(dest_name): The name of the destination aaa domain."
version_added: "2.5"
affinity_group_mappings:
description:
- "Mapper which maps affinty name between VM's OVF and the destination affinity this VM should be registered to,
relevant when C(state) is registered."
version_added: "2.5"
affinity_label_mappings:
description:
- "Mappper which maps affinity label name between VM's OVF and the destination label this VM should be registered to,
relevant when C(state) is registered."
version_added: "2.5"
lun_mappings:
description:
- "Mapper which maps lun between VM's OVF and the destination lun this VM should contain, relevant when C(state) is registered.
lun_mappings is described by the following dictionary:
- C(logical_unit_id): The logical unit number to identify a logical unit,
- C(logical_unit_port): The port being used to connect with the LUN disk.
- C(logical_unit_portal): The portal being used to connect with the LUN disk.
- C(logical_unit_address): The address of the block storage host.
- C(logical_unit_target): The iSCSI specification located on an iSCSI server
- C(logical_unit_username): Username to be used to connect to the block storage host.
- C(logical_unit_password): Password to be used to connect to the block storage host.
- C(storage_type): The storage type which the LUN reside on (iscsi or fcp)"
version_added: "2.5"
reassign_bad_macs:
description:
- "Boolean indication whether to reassign bad macs when C(state) is registered."
version_added: "2.5"
template: template:
description: description:
- Name of the template, which should be used to create Virtual Machine. - Name of the template, which should be used to create Virtual Machine.
@ -420,6 +479,56 @@ EXAMPLES = '''
cluster: mycluster cluster: mycluster
id: 1111-1111-1111-1111 id: 1111-1111-1111-1111
- name: Register VM with vnic profile mappings and reassign bad macs
ovirt_vms:
state: registered
storage_domain: mystorage
cluster: mycluster
id: 1111-1111-1111-1111
vnic_profile_mappings:
- source_network_name: mynetwork
source_profile_name: mynetwork
target_profile_id: 3333-3333-3333-3333
- source_network_name: mynetwork2
source_profile_name: mynetwork2
target_profile_id: 4444-4444-4444-4444
reassign_bad_macs: "True"
- name: Register VM with mappings
ovirt_vms:
state: registered
storage_domain: mystorage
cluster: mycluster
id: 1111-1111-1111-1111
role_mappings:
- source_name: Role_A
dest_name: Role_B
domain_mappings:
- source_name: Domain_A
dest_name: Domain_B
lun_mappings:
- source_storage_type: iscsi
source_logical_unit_id: 1IET_000d0001
source_logical_unit_port: 3260
source_logical_unit_portal: 1
source_logical_unit_address: 10.34.63.203
source_logical_unit_target: iqn.2016-08-09.brq.str-01:omachace
dest_storage_type: iscsi
dest_logical_unit_id: 1IET_000d0002
dest_logical_unit_port: 3260
dest_logical_unit_portal: 1
dest_logical_unit_address: 10.34.63.204
dest_logical_unit_target: iqn.2016-08-09.brq.str-02:omachace
affinity_group_mappings:
- source_name: Affinity_A
dest_name: Affinity_B
affinity_label_mappings:
- source_name: Label_A
dest_name: Label_B
cluster_mappings:
- source_name: cluster_A
dest_name: cluster_B
- name: Creates a stateless VM which will always use latest template version - name: Creates a stateless VM which will always use latest template version
ovirt_vms: ovirt_vms:
name: myvm name: myvm
@ -1019,6 +1128,148 @@ class VmsModule(BaseModule):
self.changed = True self.changed = True
def _get_role_mappings(module):
roleMappings = list()
for roleMapping in module.params['role_mappings']:
roleMappings.append(
otypes.RegistrationRoleMapping(
from_=otypes.Role(
name=roleMapping['source_name'],
) if roleMapping['source_name'] else None,
to=otypes.Role(
name=roleMapping['dest_name'],
) if roleMapping['dest_name'] else None,
)
)
return roleMappings
def _get_affinity_group_mappings(module):
affinityGroupMappings = list()
for affinityGroupMapping in module.params['affinity_group_mappings']:
affinityGroupMappings.append(
otypes.RegistrationAffinityGroupMapping(
from_=otypes.AffinityGroup(
name=affinityGroupMapping['source_name'],
) if affinityGroupMapping['source_name'] else None,
to=otypes.AffinityGroup(
name=affinityGroupMapping['dest_name'],
) if affinityGroupMapping['dest_name'] else None,
)
)
return affinityGroupMappings
def _get_affinity_label_mappings(module):
affinityLabelMappings = list()
for affinityLabelMapping in module.params['affinity_label_mappings']:
affinityLabelMappings.append(
otypes.RegistrationAffinityLabelMapping(
from_=otypes.AffinityLabel(
name=affinityLabelMapping['source_name'],
) if affinityLabelMapping['source_name'] else None,
to=otypes.AffinityLabel(
name=affinityLabelMapping['dest_name'],
) if affinityLabelMapping['dest_name'] else None,
)
)
return affinityLabelMappings
def _get_domain_mappings(module):
domainMappings = list()
for domainMapping in module.params['domain_mappings']:
domainMappings.append(
otypes.RegistrationDomainMapping(
from_=otypes.Domain(
name=domainMapping['source_name'],
) if domainMapping['source_name'] else None,
to=otypes.Domain(
name=domainMapping['dest_name'],
) if domainMapping['dest_name'] else None,
)
)
return domainMappings
def _get_lun_mappings(module):
lunMappings = list()
for lunMapping in module.params['lun_mappings']:
lunMappings.append(
otypes.RegistrationLunMapping(
from_=otypes.Disk(
lun_storage=otypes.HostStorage(
type=otypes.StorageType(lunMapping['source_storage_type'])
if (lunMapping['source_storage_type'] in
['iscsi', 'fcp']) else None,
logical_units=[
otypes.LogicalUnit(
id=lunMapping['source_logical_unit_id'],
)
],
),
) if lunMapping['source_logical_unit_id'] else None,
to=otypes.Disk(
lun_storage=otypes.HostStorage(
type=otypes.StorageType(lunMapping['dest_storage_type'])
if (lunMapping['dest_storage_type'] in
['iscsi', 'fcp']) else None,
logical_units=[
otypes.LogicalUnit(
id=lunMapping['dest_logical_unit_id'],
port=lunMapping['dest_logical_unit_port'],
portal=lunMapping['dest_logical_unit_portal'],
address=lunMapping['dest_logical_unit_address'],
target=lunMapping['dest_logical_unit_target'],
password=lunMapping['dest_logical_unit_password'],
username=lunMapping['dest_logical_unit_username'],
)
],
),
) if lunMapping['dest_logical_unit_id'] else None,
),
),
return lunMappings
def _get_cluster_mappings(module):
clusterMappings = list()
for clusterMapping in module.params['cluster_mappings']:
clusterMappings.append(
otypes.RegistrationClusterMapping(
from_=otypes.Cluster(
name=clusterMapping['source_name'],
),
to=otypes.Cluster(
name=clusterMapping['dest_name'],
) if clusterMapping['dest_name'] else None,
)
)
return clusterMappings
def _get_vnic_profile_mappings(module):
vnicProfileMappings = list()
for vnicProfileMapping in module.params['vnic_profile_mappings']:
vnicProfileMappings.append(
otypes.VnicProfileMapping(
source_network_name=vnicProfileMapping['source_network_name'],
source_network_profile_name=vnicProfileMapping['source_profile_name'],
target_vnic_profile=otypes.VnicProfile(
id=vnicProfileMapping['target_profile_id'],
) if vnicProfileMapping['target_profile_id'] else None,
)
)
return vnicProfileMappings
def import_vm(module, connection): def import_vm(module, connection):
vms_service = connection.system_service().vms_service() vms_service = connection.system_service().vms_service()
if search_by_name(vms_service, module.params['name']) is not None: if search_by_name(vms_service, module.params['name']) is not None:
@ -1190,6 +1441,14 @@ def main():
]), ]),
cd_iso=dict(type='str'), cd_iso=dict(type='str'),
boot_devices=dict(type='list'), boot_devices=dict(type='list'),
vnic_profile_mappings=dict(default=[], type='list'),
cluster_mappings=dict(default=[], type='list'),
role_mappings=dict(default=[], type='list'),
affinity_group_mappings=dict(default=[], type='list'),
affinity_label_mappings=dict(default=[], type='list'),
lun_mappings=dict(default=[], type='list'),
domain_mappings=dict(default=[], type='list'),
reassign_bad_macs=dict(default=None, type='bool'),
high_availability=dict(type='bool'), high_availability=dict(type='bool'),
lease=dict(type='str'), lease=dict(type='str'),
stateless=dict(type='bool'), stateless=dict(type='bool'),
@ -1375,7 +1634,24 @@ def main():
allow_partial_import=module.params['allow_partial_import'], allow_partial_import=module.params['allow_partial_import'],
cluster=otypes.Cluster( cluster=otypes.Cluster(
name=module.params['cluster'] name=module.params['cluster']
) if module.params['cluster'] else None ) if module.params['cluster'] else None,
vnic_profile_mappings=_get_vnic_profile_mappings(module)
if module.params['vnic_profile_mappings'] else None,
reassign_bad_macs=module.params['reassign_bad_macs']
if module.params['reassign_bad_macs'] is not None else None,
registration_configuration=otypes.RegistrationConfiguration(
cluster_mappings=_get_cluster_mappings(module),
role_mappings=_get_role_mappings(module),
domain_mappings=_get_domain_mappings(module),
lun_mappings=_get_lun_mappings(module),
affinity_group_mappings=_get_affinity_group_mappings(module),
affinity_label_mappings=_get_affinity_label_mappings(module),
) if (module.params['cluster_mappings']
or module.params['role_mappings']
or module.params['domain_mappings']
or module.params['lun_mappings']
or module.params['affinity_group_mappings']
or module.params['affinity_label_mappings']) else None
) )
if module.params['wait']: if module.params['wait']: