This commit is contained in:
Benjamin Bohard 2021-09-29 10:45:20 +02:00
parent d3ebf05027
commit 45aaef98cf
5 changed files with 792 additions and 35 deletions

150
README.md
View File

@ -1,3 +1,153 @@
# eole-galaxy
Utilitaires pour gérer les modules EOLE à partir dansible
## Alimentation de la configuration de Zéphir
### zephir_etab
Le module a pour but la gestion des établissements.
Un établissement est identifié par un numéro *rne*.
``` plantuml
object etablissements
etablissements : rne (character varying(8) not null pkey)
etablissements : libelle (character varying(200) not null)
etablissements : type (integer not null references types_etab)
etablissements : ville (character varying(50) not null)
etablissements : cp (character varying(5) not null)
etablissements : adresse (character varying(100))
etablissements : tel (character varying(20))
etablissements : fax (character varying(20))
etablissements : mail (character varying(100))
etablissements : responsable (character varying(30))
etablissements : remarques (text)
object types_etab
types_etab : id (integer, not null)
types_etab : libelle (character varying(80), not null)
```
Quoique les champs `libelle`, `type`, `ville`, `cp` soient également obligatoires à la création, ils ne sont pas distinctifs de létablissement.
Le champ type fait référence à la table `types_etab` qui contient déjà des valeurs appropriées pour le contexte de lÉducation nationale.
Aucune API nest prévue pour modifier cette table.
#### Fonctionnement du module
Le module tire partie de lAPI XMLRPC authentifiée accessible localement.
Le module nécessite donc, outre les paramètres attendus par la base de données, des paramètres dauthentification.
##### Implémentation cible
``` flowchart
st=>start: Entrée
e=>end: Sortie
createproxy=>operation: Création du proxy
listetabs=>operation: Liste des établissements
etabexist=>condition: Létablissement existe déjà ?
samedata=>condition: Les données sont les mêmes ?
doingnothing=>operation: Ne rien faire
modifydata=>operation: Modification des données
createetab=>operation: Création de létablissement
st->createproxy
createproxy->listetabs
listetabs->etabexist
etabexist(yes)->samedata
samedata(yes)->doingnothing
doingnothing->e
samedata(no)->modifydata
modifydata->e
etabexist(no)->createetab
createetab->e
```
##### Implémentation actuelle
``` flowchart
st3=>start: start run_module
io5=>inputoutput: input:
op8=>operation: key_mapping = {'rne': 'rne', 'libelle': 'libelle', 'ville': 'ville', 'cp': 'code_postal', 'type': 'etab_type', 'adresse': 'adresse', 'tel': 'tel', 'fax': 'fax', 'mail': 'mail', 'responsable': 'responsable', 'remarques': 'remarques'}
op10=>operation: module_args = dict(zephir_user=dict(type='str', required=True), zephir_user_password=dict(type='str', required=True), rne=dict(type='str', required=True), libelle=dict(type='str', required=True), ville=dict(type='str', required=True), code_postal=dict(type='str', required=True), etab_type=dict(type='int', required=True), adresse=dict(type='str', required=False, default=''), tel=dict(type='str', required=False, default=''), fax=dict(type='str', required=False, default=''), mail=dict(type='str', required=False, default=''), responsable=dict(type='str', required=False, default=''), remarques=dict(type='str', required=False, default=''), state=dict(type='str', required=True, default='present'))
op12=>operation: result = dict(changed=False, rne=None, msg='')
op14=>operation: module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
cond17=>operation: module.exit_json(**result) if module.check_mode
op27=>operation: port_zephir = str((int(config.PORT_ZEPHIR) + 1))
op29=>operation: proxy_addr = 'http://{0}:{1}@localhost:{2}/'.format(module.params['zephir_user'], module.params['zephir_user_password'], port_zephir)
op31=>operation: proxy = EoleProxy(proxy_addr)
op33=>operation: (return_code, etabs) = proxy.etabs.get_etab()
cond36=>operation: etabs = {m['rne']: m for m in etabs} if return_code
cond47=>condition: if (module.params['rne'] in etabs)
op51=>operation: result['msg'] = 'Etab {} already exists'.format(module.params['rne'])
op53=>operation: data_change = {}
cond56=>condition: for (key, value) in etabs[module.params['rne']]
cond73=>operation: data_change[key] = module.params[key_mapping[key]] if (module.params[key_mapping[key]] != value)
cond86=>operation: module.exit_json(**result) if (not data_change)
op99=>operation: (return_code, proxy_msg) = proxy.etabs.add_etab(module.params['rne'], module.params['libelle'], module.params['adresse'], module.params['ville'], module.params['code_postal'], module.params['tel'], module.params['fax'], module.params['mail'], module.params['responsable'], module.params['remarques'], module.params['etab_type'])
cond102=>condition: if return_code
op106=>operation: result['changed'] = True
op108=>operation: result['rne'] = proxy_msg
op110=>operation: result['msg'] = 'Etab {}'.format(module.params['rne'])
sub119=>subroutine: module.exit_json(**result)
e121=>end: end run_module
op114=>operation: result['msg'] = 'Etab {} not created: {}'.format(module.params['libelle'], proxy_msg)
sub116=>subroutine: module.fail_json(**result)
st3->io5
io5->op8
op8->op10
op10->op12
op12->op14
op14->cond17
cond17->op27
op27->op29
op29->op31
op31->op33
op33->cond36
cond36->cond47
cond47(yes)->op51
op51->op53
op53->cond56
cond56(yes)->cond73
cond73->cond56
cond56(no)->cond86
cond86->op99
op99->cond102
cond102(yes)->op106
op106->op108
op108->op110
op110->sub119
sub119->e121
cond102(no)->op114
op114->sub116
sub116->sub119
cond47(no)->op99
```
### zephir_serveur
### zephir_module
### zephir_variante
## Configuration dun serveur
### creoleset
Le module a pour but de permettre la modification des variables de configuration creole.
La difficulté réside dans les liens de dépendances qui peuvent exister entre variables et la nécessité de pouvoir faire des modifications par bloc, de façon atomique.
``` flowchart
start=>start: début dexécution
e=>end: fin dexécution
creole_loader=>operation: Création de lobjet config en lecture/écriture
tri_variables=>operation: Tri des variables à modifier
start->creole_loader
creole_loader->tri_variables
tri_variables->e
```
### zephir_register

View File

@ -53,6 +53,15 @@ diff:
from ansible.module_utils.basic import AnsibleModule
from creole.loader import creole_loader, config_save_values
def process_value(variable, value):
if variable.impl_is_multi():
if not isinstance(value, list):
return [value]
else:
if isinstance(value, list):
raise Exception('Variable {} is not multi'.format(variable))
return value
def run_module():
# define available arguments/parameters a user can pass to the module
@ -89,8 +98,10 @@ def run_module():
diff = []
c.cfgimpl_get_settings().remove('disabled')
variables = {c.find_first(byname=variable['name']): variable['value']
for variable in module.params['variables']}
variables = {}
for variable in module.params['variables']:
creole_variable = c.find_first(byname=variable['name'])
variables[creole_variable] = process_value(creole_variable, variable['value'])
slaves = [v for v in variables if v.impl_is_master_slaves('slave')]
@ -105,36 +116,41 @@ def run_module():
ordered_variables.extend(masters.items())
for variable, sub_variables in ordered_variables:
value = variables[variable]
if not isinstance(value, list):
values = [value]
else:
values = value
new_value = variables[variable]
var_path = d.impl_get_path_by_opt(variable)
old_value = c.getattr(var_path)
if not variable.impl_is_multi():
method = 'exact'
else:
method = module.params['state']
if variable.impl_is_multi():
method = module.params['state']
if method == 'exact':
if old_value != values:
diff.append('{}: {} => {}'.format(var_path, old_value, value))
c.setattr(var_path, value)
if old_value != new_value:
diff.append('{}: {} => {}'.format(var_path, old_value, new_value))
if sub_variables:
c.setattr(var_path, [])
for sub_variable in sub_variables:
sub_var_path = d.impl_get_path_by_opt(sub_variable)
c.setattr(sub_var_path, [])
c.cfgimpl_get_settings().remove('validator')
c.setattr(var_path, new_value)
for sub_variable in sub_variables:
sub_value = variables[sub_variable]
sub_var_path = d.impl_get_path_by_opt(sub_variable)
sub_old_value = c.getattr(sub_var_path)
if sub_old_value != sub_value:
diff.append('{}: {} => {}'.format(sub_var_path, sub_old_value, sub_value))
c.setattr(sub_var_path, sub_value)
for i in range(len(new_value)):
c.getattr(sub_var_path)[i] = sub_value[i]
c.cfgimpl_get_settings().append('validator')
if method == 'present':
else:
c.setattr(var_path, new_value)
elif method == 'present':
is_present = False
group_dict = {}
for sub_variable in sub_variables:
group_dict[d.impl_get_path_by_opt(sub_variable)] = variables[sub_variable]
for v_index, value in enumerate(values):
for v_index, value in enumerate(new_value):
for index, item in enumerate(c.getattr(var_path)):
if value == item and all([c.getattr(sv)[index] == group_dict[sv] for sv in group_dict]):
is_present = True
@ -148,6 +164,19 @@ def run_module():
c.getattr(sub_variable)[-1] = sub_value[v_index]
diff.append('{}: {} => {}'.format(sub_variable, c.getattr(sub_variable)[:-1], c.getattr(sub_variable)))
else:
if old_value != new_value:
diff.append('{}: {} => {}'.format(var_path, old_value, new_value))
c.setattr(var_path, new_value)
for sub_variable in sub_variables:
sub_value = variables[sub_variable]
sub_var_path = d.impl_get_path_by_opt(sub_variable)
sub_old_value = c.getattr(sub_var_path)
if sub_old_value != sub_value:
diff.append('{}: {} => {}'.format(sub_var_path, sub_old_value, sub_value))
c.setattr(sub_var_path, sub_value)
result['diff'] = diff
c.cfgimpl_get_settings().append('disabled')
#c.make_dict()

View File

@ -0,0 +1,231 @@
#!/usr/bin/python
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: zephir_variante
short_description: This is a module to automate edition of zephir configuration
# If this is part of a collection, you need to use semantic versioning,
# i.e. the version is of the form "2.5.0" and not "2.4".
version_added: "1.0.0"
description: This is my longer description explaining my test module.
options:
zephir_user:
description: zephir user authorized to perform action
required: true
type: str
zephir_user_password:
description: zephir user password
required: true
type: str
rne:
description: Unique identifier
required: true
type: str
libelle:
description: etab libelle
required: true
type: str
ville:
description: city where etab belongs
required: true
type: str
code_postal:
description: postal code related to city
required: true
type: str
etab_type:
description: integer identifying etab type
required: true
type: int
adresse:
description: etab postal address
required: false
type: str
tel:
description: telephone number used to contact the one in charge
required: false
type: str
fax:
description: fax number used to transfer data to the one in charge
required: false
type: str
mail:
description: mail address used to contact the one in charge
required: false
type: str
responsable: identity of the one in charge
description:
required: false
type: str
remarque: other information
description:
required: false
type: str
state:
description: wether data have to be added or deleted from the database
required: false
type: str
# Specify this value according to your collection
# in format of namespace.collection.doc_fragment_name
extends_documentation_fragment:
- cadoles.eole.zephir_variante
author:
- Cadoles
'''
EXAMPLES = r'''
# Pass in a message
- libelle: Test with a message
cadoles.eole.zephir_variante:
module: eolebase-2.7.2
module: dhcp
'''
RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
id:
description: id the variant is associated with.
type: int
returned: always
sample: 1
module:
description: module the variant is associated with.
type: str
returned: always
sample: eolebase-2.7.2
'''
from ansible.module_utils.basic import AnsibleModule
from zephir.eolerpclib import EoleProxy
from zephir.web import config
def run_module():
# define available arguments/parameters a user can pass to the module
key_mapping = {'rne': 'rne',
'libelle': 'libelle',
'ville': 'ville',
'cp': 'code_postal',
'type': 'etab_type',
'adresse': 'adresse',
'tel': 'tel',
'fax': 'fax',
'mail': 'mail',
'responsable': 'responsable',
'remarques': 'remarques'
}
module_args = dict(
zephir_user=dict(type='str', required=True),
zephir_user_password=dict(type='str', required=True),
rne=dict(type='str', required=True),
libelle=dict(type='str', required=True),
ville=dict(type='str', required=True),
code_postal=dict(type='str', required=True),
etab_type=dict(type='int', required=True),
adresse=dict(type='str', required=False, default=''),
tel=dict(type='str', required=False, default=''),
fax=dict(type='str', required=False, default=''),
mail=dict(type='str', required=False, default=''),
responsable=dict(type='str', required=False, default=''),
remarques=dict(type='str', required=False, default=''),
state=dict(type='str', required=False, default='present'),
)
# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
rne=None,
msg='',
)
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
if module.check_mode:
module.exit_json(**result)
#module.fail_json(**result)
#module.params['module']
port_zephir = int(config.PORT_ZEPHIR) + 1
proxy_addr = "http://{0}:{1}@localhost:{2}/".format(module.params['zephir_user'], module.params['zephir_user_password'], port_zephir)
proxy = EoleProxy(proxy_addr)
return_code, etabs = proxy.etabs.get_etab()
if return_code:
etabs = {m['rne']:m for m in etabs}
if module.params['rne'] in etabs:
if module.params['state'] == 'absent':
return_code, proxy_msg = proxy.etabs.del_etab(module.params['rne'])
result['changed'] = True
result['rne'] = proxy_msg
result['msg'] = 'Etab {}'.format(module.params['rne'])
module.exit_json(**result)
result['msg'] = 'Etab {} already exists'.format(module.params['rne'])
data_change = {}
for key, value in etabs[module.params['rne']].items():
if module.params[key_mapping[key]] != value:
data_change[key] = module.params[key_mapping[key]]
if not data_change:
result['changed'] = False
result['rne'] = proxy_msg
result['msg'] = 'Etab {}'.format(module.params['rne'])
module.exit_json(**result)
return_code, proxy_msg = proxy.etabs.edit_etab(module.params['rne'], data_change)
else:
if module.params['state'] == 'absent':
result['changed'] = False
result['rne'] = proxy_msg
result['msg'] = 'Etab {}'.format(module.params['rne'])
module.exit_json(**result)
return_code, proxy_msg = proxy.etabs.add_etab(module.params['rne'],
module.params['libelle'],
module.params['adresse'],
module.params['ville'],
module.params['code_postal'],
module.params['tel'],
module.params['fax'],
module.params['mail'],
module.params['responsable'],
module.params['remarques'],
module.params['etab_type'])
result['changed'] = True
result['rne'] = proxy_msg
result['msg'] = 'Etab {}'.format(module.params['rne'])
module.exit_json(**result)
if return_code:
result['changed'] = True
result['rne'] = proxy_msg
result['msg'] = 'Etab {}'.format(module.params['rne'])
module.exit_json(**result)
else:
result['msg'] = 'Etab {} not created: {}'.format(module.params['libelle'], proxy_msg)
module.fail_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,201 @@
#!/usr/bin/python
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: zephir_variante
short_description: This is a module to automate edition of zephir configuration
# If this is part of a collection, you need to use semantic versioning,
# i.e. the version is of the form "2.5.0" and not "2.4".
version_added: "1.0.0"
description: This is my longer description explaining my test module.
options:
zephir_user:
description: zephir user authorized to perform action
required: true
type: str
zephir_user_password:
description: zephir user password
required: true
type: str
etab:
description: etab the server is related to
required: true
type: str
module:
description: module the server is an instance of
required: true
type: str
variante:
description: module variant the server is an instance of
required: false
type: int
# Specify this value according to your collection
# in format of namespace.collection.doc_fragment_name
extends_documentation_fragment:
- cadoles.eole.zephir_variante
author:
- Cadoles
'''
EXAMPLES = r'''
# Pass in a message
- name: Test with a message
cadoles.eole.zephir_variante:
module: eolebase-2.7.2
module: dhcp
'''
RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
id:
description: id the variant is associated with.
type: int
returned: always
sample: 1
module:
description: module the variant is associated with.
type: str
returned: always
sample: eolebase-2.7.2
'''
from ansible.module_utils.basic import AnsibleModule
from zephir.eolerpclib import EoleProxy
from zephir.web import config
import time
def serveur_id_from_libelle(proxy, libelle):
return_code, servers = proxy.serveurs.get_serveur()
servers = {s['libelle']: s['id'] for s in servers}
return servers.get(libelle, None)
def etab_id_from_libelle(proxy, libelle):
return_code, etabs = proxy.etabs.get_etab()
etabs = {e['libelle']: e['rne'] for e in etabs}
return etabs.get(libelle, None)
def module_id_from_libelle(proxy, libelle):
return_code, modules = proxy.modules.get_module()
modules = {m['libelle']: m['id'] for m in modules}
return modules.get(libelle, None)
def variante_id_from_libelle(proxy, libelle):
return_code, variantes = proxy.modules.get_variante()
variantes = {v['libelle']: v['id'] for v in variantes}
return variantes.get(libelle, None)
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
zephir_user=dict(type='str', required=True),
zephir_user_password=dict(type='str', required=True),
rne=dict(type='str', required=True),
libelle=dict(type='str', required=True),
materiel=dict(type='str', required=False, default=''),
processeur=dict(type='str', required=False, default=''),
disque_dur=dict(type='str', required=False, default=''),
date_install=dict(type='str', required=False, default=time.strftime('%Y-%m-%d', time.localtime())),
installateur=dict(type='str', required=False, default=''),
tel=dict(type='str', required=False, default=''),
remarques=dict(type='str', required=False, default=''),
module_initial=dict(type='str', required=False),
module_actuel=dict(type='str', required=True),
timeout=dict(type='int', required=False, default=60),
variante=dict(type='str', required=True),
cle_rsa1=dict(type='str', required=False, default=''),
id_groupe=dict(type='int', required=False, default=-1),
)
# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
id=None,
msg='',
)
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
if module.check_mode:
module.exit_json(**result)
#module.fail_json(**result)
#module.params['module']
port_zephir = str(int(config.PORT_ZEPHIR) + 1)
proxy_addr = "http://{0}:{1}@localhost:{2}/".format(module.params['zephir_user'], module.params['zephir_user_password'], port_zephir)
proxy = EoleProxy(proxy_addr)
module.params['module_actuel'] = module_id_from_libelle(proxy, module.params['module_actuel'])
module.params['variante'] = variante_id_from_libelle(proxy, module.params['variante'])
module.params['rne'] = etab_id_from_libelle(proxy, module.params['rne'])
if not module.params.get('module_initial', None):
module.params['module_initial'] = module.params['module_actuel']
else:
module.params['module_initial'] = module_id_from_libelle(proxy, module.params['module_initial'])
return_code, module_descr = proxy.modules.get_module(module.params['module_actuel'])
if not return_code:
result['msg'] = 'Unknown module {}'.format(module.params['module_actuel'])
module.fail_json(**result)
return_code, proxy_msg = proxy.serveurs.add_serveur(
module.params['rne'],
module.params['libelle'],
module.params['materiel'],
module.params['processeur'],
module.params['disque_dur'],
module.params['date_install'],
module.params['installateur'],
module.params['tel'],
module.params['remarques'],
module.params['module_initial'],
module.params['module_actuel'],
module.params['timeout'],
module.params['variante'],
module.params['cle_rsa1'],
module.params['id_groupe'],
)
if return_code:
result['changed'] = True
result['id'] = proxy_msg
result['msg'] = 'Server {} created in etab {} with id {}'.format(module.params['libelle'], module.params['rne'], result['id'])
else:
result['msg'] = 'Server {} already exists {}'.format(module.params['libelle'], proxy_msg)
# during the execution of the module, if there is an exception or a
# conditional state that effectively causes a failure, run
# AnsibleModule.fail_json() to pass in the message and the result
# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,146 @@
#!/usr/bin/python
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: zephir_variante
short_description: This is a module to automate edition of zephir configuration
# If this is part of a collection, you need to use semantic versioning,
# i.e. the version is of the form "2.5.0" and not "2.4".
version_added: "1.0.0"
description: This is my longer description explaining my test module.
options:
zephir_user:
description: zephir user authorized to perform action
required: true
type: str
zephir_user_password:
description: zephir user password
required: true
type: str
module:
description: module the variant is related to
required: true
type: str
name:
description: variant name
required: true
type: str
copy_id:
description: variant id this variant is copied from
required: false
type: int
variant_password:
description: password protecting variant edition
required: false
type: str
# Specify this value according to your collection
# in format of namespace.collection.doc_fragment_name
extends_documentation_fragment:
- cadoles.eole.zephir_variante
author:
- Cadoles
'''
EXAMPLES = r'''
# Pass in a message
- name: Test with a message
cadoles.eole.zephir_variante:
module: eolebase-2.7.2
module: dhcp
'''
RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
id:
description: id the variant is associated with.
type: int
returned: always
sample: 1
module:
description: module the variant is associated with.
type: str
returned: always
sample: eolebase-2.7.2
'''
from ansible.module_utils.basic import AnsibleModule
from zephir.eolerpclib import EoleProxy
from zephir.web import config
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
zephir_user=dict(type='str', required=True),
zephir_user_password=dict(type='str', required=True),
module=dict(type='str', required=True),
name=dict(type='str', required=True),
copied_id=dict(type='int', required=False),
variant_password=dict(type='str', required=False),
)
# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
id=None,
msg='',
)
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
if module.check_mode:
module.exit_json(**result)
#module.fail_json(**result)
#module.params['module']
port_zephir = str(int(config.PORT_ZEPHIR) + 1)
proxy_addr = "http://{0}:{1}@localhost:{2}/".format(module.params['zephir_user'], module.params['zephir_user_password'], port_zephir)
proxy = EoleProxy(proxy_addr)
modules = {m['libelle']: m['id'] for m in proxy.modules.get_module()[1]}
module_id = modules.get(module.params['module'], None)
if not module_id:
result['msg'] = 'Unknown module {}'.format(module.params['module'])
module.fail_json(**result)
return_code, proxy_msg = proxy.modules.add_variante(module_id, module.params['name'])
if return_code:
result['changed'] = True
result['id'] = proxy_msg
result['msg'] = 'Variant {} of module {} created with id {}'.format(module.params['name'], module.params['module'], result['id'])
else:
result['msg'] = 'Variant {} already exists'.format(module.params['name'])
# during the execution of the module, if there is an exception or a
# conditional state that effectively causes a failure, run
# AnsibleModule.fail_json() to pass in the message and the result
# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()