summaryrefslogtreecommitdiff
path: root/web_infrastructure
diff options
context:
space:
mode:
authorAlejandro Guirao <lekumberri@gmail.com>2015-07-04 18:50:29 +0200
committerBrian Coca <brian.coca+git@gmail.com>2016-01-12 13:37:39 -0500
commit58a61459b3ec43df9eb36f2d2c5a9efa4fb9301f (patch)
tree21ce76b35cbb5a616fc6dad7aa3d220df8c95328 /web_infrastructure
parenta42203bdb3d2c53ffe0ce9727d7f46c8483458e6 (diff)
downloadansible-modules-extras-58a61459b3ec43df9eb36f2d2c5a9efa4fb9301f.tar.gz
Add taiga_issue module
Diffstat (limited to 'web_infrastructure')
-rw-r--r--web_infrastructure/taiga_issue.py311
1 files changed, 311 insertions, 0 deletions
diff --git a/web_infrastructure/taiga_issue.py b/web_infrastructure/taiga_issue.py
new file mode 100644
index 00000000..e90f83c1
--- /dev/null
+++ b/web_infrastructure/taiga_issue.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2015, Alejandro Guirao <lekumberri@gmail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+DOCUMENTATION = '''
+---
+module: taiga_issue
+short_description: Creates/deletes an issue in a Taiga Project Management Platform
+description:
+ - Creates/deletes an issue in a Taiga Project Management Platform (U(https://taiga.io)).
+ - An issue is identified by the combination of project, issue subject and issue type.
+ - This module implements the creation or deletion of issues (not the update).
+version_added: "1.9"
+options:
+ taiga_host:
+ description:
+ - The hostname of the Taiga instance.
+ required: False
+ default: https://api.taiga.io
+ project:
+ description:
+ - Name of the project containing the issue. Must exist previously.
+ required: True
+ subject:
+ description:
+ - The issue subject.
+ required: True
+ issue_type:
+ description:
+ - The issue type. Must exist previously.
+ required: True
+ priority:
+ description:
+ - The issue priority. Must exist previously.
+ required: False
+ default: Normal
+ status:
+ description:
+ - The issue status. Must exist previously.
+ required: False
+ default: New
+ severity:
+ description:
+ - The issue severity. Must exist previously.
+ required: False
+ default: New
+ description:
+ description:
+ - The issue description.
+ required: False
+ default: ""
+ attachment:
+ description:
+ - Path to a file to be attached to the issue.
+ required: False
+ default: None
+ attachment_description:
+ description:
+ - A string describing the file to be attached to the issue.
+ required: False
+ default: ""
+ tags:
+ description:
+ - A lists of tags to be assigned to the issue.
+ required: False
+ default: []
+ state:
+ description:
+ - Whether the issue should be present or not.
+ required: False
+ choices: ["present", "absent"]
+ default: present
+author: Alejandro Guirao (@lekum)
+requirements: [python-taiga]
+notes:
+- The authentication is achieved either by the environment variable TAIGA_TOKEN or by the pair of environment variables TAIGA_USERNAME and TAIGA_PASSWORD
+'''
+
+EXAMPLES = '''
+# Create an issue in the my hosted Taiga environment and attach an error log
+- taiga_issue:
+ taiga_host: https://mytaigahost.example.com
+ project: myproject
+ subject: An error has been found
+ issue_type: Bug
+ priority: High
+ status: New
+ severity: High
+ description: An error has been found. Please check the attached error log for details.
+ attachment: /path/to/error.log
+ attachment_description: Error log
+ tags:
+ - Error
+ - Needs manual check
+ state: present
+
+# Deletes the previously created issue
+- taiga_issue:
+ taiga_host: https://mytaigahost.example.com
+ project: myproject
+ subject: An error has been found
+ issue_type: Bug
+ state: absent
+'''
+
+from os import getenv
+from os.path import isfile
+
+try:
+ from taiga import TaigaAPI
+ from taiga.exceptions import TaigaException
+ TAIGA_MODULE_IMPORTED=True
+except ImportError:
+ TAIGA_MODULE_IMPORTED=False
+
+def manage_issue(module, taiga_host, project_name, issue_subject, issue_priority,
+ issue_status, issue_type, issue_severity, issue_description,
+ issue_attachment, issue_attachment_description,
+ issue_tags, state, check_mode=False):
+ """
+ Method that creates/deletes issues depending whether they exist and the state desired
+
+ The credentials should be passed via environment variables:
+ - TAIGA_TOKEN
+ - TAIGA_USERNAME and TAIGA_PASSWORD
+
+ Returns a tuple with these elements:
+ - A boolean representing the success of the operation
+ - A descriptive message
+ - A dict with the issue attributes, in case of issue creation, otherwise empty dict
+ """
+
+ changed = False
+
+ try:
+ token = getenv('TAIGA_TOKEN')
+ if token:
+ api = TaigaAPI(host=taiga_host, token=token)
+ else:
+ api = TaigaAPI(host=taiga_host)
+ username = getenv('TAIGA_USERNAME')
+ password = getenv('TAIGA_PASSWORD')
+ if not any([username, password]):
+ return (False, changed, "Missing credentials", {})
+ api.auth(username=username, password=password)
+
+ user_id = api.me().id
+ project_list = filter(lambda x: x.name == project_name, api.projects.list(member=user_id))
+ if len(project_list) != 1:
+ return (False, changed, "Unable to find project %s" % project_name, {})
+ project = project_list[0]
+ project_id = project.id
+
+ priority_list = filter(lambda x: x.name == issue_priority, api.priorities.list(project=project_id))
+ if len(priority_list) != 1:
+ return (False, changed, "Unable to find issue priority %s for project %s" % (issue_priority, project_name), {})
+ priority_id = priority_list[0].id
+
+ status_list = filter(lambda x: x.name == issue_status, api.issue_statuses.list(project=project_id))
+ if len(status_list) != 1:
+ return (False, changed, "Unable to find issue status %s for project %s" % (issue_status, project_name), {})
+ status_id = status_list[0].id
+
+ type_list = filter(lambda x: x.name == issue_type, project.list_issue_types())
+ if len(type_list) != 1:
+ return (False, changed, "Unable to find issue type %s for project %s" % (issue_type, project_name), {})
+ type_id = type_list[0].id
+
+ severity_list = filter(lambda x: x.name == issue_severity, project.list_severities())
+ if len(severity_list) != 1:
+ return (False, changed, "Unable to find severity %s for project %s" % (issue_severity, project_name), {})
+ severity_id = severity_list[0].id
+
+ issue = {
+ "project": project_name,
+ "subject": issue_subject,
+ "priority": issue_priority,
+ "status": issue_status,
+ "type": issue_type,
+ "severity": issue_severity,
+ "description": issue_description,
+ "tags": issue_tags,
+ }
+
+ # An issue is identified by the project_name, the issue_subject and the issue_type
+ matching_issue_list = filter(lambda x: x.subject == issue_subject and x.type == type_id, project.list_issues())
+ matching_issue_list_len = len(matching_issue_list)
+
+ if matching_issue_list_len == 0:
+ # The issue does not exist in the project
+ if state == "present":
+ # This implies a change
+ changed = True
+ if not check_mode:
+ # Create the issue
+ new_issue = project.add_issue(issue_subject, priority_id, status_id, type_id, severity_id, tags=issue_tags, description=issue_description)
+ if issue_attachment:
+ new_issue.attach(issue_attachment, description=issue_attachment_description)
+ issue["attachment"] = issue_attachment
+ issue["attachment_description"] = issue_attachment_description
+ return (True, changed, "Issue created", issue)
+
+ else:
+ # If does not exist, do nothing
+ return (True, changed, "Issue does not exist", {})
+
+ elif matching_issue_list_len == 1:
+ # The issue exists in the project
+ if state == "absent":
+ # This implies a change
+ changed = True
+ if not check_mode:
+ # Delete the issue
+ matching_issue_list[0].delete()
+ return (True, changed, "Issue deleted", {})
+
+ else:
+ # Do nothing
+ return (True, changed, "Issue already exists", {})
+
+ else:
+ # More than 1 matching issue
+ return (False, changed, "More than one issue with subject %s in project %s" % (issue_subject, project_name), {})
+
+ except TaigaException:
+ msg = "An exception happened: %s" % sys.exc_info()[1]
+ return (False, changed, msg, {})
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ taiga_host=dict(required=False, default="https://api.taiga.io"),
+ project=dict(required=True),
+ subject=dict(required=True),
+ issue_type=dict(required=True),
+ priority=dict(required=False, default="Normal"),
+ status=dict(required=False, default="New"),
+ severity=dict(required=False, default="Normal"),
+ description=dict(required=False, default=""),
+ attachment=dict(required=False, default=None),
+ attachment_description=dict(required=False, default=""),
+ tags=dict(required=False, default=[], type='list'),
+ state=dict(required=False, choices=['present','absent'], default='present'),
+ ),
+ supports_check_mode=True
+ )
+
+ if not TAIGA_MODULE_IMPORTED:
+ msg = "This module needs python-taiga module"
+ module.fail_json(msg=msg)
+
+ taiga_host = module.params['taiga_host']
+ project_name = module.params['project']
+ issue_subject = module.params['subject']
+ issue_priority = module.params['priority']
+ issue_status = module.params['status']
+ issue_type = module.params['issue_type']
+ issue_severity = module.params['severity']
+ issue_description = module.params['description']
+ issue_attachment = module.params['attachment']
+ issue_attachment_description = module.params['attachment_description']
+ if issue_attachment:
+ if not isfile(issue_attachment):
+ msg = "%s is not a file" % issue_attachment
+ module.fail_json(msg=msg)
+ issue_tags = module.params['tags']
+ state = module.params['state']
+
+ return_status, changed, msg, issue_attr_dict = manage_issue(
+ module,
+ taiga_host,
+ project_name,
+ issue_subject,
+ issue_priority,
+ issue_status,
+ issue_type,
+ issue_severity,
+ issue_description,
+ issue_attachment,
+ issue_attachment_description,
+ issue_tags,
+ state,
+ check_mode=module.check_mode
+ )
+ if return_status:
+ if len(issue_attr_dict) > 0:
+ module.exit_json(changed=changed, msg=msg, issue=issue_attr_dict)
+ else:
+ module.exit_json(changed=changed, msg=msg)
+ else:
+ module.fail_json(msg=msg)
+
+
+from ansible.module_utils.basic import *
+main()