Add the snow_record module. Module to create/update/delete/read records (#27931)

in ServiceNow

Remove "updated" as a option for state, per review from bcoca.  Update
examples section, and tested.

Update metadata to 1.1

Rip out some more instances of updated from documentation.

Update for ansible 2.5 first version
This commit is contained in:
Tim Rightnour 2017-10-16 09:58:32 -07:00 committed by ansibot
parent b44c38930b
commit 85bd104c7c

View file

@ -0,0 +1,331 @@
#!/usr/bin/python
# Copyright (c) 2017 Tim Rightnour <thegarbledone@gmail.com>
# 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
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: snow_record
short_description: Create/Delete/Update records in ServiceNow
version_added: "2.5"
description:
- Creates/Deletes/Updates a single record in ServiceNow
options:
instance:
description:
- The service now instance name
required: true
username:
description:
- User to connect to ServiceNow as
required: true
password:
description:
- Password for username
required: true
table:
description:
- Table to query for records
required: false
default: incident
state:
description:
- If C(present) is supplied with a C(number)
argument, the module will attempt to update the record with
the supplied data. If no such record exists, a new one will
be created. C(absent) will delete a record.
choices: [ present, absent ]
required: true
data:
description:
- key, value pairs of data to load into the record.
See Examples. Required for C(state:present)
number:
description:
- Record number to update. Required for C(state:absent)
required: false
lookup_field:
description:
- Changes the field that C(number) uses to find records
required: false
default: number
attachment:
description:
- Attach a file to the record
required: false
requirements:
- python pysnow (pysnow)
author:
- Tim Rightnour (@garbled1)
'''
EXAMPLES = '''
- name: Grab a user record
snow_record:
username: ansible_test
password: my_password
instance: dev99999
state: present
number: 62826bf03710200044e0bfc8bcbe5df1
table: sys_user
lookup_field: sys_id
- name: Create an incident
snow_record:
username: ansible_test
password: my_password
instance: dev99999
state: present
data:
short_description: "This is a test incident opened by Ansible"
severity: 3
priority: 2
register: new_incident
- name: Delete the record we just made
snow_record:
username: admin
password: xxxxxxx
instance: dev99999
state: absent
number: "{{new_incident['record']['number']}}"
- name: Delete a non-existant record
snow_record:
username: ansible_test
password: my_password
instance: dev99999
state: absent
number: 9872354
failed_when: false
- name: Update an incident
snow_record:
username: ansible_test
password: my_password
instance: dev99999
state: present
number: INC0000055
data:
work_notes : "Been working all day on this thing."
- name: Attach a file to an incident
snow_record:
username: ansible_test
password: my_password
instance: dev99999
state: present
number: INC0000055
attachment: README.md
tags: attach
'''
RETURN = '''
record:
description: Record data from Service Now
type: dict
returned: when supported
attached_file:
description: Details of the file that was attached via C(attachment)
type: dict
returned: when supported
'''
import os
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes, to_native
# Pull in pysnow
HAS_PYSNOW = False
try:
import pysnow
HAS_PYSNOW = True
except ImportError:
pass
def run_module():
# define the available arguments/parameters that a user can pass to
# the module
module_args = dict(
instance=dict(default=None, type='str', required=True),
username=dict(default=None, type='str', required=True, no_log=True),
password=dict(default=None, type='str', required=True, no_log=True),
table=dict(type='str', required=False, default='incident'),
state=dict(choices=['present', 'absent'],
type='str', required=True),
number=dict(default=None, required=False, type='str'),
data=dict(default=None, requried=False, type='dict'),
lookup_field=dict(default='number', required=False, type='str'),
attachment=dict(default=None, required=False, type='str')
)
module_required_if = [
['state', 'absent', ['number']],
]
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
required_if=module_required_if
)
# check for pysnow
if not HAS_PYSNOW:
module.fail_json(msg='pysnow module required')
params = module.params
instance = params['instance']
username = params['username']
password = params['password']
table = params['table']
state = params['state']
number = params['number']
data = params['data']
lookup_field = params['lookup_field']
result = dict(
changed=False,
instance=instance,
table=table,
number=number,
lookup_field=lookup_field
)
# check for attachments
if params['attachment'] is not None:
attach = params['attachment']
b_attach = to_bytes(attach, errors='surrogate_or_strict')
if not os.path.exists(b_attach):
module.fail_json(msg="Attachment {0} not found".format(attach))
result['attachment'] = attach
else:
attach = None
# Connect to ServiceNow
try:
conn = pysnow.Client(instance=instance, user=username,
password=password)
except Exception as detail:
module.fail_json(msg='Could not connect to ServiceNow: {0}'.format(str(detail)), **result)
# Deal with check mode
if module.check_mode:
# if we are in check mode and have no number, we would have created
# a record. We can only partially simulate this
if number is None:
result['record'] = dict(data)
result['changed'] = True
# do we want to check if the record is non-existent?
elif state == 'absent':
try:
record = conn.query(table=table, query={lookup_field: number})
res = record.get_one()
result['record'] = dict(Success=True)
result['changed'] = True
except pysnow.exceptions.NoResults:
result['record'] = None
except Exception as detail:
module.fail_json(msg="Unknown failure in query record: {0}".format(str(detail)), **result)
# Let's simulate modification
else:
try:
record = conn.query(table=table, query={lookup_field: number})
res = record.get_one()
for key, value in data.items():
res[key] = value
result['changed'] = True
result['record'] = res
except pysnow.exceptions.NoResults:
snow_error = "Record does not exist"
module.fail_json(msg=snow_error, **result)
except Exception as detail:
module.fail_json(msg="Unknown failure in query record: {0}".format(str(detail)), **result)
module.exit_json(**result)
# now for the real thing: (non-check mode)
# are we creating a new record?
if state == 'present' and number is None:
try:
record = conn.insert(table=table, payload=dict(data))
except pysnow.UnexpectedResponse as e:
snow_error = "Failed to create record: {0}, details: {1}".format(e.error_summary, e.error_details)
module.fail_json(msg=snow_error, **result)
result['record'] = record
result['changed'] = True
# we are deleting a record
elif state == 'absent':
try:
record = conn.query(table=table, query={lookup_field: number})
res = record.delete()
except pysnow.exceptions.NoResults:
res = dict(Success=True)
except pysnow.exceptions.MultipleResults:
snow_error = "Multiple record match"
module.fail_json(msg=snow_error, **result)
except pysnow.UnexpectedResponse as e:
snow_error = "Failed to delete record: {0}, details: {1}".format(e.error_summary, e.error_details)
module.fail_json(msg=snow_error, **result)
except Exception as detail:
snow_error = "Failed to delete record: {0}".format(str(detail))
module.fail_json(msg=snow_error, **result)
result['record'] = res
result['changed'] = True
# We want to update a record
else:
try:
record = conn.query(table=table, query={lookup_field: number})
if data is not None:
res = record.update(dict(data))
result['record'] = res
result['changed'] = True
else:
res = record.get_one()
result['record'] = res
if attach is not None:
res = record.attach(b_attach)
result['changed'] = True
result['attached_file'] = res
except pysnow.exceptions.MultipleResults:
snow_error = "Multiple record match"
module.fail_json(msg=snow_error, **result)
except pysnow.exceptions.NoResults:
snow_error = "Record does not exist"
module.fail_json(msg=snow_error, **result)
except pysnow.UnexpectedResponse as e:
snow_error = "Failed to update record: {0}, details: {1}".format(e.error_summary, e.error_details)
module.fail_json(msg=snow_error, **result)
except Exception as detail:
snow_error = "Failed to update record: {0}".format(str(detail))
module.fail_json(msg=snow_error, **result)
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()