cloud: ovirt: add developer README.rst (#22685)
* cloud: ovirt: add developer README.rst * Copy edits
This commit is contained in:
parent
61d2201a2d
commit
d0bb2a41d4
1 changed files with 217 additions and 0 deletions
217
lib/ansible/modules/cloud/ovirt/README.rst
Normal file
217
lib/ansible/modules/cloud/ovirt/README.rst
Normal file
|
@ -0,0 +1,217 @@
|
|||
oVirt Ansible Modules
|
||||
=====================
|
||||
|
||||
This is a set of modules for interacting with oVirt/RHV. This document
|
||||
serves as developer coding guidelines for creating oVIRT/RHV modules.
|
||||
|
||||
Naming
|
||||
------
|
||||
|
||||
- All modules should start with an ``ovirt_`` prefix.
|
||||
- All modules should be named after the resource it manages in singular
|
||||
form.
|
||||
- All modules that gather facts should have a ``_facts``
|
||||
suffix.
|
||||
|
||||
Interface
|
||||
---------
|
||||
|
||||
- Every module should return the ID of the resource it manages.
|
||||
- Every module should return the dictionary of the resource it manages.
|
||||
- Never change the name of the parameter, as we guarantee backward
|
||||
compatibility. Use aliases instead.
|
||||
- If a parameter can't achieve idempotency for any reason, please
|
||||
document it.
|
||||
|
||||
Interoperability
|
||||
----------------
|
||||
|
||||
- All modules should work against all minor versions of
|
||||
version 4 of the API. Version 3 of the API is not supported.
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
- All modules should use ``ovirt_full_argument_spec`` or
|
||||
``ovirt_facts_full_argument_spec`` to pick up the standard input (such
|
||||
as auth and ``fetch_nested``).
|
||||
- All modules should use ``extends_documentation_fragment``: ovirt to go
|
||||
along with ``ovirt_full_argument_spec``.
|
||||
- All facts modules should use ``extends_documentation_fragment``:
|
||||
``ovirt_facts`` to go along with ``ovirt_facts_full_argument_spec``.
|
||||
- Functions that are common to all modules should be implemeneted in the
|
||||
``module_utils/ovirt.py`` file, so they can be reused.
|
||||
- Python SDK version 4 must be used.
|
||||
|
||||
New module development
|
||||
----------------------
|
||||
|
||||
Please read
|
||||
`link <http://docs.ansible.com/ansible/dev_guide/developing_modules.html#how-to-develop-a-module>`__,
|
||||
first to know what common properties, functions and features every module must
|
||||
have.
|
||||
|
||||
In order to achieve idempotency of oVirt entity attributes, a helper class
|
||||
was created. The first thing you need to do is to extend this class and override a few
|
||||
methods:
|
||||
|
||||
.. code:: python
|
||||
|
||||
try:
|
||||
import ovirtsdk4.types as otypes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ansible.module_utils.ovirt import (
|
||||
BaseModule,
|
||||
equal
|
||||
)
|
||||
|
||||
class ClustersModule(BaseModule):
|
||||
|
||||
# The build method builds the entity we want to create.
|
||||
# Always be sure to build only the parameters the user specified
|
||||
# in his yaml file, so we don't change the values which we shouldn't
|
||||
# change. If you set the parameter to None, nothing will be changed.
|
||||
def build_entity(self):
|
||||
return otypes.Cluster(
|
||||
name=self.param('name'),
|
||||
comment=self.param('comment'),
|
||||
description=self.param('description'),
|
||||
)
|
||||
|
||||
# The update_check method checks if the update is needed to be done on
|
||||
# the entity. The equal method doesn't check the values which are None,
|
||||
# which means it doesn't check the values which user didn't set in yaml.
|
||||
# All other values are checked and if there is found some mismatch,
|
||||
# the update method is run on the entity, the entity is build by
|
||||
# 'build_entity' method. You don't have to care about calling the update,
|
||||
# it's called behind the scene by the 'BaseModule' class.
|
||||
def update_check(self, entity):
|
||||
return (
|
||||
equal(self.param('comment'), entity.comment)
|
||||
and equal(self.param('description'), entity.description)
|
||||
)
|
||||
|
||||
The code above handle the check if the entity should be updated, so we
|
||||
don't update the entity if not needed and also it construct the needed
|
||||
entity of the SDK.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ovirt import (
|
||||
check_sdk,
|
||||
create_connection,
|
||||
ovirt_full_argument_spec,
|
||||
)
|
||||
|
||||
# This module will support two states of the cluster,
|
||||
# either it will be present or absent. The user can
|
||||
# specify three parameters: name, comment and description,
|
||||
# The 'ovirt_full_argument_spec' function, will merge the
|
||||
# parameters created here with some common one like 'auth':
|
||||
argument_spec = ovirt_full_argument_spec(
|
||||
state=dict(
|
||||
choices=['present', 'absent'],
|
||||
default='present',
|
||||
),
|
||||
name=dict(default=None, required=True),
|
||||
description=dict(default=None),
|
||||
comment=dict(default=None),
|
||||
)
|
||||
|
||||
# Create the Ansible module, please always implement the
|
||||
# feautre called 'check_mode', for 'create', 'update' and
|
||||
# 'delete' operations it's implemented by default in BaseModule:
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
# Check if the user has Python SDK installed:
|
||||
check_sdk(module)
|
||||
|
||||
try:
|
||||
auth = module.params.pop('auth')
|
||||
|
||||
# Create the connection to the oVirt engine:
|
||||
connection = create_connection(auth)
|
||||
|
||||
# Create the service which manages the entity:
|
||||
clusters_service = connection.system_service().clusters_service()
|
||||
|
||||
# Create the module which will handle create, update and delete flow:
|
||||
clusters_module = ClustersModule(
|
||||
connection=connection,
|
||||
module=module,
|
||||
service=clusters_service,
|
||||
)
|
||||
|
||||
# Check the state and call the appropriate method:
|
||||
state = module.params['state']
|
||||
if state == 'present':
|
||||
ret = clusters_module.create()
|
||||
elif state == 'absent':
|
||||
ret = clusters_module.remove()
|
||||
|
||||
# The return value of the 'create' and 'remove' method is dictionary
|
||||
# with the 'id' of the entity we manage and the type of the entity
|
||||
# with filled in attributes of the entity. The 'change' status is
|
||||
# also returned by those methods:
|
||||
module.exit_json(**ret)
|
||||
except Exception as e:
|
||||
# Modules can't raises exception, it always must exit with
|
||||
# 'module.fail_json' in case of exception. Always use
|
||||
# 'exception=traceback.format_exc' for debugging purposes:
|
||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
# Logout only in case the user passed the 'token' in 'auth'
|
||||
# parameter:
|
||||
connection.close(logout=auth.get('token') is None)
|
||||
|
||||
If your module must support action handling (for example,
|
||||
virtual machine start) you must ensure that you handle the states of the
|
||||
virtual machine correctly, and document the behavior of the
|
||||
module:
|
||||
|
||||
.. code:: python
|
||||
|
||||
if state == 'running':
|
||||
ret = vms_module.action(
|
||||
action='start',
|
||||
post_action=vms_module._post_start_action,
|
||||
action_condition=lambda vm: (
|
||||
vm.status not in [
|
||||
otypes.VmStatus.MIGRATING,
|
||||
otypes.VmStatus.POWERING_UP,
|
||||
otypes.VmStatus.REBOOT_IN_PROGRESS,
|
||||
otypes.VmStatus.WAIT_FOR_LAUNCH,
|
||||
otypes.VmStatus.UP,
|
||||
otypes.VmStatus.RESTORING_STATE,
|
||||
]
|
||||
),
|
||||
wait_condition=lambda vm: vm.status == otypes.VmStatus.UP,
|
||||
# Start action kwargs:
|
||||
use_cloud_init=use_cloud_init,
|
||||
use_sysprep=use_sysprep,
|
||||
# ...
|
||||
)
|
||||
|
||||
As you can see from the preceding example, the ``action`` method accepts the ``action_condition`` and
|
||||
``wait_condition``, which are methods which accept the virtual machine
|
||||
object as a parameter, so you can check whether the virtual
|
||||
machine is in a proper state before the action. The rest of the
|
||||
parameters are for the ``start`` action. You may also handle pre-
|
||||
or post- action tasks by defining ``pre_action`` and ``post_action``
|
||||
parameters.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
- Integration testing is currently done in oVirt's CI system
|
||||
`here <http://jenkins.ovirt.org/job/ovirt_master-ansible-system-tests/>`__
|
||||
and
|
||||
`here <https://github.com/oVirt/ovirt-system-tests/tree/master/ansible-suite-master/>`__.
|
||||
- Please consider using these integrationtests if you create a new module or add a new feature to an existing
|
||||
module.
|
Loading…
Reference in a new issue