Example #1
0
    def create_jira(self, username):
        options = {
            'server': '%s' % settings.JIRA_OPTS['URL'],
            'verify': settings.JIRA_OPTS.get('VERIFY_SSL', False)
        }
        project = self.cleaned_data.get('project')
        summary = self.cleaned_data.get('summary')
        issue_type = self.cleaned_data.get('issue_type')
        component = self.cleaned_data.get('component')
        description = self.cleaned_data.get('description')
        description += '*JIRA Created by:* %s' % username

        try:
            jconn = JIRA(options, basic_auth=(settings.JIRA_OPTS['USER'], settings.JIRA_OPTS['PASSWORD']))
        except Exception as e:
            logger.error('Error creating JIRA ticket :%s' % e)
            raise forms.ValidationError(u"Error connecting to JIRA ticket, please check the server logs")

        try:
            jira_ticket = jconn.create_issue(project=project, summary=summary, description=description,
                                             issuetype={'name': issue_type}, components=[{'name': component}])
        except Exception as e:
            logger.error('Error creating JIRA ticket project=%s, summary=%s,  issue_type=%s, description=%s,' % (project,
                                                                                                               summary,
                                                                                                               issue_type,
                                                                                                               description))
            logger.error('Server message %s' % e)
            msg = u"Error creating JIRA ticket, please check the project name and issue type. If that doesn't work then check the server logs"
            raise forms.ValidationError(msg)

        if isinstance(jira_ticket, jira.resources.Issue):
            return jira_ticket.key
        else:
            raise forms.ValidationError(u"Error creating JIRA ticket, JIRA server did not return a ticket key.")
Example #2
0
def add_issue(find, push_to_jira):
    eng = Engagement.objects.get(test=find.test)
    prod =  Product.objects.get(engagement= eng)
    jpkey = JIRA_PKey.objects.get(product=prod)
    jira_conf = jpkey.conf

    if push_to_jira:
        if 'Active' in find.status() and 'Verified' in find.status():
            try:
                JIRAError.log_to_tempfile=False
                jira = JIRA(server=jira_conf.url, basic_auth=(jira_conf.username, jira_conf.password))
                if jpkey.component:
                    new_issue = jira.create_issue(project=jpkey.project_key, summary=find.title,
                                                  components=[{'name': jpkey.component}, ],
                                                  description=jira_long_description(find.long_desc(), find.id,
                                                                                    jira_conf.finding_text),
                                                  issuetype={'name': jira_conf.default_issue_type},
                                                  priority={'name': jira_conf.get_priority(find.severity)})
                else:
                    new_issue = jira.create_issue(project=jpkey.project_key, summary=find.title,
                                                  description=jira_long_description(find.long_desc(), find.id,
                                                                                    jira_conf.finding_text),
                                                  issuetype={'name': jira_conf.default_issue_type},
                                                  priority={'name': jira_conf.get_priority(find.severity)})
                j_issue = JIRA_Issue(jira_id=new_issue.id, jira_key=new_issue, finding=find)
                j_issue.save()
                issue = jira.issue(new_issue.id)

                #Add labels (security & product)
                add_labels(find, new_issue)
                #Upload dojo finding screenshots to Jira
                for pic in find.images.all():
                    jira_attachment(jira, issue, settings.MEDIA_ROOT + pic.image_large.name)

                    #if jpkey.enable_engagement_epic_mapping:
                    #      epic = JIRA_Issue.objects.get(engagement=eng)
                    #      issue_list = [j_issue.jira_id,]
                    #      jira.add_issues_to_epic(epic_id=epic.jira_id, issue_keys=[str(j_issue.jira_id)], ignore_epics=True)
            except JIRAError as e:
                log_jira_alert(e.text, find)
        else:
            log_jira_alert("Finding not active or verified.", find)
Example #3
0
class JiraApi():

  def __init__(self, instance=None):
    self.instance = instance

  @staticmethod
  def get_datetime_now():
    return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.0+0000")


  def connect(self):
    options = {'server': app.config['JIRA_HOSTNAME'],'verify':False}
    self.instance = JIRA(options,
                basic_auth=(app.config['JIRA_USERNAME'], app.config['JIRA_PASSWORD']))

  @staticmethod
  def ticket_link(issue):
    return '<a href="{}/browse/{}">{}</a>'.format(app.config['JIRA_HOSTNAME'], issue.key, issue.key)

  def resolve(self, issue):
    self.instance.transition_issue(
      issue,
      app.config['JIRA_RESOLVE_TRANSITION_ID'],
      assignee={'name': app.config['JIRA_USERNAME']},
      resolution={'id': app.config['JIRA_RESOLVE_STATE_ID']})

  def defect_for_exception(self, summary_title, e):
    return self.instance.create_issue(
      project='IPGBD',
      summary='[auto-{}] Problem: {}'.format(current_user.username, summary_title),
      description="Exception: {}".format(e),
      customfield_13842=JiraApi.get_datetime_now(),
      customfield_13838= {
        "self": "https://jira.rim.net/rest/api/2/customFieldOption/16680",
        "value": "No",
        "id": "16680"
      },
      customfield_13831 =  [
      {
        "self": "https://jira.rim.net/rest/api/2/customFieldOption/16592",
        "value": "Quality",
        "id": "16592"
      },
      {
        "self": "https://jira.rim.net/rest/api/2/customFieldOption/16594",
        "value": "Risk Avoidance",
        "id": "16594"
      }],
      issuetype={'name': 'Defect'})
Example #4
0
def add_epic(eng, push_to_jira):
    engagement = eng
    prod = Product.objects.get(engagement=engagement)
    jpkey = JIRA_PKey.objects.get(product=prod)
    jira_conf = jpkey.conf
    if jpkey.enable_engagement_epic_mapping and push_to_jira:
        issue_dict = {
            'project': {'key': jpkey.project_key},
            'summary': engagement.name,
            'description' : engagement.name,
            'issuetype': {'name': 'Epic'},
            'customfield_' + str(jira_conf.epic_name_id) : engagement.name,
            }
        jira = JIRA(server=jira_conf.url, basic_auth=(jira_conf.username, jira_conf.password))
        new_issue = jira.create_issue(fields=issue_dict)
        j_issue = JIRA_Issue(jira_id=new_issue.id, jira_key=new_issue, engagement=engagement)
        j_issue.save()
Example #5
0
	def create_issue(self, blank_issue=False):
		self.__populate_fields()
		
		if blank_issue: 
			self.DESCRIPTION_TEMPLATE = ""
		
		jira = JIRA(options=self.JIRA_OPTIONS, basic_auth=(self.USER_NAME, self.USER_PW))  # jira-python API
		new_issue = jira.create_issue(fields=self.ISSUE_FIELDS)
		issue_url = self.JIRA_SERVER + "browse/" + new_issue.key
		wb.open_new_tab(issue_url)
		
		print '[+] Created a new ticket: ', self.SUMMARY_TEMPLATE
		print '[+] Issue:', new_issue.key
		print '---------------------------------------------------'
		print ' +++++++++++++++++++++++++++++++++++++++++++++++++ '
		print '---------------------------------------------------'
		print '[+] Template used:' 
		print self.DESCRIPTION_TEMPLATE
Example #6
0
    def __init__(self):

        options = {
            'server': 'http://192.168.33.49:8080'
        }

        jira_client = JIRA(options, basic_auth=('scm', 'scm'))

        # 创建项目模块
        # print jira_client.create_component(name=u'项目共性问题', project='SCM')
        # 创建issue
        issue_dict = {
            'project': {'key': 'PUBISSUE'},
            'summary': u'项目共性问题测试002',
            'issuetype': {'name': 'New Feature'},
        }

        new_issue = jira_client.create_issue(fields=issue_dict)
Example #7
0
    def import_issue(self, message, bug):
        """import lp ____: Creates a Jira ticket based on ___ bug"""
        jira = JIRA(
            basic_auth=(settings.JIRA_USER, settings.JIRA_PASS),
            server=settings.JIRA_HOST,
            validate=False,
            options={'verify': False}
        )

        issues = jira.search_issues(self.SEARCH_PATTERN % (
            settings.JIRA_PROJ, bug, bug))

        if len(issues) > 0:

            self.reply(
                message,
                self.render_jira(issues[0]),
                html=True
            )

        else:
            launchpad = Launchpad.login_anonymously(
                'will_jira_import',
                'production'
            )
            try:
                launchpad.bugs[bug]
            except KeyError:
                self.reply(message, "That issue does not exist in Launchpad")
                return

            issue_dict = {
                'project': {'key': settings.JIRA_PROJ},
                'summary': launchpad.bugs[bug].title,
                'description': launchpad.bugs[bug].description,
                'issuetype': {'name': 'Bug'},
                'customfield_10602': launchpad.bugs[bug].web_link,
                'components': [{'id': '18567'}],
                'reporter': {'name': self.get_requester_email(message)}
            }

            self.reply(message, "I need to create that")
            new_issue = jira.create_issue(fields=issue_dict)
            self.reply(message, self.render_jira(new_issue), html=True)
Example #8
0
def create_project(params):
  api_server = params['api_url']
  api_username = params['api_username']
  api_password = params['api_password']
  auth = (api_username, api_password)
  jira = JIRA( {'server': api_server}, basic_auth=auth)
  project_key = params['project_key']
  project_name = params['project_name']
  assignee = params['assignee']

  # create project and view
  if not jira.create_project(project_key, name=project_name, assignee=assignee):
    return None
  proj = jira.project(project_key)
  board = jira.create_board("{0} View".format(project_name), project_key, preset='scrum')
  issue = jira.create_issue(
             project=project_key,
             summary="Sample Issue for {0}".format(project_name),
             description="This is just sample",
             issuetype={"name":"Task"}
          )
  return proj
Example #9
0
    def create_jira(self, username):
        options = {"server": "%s" % settings.JIRA_OPTS["URL"], "verify": settings.JIRA_OPTS.get("VERIFY_SSL", False)}
        project = self.cleaned_data.get("project")
        summary = self.cleaned_data.get("summary")
        issue_type = self.cleaned_data.get("issue_type")
        component = self.cleaned_data.get("component")
        description = self.cleaned_data.get("description")
        description += "*JIRA Created by:* %s" % username

        try:
            jconn = JIRA(options, basic_auth=(settings.JIRA_OPTS["USER"], settings.JIRA_OPTS["PASSWORD"]))
        except Exception as e:
            logger.error("Error creating JIRA ticket :%s" % e)
            raise forms.ValidationError("Error connecting to JIRA ticket, please check the server logs")

        try:
            jira_ticket = jconn.create_issue(
                project=project,
                summary=summary,
                description=description,
                issuetype={"name": issue_type},
                components=[{"name": component}],
            )
        except Exception as e:
            logger.error(
                "Error creating JIRA ticket project=%s, summary=%s,  issue_type=%s, description=%s,"
                % (project, summary, issue_type, description)
            )
            logger.error("Server message %s" % e)
            msg = "Error creating JIRA ticket, please check the project name and issue type. If that doesn't work then check the server logs"
            raise forms.ValidationError(msg)

        if isinstance(jira_ticket, jira.resources.Issue):
            return jira_ticket.key
        else:
            raise forms.ValidationError("Error creating JIRA ticket, JIRA server did not return a ticket key.")

# example
# def getAllUsers():
# for user in jira.group_members("API"):
# print(jira.user(user))
# for group in jira.groups():
# print(group)

if __name__ == "__main__":

    (credentials, ticketInfo) = loadTicketInfo()
    jira = JIRA(
        options={"server": credentials["server"]},
        basic_auth=(credentials["username"], credentials["password"]),
    )
    for assignee in ticketInfo["assignees"]:
        newIssue = jira.create_issue(
            project=ticketInfo["project"],
            summary=ticketInfo["summary"],
            description=ticketInfo["description"],
            issuetype={"name": ticketInfo["issueType"]},
            assignee={"accountId": assignee["id"]},
        )

        for attachmentName in ticketInfo["attachmentNames"]:
            with open(attachmentName, "rb") as f:
                jira.add_attachment(newIssue, f)
        print("Created ticket %s and assigned it to %s" %
              (newIssue, assignee["name"]))
Example #11
0
def submit_jira_ticket(request):
    jira_setting = jirasetting.objects.all()

    for jira in jira_setting:
        jira_url = jira.jira_server
        username = jira.jira_username
        password = jira.jira_password
    jira_server = jira_url
    jira_username = signing.loads(username)
    jira_password = signing.loads(password)

    options = {'server': jira_server}
    jira_ser = JIRA(options, basic_auth=(jira_username, jira_password))
    jira_projects = jira_ser.projects()

    if request.method == 'GET':
        summary = request.GET['summary']
        description = request.GET['description']
        scanner = request.GET['scanner']
        vuln_id = request.GET['vuln_id']
        scan_id = request.GET['scan_id']

        return render(
            request, 'submit_jira_ticket.html', {
                'jira_projects': jira_projects,
                'summary': summary,
                'description': description,
                'scanner': scanner,
                'vuln_id': vuln_id,
                'scan_id': scan_id
            })

    if request.method == 'POST':
        summary = request.POST.get('summary')
        description = request.POST.get('description')
        project_id = request.POST.get('project_id')
        issue_type = request.POST.get('issue_type')
        vuln_id = request.POST.get('vuln_id')
        scanner = request.POST.get('scanner')
        scan_id = request.POST.get('scan_id')

        issue_dict = {
            'project': {
                'id': project_id
            },
            'summary': summary,
            'description': description,
            'issuetype': {
                'name': issue_type
            },
        }
        new_issue = jira_ser.create_issue(fields=issue_dict)
        # print new_issue

        if scanner == 'zap':
            zap_scan_results_db.objects.filter(vuln_id=vuln_id).update(
                jira_ticket=new_issue)
            return HttpResponseRedirect(
                '/webscanners/zap_vul_details/?scan_id=%s&scan_name=%s' %
                (scan_id, summary))
        elif scanner == 'burp':
            burp_scan_result_db.objects.filter(vuln_id=vuln_id).update(
                jira_ticket=new_issue)
            return HttpResponseRedirect(
                '/webscanners/burp_vuln_out/?scan_id=%s&scan_name=%s' %
                (scan_id, summary))
        elif scanner == 'arachni':
            arachni_scan_result_db.objects.filter(vuln_id=vuln_id).update(
                jira_ticket=new_issue)
            return HttpResponseRedirect(
                '/webscanners/arachni_vuln_out/?scan_id=%s&scan_name=%s' %
                (scan_id, summary))
        elif scanner == 'open_vas':
            ov_scan_result_db.objects.filter(vul_id=vuln_id).update(
                jira_ticket=new_issue)
            return HttpResponseRedirect(
                '/networkscanners/vul_details/?scan_id=%s' % scan_id)
        elif scanner == 'nessus':
            nessus_report_db.objects.filter(vul_id=vuln_id).update(
                jira_ticket=new_issue)
            return HttpResponseRedirect(
                '/networkscanners/nessus_vuln_details/?scan_id=%s' % scan_id)
    jira = JIRA(parsedArgs['server'],
                basic_auth=(parsedArgs['user'], parsedArgs['password']))
    #if True:
    if False:
        issueDict = dict({
            'project': {
                'key': parsedArgs['key']
            },
            'issuetype': {
                'name': 'Test Plan'
            },
            'summary': 'Test Test Plan'
        })

        issue = jira.create_issue(fields=issueDict)
        issueDict = dict({
            'project': {
                'key': parsedArgs['key']
            },
            'issuetype': {
                'name': 'Test Case'
            },
            'parent': {
                'id': issue.key
            },
            'customfield_11493': 'SBREST-358',
            'summary': 'test it',
        })
        issue = jira.create_issue(fields=issueDict)
        jira.transition_issue(issue, 'Test')
Example #13
0
class JiraClient:
    def __init__(self, options, basic_auth, project):
        self.jira = JIRA(options, basic_auth=basic_auth)
        self.project = project

    def get_issues_by_summary(self, summary):
        """
    Find issues by using the summary (issue title)
    Args:
      summary
    Return:
      A list of issues
    """
        try:
            issues = self.jira.search_issues(
                "project={0} AND summary ~ '{1}'".format(
                    self.project, summary))
        except Exception:
            raise
        return issues

    def get_issue_by_key(self, key):
        """
    Find issue by using the key (e.g BEAM-1234)
    Args:
      key
    Return:
      issue
    """
        try:
            issue = self.jira.issue(key)
        except Exception:
            raise
        return issue

    def create_issue(self,
                     summary,
                     components,
                     description,
                     issuetype='Bug',
                     assignee=None,
                     parent_key=None):
        """
    Create a new issue
    Args:
      summary - Issue title
      components - A list of components
      description (optional) - A string that describes the issue
      issuetype (optional) - Bug, Improvement, New Feature, Sub-task, Task, Wish, etc.
      assignee (optional) - A string of JIRA user name
      parent_key (optional) - The parent issue key is required when creating a subtask.
    Return:
      Issue created
    """
        fields = {
            'project': {
                'key': self.project
            },
            'summary': summary,
            'description': description,
            'issuetype': {
                'name': issuetype
            },
            'components': [],
        }
        for component in components:
            fields['components'].append({'name': component})
        if assignee is not None:
            fields['assignee'] = {'name': assignee}
        if parent_key is not None:
            fields['parent'] = {'key': parent_key}
            fields['issuetype'] = {'name': 'Sub-task'}
        try:
            new_issue = self.jira.create_issue(fields=fields)
        except Exception:
            raise
        return new_issue

    def update_issue(self,
                     issue,
                     summary=None,
                     components=None,
                     description=None,
                     assignee=None,
                     notify=True):
        """
    Create a new issue
    Args:
      issue - Jira issue object
      summary (optional) - Issue title
      components (optional) - A list of components
      description (optional) - A string that describes the issue
      assignee (optional) - A string of JIRA user name
      notify - Query parameter notifyUsers. If true send the email with notification that the issue was updated to users that watch it.
               Admin or project admin permissions are required to disable the notification.
    Return:
      Issue created
    """
        fields = {}
        if summary:
            fields['summary'] = summary
        if description:
            fields['description'] = description
        if assignee:
            fields['assignee'] = {'name': assignee}
        if components:
            fields['components'] = []
            for component in components:
                fields['components'].append({'name': component})
        try:
            issue.update(fields=fields, notify=notify)
        except Exception:
            raise

    def reopen_issue(self, issue):
        """
    Reopen an issue
    Args:
      issue - Jira issue object
    """
        try:
            self.jira.transition_issue(issue.key, 3)
        except:
            raise
	# queries to find other created N&N Task issues, not issues for which N&N should be written
	nnissuesqueryall = 'summary ~ "New and Noteworthy" AND project in (JBIDE, JBDS) ORDER BY key DESC'
	nnissuesquerythisversion = 'summary ~ "New and Noteworthy" AND ((project in (JBDS) and fixVersion = "' + jbds_fixversion + '") or (project in (JBIDE) and fixVersion = "' + jbide_fixversion + '")) ORDER BY key DESC'

	rootnn_description = 'This [query|' + nnsearch + '] contains the search for all N&N. See subtasks below.'
	rootnn_dict = {
		'project' : { 'key' : 'JBIDE' },
		'summary' : 'Create New and Noteworthy for ' + jbide_fixversion,
		'description' : rootnn_description,
		'issuetype' : { 'name' : 'Task' },
		'priority' : { 'name' :'Blocker'},
		'fixVersions' : [{ "name" : jbide_fixversion }],
		'components' : [{ "name" : "website" }]
		}
	rootnn = jira.create_issue(fields=rootnn_dict)
	componentLead = defaultAssignee()
	try:
		jira.assign_issue(rootnn, componentLead)
	except:
		print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format(options.usernameJIRA, rootnn, componentLead, sys.exc_info()[0])

	print("JBoss Tools       : " + jiraserver + '/browse/' + rootnn.key + " => " + componentLead + "")

	def nametuple(x):
		return { "name" : x }

	def quote(x):
		return '"' + x + '"'

	# see JIRA_components listing in components.py
Example #15
0
     '\n\n[Search for all task JIRA|' + tasksearch + ']',
     'issuetype': {
         'name': 'Task'
     },
     'priority': {
         'name': 'Blocker'
     },
     'fixVersions': [{
         "name": jbds_fixversion
     }],
     'components': [{
         "name": "installer"
     }],
     'labels': ["task"],
 }
 rootJBDS = jira.create_issue(fields=rootJBDS_dict)
 installerLead = queryComponentLead(CLJBDS, 'installer', 0)
 try:
     jira.assign_issue(rootJBDS, installerLead)
 except:
     if (not options.jiraonly):
         print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format(
             options.jirauser, rootJBDS, installerLead,
             sys.exc_info()[0])
 if (options.jiraonly):
     print(rootJBDS.key)
 else:
     print("Task JIRA created for this milestone include:")
     print("")
     print("JBDS              : " + jiraserver + '/browse/' +
           rootJBDS.key + " => " + installerLead)
def main_function():
    # Find your Account SID and Auth Token at twilio.com/console
    # and set the environment variables. See http://twil.io/secure
    account_sid = os.environ['TWILIO_ACCOUNT_SID']
    auth_token = os.environ['TWILIO_AUTH_TOKEN']
    client = Client(account_sid, auth_token)

    # Extract new recordings and then delete them
    recordings = client.recordings.list()
    for recording in recordings:
        recording_id = recording.sid
        url = f"https://api.twilio.com/{recording.uri.replace('.json', '.wav')}"
        r = requests.get(url, allow_redirects=True)
        open(f'unprocessed_recordings/{recording_id}.wav',
             'wb').write(r.content)
        recording.delete()

    # Save all transcripts in a CSV file
    current_directory = os.path.dirname(os.path.realpath("__file__")) + "/"
    unprocessed_directory = os.path.join(current_directory,
                                         'unprocessed_recordings')
    processed_directory = os.path.join(current_directory,
                                       'processed_recordings')
    tickets_to_make_directory = os.path.join(current_directory,
                                             'tickets_to_make')
    tickets_to_make_file = f"{tickets_to_make_directory}/data.csv"
    wav_files = glob.glob(f"{unprocessed_directory}/*")
    for wav_file in wav_files:
        command = react_to_recording(wav_file)
        command = "" if command == None else command.lower()
        response = command
        append_ideas_to_list(response, tickets_to_make_file)
        os.rename(
            wav_file,
            wav_file.replace(
                "unprocessed_recordings/",
                "processed_recordings/",
            ))

    # Write each record in the CSV file as a JIRA ticket
    jira_user = os.environ['JIRA_USER']
    jira_apikey = os.environ['JIRA_TOKEN']
    jira_server = os.environ['JIRA_SERVER']
    jira = JIRA(basic_auth=(jira_user, jira_apikey),
                options={'server': jira_server})
    df = pd.read_csv(tickets_to_make_file)
    for index, row in df.iterrows():
        idea = str(row['Ideas'])
        # Ticket
        issue_dict = {
            'project': "DS",
            'summary': idea if len(idea) < 120 else "New Ticket",
            'description': "" if len(idea) < 120 else idea,
            'issuetype': {
                'name': 'Story'
            },
        }
        _ = jira.create_issue(issue_dict)
    os.remove(tickets_to_make_file)
    tickets_made = len(wav_files)
    speak_text(
        f"Created {tickets_made} ticket{'s' if tickets_made != 1 else ''}")
logger.info("{0} Jira bugs were found".format(len(Jbugs)))
logger.info("{0} Launchpad bugs were found".format(len(lp_bugs)))

for Lbug in lp_bugs:
    m = str(Lbug.milestone).replace('https://api.launchpad.net/' + lp_api +
                                    '/' + lp_project + '/+milestone/', '')
    logger.info("{0} milestone: {1}".format(Lbug.title.encode('utf-8'), m))
    it_created = False
    for Jbug in Jbugs:
        if str(Lbug.bug.id) in Jbug.fields.summary:
            for ver in Jbug.fields.fixVersions:
                if milestones[m] in ver.name:
                    logger.info("Matched to Jira issue {0} ({1})".format(
                        Jbug.key, Jbug.fields.summary.encode('utf-8')))
                    it_created = True
                    sync_jira_status(Jbug, Lbug)
                    break
    if not it_created and not Lbug.bug.duplicate_of and Lbug.status not in \
            ["Won't Fix", 'Invalid', 'Fix Released']:
        summary = Lbug.title
        newJbug = jira.create_issue(project=jira_project,
                                    summary=summary,
                                    description=Lbug.web_link,
                                    labels=['launchpad'],
                                    issuetype={'name': 'Bug'})
        logger.info("Jira issue {0} ({1}) was successfully added".format(
            newJbug.key, newJbug.fields.summary.encode('utf-8')))
        issue_dict = {"fixVersions": [{"name": milestones[m]}]}
        newJbug.update(fields=issue_dict)
        sync_jira_status(newJbug, Lbug)
Example #18
0
							places.append("Akai Hana")
							places.append("Brett's BBQ")
							places.append("Which Wich")
							places.append("Rubios")
							places.append("Tandoori Xpress")
							places.append("Cana Cafe")
							places.append("Donut Touch")
							places.append("New York Bagels")
							places.append("Karl Strauss")

							number = random.randint(0,len(places) - 1)
							sc.rtm_send_message(chan, "You should go to %s to for food." % places[number])						
####JIRA STUFF
						elif "!helpdesk" in message:
							request = message[10:]
							new_issue = j.create_issue(project="IT", summary=request, description="Created by Slack", issuetype={'name':'Service Request'}, reporter={"name": email}) #edit project ID to match.
							sc.rtm_send_message(chan, "Your helpdesk ticket for '%s' has been created." % request)
####Hacker News Stuff
						elif "!hn" in message:
							n=0
							sc.rtm_send_message(chan,"Top 2 HackerNews Stories:")
							for story_id in hn.top_stories(limit=2):
								derp = hn.get_item(story_id)
								derp = str(derp)
								print "derp is:"
								print derp
								herp = derp
								print "herp is:"
								print herp
								derpy = derp.split(":")[1]
								print "derpy is:"
def update_or_create_jira_issue(user_token, is_curator):
    try:
        params = app.config.get('JIRA_PARAMS')
        user_name = params['username']
        password = params['password']
        default_curator = 'metabolights-api'

        updated_studies = []
        try:
            jira = JIRA(options=options, basic_auth=(user_name, password))
        except:
            return False, 'Could not connect to JIRA server, incorrect username or password?', updated_studies

        # Get the MetaboLights project
        mtbls_project = jira.project(project)

        if is_curator:
            studies = get_all_studies(user_token)

        for study in studies:
            study_id = None
            user_name = None
            release_date = None
            update_date = None
            study_status = None
            curator = None
            status_change = None
            curation_due_date = None

            try:
                study_id = safe_str(study[0])
                user_name = safe_str(study[1])
                release_date = safe_str(study[2])
                update_date = safe_str(study[3])
                study_status = safe_str(study[4])
                curator = safe_str(study[5])
                status_change = safe_str(study[6])
                curation_due_date = safe_str(study[7])
            except Exception as e:
                logger.error(str(e))
            issue = []
            summary = None

            # date is 'YYYY-MM-DD HH24:MI'
            due_date = status_change[:10]

            logger.info('Checking Jira ticket for ' + study_id + '. Values: ' +
                        user_name + '|' + release_date + '|' + update_date + '|' + study_status + '|' +
                        curator + '|' + status_change + '|' + due_date)

            # Get an issue based on a study accession search pattern
            search_param = "project='" + mtbls_project.key + "' AND summary  ~ '" + study_id + " \\\-\\\ 20*'"
            issues = jira.search_issues(search_param)  # project = MetaboLights AND summary ~ 'MTBLS121 '
            new_summary = study_id + ' - ' + release_date.replace('-', '') + ' - ' + \
                          study_status + ' (' + user_name + ')'
            try:
                if issues:
                    issue = issues[0]
                else:
                    if study_status == 'Submitted' or study_status == 'In Curation':
                        logger.info("Could not find Jira issue for " + search_param)
                        print("Creating new Jira issue for " + search_param)
                        issue = jira.create_issue(project=mtbls_project.key, summary='MTBLS study - To be updated',
                                                  description='Created by API', issuetype={'name': 'Story'})
                    else:
                        continue  # Only create new cases if the study is in status Submitted/In Curation
            except:  # We could not find or create a Jira issue.
                continue

            summary = issue.fields.summary  # Follow pattern 'MTBLS123 - YYYYMMDD - Status'

            if not summary.startswith('MTBLS'):
                continue  # Skip all cases that are not related the study accession numbers

            try:
                assignee = issue.fields.assignee.name
            except:
                assignee = ""

            assignee_changed = False
            valid_curator = False
            jira_curator = ""
            if curator:
                if curator.lower() == 'mark':
                    jira_curator = 'mwilliam'
                    valid_curator = True
                elif curator.lower() == 'pamela':
                    jira_curator = 'ppruski'
                    valid_curator = True
                elif curator.lower() == 'xuefei' or curator.lower() == 'reza' or curator.lower() == 'keeva':
                    jira_curator = default_curator  # We do not have a current curation listed in the log
                    valid_curator = True

                assignee_changed = True if assignee != jira_curator else False
            else:
                jira_curator = ""

            if not status_change:
                status_change = "No status changed date reported"
            # Release date or status has changed, or the assignee (curator) has changed

            summary_changed = True if summary != new_summary else False
            curator_update = True if assignee != default_curator and jira_curator != default_curator else False
            if assignee_changed or summary_changed:

                # Add "Curation" Epic
                issues_to_add = [issue.key]
                jira.add_issues_to_epic(curation_epic, issues_to_add)  # Add the Curation Epic
                labels = maintain_jira_labels(issue, study_status, user_name)

                # Add a comment to the issue.
                comment_text = 'Current status ' + study_status + '. Status last changed date ' + status_change + \
                               '. Curation due date ' + due_date + '. Database update date ' + update_date
                if jira_curator == default_curator:
                    comment_text = comment_text + '. Default curator has been changed from "' \
                                   + curator + '" to "' + default_curator + '"'
                if assignee_changed:
                    comment_text = comment_text + '. Curator in Jira changed from "' + assignee + '" to "' + jira_curator + '"'

                if summary_changed:
                    comment_text = comment_text + '. Summary in Jira changed from "' + summary + '" to "' + new_summary + '"'

                jira.add_comment(issue, comment_text)

                # Change the issue's summary, comments and description.
                issue.update(summary=new_summary, fields={"labels": labels}, notify=False)

                # if valid_curator:  # ToDo, what if the curation log is not up to date?
                issue.update(assignee={'name': jira_curator}, notify=False)

                updated_studies.append(study_id)
                logger.info('Updated Jira case for study ' + study_id)
                print('Updated Jira case for study ' + study_id)
    except Exception as e:
        logger.error("Jira updated failed for " + study_id + ". " + str(e))
        return False, 'Update failed: ' + str(e), str(study_id)
    return True, 'Ticket(s) updated successfully', updated_studies
Example #20
0
class JiraAPI:
    """
    Jira client has no documentation, so if you need one, use one for REST API:
    https://developer.atlassian.com/cloud/jira/platform/rest/v3/
    """
    def __init__(self, settings: Settings):
        self._settings = settings
        self.transition = settings.jira.transition
        self.release_task = settings.jira.release_task
        self.release_task_name = self.release_task.name.format(
            version=settings.version, component=self.release_task.component)

        self._api = JIRA(
            {"server": settings.jira.connection.server},
            basic_auth=(settings.jira.connection.user,
                        settings.jira.connection.token),
        )

    def _create_version(self, project: Project):
        proposed_name = "Hotfix" if self._settings.version.minor > 0 else "Release"
        user_input = input(f"Input new Jira version name: [{proposed_name}]: ")
        name = user_input if user_input else proposed_name

        return self._api.create_version(name,
                                        project,
                                        startDate=_get_formatted_date())

    def _select_version(self, project: Project,
                        unreleased_versions) -> Optional[Version]:
        print("Jira versions:")
        print("1) Skip")
        print("2) Create new")
        print("or select existing one:")

        unreleased_versions = {
            idx: version
            for idx, version in enumerate(unreleased_versions, 3)
        }

        for idx, version in unreleased_versions.items():
            print(f"{idx}) {version.name}")

        user_input = 0
        valid_choices = list(range(1, len(unreleased_versions) + 3))

        while user_input not in valid_choices:
            try:
                user_input = int(
                    input(
                        "\nChoose which Jira version use for this release: "))
            except Exception:
                continue

        if user_input == 1:
            return None
        elif user_input == 2:
            return self._create_version(project)
        else:
            return unreleased_versions[user_input]

    def get_version(self) -> Optional[Version]:
        print_title(f"Searching for Jira release version")
        project = self._api.project(self.release_task.project)

        unreleased_versions = [
            v for v in self._api.project_versions(project) if not v.released
        ]

        return self._select_version(project, unreleased_versions)

    def _get_jira_release_unfinished_tasks(self, version: Version):
        """
        To check that all tasks in Jira release is finished
        select them using jql
        """
        final_statuses = '", "'.join(self.transition.child_final_statuses)
        types_to_skip = '", "'.join(self.transition.child_task_types_to_skip)

        return self._api.search_issues(
            f'project = "{self.release_task.project}"'
            f' AND fixVersion = "{version.name}"'
            f' AND fixVersion in unreleasedVersions("{self.release_task.project}")'
            f' AND status NOT IN ("{final_statuses}")'
            f' AND type NOT IN ("{types_to_skip}")')

    def _get_transition(self, issue, transition_name):
        transitions = [
            t for t in self._api.transitions(issue)
            if t["name"].lower() == transition_name.lower()
        ]
        if not transitions:
            return None

        return transitions[0]

    def release_version(self, release_task_key: str):
        print_title(
            f"Releasing Jira version of release task {release_task_key}")
        release_task = self._api.issue(release_task_key)

        for version in release_task.fields.fixVersions:
            version: Version
            print(f'Checking Jira release version: "{version.name}"...')

            if version.released:
                print_error("Version is already released")
                continue

            unfinished_tasks = self._get_jira_release_unfinished_tasks(version)
            if unfinished_tasks:
                tasks_str = ", ".join([i.key for i in unfinished_tasks])
                print_error(
                    f'Can\'t release Jira version: "{version.name}", it has unfinished tasks: {tasks_str}'
                )
                continue

            print("Jira version is safe to release, releasing...", end=" ")
            version.update(released=True, releaseDate=_get_formatted_date())
            print("Ok!")

    def _add_to_release_version(self, version: Version, release_task_key: str):
        issue = self._api.issue(release_task_key)
        issue.add_field_value("fixVersions", {"name": version.name})

    def make_links(self, version: Optional[Version], release_task_key,
                   related_keys):
        version_name = version.name if version else "-"
        print_title(f"Linking tasks found in release branch"
                    f" to release task ({release_task_key})"
                    f' and to Jira version "{version_name}"')
        if version:
            self._add_to_release_version(version, release_task_key)

        print(f"Linking {len(related_keys)} tasks:")
        partial_make_links = partial(self._make_links, version,
                                     release_task_key)
        with ThreadPool(5) as pool:
            pool.map(partial_make_links, related_keys)

    def _make_links(self, version: Optional[Version], release_task_key: str,
                    child_task_key: str):
        print(f"* {child_task_key}")
        self._api.create_issue_link(self.release_task.link_type,
                                    release_task_key, child_task_key)
        if version:
            self._add_to_release_version(version, child_task_key)

    def make_release_task(self):
        print_title("Creating Jira release task")
        extra_fields = {"components": [{"name": self.release_task.component}]}

        issue = self._api.create_issue(
            project=self.release_task.project,
            summary=self.release_task_name,
            issuetype={"name": self.release_task.type},
            **extra_fields,
        )
        print(f"Created Jira release task: {issue.key}")
        return issue.key

    def get_release_task(self):
        print_title("Searching for Jira release task")
        query = (f'project = "{self.release_task.project}"'
                 f' AND summary ~ "{self.release_task_name}"'
                 f' AND type = "{self.release_task.type}"')
        found_issues = self._api.search_issues(query)

        if not found_issues:
            print("Did not find existing release task")
            return self.make_release_task()

        if len(found_issues) > 1:
            issues_str = ", ".join([i.key for i in found_issues])
            print_error(
                f"Your release task has not unique name, fix it before using this functionality,"
                f" found issues: {issues_str}")
            exit(1)

        release_issue = found_issues[0]
        print(f"Found Jira release task: {release_issue.key}")
        return release_issue.key

    def mark_release_task_done(self, release_task_key):
        print_title(
            f'Transition release task "{release_task_key}" from "{self.transition.release_from_status}" to "{self.transition.release_to_status}"'
        )

        release_issue = self._api.issue(release_task_key)
        print_title(
            f'Current release task status is "{release_issue.fields.status}"')

        if (release_issue.fields.status.name.lower() !=
                self.transition.release_from_status.lower()):
            print_error(
                f'Release task "{release_task_key}" has inproper status')
            return

        transition = self._get_transition(release_issue,
                                          self.transition.release_to_status)

        if not transition:
            print_error(
                f'Release task "{release_task_key}" has no transition to "{self.transition.release_to_status}"'
            )
            return

        self._api.transition_issue(release_issue, transition["id"])

        print(
            f'Release task {release_issue.key} has been transited to status "{transition["name"]}"'
        )

    def mark_children_tasks_done(self, release_task_key):
        print_title(
            f'Transition children of "{release_task_key}" from "{self.transition.child_from_status}" to "{self.transition.child_to_status}"'
        )

        query = (f'issue in linkedIssues("{release_task_key}")'
                 f' AND status = "{self.transition.child_from_status}"')
        found_issues = self._api.search_issues(query)

        to_status = self.transition.child_to_status.lower()

        if not found_issues:
            print("Did not find any task for transition")
            return

        for issue in found_issues:
            transition = self._get_transition(issue, to_status)
            if not transition:
                print_error(
                    f'Issue "{issue.key}" does not have transition to status "{self.transition.child_to_status}"'
                )
                continue

            self._api.transition_issue(issue, transition["id"])
            print(
                f'Task {issue.key} has been transited to status "{transition["name"]}"'
            )
Example #21
0
class JiraLocal(object):
    def __init__(self, jira_url, auth_user, auth_token, rule,
                 jira_fields_dict):
        self.jira_instance = JIRA(jira_url, basic_auth=(auth_user, auth_token))
        self.jira_config = rule['jira_config']
        self.jira_fields_dict = jira_fields_dict
        self.jira_issue_id_field_key = jira_fields_dict[
            rule['jira_config']["jira_issue_id_field"]]
        self.log = Logger(rule=rule)
        return

    def get_jira_issues(self, project_key, halo_issues):
        jira_issues_dict = {}
        with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
            future_to_issue_id = {
                executor.submit(self.get_jira_issues_for_halo_issue,
                                issue["id"], project_key): issue["id"]
                for issue in halo_issues
            }
            for future in as_completed(future_to_issue_id):
                issue_id = future_to_issue_id[future]
                jira_issues = future.result()
                jira_issues_dict[issue_id] = jira_issues
        return jira_issues_dict

    def get_jira_epics_or_issues(self,
                                 project_keys,
                                 issuetype,
                                 dict_format=True):
        if isinstance(project_keys, str):
            project_keys = [project_keys]
        jira_issues_dict = defaultdict(list)
        jira_issues = self.jira_instance.search_issues(
            f'project in ({", ".join(x for x in project_keys)}) AND '
            f'resolution = Unresolved AND '
            f'issuetype={issuetype} AND '
            f'"{self.jira_config["jira_issue_id_field"]}" is not EMPTY',
            maxResults=False)
        if not dict_format:
            return jira_issues
        for issue in jira_issues:
            jira_issues_dict[issue.raw["fields"][
                self.jira_issue_id_field_key]].append(issue)
        return jira_issues_dict

    def get_jira_issues_for_halo_issue(self, issue_id, project_key):
        results = self.jira_instance.search_issues(
            f'project="{project_key}" AND '
            f'"{self.jira_config["jira_issue_id_field"]}"~{issue_id} AND '
            f'issuetype="{self.jira_config["jira_issue_type"]}"')
        return results

    def create_jira_epic(self, group_key_hash, group_key_str, project_key):
        # Get IDs for epic fields
        epic_dict = {
            'project': {
                'key': project_key
            },
            'summary': group_key_str,
            self.jira_fields_dict["Epic Name"]: group_key_str,
            'description': group_key_str,
            'issuetype': {
                'name': 'Epic'
            },
            self.jira_issue_id_field_key: group_key_hash
        }
        epic = self.jira_instance.create_issue(fields=epic_dict)
        return epic

    def create_jira_issue(self, issue, epic, jira_fields_dict, fields,
                          project_key):
        epic_link = None
        if epic:
            epic_link = epic.key

        summary, description, field_mapping = self.prepare_issue(
            issue, fields, jira_fields_dict)

        issue_dict = {
            'project': {
                'key': project_key
            },
            'issuetype': {
                'name': self.jira_config['jira_issue_type']
            },
            self.jira_issue_id_field_key: issue["id"],
            self.jira_fields_dict["Epic Link"]: epic_link,
            'summary': summary,
            'description': description
        }

        issue_dict.update(field_mapping)
        self.log.info(f"Creating issue: {issue['id']}")
        self.jira_instance.create_issue(fields=issue_dict)

    def update_jira_issue(self, issue, jira_issues, jira_fields_dict, fields):
        self.log.info(f"Updating issue: {issue['id']}")
        for jira_issue in jira_issues:
            summary, description, field_mapping = self.prepare_issue(
                issue, fields, jira_fields_dict)
            issue_dict = {'summary': summary, 'description': description}
            issue_dict.update(field_mapping)
            jira_issue.update(fields=issue_dict)
            if issue["status"] == "resolved":
                self.transition_issue(jira_issue,
                                      self.jira_config["issue_status_closed"])
            elif jira_issue.raw["fields"]["status"][
                    "name"] == self.jira_config["issue_status_closed"]:
                self.transition_issue(
                    jira_issue, self.jira_config["issue_status_reopened"])

    def transition_issue(self, issue, transition_name):
        self.log.info(f"Transitioning issue {issue.key} to {transition_name}")
        transition_id = self.jira_instance.find_transitionid_by_name(
            issue, transition_name)
        try:
            self.jira_instance.transition_issue(issue, transition_id)
        except JIRAError:
            self.log.error(
                f"Could not transition Jira Issue '{issue.key}' "
                f"from {issue.raw['fields']['status']['name']} to {transition_name}"
            )

    def prepare_issue(self, issue, fields, jira_fields_dict):
        asset_formatted = Formatter.format_object(issue["asset_type"],
                                                  issue.pop("asset"))
        finding_formatted = Formatter.format_object("findings",
                                                    issue.pop("findings"))
        issue_formatted = Formatter.format_object("issue", issue)

        summary = Formatter.format_summary(issue)
        description = issue_formatted + asset_formatted + finding_formatted
        description = description[:32759] + '{code}\n\n'

        dynamic_map = fields.get("mapping") or {}
        static = fields.get("static") or {}
        field_mapping = map_fields(dynamic_map, static, issue,
                                   jira_fields_dict)

        return summary, description, field_mapping

    def push_issues(self,
                    issues,
                    jira_epics_dict,
                    jira_issues_dict,
                    jira_fields_dict,
                    fields,
                    project_key=None):
        with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
            for issue in issues:
                jira_issues = jira_issues_dict.get(issue["id"])
                if jira_issues:
                    executor.submit(self.update_jira_issue, issue, jira_issues,
                                    jira_fields_dict, fields)
                else:
                    groupby_key = issue.pop("groupby_key", "")
                    epic = jira_epics_dict.get(groupby_key)
                    executor.submit(self.create_jira_issue, issue, epic,
                                    jira_fields_dict, fields, project_key)

    def cleanup_epics(self, project_keys):
        jira_issues = self.get_jira_epics_or_issues(
            project_keys,
            self.jira_config["jira_issue_type"],
            dict_format=False)
        epics_set = set(issue.raw["fields"][self.jira_fields_dict["Epic Link"]]
                        for issue in jira_issues)
        jira_epics = self.get_jira_epics_or_issues(project_keys,
                                                   "Epic",
                                                   dict_format=False)

        with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
            for epic in jira_epics:
                if epic.key not in epics_set:
                    self.log.info(f"Deleting epic: {epic.key}")
                    executor.submit(self.transition_issue, epic,
                                    self.jira_config["issue_status_closed"])
Example #22
0
def process_event(helper, *args, **kwargs):
    helper.log_info("Alert action jirable started.")

    # addinfo adds _search_et, _search_lt, _timestamp to helper.info
    # we use these fields for the kvstore, so grab them up here (just once)
    helper.addinfo()

    # jirable.py checks for the presence of these mandatory settings, so don't bother doing so here
    jira_url = helper.get_global_setting("jira_url")
    username = helper.get_global_setting("username")
    password = helper.get_global_setting("password")
    dynamic_field_prefix = helper.get_global_setting("dynamic_field_prefix")

    # unique_id_field_name is optional, and is not checked in jirable.py
    unique_id_field_name = helper.get_global_setting("unique_id_field_name")

    # try to connect, bail out if unable to do so
    jira = None
    try:
        jira = JIRA(jira_url, basic_auth=(username, password))
    except:
        helper.log_info(
            "Unable to connect to JIRA.  Check URL and authentication settings."
        )
        return 1

    # The following example gets the alert action parameters and prints them to the log
    project = helper.get_param("project")
    helper.log_info("project={}".format(project))

    drilldown_dashboard = helper.get_param("drilldown_dashboard")
    helper.log_info("drilldown_dashboard={}".format(drilldown_dashboard))

    unique_id_value = helper.get_param("unique_id_value")
    helper.log_info("unique_id_value={}".format(unique_id_value))

    issue_type = helper.get_param("issue_type")
    helper.log_info("issue_type={}".format(issue_type))

    summary = helper.get_param("summary")
    helper.log_info("summary={}".format(summary))

    dedup_by_unique_id_value = helper.get_param("dedup_by_unique_id_value")
    helper.log_info(
        "dedup_by_unique_id_value={}".format(dedup_by_unique_id_value))

    drilldown_search = helper.get_param("drilldown_search")
    helper.log_info("drilldown_search={}".format(drilldown_search))

    # quit if asked to dedup without the unique field name
    # note that "yes" is hardcoded, and this is the string that must be in savedsearches.conf
    # the use of 0, false, etc is not supported
    if dedup_by_unique_id_value == "yes" and not unique_id_field_name:
        helper.log_info(
            "Dedup by Unique ID Value was checked, but Unique ID Field Name is not set.  Bailing out."
        )
        return 1

    # JIRA forces setting customfields by customfield id instead of customfield name, so fetch the customfield info here to find our unique field id
    customfield_ids = {}
    for customfield in jira.fields():
        customfield_ids[customfield['name']] = customfield['id']
    # we fetched all field ids, but only set unique_customfield_id if we have unique_id_field_name

    events = helper.get_events()
    for event in events:
        templated_project = Template(project).render(**event)
        templated_issue_type = Template(issue_type).render(**event)
        templated_summary = Template(summary).render(**event)

        # what our new issue will look like (to start with)
        issue_fields = {
            'project': templated_project,
            'issuetype': {
                'name': templated_issue_type
            },
            'summary': templated_summary,
        }

        # we need search_et, search_lt, and alert_time in several of the below blocks
        # _timestamp is the time the search was run
        alert_time = helper.info.get('_timestamp')
        # if no _search_et, use start of time (0)
        search_et = helper.info.get('_search_et', 0)
        # if no _search_lt, use the time of the search
        search_lt = helper.info.get('_search_lt', alert_time)

        # templated_drilldown_search goes into kvstore even when not using it to create a new issue, so define prior to those blocks
        # if not provided, it will be an empty string so no need to check for its presence first
        templated_drilldown_search = Template(drilldown_search).render(**event)

        # a new issue will be created if issue remains False
        issue = False

        # don't bother searching unless configured to dedup
        if dedup_by_unique_id_value == "yes":

            templated_unique_id_value = Template(unique_id_value).render(
                **event)
            unique_customfield_id = customfield_ids[unique_id_field_name]
            issue_fields[unique_customfield_id] = templated_unique_id_value

            # JIRA only allows CONTAINS searches against text fields, so we search for our full unique_id_value, then have to check for exact match against each found issue
            for existing_issue in jira.search_issues(
                    '{} ~ "{}" and status!=Done and status!=Resolved'.format(
                        unique_id_field_name,
                        templated_unique_id_value.replace('"', '\\"'))):
                try:
                    # this is apparently how you do something like existing_issue.$unique_customfield_id
                    existing_issue_unique_id_value = getattr(
                        existing_issue.fields, unique_customfield_id)
                    if existing_issue_unique_id_value == templated_unique_id_value:
                        issue = existing_issue
                        jira_action = "append"
                except:
                    # it's fine if the custom field doesn't exist, because we're not sure if the events that matched have the field
                    pass

        # issue = False if not asked to dedup or if no matching dedup issue was found
        if not issue:
            issue = jira.create_issue(fields=issue_fields)
            jira_action = "create"

            dynamic_field_regex = re.compile(r"^" + dynamic_field_prefix +
                                             "(?P<dynamic_field_name>.*)$")
            for field in event:
                match = dynamic_field_regex.match(field)
                if match:
                    try:
                        issue.update(
                            fields={
                                customfield_ids[match.group('dynamic_field_name')]:
                                event[field]
                            })
                    except:
                        # in case a dynamic field was improperly named, etc, don't bail out, just log it
                        helper.log_info("Unable to set field: {}".format(
                            match.group('dynamic_field_name')))

            # attach raw events if drilldown_search defined (only for new issues)
            if templated_drilldown_search:
                session_key = helper.session_key
                service = client.connect(token=session_key)

                # If the query doesn't already start with the 'search' operator or another
                # generating command (e.g. "| inputcsv"), then prepend "search " to it.
                # But leave templated_drilldown_search alone, so it gets added to the kvstore as entered in the alert params
                search_to_run = templated_drilldown_search
                if not (search_to_run.startswith('search')
                        or search_to_run.startswith("|")):
                    search_to_run = 'search ' + search_to_run

                job = service.jobs.create(search_to_run,
                                          earliest_time=search_et,
                                          latest_time=search_lt,
                                          exec_mode="blocking")
                raw_values = []
                for result in results.ResultsReader(job.results()):
                    raw_values.append(result['_raw'])
                full_raw = "\n".join(raw_values)
                jira.add_comment(issue, full_raw)

        # new or existing issue, we continue processing to add to the kvstore
        session_key = helper.session_key

        # need to set owner="nobody" to use kvstore
        service = client.connect(owner="nobody", token=session_key)
        jirables_collection = service.kvstore['jirables']
        jirables_collection.data.insert(
            json.dumps({
                "jira_key": issue.key,
                "alert_time": alert_time,
                "search_et": search_et,
                "search_lt": search_lt,
                "jira_action": jira_action,
                "drilldown_dashboard": drilldown_dashboard,
                "drilldown_search": templated_drilldown_search,
            }))

    return 0
Example #23
0
class TaskJira(TaskManager):
    """Subclass of TaskManager implementing HMGM task management with Jira platforms."""

    # Set up jira instance with properties in the given configuration instance.
    def __init__(self, config):
        super().__init__(config)

        # get Jira server info from the config
        jira_server = config["jira"]["server"]
        jira_username = config["jira"]["username"]
        jira_password = config["jira"]["password"]
        self.jira_project = config["jira"]["project"]
        self.jira = JIRA(server=jira_server,
                         basic_auth=(jira_username, jira_password))

    # Create a jira issue given the task_type, context, input/output json,
    # save information about the created issue into a JSON file, and return the issue.
    def create_task(self, task_type, context, editor_input, task_json):
        # populate the jira fields into a dictionary with information from task_type and context etc
        project = {"key": self.jira_project}
        issuetype = {"name": "Task"}
        labels = [task_type]
        summary = context["primaryfileName"] + " - " + context["workflowName"]
        description = self.get_task_description(task_type, context,
                                                editor_input)
        jira_fields = {
            "project": project,
            "issuetype": issuetype,
            "labels": labels,
            "summary": summary,
            "description": description
        }

        # create a new task jira using jira module
        issue = self.jira.create_issue(fields=jira_fields)

        # extract important information (ID, key, and URL) of the created issue into a dictionary, which is essentially the response returned by Jira server
        issue_dict = {
            "id": issue.id,
            "key": issue.key,
            "url": issue.permalink()
        }

        # write jira issue into task_json file to indicate successful creation of the task
        with open(task_json, "w") as task_file:
            json.dump(issue_dict, task_file)

        return issue

    # Close the jira issue specified in task_json by updating its status and relevant fields, and return the issue.
    def close_task(self, task_json):
        # read jira issue info from task_json into a dictionary
        with open(task_json, 'r') as task_file:
            issue_dict = json.load(task_file)

        # get the jira issue using id
        issue = self.jira.issue(issue_dict["id"])

        # retrieve transition ID based on name = Done instead of hard coding it, in case the ID might be different
        transitions = self.jira.transitions(issue)
        transition = None
        for t in transitions:
            if t["name"] == "Done":  # Done is the status when an issue is closed
                transition = t["id"]
                break

        # if transition is None, that means issue is already in Done status
        if transition is None:
            print("Issue  " + issue.id +
                  " is already Done, probably closed manually by someone")
        # otherwise update the jira status to Done via transition
        else:
            print("Transition issue " + issue.id + " to status " + transition)
            self.jira.transition_issue(issue, transition)

        return issue
            if env == 'qa':
                rfd_dict['issuetype'] = {'name': 'Task'}
                rfd_dict['description'] = 'QA code review.'
            else:
                rfd_dict['issuetype'] = {'name': 'RFD'}

            rfd_dict['summary'] = 'Deploy ' + app.upper() + ' ' + \
                version.upper() + ' to ' + env.upper() + ''
            rfd_dict['fixVersions'] = [{'name': '' + version.upper() + ''}]
            rfd_dict['priority'] = {'name': 'Medium'}
            rfd_dict['customfield_10121'] = {
                'value': '' + targetenv.upper() + ''
            }
            rfd_dict['customfield_10636'] = '' + datetime + ':00.000-0700'

            rfd = jira.create_issue(fields=rfd_dict)
            print "Created RFD: %s" % rfd
            if verbose:
                print "RFD Dict"
                print rfd_dict

            if dba_needed:
                # Populate the KV pairs for the DBA RFD-subtask
                rfd_st_dba_dict['project'] = {'key': '' + app.upper() + ''}

                if env == 'qa':
                    rfd_st_dba_dict['issuetype'] = {'name': 'Sub-task'}
                else:
                    rfd_st_dba_dict['issuetype'] = {'name': 'RFD-subtask'}

                rfd_st_dba_dict['parent'] = {'key': '' + str(rfd) + ''}
Example #25
0
class JiraApi():

  str_jira_scheduled = "%Y-%m-%dT%H:%M:%S.0%z"

  def __init__(self,
               instance=None,
               approver_instance=None):
    self.instance = instance
    self.approver_instance = approver_instance

  @staticmethod
  def next_immediate_window_dates():
    tz = pytz.timezone(app.config['CM_TZ'])
    now_utc = datetime.utcnow()
    now_tz = tz.localize(now_utc)
    start = None
    if now_tz.hour <= app.config['CM_DEADLINE_HOUR']  and now_tz.minute < app.config['CM_DEADLINE_MIN']:
      start = tz.localize(datetime(now_tz.year, now_tz.month, now_tz.day, app.config['CM_SAME_DAY_START_HOUR']))
    else:
      delay_hours = timedelta(hours=app.config['CM_DEADLINE_MISSED_DELAY_HOURS'])
      start_day = now_tz + delay_hours
      start = tz.localize(datetime(
        start_day.year, start_day.month, start_day.day, app.config['CM_DEADLINE_MISSED_START_HOUR']))
    end = start + timedelta(hours=app.config['CM_WINDOW_LEN_HOURS'])
    return start.strftime(JiraApi.str_jira_scheduled), \
           end.strftime(JiraApi.str_jira_scheduled)

  @staticmethod
  def get_datetime_now():
    tz = pytz.timezone(app.config['CM_TZ'])
    now = pytz.utc.localize(datetime.utcnow()).astimezone(tz)
    return now.strftime(JiraApi.str_jira_scheduled)

  def connect(self):
    options = {'server': app.config['JIRA_HOSTNAME'], 'verify': False, 'check_update': False}
    self.instance = JIRA(options,
                basic_auth=(app.config['JIRA_USERNAME'], app.config['JIRA_PASSWORD']))
    self.approver_instance = JIRA(options,
                basic_auth=(app.config['JIRA_APPROVER_USERNAME'], app.config['JIRA_APPROVER_PASSWORD']))

  @staticmethod
  def ticket_link(issue):
    return '<a href="{}/browse/{}">{}</a>'.format(app.config['JIRA_HOSTNAME'], issue.key, issue.key)

  def resolve(self, issue):
    self.instance.transition_issue(
      issue,
      app.config['JIRA_RESOLVE_TRANSITION_ID'],
      assignee={'name': app.config['JIRA_USERNAME']},
      resolution={'id': app.config['JIRA_RESOLVE_STATE_ID']})

  def defect_for_exception(self, summary_title, e):
    return self.instance.create_issue(
      project=app.config['JIRA_PROJECT'],
      summary='[auto-{}] Problem: {}'.format(current_user.username, summary_title),
      description="Exception: {}".format(e),
      customfield_13842=JiraApi.get_datetime_now(),
      customfield_13838= {"value": "No"},
      customfield_13831 =  [
        {"value": "Quality"},
        {"value": "Risk Avoidance"}
      ],
      issuetype={'name': 'Defect'})
class JiraClient():
    jira_con = None
    board_details = None

    def __init__(self):
        self._get_jira_board_details()
        secrets = _get_secrets_from_netrc()

        if not secrets:
            err_msg = 'Unable to locate or load suitable `.netrc` file for JIRA integration'
            logger.error(err_msg)
            raise ValueError(err_msg)

        try:
            username, account, apikey = secrets.authenticators(
                self.jira_hostname)
        except TypeError:
            err_msg = 'JIRA Connection. Unable to find details for machine "{}" `.netrc` file.'.format(
                self.jira_hostname)
            logger.error(err_msg)
            raise ValueError(err_msg)

        self.jira_con = JIRA(options={'server': account},
                             basic_auth=(username, apikey))
        _check_jira_con(self.jira_con, username)
        logger.debug('JIRA Connection. Details = {}'.format(
            self.jira_con.myself()))

    def _get_jira_board_details(self):
        # TODO read these in from a config file
        self.jira_hostname = 'mapaction.atlassian.net'
        self.project_key = 'PIPET'
        # The target column should be were the column where new issues are created
        self.target_column = '10110'
        self.common_task_fields = {
            'project': self.project_key,
            'issuetype': {
                'id': '10235'
            }
        }

    def __del__(self):
        try:
            self.jira_con.kill_session()
        except (TypeError, AttributeError):
            pass

    def task_handler(self, fail_threshold, msg, task_referal=None):
        logger.debug(
            'JiraClient.task_handler called with status="{}", and msg="{}"'.
            format(fail_threshold, msg))

        assured_referal = self.ensure_task_referal_type(
            task_referal, msg, fail_threshold)

        if not assured_referal:
            logger.debug(
                'JiraClient.task_handler; `None` value passed for task_referal parameter. Nothing to handle.'
            )
            return

        unique_summary = assured_referal.get_task_unique_summary()
        task_desc = assured_referal.get_task_description()
        op_id = assured_referal.get_operation_id()

        j_issue = self.search_issue_by_unique_summary(unique_summary, op_id)

        if j_issue:
            # Update existing card and maybe move it back into "Doing" column
            self.update_jira_issue(j_issue, task_desc, fail_threshold)
        else:
            if fail_threshold > logging.INFO:
                # Create a new task
                self.create_new_jira_issue(unique_summary, task_desc, op_id)

    def ensure_task_referal_type(self, task_referal, msg, fail_threshold):
        """
        Check whether or not the `task_referal` is an instance of TaskReferralBase object. If it is the object is
        then it is returned unchanged. If not then an generic TaskReferralBase will be created and returned. The
        value of `str(task_referal)` will be used.

        @param task_referal: An object that may or may not be a TaskReferralBase object.
        @returns: If the `task_referal` param is an instance of TaskReferralBase object, then `task_referal` is
                  returned.
                  If `task_referal` param is NOT an instance of TaskReferralBase AND fail_threshold is logging.ERROR
                  then a new TaskReferralBase object is created (using `msg` and `str(task_referal)` for context).
                  Else `None` is returned.
        """
        if isinstance(task_referal, TaskReferralBase):
            logger.debug(
                'JiraClient.ensure_task_referal_type found a TaskReferralBase object'
            )
            return task_referal

        if task_referal and (fail_threshold > logging.WARNING):
            logger.debug(
                'JiraClient.ensure_task_referal_type created a new TaskReferralBase object'
            )
            return TaskReferralBase(None, msg=msg, other=str(task_referal))

        logger.debug(
            'JiraClient.ensure_task_referal_type passed "{}" but returned `None`'
            .format(str(task_referal)))
        return None

    def search_issue_by_unique_summary(self, search_summary, op_id):
        # Default if `op_id` is None
        jql_op_id = 'operational_id is EMPTY'
        if op_id:
            jql_op_id = 'operational_id ~ "{}"'.format(op_id)

        jql_str = 'project={} AND {} AND summary ~ "{}"'.format(
            self.project_key, jql_op_id, search_summary)
        found_issues = self.jira_con.search_issues(jql_str, maxResults=2)

        if found_issues:
            if len(found_issues) > 1:
                raise ValueError(
                    'More than one JIRA Issue found with the summary "{}". This suggests that additional'
                    ' issues have been raised manualy on the board "{}". Please ensure that there is exactly'
                    ' one issues with this summary, by deleting those which have not been created by the'
                    ' user "{}"'.format(
                        search_summary, self.project_key,
                        self.jira_con.myself()['emailAddress']))
            else:
                return found_issues[0]
        else:
            return None

    def create_new_jira_issue(self, unique_summary, task_desc, op_id):
        flds = self.common_task_fields.copy()
        flds['summary'] = unique_summary
        flds['description'] = task_desc
        # This is the JIRA API's field ID for operational_id. To work this out execute:
        # ```
        # a = j.jira_con.createmeta(projectKeys=['PIPET'], issuetypeIds=[10235], expand='projects.issuetypes.fields')
        # print(a)
        # ```
        # Then search the output for your custom field name. Doubtless there is a programmatic way to do this.
        flds['customfield_10234'] = op_id

        new_task = self.jira_con.create_issue(fields=flds)
        # new_task.update(fields={'operational_id':op_id})
        # new_task.update(operational_id=op_id)
        print(new_task)
        # print('desc', new_task.fields.description)
        # print('opid', new_task.fields.operational_id)
        # for f in new_task.fields:
        #     print('field itr', f)

    def update_jira_issue(self, j_issue, task_desc, fail_threshold):
        now_utc = pytz.utc.localize(datetime.now())
        time_stamp = now_utc.strftime('%Y-%m-%d %H:%M:%S %Z%z')

        # prev_desc =
        if task_desc != j_issue.fields.description:
            j_issue.update(description=task_desc)

        if fail_threshold > logging.INFO:
            self.jira_con.add_comment(
                j_issue.id,
                'This Issue was still current when MapChef was run at {}'.
                format(time_stamp))
Example #27
0
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 23 06:05:32 2016

@author: bkunneke
"""

# Great write up on how to do everything in JIRA here:
# https://pythonhosted.org/jira/
from jira import JIRA

jira = JIRA(server='https://te2web.atlassian.net', basic_auth=('user', 'pwd'))    # a username/password tuple
projects = jira.projects() # Gets a list of all projects

# Create an issue
new_issue = jira.create_issue(project='PROJ_key_or_id', summary='New issue from jira-python',
                              description='Look into this one', issuetype={'name': 'Bug'})

# Find a specific issue
issues = jira.search_issues("key = 'HIL-163'")

Example #28
0
        },
        'parent': {
            'id': key
        },
        'assignee': {
            'name': data[7]
        },
        #'priority' : { 'name' : data[3]},
        #'description' : data[9].decode('iso-8859-1').encode('utf8'),
        'duedate': dueDate.isoformat().decode('iso-8859-1').encode('utf8')
    }

    if not issueId == 10501 or 283:
        further = {
            #'customfield_23431' : estDate.isoformat().decode('iso-8859-1').encode('utf8'),
            #'customfield_21331' : { 'value' : data[8]}
        }

        subtask = merge_two_dicts(subtask, further)

    if issueId == 292:
        subtask['components'] = [{'name': "Cendoc"}]
    elif component:
        subtask['components'] = [{'name': component}]

    #pdb.set_trace()

    child = jira.create_issue(fields=subtask)

    print(child.key)
Example #29
0
class JiraAPI(object):
    """Access Jira api using python-jira project."""
    def __init__(self, user, passwd, logger):
        """Init JiraApi object and logger."""
        self.logger = logger
        self.jira = JIRA(server=config.JIRA_HOST, basic_auth=(user, passwd))

    def get_boards(self):
        """Get Jira boards list as json."""
        self.logger.info("Getting Jira boards")
        json = {'boards': []}
        boards = self.jira.boards()
        for board in boards:
            json.get('boards').append({'id': board.id, 'name': board.name})
        return json

    # ###############
    # ### Sprints ###
    # ###############

    def _get_board_sprints(self, board_id):
        """Get sprints of a board as json."""
        self.logger.info("Getting board {} sprints".format(board_id))
        json = {'sprints': []}
        sprints = self.jira.sprints(board_id, extended=True)
        for sprint in sprints:
            # self.logger.debug("Sprint content: {}".format(sprint.__dict__))
            json.get('sprints').append({
                'id': sprint.id,
                'key': sprint.id,
                'name': sprint.name,
                'start': sprint.startDate,
                'end': sprint.endDate
            })
        return json

    def search_sprint(self, board_key, sprint_name):
        """Search a sprint in a board and returns its info."""
        self.logger.info("Searching board {} sprint '{}'".format(
            board_key, sprint_name))
        found_sprint = None
        sprints = self._get_board_sprints(board_key)
        for sprint in sprints.get('sprints'):
            if (sprint.get('name') == sprint_name):
                found_sprint = sprint
                break
        return found_sprint

    def create_sprint(self, board_id, sprint_name, start, end):
        """Create a sprint in a board with given start and end dates.

        Dates must be in Jira format.
        """
        self.logger.info("Creating sprint {}".format(sprint_name))
        sprint = self.jira.create_sprint(name=sprint_name,
                                         board_id=board_id,
                                         startDate=start,
                                         endDate=end)
        # self.logger.debug("Created sprint content {}".format(sprint.__dict__))
        return sprint.id

    def start_sprint(self, sprint_id, sprint_name, start, end):
        """Start a Jira sprint with given dates. *** Does not work because Jira API call fails with 'state' param. ***

        Dates must be in Jira format.
        """
        self.logger.info("Starting sprint {} [Not working]".format(sprint_id))
        # self.jira.update_sprint(sprint_id, name=sprint_name, startDate=start, endDate=end, state="active")

    def close_sprint(self, sprint_id, sprint_name, start, end):
        """Closes a Jira sprint with given dates. *** Does not work because Jira API call fails with 'state' param. ***

        Dates must be in Jira format.
        """
        self.logger.info("Closing sprint {} [Not working]".format(sprint_id))
        # self.jira.update_sprint(sprint_id, name=sprint_name, startDate=start, endDate=end, state=None)

    def delete_board_sprints(self, board_id):
        """Delete all sprints of a board."""
        self.logger.info("Deleting board {} sprints".format(board_id))
        sprints = self.jira.sprints(board_id, extended=False)
        for sprint in tqdm(sprints):
            self.logger.info("Deleting sprint {}".format(sprint.name))
            sprint.delete()

    def _get_board_project_id(self, board_id):
        """Get the project id of a board."""
        self.logger.debug("Getting project id of board {}".format(board_id))
        project_id = None
        boards = self.jira.boards()
        for board in boards:
            # self.logger.debug("Comparing boards {}-{}".format(board.id, board_id))
            if str(board.id) == str(board_id):
                # self.logger.debug("Found matching board {}".format(board.raw))
                project_id = board.raw.get("filter").get("queryProjects").get(
                    "projects")[0].get("id")
                break
        return project_id

    # ####################
    # ### User stories ###
    # ####################

    def _get_project_user_stories(self, project_key):
        """Get all user stories of a project."""
        self.logger.info("Getting project {} user stories".format(project_key))
        issues = self.jira.search_issues('project=' + project_key +
                                         ' and issuetype=' +
                                         config.JIRA_USER_STORY_TYPE,
                                         maxResults=200)
        return issues

    def delete_all_project_user_stories(self, project_key):
        """Delete all user stories of a project."""
        self.logger.info(
            "Deleting project {} user stories".format(project_key))
        issues = self._get_project_user_stories(project_key)
        for issue in tqdm(issues):
            self.logger.info("Deleting user story {}".format(issue))
            issue.delete()

    def _create_user_story(self, project_id, subject, description, tags,
                           points):
        """Create a user story with provided information."""
        story_fields = {
            "project": project_id,
            "issuetype": config.JIRA_USER_STORY_TYPE,
            "summary": subject,
            "description": description,
            "labels": tags,
            config.JIRA_ESTIMATION_FIELD: points
        }
        created_story = self.jira.create_issue(fields=story_fields)
        self.logger.info("Created user story {} # {}".format(
            created_story.id, created_story.key))
        # self.logger.debug("Created user story details: {}".format(created_story.__dict__))
        return created_story

    def update_user_story_status(self, user_story_key, is_closed, status):
        """Update the status of a user story to Done or Not Done only if it's closed.

        A translation is made between the status given, which is the status in Taiga and the Jira status as
        defined in JIRA_USER_STORY_STATUS_DONE, JIRA_USER_STORY_STATUS_NOT_DONE and TAIGA_USER_STORY_STATUS_NOT_DONE
        constants of config.py file.
        """
        self.logger.info(
            "Updating user story {} with Taiga status={} ({})".format(
                user_story_key, status, is_closed))
        if is_closed:
            task_status = config.JIRA_USER_STORY_STATUS_DONE
            if status == config.TAIGA_USER_STORY_STATUS_NOT_DONE:
                task_status = config.JIRA_USER_STORY_STATUS_NOT_DONE
            self.jira.transition_issue(user_story_key, task_status)
            self.logger.info("Updated user story {} status to {}".format(
                user_story_key, task_status))
        else:
            self.logger.warn(
                "Not updated user story {} beacuse is not closed: is_closed={}"
                .format(user_story_key, is_closed))

    # ########################
    # ### User story tasks ###
    # ########################

    def _create_user_story_task(self, project_id, user_story_id, subject,
                                description, finished_date):
        """Create a task inside a user story.

        The story task type is defined in config.py in JIRA_USER_STORY_TASK_TYPE constant. default is 'Sub-type'.
        """
        self.logger.info("Creating user story task {}".format(subject))
        created_task = self.jira.create_issue(
            project=project_id,
            parent={"id": user_story_id},
            issuetype=config.JIRA_USER_STORY_TASK_TYPE,
            summary=subject)
        self.logger.info("Created user story task {} # {}".format(
            created_task.id, created_task.key))
        # self.logger.debug("Created story task details: {}".format(created_task.__dict__))
        return created_task

    def _update_task_status(self, task_key, status):
        """Update task status with Done or not Done status depending on incoming Taiga status.

        Taiga done status is defined in TAIGA_TASK_STATUS_DONE constant inside config.py file.
        Jira done and not done statuses are defined in JIRA_TASK_STATUS_DONE and JIRA_TASK_STATUS_NOT_DONE inside
        config.py file.
        """
        self.logger.info(
            "Updating user story task {} with Taiga status={}".format(
                task_key, status))
        task_status = config.JIRA_TASK_STATUS_DONE
        if status != config.TAIGA_TASK_STATUS_DONE:
            task_status = config.JIRA_TASK_STATUS_NOT_DONE
        self.jira.transition_issue(task_key, task_status)
        self.logger.info("Updated user story task {} status to {}".format(
            task_key, task_status))

    # #######################
    # ### Sprint creation ###
    # #######################

    def _add_comment(self, issue_id, comment):
        """Add a comment to an issue."""
        self.logger.debug("Adding comment to issue {}".format(issue_id))
        self.jira.add_comment(issue_id,
                              comment,
                              visibility={'group': 'jira-users'})

    def create_sprint_stories(self, board_id, sprint_id, user_stories):
        """Create user stories in a board and link them to a sprint.

        A json array with user story key, is_closed and status info is returned.
        User stories subtasks are also added to the story.
        User story tasks finished date and current taiga status are added as comments.
        User story finished date and current taiga status are added as comments.
        """
        project_id = self._get_board_project_id(board_id)
        self.logger.info(
            "Creating user stories in project {} - sprint {}".format(
                project_id, sprint_id))
        created_stories = []
        for user_story in user_stories:
            self.logger.info("Creating user story {}".format(
                user_story.get("subject")))
            created_story = self._create_user_story(
                project_id, user_story.get("subject"),
                user_story.get("description"), user_story.get("tags"),
                user_story.get("total_points"))
            self.logger.info("Adding user story {} to sprint {}".format(
                created_story.key, sprint_id))
            self.jira.add_issues_to_sprint(sprint_id, [created_story.key])
            for task in user_story.get("tasks"):
                created_task = self._create_user_story_task(
                    project_id, created_story.id, task.get("subject"),
                    task.get("description"), task.get("finished_date"))
                # Add as comment user story finished date
                self._add_comment(
                    created_task.id,
                    "Finished date: '{}'".format(task.get('finished_date')))
                self._add_comment(
                    created_task.id,
                    "Taiga status: '{}'".format(task.get("status")))
                # Update task status
                self._update_task_status(created_task.key, task.get("status"))
            created_stories.append({
                "key": created_story.key,
                "is_closed": user_story.get("is_closed"),
                "status": user_story.get("status")
            })
            # Add as comment user story finished date
            self._add_comment(
                created_story.id,
                "Finished date: '{}'".format(user_story.get('finish_date')))
            self._add_comment(
                created_story.id,
                "Taiga status: '{}'".format(user_story.get("status")))
        return created_stories

    def add_backlogs_stories(self, board_id, user_stories):
        """Add user stories to the backlog of the board.

        Taiga original status and backlog order are added as comments.
        """
        project_id = self._get_board_project_id(board_id)
        self.logger.info(
            "Creating user stories in project {} backlog".format(project_id))
        created_stories = []
        for user_story in user_stories:
            self.logger.info("Creating user story {}".format(
                user_story.get("subject")))
            created_story = self._create_user_story(
                project_id, user_story.get("subject"),
                user_story.get("description"), user_story.get("tags"),
                user_story.get("total_points"))
            created_stories.append({"key": created_story.key})
            # Add as comment user story finished date
            self._add_comment(
                created_story.id,
                "Taiga status: '{}'".format(user_story.get("status")))
            self._add_comment(
                created_story.id, "Taiga backlog order: '{}'".format(
                    user_story.get("backlog_order")))
        return created_stories
Example #30
0
    def run(
        self,
        username: str = None,
        access_token: str = None,
        server_url: str = None,
        project_name: str = None,
        assignee: str = "-1",
        issue_type: str = None,
        summary: str = None,
        description: str = None,
    ) -> None:
        """
        Run method for this Task. Invoked by calling this Task after initialization within a
        Flow context, or by using `Task.bind`.

        Args:
            - username(str): the jira username, provided with a Prefect secret (defaults to
                JIRAUSER in JIRASECRETS)
            - access_token (str): a Jira access token, provided with a Prefect secret (defaults
                to JIRATOKEN in JIRASECRETS)
            - server_url (str): the URL of your atlassian account e.g.
                "https://test.atlassian.net".  Can also be set as a Prefect Secret. Defaults to
                the one provided at initialization
            - project_name(str):  the key for your jira project; defaults to the one provided
                at initialization
            - assignee (str, optional): the atlassian accountId of the person you want to
                assign the ticket to; defaults to "automatic" if this is not set; defaults to
                the one provided at initialization
            - issue_type (str, optional): the type of issue you want to create; defaults to
                'Task'
            - summary (str, optional): summary or title for your issue; defaults to the one
                provided at initialization
            - description (str, optional): description or additional information for the issue;
                defaults to the one provided at initialization

        Raises:
            - ValueError: if a `project_name` or 'summary' are not provided

        Returns:
            - None
        """

        jira_credentials = cast(dict, Secret("JIRASECRETS").get())

        if username is None:
            username = jira_credentials["JIRAUSER"]

        if access_token is None:
            access_token = jira_credentials["JIRATOKEN"]

        if server_url is None:
            server_url = jira_credentials["JIRASERVER"]

        if issue_type is None:
            issue_type = "Task"

        if project_name is None:
            raise ValueError("A project name must be provided")

        if summary is None:
            raise ValueError("A summary must be provided")

        jira = JIRA(basic_auth=(username, access_token),
                    options={"server": server_url})

        options = {
            "project": project_name,
            "assignee": {
                "accountId": assignee
            },
            "issuetype": {
                "name": issue_type
            },
            "summary": summary,
            "description": description,
        }
        created = jira.create_issue(options)

        if not created:
            raise ValueError("Creating Jira Issue failed")
Example #31
0
versions = list()

for branch in branches.split(","):
    versions.append({"id": versions_map[branch]})

issue_dict = {
    'project': {"key": "LIT"},
    'issuetype': {"name": "Bug"},
    'components': [{"name": "LIT-BTD"}],
    'assignee': {"name": the_assignee},
    'description': description,
    'summary': summary,
    'customfield_10500': [{"value": "Multi"}],
    'versions': versions,
    'labels': labels,
    'customfield_10500': None,
    'priority': { "name" : priority }
}

#print(json.dumps(issue_dict, indent=4))
print "please wait while creating the issue and uploading the file .."
issue = jira.create_issue(fields=issue_dict)

if attachment_name is not None:
    with open(attachment_name, 'rb') as the_attachment:
        jira.add_attachment(issue, the_attachment, attachment_name)
print ""
print "issue created with ID: ",issue
print ""
#webbrowser.open_new("https://jira.atypon.com/browse/{}".format(issue))
Example #32
0
def submit_jira_ticket(request):
    username = request.user.username
    jira_setting = jirasetting.objects.filter(username=username)
    user = request.user

    for jira in jira_setting:
        jira_url = jira.jira_server
        username = jira.jira_username
        password = jira.jira_password
    jira_server = jira_url
    jira_username = signing.loads(username)
    jira_password = signing.loads(password)

    options = {'server': jira_server}
    try:
        jira_ser = JIRA(options, basic_auth=(jira_username, jira_password))
        jira_projects = jira_ser.projects()
    except Exception as e:
        print(e)
        notify.send(user, recipient=user, verb='Jira settings not found')

    if request.method == 'GET':
        summary = request.GET['summary']
        description = request.GET['description']
        scanner = request.GET['scanner']
        vuln_id = request.GET['vuln_id']
        scan_id = request.GET['scan_id']

        return render(
            request, 'submit_jira_ticket.html', {
                'jira_projects': jira_projects,
                'summary': summary,
                'description': description,
                'scanner': scanner,
                'vuln_id': vuln_id,
                'scan_id': scan_id
            })

    if request.method == 'POST':
        summary = request.POST.get('summary')
        description = request.POST.get('description')
        project_id = request.POST.get('project_id')
        issue_type = request.POST.get('issue_type')
        vuln_id = request.POST.get('vuln_id')
        scanner = request.POST.get('scanner')
        scan_id = request.POST.get('scan_id')

        issue_dict = {
            'project': {
                'id': project_id
            },
            'summary': summary,
            'description': description,
            'issuetype': {
                'name': issue_type
            },
        }
        new_issue = jira_ser.create_issue(fields=issue_dict)
        # print new_issue

        if scanner == 'zap':
            zap_scan_results_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('zapscanner:zap_vuln_details') +
                '?scan_id=%s&scan_name=%s' % (scan_id, summary))
        elif scanner == 'burp':
            burp_scan_result_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('burpscanner:burp_vuln_out') +
                '?scan_id=%s&scan_name=%s' % (scan_id, summary))
        elif scanner == 'arachni':
            arachni_scan_result_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('arachniscanner:arachni_vuln_out') +
                '?scan_id=%s&scan_name=%s' % (scan_id, summary))

        elif scanner == 'netsparker':
            netsparker_scan_result_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('netsparkerscanner:netsparker_vuln_out') +
                '?scan_id=%s&scan_name=%s' % (scan_id, summary))

        elif scanner == 'webinspect':
            webinspect_scan_result_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('webinspectscanner:webinspect_vuln_out') +
                '?scan_id=%s&scan_name=%s' % (scan_id, summary))

        elif scanner == 'bandit':
            bandit_scan_results_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('banditscanner:banditscan_vuln_data') +
                '?scan_id=%s&test_name=%s' % (scan_id, summary))

        elif scanner == 'dependencycheck':
            dependencycheck_scan_results_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('dependencycheck:dependencycheck_vuln_data') +
                '?scan_id=%s&test_name=%s' % (scan_id, summary))

        elif scanner == 'findbugs':
            findbugs_scan_results_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('findbugs:findbugs_vuln_data') +
                '?scan_id=%s&test_name=%s' % (scan_id, summary))

        elif scanner == 'clair':
            clair_scan_results_db.objects.filter(
                username=username,
                vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('clair:clair_vuln_data') + '?scan_id=%s&test_name=%s' %
                (scan_id, summary))

        elif scanner == 'open_vas':
            ov_scan_result_db.objects.filter(
                username=username,
                vul_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('networkscanners:vul_details') +
                '?scan_id=%s' % scan_id)
        elif scanner == 'nessus':
            nessus_report_db.objects.filter(
                username=username,
                vul_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect(
                reverse('networkscanners:nessus_vuln_details') +
                '?scan_id=%s' % scan_id)
Example #33
0
    def report_issue(self, build):
        try:
            jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(JIRA_USER, JIRA_PASS))
        except:
            logging.exception('Could not connect to Jira')
            return

        build_report_url = self.jhost + '/job/' + job + '/' + str(build) + '/api/python'
        build_report = eval(self.read_url(build_report_url))
        build_url = build_report.get('url')
        build_result = build_report.get('result')

        if build_result == 'SUCCESS':  # only generate Jira issue if the test fails
            print 'No new issue created. Build ' + str(build) + 'resulted in: ' + build_result
            return

        summary_url = self.jhost + '/job/' + job + '/' + str(build) + '/artifact/tests/sqlgrammar/summary.out'
        summary_report = self.read_url(summary_url)

        pframe_split = summary_report.split('Problematic frame:')
        pframe_split = pframe_split[1].split('C')
        pframe_split = pframe_split[1].split(']')
        pframe_split = pframe_split[1].split('#')
        pframe = pframe_split[0].strip()

        summary = job + ':' + str(build) + ' - ' + pframe
        # search_issues gets a parsing error on (), so escape it.
        existing = jira.search_issues('summary ~ \'%s\'' % summary.replace('()','\\\\(\\\\)',10))
        if len(existing) > 0:
            print 'No new Jira issue created. Build ' + str(build) + ' has already been reported.'
            return 'Already reported'

        old_issue = ''
        existing = jira.search_issues('summary ~ \'%s\'' % pframe_split[0].strip().replace('()','\\\\(\\\\)',10))
        for issue in existing:
            if str(issue.fields.status) != 'Closed' and u'grammar-gen' in issue.fields.labels:
                old_issue = issue

        build_artifacts = build_report.get('artifacts')[0]
        pid_fileName = build_artifacts['fileName']
        pid_url = build_url + 'artifact/' + pid_fileName

        query_split = summary_report.split('(or it was never started??), after SQL statement:')
        crash_query = query_split[1]

        hash_split = summary_report.split('#', 1)
        hash_split = hash_split[1].split('# See problematic frame for where to report the bug.')
        sigsegv_message = hash_split[0] + '# See problematic frame for where to report the bug.\n#'

        description = job + ' build ' + str(build) + ' : ' + str(build_result) + '\n' \
            + 'Jenkins build: ' + build_url + ' \n \n' \
            + 'DDL: ' + 'https://github.com/VoltDB/voltdb/blob/master/tests/sqlgrammar/DDL.sql' + ' \n \n' \
            + 'hs_err_pid: ' + pid_url + ' \n \n' \
            + 'SIGSEGV Message: \n' + '#' + sigsegv_message + ' \n \n' \
            + 'Query that Caused the Crash: ' + crash_query
        description = description.replace('#', '\#')

        labels = ['grammar-gen']

        component = 'Core'
        components = jira.project_components(JIRA_PROJECT)
        jira_component = {}
        for c in components:
            if c.name == component:
                jira_component = {
                    'name': c.name,
                    'id': c.id
                }
                break

        current_version_raw = str(self.read_url('https://raw.githubusercontent.com/VoltDB/voltdb/master/version.txt'))
        current_version_float = float(current_version_raw)
        current_version = 'V' + current_version_raw
        current_version = current_version.strip()
        next_version = current_version_float + .1
        next_version = str(next_version)
        next_version = 'V' + next_version
        next_version = next_version[:4]

        jira_versions = jira.project_versions(JIRA_PROJECT)
        this_version = {}
        new_version = {}

        for v in jira_versions:
            if str(v.name) == current_version:
                this_version = {
                    'name': v.name,
                    'id': v.id
                }
            if str(v.name) == next_version:
                new_version = {
                    'name': v.name,
                    'id': v.id
                }

        issue_dict = {
            'project': JIRA_PROJECT,
            'summary': summary,
            'description': description,
            'issuetype': {'name': 'Bug'},
            'priority': {'name': 'Blocker'},
            'labels': labels,
            'customfield_10430': {'value': 'CORE team'},
            'components': [jira_component]
        }

        if new_version:
            issue_dict['versions'] = [new_version]
            issue_dict['fixVersions'] = [new_version]

        elif this_version:
            issue_dict['versions'] = [this_version]
            issue_dict['fixVersions'] = [this_version]

        if old_issue:
            new_comment = jira.add_comment(old_issue, description)
            print 'JIRA-action: New comment on issue: ' + str(old_issue) + ' created for failure on build ' + str(build)
        else:
            new_issue = jira.create_issue(fields=issue_dict)
            print 'JIRA-action: New issue ' + new_issue.key + ' created for failure on build ' + str(build)
Example #34
0
def main():
    context = utils.collect_context()
    tmpl_host_summary = 'Check_MK: $HOSTNAME$ - $HOSTSHORTSTATE$'
    tmpl_service_summary = 'Check_MK: $HOSTNAME$/$SERVICEDESC$ $SERVICESHORTSTATE$'
    tmpl_label = 'monitoring'

    for necessary in [
            'PARAMETER_URL', 'PARAMETER_USERNAME', 'PARAMETER_PASSWORD',
            'PARAMETER_HOST_CUSTOMID', 'PARAMETER_SERVICE_CUSTOMID'
    ]:
        if necessary not in context:
            sys.stderr.write("%s not set" % necessary)
            return 2

    if "PARAMETER_IGNORE_SSL" in context:
        sys.stdout.write(
            "Unverified HTTPS request warnings are ignored. Use with caution.\n"
        )
        jira = JIRA(server=context['PARAMETER_URL'],
                    basic_auth=(context['PARAMETER_USERNAME'],
                                context['PARAMETER_PASSWORD']),
                    options={'verify': False})
    else:
        jira = JIRA(server=context['PARAMETER_URL'],
                    basic_auth=(context['PARAMETER_USERNAME'],
                                context['PARAMETER_PASSWORD']))

    if context['WHAT'] == 'HOST':
        summary = context.get('PARAMETER_HOST_SUMMARY') or tmpl_host_summary
        svc_desc = context['HOSTOUTPUT']
        custom_field = int(context['PARAMETER_HOST_CUSTOMID'])
        custom_field_value = int(context['HOSTPROBLEMID'])
    else:
        summary = context.get(
            'PARAMETER_SERVICE_SUMMARY') or tmpl_service_summary
        svc_desc = context['SERVICEOUTPUT']
        custom_field = int(context['PARAMETER_SERVICE_CUSTOMID'])
        custom_field_value = int(context['SERVICEPROBLEMID'])

    context['SUBJECT'] = utils.substitute_context(summary, context)
    label = context.get('PARAMETER_LABEL') or tmpl_label
    newissue = {
        u'labels': [label],
        u'summary': context['SUBJECT'],
        u'description': svc_desc,
    }

    if 'PARAMETER_PROJECT' in context:
        newissue[u'project'] = {u'id': context['PARAMETER_PROJECT']}
    if 'CONTACT_JIRAPROJECT' in context:
        newissue[u'project'] = {u'id': context['CONTACT_JIRAPROJECT']}
    if 'PARAMETER_ISSUETYPE' in context:
        newissue[u'issuetype'] = {u'id': context['PARAMETER_ISSUETYPE']}
    if 'CONTACT_JIRAISSUETYPE' in context:
        newissue[u'issuetype'] = {u'id': context['CONTACT_JIRAISSUETYPE']}
    if 'PARAMETER_PRIORITY' in context:
        newissue[u'priority'] = {u'id': context['PARAMETER_PRIORITY']}
    if 'CONTACT_JIRAPRIORITY' in context:
        newissue[u'priority'] = {u'id': context['CONTACT_JIRAPRIORITY']}
    if 'project' not in newissue:
        sys.stderr.write("No JIRA project ID set, discarding notification")
        return 2
    if 'issuetype' not in newissue:
        sys.stderr.write("No JIRA issue type ID set")
        return 2

    try:
        custom_field_exists = jira.search_issues(
            "cf[%d]=%d" % (custom_field, custom_field_value))
    except JIRAError as err:
        sys.stderr.write(
            'Unable to query custom field search, JIRA response code %s, %s' %
            (err.status_code, err.text))
        return 2

    if not custom_field_exists:
        newissue[u'customfield_%d' % custom_field] = custom_field_value

    if context['NOTIFICATIONTYPE'] == 'PROBLEM':
        try:
            issue = jira.create_issue(fields=newissue)
        except JIRAError as err:
            sys.stderr.write(
                'Unable to create issue, JIRA response code %s, %s' %
                (err.status_code, err.text))
            return 2
        sys.stdout.write('Created %s\n' % issue.permalink())
        if 'PARAMETER_MONITORING' in context:
            if context['PARAMETER_MONITORING'].endswith('/'):
                # remove trailing slash
                context['PARAMETER_MONITORING'] = context[
                    'PARAMETER_MONITORING'][:-1]
            if context['WHAT'] == 'SERVICE':
                url = context['PARAMETER_MONITORING'] + context['SERVICEURL']
            else:
                url = context['PARAMETER_MONITORING'] + context['HOSTURL']
            try:
                rl = jira.add_simple_link(issue, {
                    'url': url,
                    'title': 'Monitoring'
                })
            except JIRAError as err:
                sys.stderr.write(
                    'Unable to create link in issue, JIRA response code %s, %s\n'
                    % (err.status_code, err.text))
                return 2
            sys.stdout.write('Created JIRA simple link: %s' % rl)

    if context['NOTIFICATIONTYPE'] == 'RECOVERY' and custom_field_exists:
        if "PARAMETER_RESOLUTION" not in context:
            sys.stderr.write(
                "Ticket resolution not enabled in wato rule. Don't send a resolution to jira\n"
            )
            return 0
        else:
            resolution = None
            if 'PARAMETER_RESOLUTION' in context:
                resolution = context['PARAMETER_RESOLUTION']
            if 'CONTACT_JIRARESOLUTION' in context:
                resolution = context['CONTACT_JIRARESOLUTION']
            if resolution is None:
                sys.stderr.write("No JIRA resolution ID set")
                return 2
        for issue in custom_field_exists:
            try:
                jira.transition_issue(issue,
                                      resolution,
                                      comment=newissue['description'])
                sys.stdout.write('Resolved %s' % issue.permalink())
            except JIRAError as err:
                sys.stderr.write(
                    'Unable to resolve %s, JIRA response code %s, %s' %
                    (issue.permalink(), err.status_code, err.text))
                return 2
Example #35
0
# Developed by Koushik - Apr 2020
# Purpose: Create a JIRA issue using dictionary
from jira import JIRA
import getpass

# login to JIRA using username and password
print("Enter credentials to login to JIRA:")
user = input("Username: ")
pw = getpass.getpass()
jira = JIRA(auth=(user, pw), options={'server': 'https://jira.kfplc.com'})

# ---- Create JIRA issue using dictionary
jira_dict_convert = {
    'project': {
        'key': 'DRRR'
    },
    'summary': 'Test Bug title using Dictionary - JIRA Python',
    'issuetype': {
        'name': 'Bug'
    },
    'priority': {
        'name': 'High'
    },
    'description': 'Test story description using Dict - JIRA Python'
}
newID = jira.create_issue(jira_dict_convert)
print('\nNew issue created using dictionary: ', newID)
Example #36
0
    def report_issue(self, build):
        try:
            jira = JIRA(server='https://issues.voltdb.com/',
                        basic_auth=(JIRA_USER, JIRA_PASS))
        except:
            logging.exception('Could not connect to Jira')
            return

        build_report_url = self.jhost + '/job/' + job + '/' + str(
            build) + '/api/python'
        build_report = eval(self.read_url(build_report_url))
        build_url = build_report.get('url')
        build_result = build_report.get('result')

        if build_result == 'SUCCESS':  # only generate Jira issue if the test fails
            print 'No new issue created. Build ' + str(
                build) + 'resulted in: ' + build_result
            return

        summary_url = self.jhost + '/job/' + job + '/' + str(
            build) + '/artifact/tests/sqlgrammar/summary.out'
        summary_report = self.read_url(summary_url)

        pframe_split = summary_report.split('Problematic frame:')
        pframe_split = pframe_split[1].split('C')
        pframe_split = pframe_split[1].split(']')
        pframe_split = pframe_split[1].split('#')
        pframe = pframe_split[0].strip()

        summary = job + ':' + str(build) + ' - ' + pframe
        # search_issues gets a parsing error on (), so escape it.
        existing = jira.search_issues('summary ~ \'%s\'' %
                                      summary.replace('()', '\\\\(\\\\)', 10))
        if len(existing) > 0:
            print 'No new Jira issue created. Build ' + str(
                build) + ' has already been reported.'
            return 'Already reported'

        old_issue = ''
        existing = jira.search_issues(
            'summary ~ \'%s\'' %
            pframe_split[0].strip().replace('()', '\\\\(\\\\)', 10))
        for issue in existing:
            if str(issue.fields.status
                   ) != 'Closed' and u'grammar-gen' in issue.fields.labels:
                old_issue = issue

        build_artifacts = build_report.get('artifacts')[0]
        pid_fileName = build_artifacts['fileName']
        pid_url = build_url + 'artifact/' + pid_fileName

        query_split = summary_report.split(
            '(or it was never started??), after SQL statement:')
        crash_query = query_split[1]

        hash_split = summary_report.split('#', 1)
        hash_split = hash_split[1].split(
            '# See problematic frame for where to report the bug.')
        sigsegv_message = hash_split[
            0] + '# See problematic frame for where to report the bug.\n#'

        description = job + ' build ' + str(build) + ' : ' + str(build_result) + '\n' \
            + 'Jenkins build: ' + build_url + ' \n \n' \
            + 'DDL: ' + 'https://github.com/VoltDB/voltdb/blob/master/tests/sqlgrammar/DDL.sql' + ' \n \n' \
            + 'hs_err_pid: ' + pid_url + ' \n \n' \
            + 'SIGSEGV Message: \n' + '#' + sigsegv_message + ' \n \n' \
            + 'Query that Caused the Crash: ' + crash_query
        description = description.replace('#', '\#')

        labels = ['grammar-gen']

        component = 'Core'
        components = jira.project_components(JIRA_PROJECT)
        jira_component = {}
        for c in components:
            if c.name == component:
                jira_component = {'name': c.name, 'id': c.id}
                break

        current_version_raw = str(
            self.read_url(
                'https://raw.githubusercontent.com/VoltDB/voltdb/master/version.txt'
            ))
        current_version_float = float(current_version_raw)
        current_version = 'V' + current_version_raw
        current_version = current_version.strip()
        next_version = current_version_float + .1
        next_version = str(next_version)
        next_version = 'V' + next_version
        next_version = next_version[:4]

        jira_versions = jira.project_versions(JIRA_PROJECT)
        this_version = {}
        new_version = {}

        for v in jira_versions:
            if str(v.name) == current_version:
                this_version = {'name': v.name, 'id': v.id}
            if str(v.name) == next_version:
                new_version = {'name': v.name, 'id': v.id}

        issue_dict = {
            'project': JIRA_PROJECT,
            'summary': summary,
            'description': description,
            'issuetype': {
                'name': 'Bug'
            },
            'priority': {
                'name': 'Blocker'
            },
            'labels': labels,
            'customfield_10430': {
                'value': 'CORE team'
            },
            'components': [jira_component]
        }

        if new_version:
            issue_dict['versions'] = [new_version]
            issue_dict['fixVersions'] = [new_version]

        elif this_version:
            issue_dict['versions'] = [this_version]
            issue_dict['fixVersions'] = [this_version]

        if old_issue:
            new_comment = jira.add_comment(old_issue, description)
            print 'JIRA-action: New comment on issue: ' + str(
                old_issue) + ' created for failure on build ' + str(build)
        else:
            new_issue = jira.create_issue(fields=issue_dict)
            print 'JIRA-action: New issue ' + new_issue.key + ' created for failure on build ' + str(
                build)
Example #37
0
    def create_bug_issue(self, channel, summary, description, component, version, labels,
                         user=JIRA_USER, passwd=JIRA_PASS, project=JIRA_PROJECT):
        """
        Creates a bug issue on Jira
        :param channel: The channel to notify
        :param summary: The title summary
        :param description: Description field
        :param component: Component bug affects
        :param version: Version this bug affects
        :param labels: Labels to attach to the issue
        :param user: User to report bug as
        :param passwd: Password
        :param project: Jira project
        """
        if user and passwd and project:
            try:
                jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(user, passwd))
            except:
                self.logger.exception('Could not connect to Jira')
                return
        else:
            self.logger.error('Did not provide either a Jira user, a Jira password or a Jira project')
            return

        # Check for existing bug with same summary
        existing = jira.search_issues('summary ~ \'%s\'' % summary)
        if len(existing) > 0:
            # Already reported
            self.logger.info('OLD: Already reported issue with summary "' + summary + '"')
            return

        issue_dict = {
            'project': project,
            'summary': summary,
            'description': description,
            'issuetype': {
                'name': 'Bug'
            },
            'labels': labels
        }

        jira_component = None
        components = jira.project_components(project)
        for c in components:
            if c.name == component:
                jira_component = {
                    'name': c.name,
                    'id': c.id
                }
                break
        if jira_component:
            issue_dict['components'] = [jira_component]
        else:
            # Components is still a required field
            issue_dict['components'] = ['Core']

        jira_version = None
        versions = jira.project_versions(project)
        version = 'V' + version
        for v in versions:
            if str(v.name) == version.strip():
                jira_version = {
                    'name': v.name,
                    'id': v.id
                }
                break
        if jira_version:
            issue_dict['versions'] = [jira_version]
        else:
            # Versions is still a required field
            issue_dict['versions'] = ['DEPLOY-Integration']

        issue_dict['fixVersions'] = [{'name':'Backlog'}]
        issue_dict['priority'] = {'name': 'Blocker'}

        new_issue = jira.create_issue(fields=issue_dict)
        self.logger.info('NEW: Reported issue with summary "' + summary + '"')

        if self.connect_to_slack():
            self.post_message(channel, 'Opened issue at https://issues.voltdb.com/browse/' + new_issue.key)
Example #38
0
class JiraIntegrator:
    def __init__(self, user, api_token, host):
        options = {"server": host}
        self.jira_connection = JIRA(options, basic_auth=(user, api_token))
        self.event_handlers = {
            enums.GitHubAction.OPENED.value: self.create_new_jira_issue,
            enums.GitHubAction.CREATED.value: self.create_new_jira_issue,
            enums.GitHubAction.EDITED.value: self.update_jira_issue,
            enums.GitHubAction.LABELED.value: self.update_jira_label,
            enums.GitHubAction.UNLABELED.value: self.update_jira_label,
            enums.GitHubAction.CLOSED.value: self.close_jira_issue,
            enums.GitHubAction.REOPENED.value: self.reopen_jira_issue,
        }

    def handle(self, event):
        status_code = 500
        try:
            handler = self.event_handlers[event["action"]]
            handler(event["issue"], event["repository"])
            status_code = 200
        except (KeyError, ValueError) as err:
            status_code = 404
            logging.exception(f"Can not find: {repr(err)}")
        except Exception as err:
            logging.exception(f"Error occurs: {repr(err)}")

        return {
            "statusCode": status_code,
            "headers": {"Access-Control-Allow-Origin": "*"},
        }

    def create_new_jira_issue(self, issue, repository):
        issuetype = {"name": utils.get_issue_type(issue["labels"])}
        github_link_field_name = self._get_field_id("GitHub Link")
        github_author_field_name = self._get_field_id("GitHub Author")
        github_link = utils.find_github_link(repository["full_name"], issue["number"])
        labels = utils.get_jira_labels(issue["labels"])
        project = utils.find_project(repository)
        fields = {
            "project": project,
            "summary": issue["title"],
            "description": issue["body"],
            "issuetype": issuetype,
            "labels": labels,
            github_link_field_name: github_link,
            github_author_field_name: issue.get("user", {}).get("login"),
        }

        component = None

        try:
            if "mirumee/saleor-dashboard" in github_link:
                component = self.jira_connection.component(COMPONENT_ID_DASHBOARD)
            if "mirumee/saleor-docs" in github_link:
                component = self.jira_connection.component(COMPONENT_ID_DOCS)
        except Exception as err:
            logging.exception(f"Failed to assign component: {repr(err)}")

        if component:
            fields["components"] = [{"name": component.name}]

        self.jira_connection.create_issue(**fields)

    def update_jira_issue(self, issue, repository):
        jira_issue = self._find_jira_issue(repository["full_name"], issue["number"])
        fields = {"summary": issue["title"], "description": issue["body"]}

        jira_issue.update(fields=fields)

    def update_jira_label(self, issue, repository):
        jira_issue = self._find_jira_issue(repository["full_name"], issue["number"])
        issuetype = {"name": utils.get_issue_type(issue["labels"])}
        labels = utils.get_jira_labels(issue["labels"])
        fields = {"issuetype": issuetype, "labels": labels}

        jira_issue.update(fields=fields)

    def close_jira_issue(self, issue, repository):
        jira_issue = self._find_jira_issue(repository["full_name"], issue["number"])
        ready_to_test_transition = self._get_ready_to_test_transition(jira_issue)

        self.jira_connection.transition_issue(
            jira_issue, ready_to_test_transition["id"]
        )

    def reopen_jira_issue(self, issue, repository):
        jira_issue = self._find_jira_issue(repository["full_name"], issue["number"])
        to_do_transition = self._get_to_do_transition(jira_issue)

        self.jira_connection.transition_issue(jira_issue, to_do_transition["id"])

    def _get_field_id(self, field_name):
        fields = self.jira_connection.fields()
        selected_field = filter(
            lambda field: field["name"].lower() == field_name.lower(), fields
        )
        return next(selected_field)["key"]

    def _find_jira_issue(self, github_project, issue_number):
        query = '"GitHub Link" = "{github_link}"'.format(
            github_link=utils.find_github_link(github_project, issue_number)
        )
        issues = self.jira_connection.search_issues(query)
        if not issues:
            raise ValueError(f"Can not find jira issue with number {issue_number}")

        return issues[0]

    def _get_to_do_transition(self, issue):
        return self._get_transition_by_name(issue, enums.JiraTransition.TO_DO.value)

    def _get_ready_to_test_transition(self, issue):
        return self._get_transition_by_name(
            issue, enums.JiraTransition.READY_TO_TEST.value
        )

    def _get_transition_by_name(self, issue, transition):
        transitions = self.jira_connection.transitions(issue)
        ready_to_test_transition = next(
            filter(lambda tr: tr["name"].lower() == transition, transitions)
        )
        return ready_to_test_transition
Example #39
0
class JiraWrapper:
    JIRA_REQUEST = 'project={} AND labels in ({})'

    def __init__(self,
                 url,
                 user,
                 password,
                 jira_project,
                 assignee,
                 issue_type='Bug',
                 labels=None,
                 watchers=None,
                 jira_epic_key=None):
        self.valid = True
        self.url = url
        self.password = password
        self.user = user
        try:
            self.connect()
        except:
            self.valid = False
            return
        self.projects = [project.key for project in self.client.projects()]
        self.project = jira_project
        if self.project not in self.projects:
            self.client.close()
            self.valid = False
            return
        self.assignee = assignee
        self.issue_type = issue_type
        self.labels = list()
        if labels:
            self.labels = [label.strip() for label in labels.split(",")]
        self.watchers = list()
        if watchers:
            self.watchers = [
                watcher.strip() for watcher in watchers.split(",")
            ]
        self.jira_epic_key = jira_epic_key
        self.client.close()

    def connect(self):
        self.client = JIRA(self.url, basic_auth=(self.user, self.password))

    def markdown_to_jira_markdown(self, content):
        return content.replace("###", "h3.").replace("**", "*")

    def create_issue(self,
                     title,
                     priority,
                     description,
                     issue_hash,
                     attachments=None,
                     get_or_create=True,
                     additional_labels=None):
        description = self.markdown_to_jira_markdown(description)
        _labels = [issue_hash]
        if additional_labels and isinstance(additional_labels, list):
            _labels.extend(additional_labels)
        _labels.extend(self.labels)
        issue_data = {
            'project': {
                'key': self.project
            },
            'summary': re.sub('[^A-Za-z0-9//\. _]+', '', title),
            'description': description,
            'issuetype': {
                'name': self.issue_type
            },
            'assignee': {
                'name': self.assignee
            },
            'priority': {
                'name': priority
            },
            'labels': _labels
        }
        jira_request = self.JIRA_REQUEST.format(issue_data["project"]["key"],
                                                issue_hash)
        if get_or_create:
            issue, created = self.get_or_create_issue(jira_request, issue_data)
        else:
            issue = self.post_issue(issue_data)
            created = True
        if attachments:
            for attachment in attachments:
                if 'binary_content' in attachment:
                    self.add_attachment(
                        issue.key,
                        attachment=attachment['binary_content'],
                        filename=attachment['message'])
        for watcher in self.watchers:
            self.client.add_watcher(issue.id, watcher)
        if self.jira_epic_key:
            self.client.add_issues_to_epic(self.jira_epic_key, [issue.id])
        return issue, created

    def add_attachment(self, issue_key, attachment, filename=None):
        issue = self.client.issue(issue_key)
        for _ in issue.fields.attachment:
            if _.filename == filename:
                return
        self.client.add_attachment(issue, attachment, filename)

    def post_issue(self, issue_data):
        print(issue_data)
        issue = self.client.create_issue(fields=issue_data)
        return issue

    def get_or_create_issue(self, search_string, issue_data):
        issuetype = issue_data['issuetype']['name']
        created = False
        jira_results = self.client.search_issues(search_string)
        issues = []
        for each in jira_results:
            if each.fields.summary == issue_data.get('summary', None):
                issues.append(each)
        if len(issues) == 1:
            issue = issues[0]
            if len(issues) > 1:
                print('  more then 1 issue with the same summary')
            else:
                print(issuetype + 'issue already exists:' + issue.key)
        else:
            issue = self.post_issue(issue_data)
            created = True
        return issue, created
Example #40
0
flag=0
print("Flag initiated to 0")
if "Recovery" in sys.argv[2]:
        print("1")
        extractedSubject=sys.argv[2][10:]
        for issue in issues:
                print("2")
                print(issue.fields.summary)
                print(extractedSubject)
                if issue.fields.summary==extractedSubject:
                        if "To Do" == str(issue.fields.status):
                                jira.transition_issue(issue, '61')
                                jira.add_comment(issue, sys.argv[3])
else:
        print('3')
        for issue in issues:
                if issue.fields.summary==sys.argv[2]:
                        print("4")
                        if "Resolved For Now" == str(issue.fields.status):
                                print("5")
                                jira.transition_issue(issue, '81')
                        if "Closed" != str(issue.fields.status):
                                print("6")
                                flag=1
                                jira.add_comment(issue, sys.argv[3])

        if(flag==0):
                print("Inside flag=0")
                new_issue = jira.create_issue(project='PCAB', summary=sys.argv[2],description=sys.argv[3], issuetype={'name': 'Bug'})
                #jira.create_issue_link('Duplicate',new_issue,parent_issue,None)
def update_or_create_jira_issue(study_id, user_token, is_curator):
    try:
        params = app.config.get('JIRA_PARAMS')
        user_name = params['username']
        password = params['password']

        updated_studies = []
        try:
            jira = JIRA(options=options, basic_auth=(user_name, password))
        except:
            return False, 'Could not connect to JIRA server, incorrect username or password?', updated_studies

        # Get the MetaboLights project
        mtbls_project = jira.project(project)

        studies = [study_id]
        if not study_id and is_curator:
            studies = get_all_studies(user_token)

        for study in studies:
            study_id = study[0]
            user_name = study[1]
            release_date = study[2]
            update_date = study[3]
            study_status = study[4]
            curator = study[5]
            issue = []
            summary = None

            # Get an issue based on a study accession search pattern
            search_param = "project='" + mtbls_project.key + "' AND summary  ~ '" + study_id + " \\\-\\\ 20*'"
            issues = jira.search_issues(search_param)  # project = MetaboLights AND summary ~ 'MTBLS121 '
            new_summary = study_id + ' - ' + release_date.replace('-', '') + ' - ' + \
                          study_status + ' (' + user_name + ')'
            try:
                if issues:
                    issue = issues[0]
                else:
                    if study_status == 'Submitted':
                        logger.info("Could not find Jira issue for " + search_param)
                        print("Creating new Jira issue for " + search_param)
                        issue = jira.create_issue(project=mtbls_project.key, summary='MTBLS study - To be updated',
                                                  description='Created by API', issuetype={'name': 'Story'})
                    else:
                        continue  # Only create new cases if the study is in status Submitted
            except Exception:  # We could not find or create a Jira issue
                    continue

            summary = issue.fields.summary  # Follow pattern 'MTBLS123 - YYYYMMDD - Status'
            try:
                assignee = issue.fields.assignee.name
            except:
                assignee = ""

            valid_curator = False
            jira_curator = ""
            if curator:
                if curator.lower() == 'mark':
                    jira_curator = 'mwilliam'
                    valid_curator = True
                elif curator.lower() == 'keeva':
                    jira_curator = 'keeva'
                    valid_curator = True
            else:
                jira_curator = ""

            # Release date or status has changed, or the assignee (curator) has changed
            if summary.startswith('MTBLS') and (summary != new_summary or assignee != jira_curator):

                # Add "Curation" Epic
                issues_to_add = [issue.key]
                jira.add_issues_to_epic(curation_epic, issues_to_add)  # Add the Curation Epic
                labels = maintain_jira_labels(issue, study_status, user_name)

                # Add a comment to the issue.
                comment_text = 'Status ' + study_status + '. Database update date ' + update_date
                jira.add_comment(issue, comment_text)

                # Change the issue's summary, comments and description.
                issue.update(summary=new_summary, fields={"labels": labels}, notify=False)

                if valid_curator:  # ToDo, what if the curation log is not up to date?
                    issue.update(assignee={'name': jira_curator})

                updated_studies.append(study_id)
                logger.info('Updated Jira case for study ' + study_id)
                print('Updated Jira case for study ' + study_id)
    except Exception:
        return False, 'Update failed', updated_studies
    return True, 'Ticket(s) updated successfully', updated_studies
class JiraClient:

  def __init__(self, options, basic_auth, project):
    self.jira = JIRA(options, basic_auth=basic_auth)
    self.project = project


  def get_issues_by_summary(self, summary):
    """
    Find issues by using the summary (issue title)
    Args:
      summary
    Return:
      A list of issues
    """
    try:
      issues = self.jira.search_issues("project={0} AND summary ~ '{1}'".format(self.project, summary))
    except Exception:
      raise
    return issues


  def get_issue_by_key(self, key):
    """
    Find issue by using the key (e.g BEAM-1234)
    Args:
      key
    Return:
      issue
    """
    try:
      issue = self.jira.issue(key)
    except Exception:
      raise
    return issue


  def create_issue(self, summary, components, description, issuetype='Bug', assignee=None, parent_key=None):
    """
    Create a new issue
    Args:
      summary - Issue title
      components - A list of components
      description (optional) - A string that describes the issue
      issuetype (optional) - Bug, Improvement, New Feature, Sub-task, Task, Wish, etc.
      assignee (optional) - A string of JIRA user name
      parent_key (optional) - The parent issue key is required when creating a subtask.
    Return:
      Issue created
    """
    fields = {
      'project': {'key': self.project},
      'summary': summary,
      'description': description,
      'issuetype': {'name': issuetype},
      'components': [],
    }
    for component in components:
      fields['components'].append({'name': component})
    if assignee is not None:
      fields['assignee'] = {'name': assignee}
    if parent_key is not None:
      fields['parent'] = {'key': parent_key}
      fields['issuetype'] = {'name': 'Sub-task'}
    try:
      new_issue = self.jira.create_issue(fields = fields)
    except Exception:
      raise
    return new_issue


  def update_issue(self, issue, summary=None, components=None, description=None, assignee=None, notify=True):
    """
    Create a new issue
    Args:
      issue - Jira issue object
      summary (optional) - Issue title
      components (optional) - A list of components
      description (optional) - A string that describes the issue
      assignee (optional) - A string of JIRA user name
      notify - Query parameter notifyUsers. If true send the email with notification that the issue was updated to users that watch it.
               Admin or project admin permissions are required to disable the notification.
    Return:
      Issue created
    """
    fields={}
    if summary:
      fields['summary'] = summary
    if description:
      fields['description'] = description
    if assignee:
      fields['assignee'] = {'name': assignee}
    if components:
      fields['components'] = []
      for component in components:
        fields['components'].append({'name': component})
    try:
      issue.update(fields=fields, notify=notify)
    except Exception:
      raise


  def reopen_issue(self, issue):
    """
    Reopen an issue
    Args:
      issue - Jira issue object
    """
    try:
      self.jira.transition_issue(issue.key, 3)
    except:
      raise
Example #43
0
class JiraAPI(object):
    def __init__(self, hostname=None, username=None, password=None, path="", debug=False, clean_obsolete=True, max_time_window=12):
        self.logger = logging.getLogger('JiraAPI')
        if debug:
            self.logger.setLevel(logging.DEBUG)

        if "https://" not in hostname:
            hostname = "https://{}".format(hostname)
        self.username = username
        self.password = password
        self.jira = JIRA(options={'server': hostname}, basic_auth=(self.username, self.password))
        self.logger.info("Created vjira service for {}".format(hostname))
        self.all_tickets = []
        self.JIRA_REOPEN_ISSUE = "Reopen Issue"
        self.JIRA_CLOSE_ISSUE = "Close Issue"
        self.max_time_tracking = max_time_window #in months
        #<JIRA Resolution: name=u'Obsolete', id=u'11'>
        self.JIRA_RESOLUTION_OBSOLETE = "Obsolete"
        self.JIRA_RESOLUTION_FIXED = "Fixed"
        self.clean_obsolete = clean_obsolete
        self.template_path = 'vulnwhisp/reporting/resources/ticket.tpl'
        if path:
            self.download_tickets(path)
        else:
            self.logger.warn("No local path specified, skipping Jira ticket download.")

    def create_ticket(self, title, desc, project="IS", components=[], tags=[]):
        labels = ['vulnerability_management']
        for tag in tags:
            labels.append(str(tag))

        self.logger.info("creating ticket for project {} title[20] {}".format(project, title[:20]))
        self.logger.info("project {} has a component requirement: {}".format(project, self.PROJECT_COMPONENT_TABLE[project]))
        project_obj = self.jira.project(project)
        components_ticket = []
        for component in components:
            exists = False
            for c in project_obj.components:
                if component == c.name:
                    self.logger.debug("resolved component name {} to id {}".format(c.name, c.id))
                    components_ticket.append({ "id": c.id })
                    exists=True
            if not exists:
                self.logger.error("Error creating Ticket: component {} not found".format(component))
                return 0
                    
        new_issue = self.jira.create_issue(project=project,
                                           summary=title,
                                           description=desc,
                                           issuetype={'name': 'Bug'},
                                           labels=labels,
                                           components=components_ticket)

        self.logger.info("Ticket {} created successfully".format(new_issue))
        return new_issue
    
    #Basic JIRA Metrics
    def metrics_open_tickets(self, project=None):
        jql = "labels= vulnerability_management and resolution = Unresolved" 
        if project:
            jql += " and (project='{}')".format(project)
        self.logger.debug('Executing: {}'.format(jql)) 
        return len(self.jira.search_issues(jql, maxResults=0))

    def metrics_closed_tickets(self, project=None):
        jql = "labels= vulnerability_management and NOT resolution = Unresolved AND created >=startOfMonth(-{})".format(self.max_time_tracking) 
        if project:
            jql += " and (project='{}')".format(project)
        return len(self.jira.search_issues(jql, maxResults=0))

    def sync(self, vulnerabilities, project, components=[]):
        #JIRA structure of each vulnerability: [source, scan_name, title, diagnosis, consequence, solution, ips, risk, references]
        self.logger.info("JIRA Sync started")

        # [HIGIENE] close tickets older than 12 months as obsolete
        # Higiene clean up affects to all tickets created by the module, filters by label 'vulnerability_management'
        if self.clean_obsolete:
            self.close_obsolete_tickets()

        for vuln in vulnerabilities:
            # JIRA doesn't allow labels with spaces, so making sure that the scan_name doesn't have spaces
            # if it has, they will be replaced by "_"
            if " " in  vuln['scan_name']:
                vuln['scan_name'] = "_".join(vuln['scan_name'].split(" "))
            
            exists = False
            to_update = False
            ticketid = ""
            ticket_assets = []
            exists, to_update, ticketid, ticket_assets = self.check_vuln_already_exists(vuln)

            if exists:
                # If ticket "resolved" -> reopen, as vulnerability is still existent
                self.reopen_ticket(ticketid)
                self.add_label(ticketid, vuln['risk'])
                continue
            elif to_update:
                self.ticket_update_assets(vuln, ticketid, ticket_assets)
                self.add_label(ticketid, vuln['risk'])
                continue

            try:
                tpl = template(self.template_path, vuln)
            except Exception as e:
                self.logger.error('Exception templating: {}'.format(str(e)))
                return 0
            self.create_ticket(title=vuln['title'], desc=tpl, project=project, components=components, tags=[vuln['source'], vuln['scan_name'], 'vulnerability', vuln['risk']])
        
        self.close_fixed_tickets(vulnerabilities)
        # we reinitialize so the next sync redoes the query with their specific variables
        self.all_tickets = []
        return True

    def check_vuln_already_exists(self, vuln):
        # we need to return if the vulnerability has already been reported and the ID of the ticket for further processing
        #function returns array [duplicated(bool), update(bool), ticketid, ticket_assets]
        title = vuln['title']
        labels = [vuln['source'], vuln['scan_name'], 'vulnerability_management', 'vulnerability'] 
        #list(set()) to remove duplicates
        assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", ",".join(vuln['ips']))))
        
        if not self.all_tickets:
            self.logger.info("Retrieving all JIRA tickets with the following tags {}".format(labels))
            # we want to check all JIRA tickets, to include tickets moved to other queues
            # will exclude tickets older than 12 months, old tickets will get closed for higiene and recreated if still vulnerable
            jql = "{} AND NOT labels=advisory AND created >=startOfMonth(-{})".format(" AND ".join(["labels={}".format(label) for label in labels]), self.max_time_tracking)
            
            self.all_tickets = self.jira.search_issues(jql, maxResults=0)
        
        #WARNING: function IGNORES DUPLICATES, after finding a "duplicate" will just return it exists
        #it wont iterate over the rest of tickets looking for other possible duplicates/similar issues
        self.logger.info("Comparing Vulnerabilities to created tickets")
        for index in range(len(self.all_tickets)-1):
            checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(self.all_tickets[index])
            if title == checking_title: 
                difference = list(set(assets).symmetric_difference(checking_assets))
                #to check intersection - set(assets) & set(checking_assets)
                if difference: 
                    self.logger.info("Asset mismatch, ticket to update. Ticket ID: {}".format(checking_ticketid))
                    return False, True, checking_ticketid, checking_assets #this will automatically validate
                else:
                    self.logger.info("Confirmed duplicated. TickedID: {}".format(checking_ticketid))
                    return True, False, checking_ticketid, [] #this will automatically validate
        return False, False, "", []

    def ticket_get_unique_fields(self, ticket):
        title = ticket.raw.get('fields', {}).get('summary').encode("ascii").strip()
        ticketid = ticket.key.encode("ascii")
        try:
            affected_assets_section = ticket.raw.get('fields', {}).get('description').encode("ascii").split("{panel:title=Affected Assets}")[1].split("{panel}")[0]
            assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", affected_assets_section)))
        except:
            self.logger.error("Ticket IPs regex failed. Ticket ID: {}".format(ticketid))
            assets = []
        
        return ticketid, title, assets

    def get_ticket_reported_assets(self, ticket):
        #[METRICS] return a list with all the affected assets for that vulnerability (including already resolved ones) 
        return list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b",str(self.jira.issue(ticket).raw))))

    def get_resolution_time(self, ticket):
        #get time a ticket took to be resolved
        ticket_obj = self.jira.issue(ticket)
        if self.is_ticket_resolved(ticket_obj):
            ticket_data = ticket_obj.raw.get('fields')
            #dates follow format '2018-11-06T10:36:13.849+0100'
            created = [int(x) for x in ticket_data['created'].split('.')[0].replace('T', '-').replace(':','-').split('-')]
            resolved =[int(x) for x in ticket_data['resolutiondate'].split('.')[0].replace('T', '-').replace(':','-').split('-')]
            
            start = datetime(created[0],created[1],created[2],created[3],created[4],created[5])
            end = datetime(resolved[0],resolved[1],resolved[2],resolved[3],resolved[4],resolved[5])
            
            return (end-start).days
        else:
            self.logger.error("Ticket {ticket} is not resolved, can't calculate resolution time".format(ticket=ticket))

        return False

    def ticket_update_assets(self, vuln, ticketid, ticket_assets):
        # correct description will always be in the vulnerability to report, only needed to update description to new one
        self.logger.info("Ticket {} exists, UPDATE requested".format(ticketid))
        
        if self.is_ticket_resolved(self.jira.issue(ticketid)):
            self.reopen_ticket(ticketid)
        try:
            tpl = template(self.template_path, vuln)
        except Exception as e:
            self.logger.error('Exception updating assets: {}'.format(str(e)))
            return 0

        ticket_obj = self.jira.issue(ticketid)
        ticket_obj.update()
        assets = list(set(re.findall(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", ",".join(vuln['ips']))))
        difference = list(set(assets).symmetric_difference(ticket_assets))
        
        comment = ''
        #put a comment with the assets that have been added/removed
        for asset in difference:
            if asset in assets:
                comment += "Asset {} have been added to the ticket as vulnerability *has been newly detected*.\n".format(asset)
            elif asset in ticket_assets:
                comment += "Asset {} have been removed from the ticket as vulnerability *has been resolved*.\n".format(asset)
       
        try:
            ticket_obj.update(description=tpl, comment=comment, fields={"labels":ticket_obj.fields.labels})
            self.logger.info("Ticket {} updated successfully".format(ticketid))
            self.add_label(ticketid, 'updated')
        except:
            self.logger.error("Error while trying up update ticket {}".format(ticketid))
        return 0

    def add_label(self, ticketid, label):
        ticket_obj = self.jira.issue(ticketid)
        
        if label not in ticket_obj.fields.labels:
                ticket_obj.fields.labels.append(label)
        
        try:
            ticket_obj.update(fields={"labels":ticket_obj.fields.labels})
            self.logger.info("Added label {label} to ticket {ticket}".format(label=label, ticket=ticketid))
        except:
            self.logger.error("Error while trying to add label {label} to ticket {ticket}".format(label=label, ticket=ticketid))
        return 0

    def close_fixed_tickets(self, vulnerabilities):
        # close tickets which vulnerabilities have been resolved and are still open
        found_vulns = []
        for vuln in vulnerabilities:
            found_vulns.append(vuln['title'])

        comment = '''This ticket is being closed as it appears that the vulnerability no longer exists.
        If the vulnerability reappears, a new ticket will be opened.'''

        for ticket in self.all_tickets:
            if ticket.raw['fields']['summary'].strip() in found_vulns:
                self.logger.info("Ticket {} is still vulnerable".format(ticket))
                continue
            self.logger.info("Ticket {} is no longer vulnerable".format(ticket))
            self.close_ticket(ticket, self.JIRA_RESOLUTION_FIXED, comment) 
        return 0


    def is_ticket_reopenable(self, ticket_obj):
        transitions = self.jira.transitions(ticket_obj)
        for transition in transitions:
            if transition.get('name') == self.JIRA_REOPEN_ISSUE:
                self.logger.debug("Ticket is reopenable")
                return True
        self.logger.warn("Ticket can't be opened. Check Jira transitions.")
        return False

    def is_ticket_closeable(self, ticket_obj):
        transitions = self.jira.transitions(ticket_obj)
        for transition in transitions:
            if transition.get('name') == self.JIRA_CLOSE_ISSUE:
                return True
        self.logger.warn("Ticket can't closed. Check Jira transitions.")
        return False

    def is_ticket_resolved(self, ticket_obj):
        #Checks if a ticket is resolved or not
        if ticket_obj is not None:
            if ticket_obj.raw['fields'].get('resolution') is not None:
                if ticket_obj.raw['fields'].get('resolution').get('name') != 'Unresolved':
                    self.logger.debug("Checked ticket {} is already closed".format(ticket_obj))
                    self.logger.info("Ticket {} is closed".format(ticket_obj))
                    return True
        self.logger.debug("Checked ticket {} is already open".format(ticket_obj))
        return False


    def is_risk_accepted(self, ticket_obj):
        if ticket_obj is not None:
            if ticket_obj.raw['fields'].get('labels') is not None:
                labels = ticket_obj.raw['fields'].get('labels')
                if "risk_accepted" in labels:
                    self.logger.warn("Ticket {} accepted risk, will be ignored".format(ticket_obj))
                    return True
                elif "server_decomission" in labels:
                    self.logger.warn("Ticket {} server decomissioned, will be ignored".format(ticket_obj))
                    return True
        self.logger.info("Ticket {} risk has not been accepted".format(ticket_obj))
        return False

    def reopen_ticket(self, ticketid):
        self.logger.debug("Ticket {} exists, REOPEN requested".format(ticketid))
        # this will reopen a ticket by ticketid
        ticket_obj = self.jira.issue(ticketid)
        
        if self.is_ticket_resolved(ticket_obj):
            if not self.is_risk_accepted(ticket_obj):
                try:
                    if self.is_ticket_reopenable(ticket_obj):
                        comment = '''This ticket has been reopened due to the vulnerability not having been fixed (if multiple assets are affected, all need to be fixed; if the server is down, lastest known vulnerability might be the one reported).
                        In the case of the team accepting the risk and wanting to close the ticket, please add the label "*risk_accepted*" to the ticket before closing it.
                        If server has been decomissioned, please add the label "*server_decomission*" to the ticket before closing it.
                        If you have further doubts, please contact the Security Team.'''
                        error = self.jira.transition_issue(issue=ticketid, transition=self.JIRA_REOPEN_ISSUE, comment = comment)
                        self.logger.info("Ticket {} reopened successfully".format(ticketid))
                        self.add_label(ticketid, 'reopened')
                        return 1
                except Exception as e:
                    # continue with ticket data so that a new ticket is created in place of the "lost" one
                    self.logger.error("error reopening ticket {}: {}".format(ticketid, e))
                    return 0
        return 0

    def close_ticket(self, ticketid, resolution, comment):
        # this will close a ticket by ticketid
        self.logger.debug("Ticket {} exists, CLOSE requested".format(ticketid))
        ticket_obj = self.jira.issue(ticketid)
        if not self.is_ticket_resolved(ticket_obj):
            try:
                if self.is_ticket_closeable(ticket_obj):
                    #need to add the label before closing the ticket
                    self.add_label(ticketid, 'closed')
                    error = self.jira.transition_issue(issue=ticketid, transition=self.JIRA_CLOSE_ISSUE, comment = comment, resolution = {"name": resolution })
                    self.logger.info("Ticket {} closed successfully".format(ticketid))
                    return 1
            except Exception as e:
                # continue with ticket data so that a new ticket is created in place of the "lost" one
                self.logger.error("error closing ticket {}: {}".format(ticketid, e))
                return 0
                
        return 0

    def close_obsolete_tickets(self):
        # Close tickets older than 12 months, vulnerabilities not solved will get created a new ticket 
        self.logger.info("Closing obsolete tickets older than {} months".format(self.max_time_tracking))
        jql = "labels=vulnerability_management AND created <startOfMonth(-{}) and resolution=Unresolved".format(self.max_time_tracking)
        tickets_to_close = self.jira.search_issues(jql, maxResults=0)
        
        comment = '''This ticket is being closed for hygiene, as it is more than 12 months old.
        If the vulnerability still exists, a new ticket will be opened.'''
        
        for ticket in tickets_to_close:
                self.close_ticket(ticket, self.JIRA_RESOLUTION_OBSOLETE, comment)
        
        return 0

    def project_exists(self, project):
        try:
            self.jira.project(project)
            return True
        except:
            return False
        return False

    def download_tickets(self, path):
        #saves all tickets locally, local snapshot of vulnerability_management ticktes
        #check if file already exists
        check_date = str(date.today())
        fname = '{}jira_{}.json'.format(path, check_date) 
        if os.path.isfile(fname):
            self.logger.info("File {} already exists, skipping ticket download".format(fname))
            return True
        try:
            self.logger.info("Saving locally tickets from the last {} months".format(self.max_time_tracking))
            jql = "labels=vulnerability_management AND created >=startOfMonth(-{})".format(self.max_time_tracking)
            tickets_data = self.jira.search_issues(jql, maxResults=0)
            
            #end of line needed, as writelines() doesn't add it automatically, otherwise one big line
            to_save = [json.dumps(ticket.raw.get('fields'))+"\n" for ticket in tickets_data]
            with open(fname, 'w') as outfile:
                outfile.writelines(to_save)
                self.logger.info("Tickets saved succesfully.")
            
            return True

        except Exception as e:
            self.logger.error("Tickets could not be saved locally: {}.".format(e))
        
        return False 
Example #44
0
def main(arguments):
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--config-file', required=True)
    parser.add_argument('-w', '--write-jira', action='store_true')
    args = parser.parse_args(arguments)

    settings = ConfigParser.ConfigParser()
    settings.read(args.config_file)
    
    # Connect to Bugzilla 
    bz_url = settings.get('bugzilla', 'url')
    bz_user = settings.get('bugzilla', 'user')
    bz_pass = settings.get('bugzilla', 'pass')
    bz_product = settings.get('bugzilla', 'product')

    bzURL = 'https://%s:%s@%s' % (bz_user, bz_pass, bz_url)
    bzapi = bugzilla.Bugzilla(url=bzURL,
                              user=bz_user,
                              password=bz_pass,
                              sslverify=False,
                              use_creds=False)

    # Connect to JIRA
    jira_url = settings.get('jira', 'url')
    jira_user = settings.get('jira', 'user')
    jira_pass = settings.get('jira', 'pass')
    jira_product = settings.get('jira', 'product')
    jira_project = settings.get('jira', 'project')

    jac = JIRA(server=jira_url,
               basic_auth=(jira_user, jira_pass))

    # Obtain Bugzilla bugs
    query = bzapi.build_query(product=bz_product)

    t1 = time.time()
    bugs = bzapi.query(query)
    t2 = time.time()
    print("Found %d bugs in BugZilla with our query" % len(bugs))
    print("Quicker query processing time: %s" % (t2 - t1))

    # Sync Bugzilla bugs with Jira
    # For simplicity and for reporting only 2 states are considered in JIRA
    # Open and Closed(Resolved)
    cnt_update = 0
    cnt_new = 0
    for bzbug in bugs:
        if bzbug.see_also:
            # Check if the bug exists in Jira and sync the status
            # we look for a JIRA in link in the see_also fields in bugzilla
            for url in bzbug.see_also:
                if jira_url in url:
                    issue = jac.issue(url.rsplit('/',1)[-1])
                    # Assuming that JIRA issues will only have 2 status if the bug is
                    # resolved in bugzilla we close it in JIRA
                    if not args.write_jira and bzbug.status == "RESOLVED" and issue.fields.status == "Open":
                        print("Sync status Bug id=%s summary=%s status=%s jira_status=%s"
                              % (bzbug.id, bzbug.summary, status_mapping[bzbug.status], issue.fields.status))
                    elif bzbug.status == "RESOLVED" and issue.fields.status == "Open":
                        # Close Issue
                        jira.transition_issue(issue, '2')
                        cnt_update += 1
                    else:
                        print("Not need to Sync Bug id=%s summary=%s status=%s jira_status=%s"
                              % (bzbug.id, bzbug.summary, status_mapping[bzbug.status], issue.fields.status))
                    # We assume 1<->1 links from bugzilla to jira
                    break
        else:
            # Create Bugzilla bug in JIRA
            bzbug_url = 'https://%s/show_bug.cgi?id=%i' % (bz_url, bzbug.id)
            if args.write_jira:
                new_issue = jac.create_issue(project=jira_project,
                                              summary=bzbug.summary,
                                              labels=[jira_product, "bugzilla"],
                                              customfield_16700=bzbug_url,
                                              issuetype={'name': 'Bug'})
                issue_url = 'https://%s/browse/%s' % (jira_url, new_issue.key)
                # add the JIRA link to the see_also field
                update = bzapi.build_update(see_also_add=[issue_url])
                bzapi.update_bugs([bzbug.id], update)
                if status_mapping[bzbug.status] != new_issue.fields.status:
                    sync_bug_status(jac, new_issue, bzbug.status, new_issue.fields.status)
            else:
                print("Create new Bug id=%s summary=%s status=%s"
                      % (bzbug.id, bzbug.summary, bzbug.status))                
            cnt_new += 1
    if args.write_jira:
        print("%i bugs will be created in JIRA" % cnt_new)
        print("%i bugs will be synced between JIRA and Bugzilla" % cnt_update)
Example #45
0
class JiraTestManager(object):
    """
    Used to instantiate and populate the JIRA instance with data used by the unit tests.
    Attributes:
        CI_JIRA_ADMIN (str): Admin user account name.
        CI_JIRA_USER (str): Limited user account name.
        max_retries (int): number of retries to perform for recoverable HTTP errors.
    """
    __shared_state = {}

    def __init__(self):
        self.__dict__ = self.__shared_state

        if not self.__dict__:
            self.initialized = 0

            try:

                if CI_JIRA_URL in os.environ:
                    self.CI_JIRA_URL = os.environ['CI_JIRA_URL']
                    self.max_retries = 5
                else:
                    self.CI_JIRA_URL = "https://pycontribs.atlassian.net"
                    self.max_retries = 5

                if CI_JIRA_ADMIN in os.environ:
                    self.CI_JIRA_ADMIN = os.environ['CI_JIRA_ADMIN']
                else:
                    self.CI_JIRA_ADMIN = 'ci-admin'

                if CI_JIRA_ADMIN_PASSWORD in os.environ:
                    self.CI_JIRA_ADMIN_PASSWORD = os.environ[
                        'CI_JIRA_ADMIN_PASSWORD']
                else:
                    self.CI_JIRA_ADMIN_PASSWORD = '******'

                if 'CI_JIRA_USER' in os.environ:
                    self.CI_JIRA_USER = os.environ['CI_JIRA_USER']
                else:
                    self.CI_JIRA_USER = '******'

                if 'CI_JIRA_USER_PASSWORD' in os.environ:
                    self.CI_JIRA_USER_PASSWORD = os.environ[
                        'CI_JIRA_USER_PASSWORD']
                else:
                    self.CI_JIRA_USER_PASSWORD = '******'

                self.CI_JIRA_ISSUE = os.environ.get('CI_JIRA_ISSUE', 'Bug')

                if OAUTH:
                    self.jira_admin = JIRA(oauth={
                        'access_token': 'hTxcwsbUQiFuFALf7KZHDaeAJIo3tLUK',
                        'access_token_secret': 'aNCLQFP3ORNU6WY7HQISbqbhf0UudDAf',
                        'consumer_key': CONSUMER_KEY,
                        'key_cert': KEY_CERT_DATA})
                else:
                    if self.CI_JIRA_ADMIN:
                        self.jira_admin = JIRA(self.CI_JIRA_URL, basic_auth=(self.CI_JIRA_ADMIN,
                                                                             self.CI_JIRA_ADMIN_PASSWORD),
                                               logging=False, validate=True, max_retries=self.max_retries)
                    else:
                        self.jira_admin = JIRA(self.CI_JIRA_URL, validate=True,
                                               logging=False, max_retries=self.max_retries)
                if self.jira_admin.current_user() != self.CI_JIRA_ADMIN:
                    # self.jira_admin.
                    self.initialized = 1
                    sys.exit(3)

                if OAUTH:
                    self.jira_sysadmin = JIRA(oauth={
                        'access_token': '4ul1ETSFo7ybbIxAxzyRal39cTrwEGFv',
                        'access_token_secret':
                            'K83jBZnjnuVRcfjBflrKyThJa0KSjSs2',
                        'consumer_key': CONSUMER_KEY,
                        'key_cert': KEY_CERT_DATA}, logging=False, max_retries=self.max_retries)
                else:
                    if self.CI_JIRA_ADMIN:
                        self.jira_sysadmin = JIRA(self.CI_JIRA_URL,
                                                  basic_auth=(self.CI_JIRA_ADMIN,
                                                              self.CI_JIRA_ADMIN_PASSWORD),
                                                  logging=False, validate=True, max_retries=self.max_retries)
                    else:
                        self.jira_sysadmin = JIRA(self.CI_JIRA_URL,
                                                  logging=False, max_retries=self.max_retries)

                if OAUTH:
                    self.jira_normal = JIRA(oauth={
                        'access_token': 'ZVDgYDyIQqJY8IFlQ446jZaURIz5ECiB',
                        'access_token_secret':
                            '5WbLBybPDg1lqqyFjyXSCsCtAWTwz1eD',
                        'consumer_key': CONSUMER_KEY,
                        'key_cert': KEY_CERT_DATA})
                else:
                    if self.CI_JIRA_ADMIN:
                        self.jira_normal = JIRA(self.CI_JIRA_URL,
                                                basic_auth=(self.CI_JIRA_USER,
                                                            self.CI_JIRA_USER_PASSWORD),
                                                validate=True, logging=False, max_retries=self.max_retries)
                    else:
                        self.jira_normal = JIRA(self.CI_JIRA_URL,
                                                validate=True, logging=False, max_retries=self.max_retries)

                # now we need some data to start with for the tests

                # jira project key is max 10 chars, no letter.
                # [0] always "Z"
                # [1-6] username running the tests (hope we will not collide)
                # [7-8] python version A=0, B=1,..
                # [9] A,B -- we may need more than one project

                prefix = 'Z' + (re.sub("[^A-Z]", "",
                                       getpass.getuser().upper()))[0:6] + \
                         chr(ord('A') + sys.version_info[0]) + \
                         chr(ord('A') + sys.version_info[1])

                self.project_a = prefix + 'A'  # old XSS
                self.project_a_name = "Test user=%s python=%s.%s A" \
                                      % (getpass.getuser(), sys.version_info[0],
                                         sys.version_info[1])
                self.project_b_name = "Test user=%s python=%s.%s B" \
                                      % (getpass.getuser(), sys.version_info[0],
                                         sys.version_info[1])
                self.project_b = prefix + 'B'  # old BULK

                try:
                    self.jira_admin.project(self.project_a)
                except Exception as e:
                    logging.warning(e)
                    pass
                else:
                    self.jira_admin.delete_project(self.project_a)

                try:
                    self.jira_admin.project(self.project_b)
                except Exception as e:
                    logging.warning(e)
                    pass
                else:
                    self.jira_admin.delete_project(self.project_b)

                self.jira_admin.create_project(self.project_a,
                                               self.project_a_name)
                self.project_a_id = self.jira_admin.project(self.project_a).id

                self.jira_admin.create_project(self.project_b,
                                               self.project_b_name)

                self.project_b_issue1_obj = self.jira_admin.create_issue(project=self.project_b,
                                                                         summary='issue 1 from %s'
                                                                                 % self.project_b,
                                                                         issuetype=self.CI_JIRA_ISSUE)
                self.project_b_issue1 = self.project_b_issue1_obj.key

                self.project_b_issue2_obj = self.jira_admin.create_issue(project=self.project_b,
                                                                         summary='issue 2 from %s'
                                                                                 % self.project_b,
                                                                         issuetype={'name': self.CI_JIRA_ISSUE})
                self.project_b_issue2 = self.project_b_issue2_obj.key

                self.project_b_issue3_obj = self.jira_admin.create_issue(project=self.project_b,
                                                                         summary='issue 3 from %s'
                                                                                 % self.project_b,
                                                                         issuetype={'name': self.CI_JIRA_ISSUE})
                self.project_b_issue3 = self.project_b_issue3_obj.key

            except Exception as e:
                # exc_type, exc_value, exc_traceback = sys.exc_info()
                formatted_lines = traceback.format_exc().splitlines()
                msg = "Basic test setup failed: %s\n\t%s" % (
                    e, "\n\t".join(formatted_lines))
                logging.fatal(msg)
                self.initialized = 1
                pytest.exit("FATAL")

            self.initialized = 1

        else:
            # already exist but we need to be sure it was initialized
            counter = 0
            while not self.initialized:
                sleep(1)
                counter += 1
                if counter > 60:
                    logging.fatal("Something is clearly not right with " +
                                  "initialization, killing the tests to prevent a " +
                                  "deadlock.")
                    sys.exit(3)
Example #46
0
def create_jira_ticket(summary, description, **kwargs):
    """
    Create a new jira ticket, returning the associated number.

    Examples:

        Synchronously create a jira ticket::

            create_jira_ticket("Test Ticket", "This is a test")

        Asynchronously create a jira ticket::

            create_jira_ticket.delay("Test Ticket", "This is a test")

    Inputs:

    .. note:: watchers and watcher_group are mutually exclusive.

        :summary: The ticket summary
        :description: The ticket description
        :assignee: Who the ticket should be assigned to. Defaults to "-1" which
                   is analogous to selecting "automatic" on the JIRA web form.
        :reporter: Who created the ticket (or is responsible for QCing it).
                   Defaults to "automaticagent".
        :issuetype: The type of issue. Defaults to "Task".
        :project: The project the ticket should be created in. Defaults to
                  "ST", which is Product Support.
        :priority: Ticket Priority. Defaults to "Major".
        :components: A list of components this ticket belongs to.
        :watchers: A list of user names to add as watchers of this ticket.
        :watcher_group: A group to assign as watchesr.

    Output:

    .. note:: The instance isn't returned because we need the ability to pass
              the results to another asynchronous task without blocking, which
              requires that all arguments be serializable.

        The ticket key which corresponds to the created JIRA ticket.
    """
    jira = JIRA(options=options, basic_auth=housekeeping_auth)

    assignee = {'name': kwargs.setdefault('assignee', '-1')}
    reporter = {'name': kwargs.setdefault('reporter', 'automationagent')}
    issuetype = {'name': kwargs.setdefault('issuetype', 'Task')}
    project = {'key': kwargs.setdefault('project', 'ST')}
    priority = {'name': kwargs.setdefault('priority', 'Major')}
    components = [{'name': name}
                  for name in kwargs.setdefault('components', [])]

    watchers = kwargs.setdefault('watchers', set())
    if 'watcher_group' in kwargs:
        watchers = watchers.union(
            jira.group_members(kwargs['watcher_group']).keys())

    if assignee == reporter:
        raise ValueError("Assignee and reporter must be different.")

    fields = {
        'project': project,
        'summary': summary,
        'description': description,
        'issuetype': issuetype,
        'priority': priority,
        'reporter': reporter,
        'assignee': assignee,
        'components': components,
    }

    issue = jira.create_issue(fields=fields)

    for watcher in watchers:
        jira.add_watcher(issue, watcher)

    return issue.key
Example #47
0
            'customfield_17680' : { 'value' : 'Low' }, # complexity of change
            'customfield_17681' : { 'value' : 'Low' }, # Pre-Testing
            'customfield_17682' : { 'value' : 'Low' }, # Scope of Validation
            'customfield_17683' : { 'value' : 'Low' }, # Rollback Plan
            'customfield_17684' : { 'value' : 'Low' }, # Customer landscape impact
            'customfield_16583' : {'value':'App', 'child': {'value':'Update/Upgrade'}}, # Category(s) 
            'components' : [{'name': 'JAM'}], #Components
            'customfield_15282' : {'value':'No Data Protection Regulation'}, # Customer Data Protection
            'customfield_10802' : { 'value' : dc_total }, # Data Center
            'customfield_16590' : { 'id' : time_Zone },# DC Time Zone
            'customfield_10842' : { 'id' : env_type }, # Environment Type 
            'customfield_16794' : {'value':'None'}, # Customer Impact
            'customfield_17055' : {'value':'No'}, # CCM Notify
            'customfield_17056' : { 'value' : 'Not Required' }, # Maintenance Page Required?
            'customfield_17057' : { 'value' : 'No' }, # Pop up enable required?
            'customfield_16814' : actual_date+ 'T11:30:00.000-0700', # requested Start 
            'customfield_16815' : actual_date+ 'T11:30:00.000-0700', # requested End 
            'description' : descript, # description
            'customfield_17426' : test_details, # Pre-Test Details
            'customfield_17427' : justification, # Business Justification
            'customfield_17058' : imp_steps, # Implementation Steps
            'customfield_16800' : back_steps, # Backout Steps
            'customfield_16801' : validate, # Validation Steps
            'customfield_16802' : { 'name' : 'svishwanath' }, # Validator
            'customfield_17059' : [{ 'value' : 'TechOwn - JAM' }],  #Tech Approver 17059
            'customfield_17060' : [{ 'value' : 'BusOwn - JAM' }], # Impact/Impl Approver 17060
                    }
        new_issue = jira.create_issue(fields=values)
        jira.assign_issue(new_issue, 'sureshkumara')
        print new_issue.key
Example #48
0
def submit_jira_ticket(request):
    jira_setting = jirasetting.objects.all()

    for jira in jira_setting:
        jira_url = jira.jira_server
        username = jira.jira_username
        password = jira.jira_password
    jira_server = jira_url
    jira_username = signing.loads(username)
    jira_password = signing.loads(password)

    options = {'server': jira_server}
    jira_ser = JIRA(options, basic_auth=(jira_username, jira_password))
    jira_projects = jira_ser.projects()

    if request.method == 'GET':
        summary = request.GET['summary']
        description = request.GET['description']
        scanner = request.GET['scanner']
        vuln_id = request.GET['vuln_id']
        scan_id = request.GET['scan_id']

        return render(request, 'submit_jira_ticket.html', {'jira_projects': jira_projects,
                                                           'summary': summary,
                                                           'description': description,
                                                           'scanner': scanner,
                                                           'vuln_id': vuln_id,
                                                           'scan_id': scan_id
                                                           })

    if request.method == 'POST':
        summary = request.POST.get('summary')
        description = request.POST.get('description')
        project_id = request.POST.get('project_id')
        issue_type = request.POST.get('issue_type')
        vuln_id = request.POST.get('vuln_id')
        scanner = request.POST.get('scanner')
        scan_id = request.POST.get('scan_id')

        issue_dict = {
            'project': {'id': project_id},
            'summary': summary,
            'description': description,
            'issuetype': {'name': issue_type},
        }
        new_issue = jira_ser.create_issue(fields=issue_dict)
        print new_issue

        if scanner == 'zap':
            zap_scan_results_db.objects.filter(vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect('/webscanners/zap_vul_details/?scan_id=%s&scan_name=%s' % (
                scan_id,
                summary
            )
        )
        elif scanner == 'burp':
            burp_scan_result_db.objects.filter(vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect('/webscanners/burp_vuln_out/?scan_id=%s&scan_name=%s' % (
                scan_id,
                summary
            )
        )
        elif scanner == 'arachni':
            arachni_scan_result_db.objects.filter(vuln_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect('/webscanners/arachni_vuln_out/?scan_id=%s&scan_name=%s' % (scan_id, summary))
        elif scanner == 'open_vas':
            ov_scan_result_db.objects.filter(vul_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect('/networkscanners/vul_details/?scan_id=%s' % scan_id)
        elif scanner == 'nessus':
            nessus_report_db.objects.filter(vul_id=vuln_id).update(jira_ticket=new_issue)
            return HttpResponseRedirect('/networkscanners/nessus_vuln_details/?scan_id=%s' % scan_id)
Example #49
0
class Jira(object):
    """
    jira operation class
    """
    def __init__(self, **args):
        """
        Init JIRA connection
        """
        self.server = settings.CASELINK_JIRA['SERVER']
        self.username = settings.CASELINK_JIRA['USER']
        self.password = settings.CASELINK_JIRA['PASSWORD']
        self.verify = False  # TODO: move to settings
        self._jira = JIRA(options={
            'server': self.server,
            'verify': self.verify,
        }, basic_auth=(self.username, self.password))

    def search_issues(self, project_name, jql_str, fields=None):
        """
        Search issue under the project and return result
        """
        jql_str = "project = " + project_name + " and " + jql_str
        return self.jira_.search_issues(jql_str, maxResults=-1, fields=fields)

    def add_comment(self, issue, comment):
        """
        Add comments in the issue
        """
        if isinstance(issue, (str, unicode)):
            issue = self._jira.issue(issue)
        return self._jira.add_comment(issue, comment)

    def transition_issue(self, issue, status):
        """
        Transition issue status to another
        """
        self.jira_.transition_issue(issue, status)

    def get_watchers(self, issue):
        """
        Get a watchers Resource from the server for an issue
        """
        watcher_data = self.jira_.watchers(issue)
        return [str(w.key) for w in watcher_data.watchers]

    def add_watcher(self, issue, watchers):
        """
        Append an issue's watchers list
        """
        new_watchers = []
        if isinstance(watchers, str):
            new_watchers.append(watchers)
        elif isinstance(watchers, list):
            new_watchers = watchers
        for watcher in new_watchers:
            self.jira_.add_watcher(issue, watcher)

    def remove_watcher(self, issue, watchers):
        """
        Remove watchers from an issue's watchers list
        """
        del_watchers = []
        if isinstance(watchers, str):
            del_watchers.append(watchers)
        elif isinstance(watchers, list):
            del_watchers = watchers
        for watcher in del_watchers:
            self.jira_.remove_watcher(issue, watcher)

    def create_issue(self, issue_dict):
        """
        Create Issue and apply some default properties
        """
        dict_ = {
            'project': {
                'key': 'LIBVIRTAT',
            },
            'summary': None,
            'description': None,
            'priority': {
                'name': 'Major',
            },
            'assignee': {
                'name': None
            },
        }

        parent_issue = issue_dict.pop('parent_issue', None) or settings.CASELINK_JIRA['DEFAULT_PARENT_ISSUE']
        assignee = issue_dict.pop('assignee', None) or settings.CASELINK_JIRA['DEFAULT_ASSIGNEE']

        dict_.update({
            'assignee': {
                'name': assignee
            }
        })

        if parent_issue:
            dict_.update({
                'parent': {
                    'id': parent_issue
                },
                'issuetype': {
                    'name': 'Sub-task'
                }
            })
        else:
            dict_.update({
                'issuetype': {
                    'name': 'Task'
                }
            })

        dict_.update(issue_dict)

        return self._jira.create_issue(dict_)

    # Helper functions
    def update_issue(self, issue, changes=None):
        trans = settings.CASELINK_JIRA['REOPEN_TRANSITION']
        try:
            if isinstance(issue, (str, unicode)):
                issue = self._jira.issue(issue)
            return self._jira.transition_issue(issue, trans)
        except Exception:
            pass

        if changes:
            self.add_comment(issue, 'Polarion Workitem Changed: {code}%s{code}' % changes)

    def create_automation_request(self, wi_id, wi_title, wi_url, changes, assignee, parent_issue=None):
        description = AUTOMATION_REQUEST_DESCRIPTION.format(
            polarion_wi_id=wi_id,
            polarion_wi_title=wi_title,
            polarion_wi_url=wi_url,
        )
        summary = AUTOMATION_REQUEST_SUMMARY.format(
            polarion_wi_id=wi_id,
            polarion_wi_title=wi_title
        )
        assignee = assignee
        parent_issue = parent_issue

        return self.create_issue({
            'summary': summary,
            'description': description,
            'assignee': assignee,
            'parent_issue': parent_issue
        })

    def create_update_request(self, wi_id, wi_title, wi_url, changes, assignee, parent_issue=None):
        description = UPDATE_REQUEST_DESCRIPTION.format(
            polarion_wi_id=wi_id,
            polarion_wi_title=wi_title,
            polarion_wi_url=wi_url,
            polarion_wi_changes=changes
        )
        summary = UPDATE_REQUEST_SUMMARY.format(
            polarion_wi_id=wi_id,
            polarion_wi_title=wi_title
        )
        assignee = assignee
        parent_issue = parent_issue

        return self.create_issue({
            'summary': summary,
            'description': description,
            'assignee': assignee,
            'parent_issue': parent_issue
        })

    def get_issue_feedback(self, issue_key):
        issue = self._jira.issue(issue_key)
        status = str(issue.fields.status)
        resolution = str(issue.fields.resolution)
        comments = issue.fields.comment.comments
        if status == 'Done':
            for comment in reversed(comments):
                match = re.match(UPDATED_PATTERN_REGEX, comment.body, re.M)
                if match:
                    return {
                        "status": status,
                        "resolution": resolution,
                        "cases": filter(lambda x: x, map(lambda x: x.strip(), match.groupdict()['cases'].splitlines())),
                        "prs": filter(lambda x: x, map(lambda x: x.strip(), match.groupdict()['prs'].splitlines())),
                    }
        return {
            "status": status,
            "resolution": resolution,
            "cases": None,
            "prs": None,
        }
Example #50
0
class Manager(object):
    """Issue manager."""

    JINJA_ENV = jinja2.Environment(loader=jinja2.FileSystemLoader(ROOT_DIR))

    SUMMARY_TMPL = JINJA_ENV.get_template("templates/summary.template")
    DESCRIPTION_TMPL = JINJA_ENV.get_template("templates/description.template")
    DESCRIPTION_BOUNDARY = "_-- Alertmanager -- [only edit above]_"

    # Order for the search query is important for the query performance. It relies
    # on the 'alert_group_key' field in the description that must not be modified.
    SEARCH_QUERY = ('project = "{project}" and '
                    'issuetype = "{issuetype}" and '
                    'labels = "alert" and '
                    "status not in ({status}) and "
                    'labels = "jiralert:{group_label_key}"')

    logger = logging.getLogger(__name__)

    def __init__(
            self,
            basic_auth=None,
            server=None,
            resolve_transitions=(),
            resolved_status=(),
            threadpool=None,
    ):
        self.jira = None
        self.basic_auth = basic_auth
        self.server = server
        self.resolve_transitions = resolve_transitions
        self.resolved_status = resolved_status
        self.threadpool = threadpool
        # TODO: Keep an history of the last handled payloads and associated tickets.
        # (updated or created). Display that on the UI.
        self.history = collections.deque(20 * [None], 20)

    def connect(self):
        self.logger.info("Connecting to %s" % self.server)
        self.jira = JIRA(basic_auth=self.basic_auth, server=self.server)
        self.logger.info("Connected to %s" % self.server)

    def ready(self):
        return bool(self.jira)

    def shutdown(self):
        self.jira.close()
        self.jira = None
        if self.threadpool:
            self.threadpool.stop()
            self.threadpool = None

    def record(self, project, issue_type, request, response):
        event = Event(project, issue_type, request, response)
        self.history.appendleft(event)

    def response(self, status, code, issues=None):
        return {"status": status, "issues": issues}, code

    @jira_errors_transitions.count_exceptions()
    @jira_request_time_transitions.time()
    def transitions(self, issue):
        return self.jira.transitions(issue)

    @jira_errors_close.count_exceptions()
    @jira_request_time_close.time()
    def close(self, issue, tid):
        return self.jira.transition_issue(issue, tid)

    @jira_errors_update.count_exceptions()
    @jira_request_time_update.time()
    def update_issue(self, issue, summary, description, tags):
        custom_desc = issue.fields.description.rsplit(
            self.DESCRIPTION_BOUNDARY, 1)[0]

        # Merge expected tags and existing ones
        fields = {"labels": list(set(issue.fields.labels + tags))}

        return issue.update(
            summary=summary,
            fields=fields,
            description="%s\n\n%s\n%s" %
            (custom_desc.strip(), self.DESCRIPTION_BOUNDARY, description),
        )

    @jira_errors_create.count_exceptions()
    @jira_request_time_create.time()
    def create_issue(self, project, issue_type, summary, description, tags):
        return self.jira.create_issue({
            "project": {
                "key": project
            },
            "summary":
            summary,
            "description":
            "%s\n\n%s" % (self.DESCRIPTION_BOUNDARY, description),
            "issuetype": {
                "name": issue_type
            },
            "labels":
            tags,
        })

    @request_time_generic_issues.time()
    def post_issues(self, payload):
        """
        This endpoint accepts a JSON encoded notification according to the version 3 or 4
        of the generic webhook of the Prometheus Alertmanager.
        """

        common_labels = payload["commonLabels"]
        if "issue_type" not in common_labels or "project" not in common_labels:
            self.logger.error(
                "/issue, required commonLabels not found: issue_type or project"
            )
            project = None
            issue_type = None
            resp = self.response(
                "Required commonLabels not found: issue_type or project", 400)
        else:
            issue_type = common_labels["issue_type"]
            project = common_labels["project"]
            resp = self.do_file_issue(project, issue_type, payload)

        self.record(project, issue_type, payload, resp)
        return resp

    @request_time_qualified_issues.time()
    def post_issues_with_project(self, project, issue_type, payload):
        """
        This endpoint accepts a JSON encoded notification according to the version 3 or 4
        of the generic webhook of the Prometheus Alertmanager.
        """
        if payload["version"] not in ["3", "4"]:
            self.logger.error("/issue, unknown message version: %s" %
                              payload["version"])
            resp = self.response(
                "unknown message version %s" % payload["version"], 400)
        else:
            resp = self.do_file_issue(project, issue_type, payload)

        self.record(project, issue_type, payload, resp)
        return resp

    def update_or_resolve_issue(self, project, issue_type, issue, resolved,
                                summary, description, tags):
        """Update and maybe resolve an issue."""
        is_closed = False
        self.logger.debug("issue (%s, %s), jira issue found: %s" %
                          (project, issue_type, issue.key))

        # Try different possible transitions for resolved incidents
        # in order of preference. Different ones may work for different boards.
        if resolved:
            valid_trans = [
                t for t in self.transitions(issue)
                if t["name"].lower() in self.resolve_transitions
            ]
            if valid_trans:
                self.close(issue, valid_trans[0]["id"])
                is_closed = True
            else:
                self.logger.warning("Unable to find transition to close %s" %
                                    issue)

        # Update the base information regardless of the transition.
        self.update_issue(issue, summary, description, tags)
        self.logger.info("issue (%s, %s), %s updated" %
                         (project, issue_type, issue.key))
        return is_closed

    def do_file_issue(self, project, issue_type, payload):
        if not self.ready():
            return self.response("Not ready yet", 503)

        if payload["version"] not in ["3", "4"]:
            self.logger.error("issue (%s, %s), unknown message version: %s" %
                              (project, issue_type, payload["version"]))
            return self.response(
                "unknown message version %s" % payload["version"], 400)

        if self.threadpool:
            # We want a separate thread pool here to avoid blocking incoming
            # requests.
            self.threadpool.callInThread(self.do_file_issue_async, project,
                                         issue_type, payload)
            return self.response("OK (async)", 201)
        else:
            issues = self.do_file_issue_sync(project, issue_type, payload)
            return self.response("OK", 200, issues)

    def do_file_issue_async(self, project, issue_type, data):
        try:
            issues = self.do_file_issue_sync(project, issue_type, data)
            resp = self.response("OK", 200, issues)
        except JIRAError as e:
            resp = self.response(str(e), 503)

        # Record a fake response for async requests.
        self.record(project, issue_type, data, resp)

    @staticmethod
    def prepare_data(data):
        # Patch data to make sure it has all we need.
        data = copy.deepcopy(data)
        if "alerts" not in data:
            data["alerts"] = []
        for alert in data["alerts"]:
            # Generate a short hash to make sorting more stable.
            simple_alert = copy.deepcopy(alert)
            # Remove things that change all the time.
            if "startsAt" in simple_alert:
                del simple_alert["startsAt"]
            if "endsAt" in simple_alert:
                del simple_alert["endsAt"]
            alert["hash"] = hashlib.sha1(
                json.dumps(simple_alert,
                           sort_keys=True).encode()).hexdigest()[:8]
        return data

    @errors.count_exceptions()
    def do_file_issue_sync(self, project, issue_type, data):
        issues = {"created": [], "found": [], "updated": [], "resolved": []}

        self.logger.info("issue: %s %s" % (project, issue_type))

        data = self.prepare_data(data)

        resolved = data["status"] == "resolved"
        tags = prepare_tags(data["commonLabels"])
        tags.append("jiralert:%s" % prepare_group_label_key(data["groupKey"]))

        description = self.DESCRIPTION_TMPL.render(data)
        summary = self.SUMMARY_TMPL.render(data)

        # If there's already a ticket for the incident, update it and close if necessary.
        query = self.SEARCH_QUERY.format(
            project=project,
            issuetype=issue_type,
            status=",".join(self.resolved_status),
            group_label_key=prepare_group_label_key(data["groupKey"]),
        )

        self.logger.debug(query)
        result = self.jira.search_issues(query) or []
        # sort issue by key to have them in order of creation.
        sorted(result, key=lambda i: i.key)
        issues["found"] = [issue.permalink() for issue in result]

        for issue in result:
            is_closed = self.update_or_resolve_issue(project, issue_type,
                                                     issue, resolved, summary,
                                                     description, tags)
            issues["resolved" if is_closed else "updated"].append(
                issue.permalink())
        if not result:
            # Do not create an issue for resolved incidents that were never filed.
            if not resolved:
                issue = self.create_issue(project, issue_type, summary,
                                          description, tags)
                issues["created"].append(issue.permalink())
                self.logger.info("issue (%s, %s), new issue created (%s)" %
                                 (project, issue_type, issue.key))
        return issues
	def quote(x):
		return '"' + x + '"'

	if not options.componentjbide and not options.componentjbds:
		rootJBDS_dict = {
			'project' : { 'key': 'JBDS' },
			'summary' : 'For JBDS ' + jbds_fixversion + ': ' + taskdescription,
			'description' : 'For JBDS ' + jbds_fixversion + ': ' + taskdescriptionfull + '\n\n[Search for all task JIRA|' + tasksearch + ']',
			'issuetype' : { 'name' : 'Task' },
			'priority' : { 'name' :'Blocker'},
			'fixVersions' : [{ "name" : jbds_fixversion }],
			'components' : [{ "name" : "installer" }],
			'labels' : [ "task" ],
			}
		rootJBDS = jira.create_issue(fields=rootJBDS_dict)
		installerLead = queryComponentLead(CLJBDS, 'installer', 0)
		try:
			jira.assign_issue(rootJBDS, installerLead)
		except:
			if (not options.jiraonly):
				print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format(options.usernameJIRA, rootJBDS, installerLead, sys.exc_info()[0])
		if (options.jiraonly):
			print(rootJBDS.key)
		else:
			print("Task JIRA created for this milestone include:")
			print("")
			print("JBDS              : " + jiraserver + '/browse/' + rootJBDS.key + " => " + installerLead)

		rootJBIDE_dict = {
			'project' : { 'key': 'JBIDE' },
			failureDetails = failureDetails + '{code:title=' + testurl + '' + className_re + '/' + name_re + '}\n'
			failureDetails = failureDetails + prettyXML(s)
			failureDetails = failureDetails + '\n{code}\n\n'
			#print failureDetails

		rootJBIDE_dict = {
			'project' : { 'key': 'JBIDE' },
			'summary' : str(len(testcaselist)) + ' Test Failure(s) in JBIDE ' + jbide_affectedversion + ' for ' + component + ' component',
			'description' :  failureSummary + failureDetails,
			'issuetype' : { 'name' : 'Task' },
			'priority' : { 'name' :'Critical'},
			'versions' : [{ "name" : jbide_affectedversion }],
			'components' : [{ "name" : component }],
			'labels' : [ "testfailure" ]
			}

		jira = JIRA(options={'server':jiraserver}, basic_auth=(options.usernameJIRA, options.passwordJIRA))
		CLJBIDE = jira.project_components(jira.project('JBIDE')) # full list of components in JBIDE
		rootJBIDE = jira.create_issue(fields=rootJBIDE_dict)
		componentLead = queryComponentLead(CLJBIDE, component, 0)
		try:
			jira.assign_issue(rootJBIDE, componentLead)
		except:
			print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format(options.usernameJIRA, rootJBIDE, componentLead, sys.exc_info()[0])
		accept = raw_input("\nAccept new JIRA " + jiraserver + '/browse/' + rootJBIDE.key + " => " + componentLead + " ? [Y/n] ")
		if accept.capitalize() in ["N"] :
			rootJBIDE.delete()

	# see JIRA_components listing in components.py

	# Sample usage: see createTestFailureJIRA.py.examples.txt
Example #53
0
class JiraTool:
    def __init__(self):
        self.server = jira_url
        self.basic_auth = (usrer, password)
        self.jiraClinet = None

    def login(self):
        self.jiraClinet = JIRA(server=self.server, basic_auth=self.basic_auth)
        if self.jiraClinet != None:
            return True
        else:
            return False

    def findIssueById(self, issueId):
        if issueId:
            if self.jiraClinet == None:
                self.login()
            return self.jiraClinet.issue(issueId)
        else:
            return 'Please input your issueId'

    def deleteAllIssue(self, project):
        project_issues = self.jiraClinet.search_issues('project=' +
                                                       project.name)
        for issue in project_issues:
            logging.info('delete issue %s' % (issue))
            issue.delete()

    def deleteAllSprint(self, board):
        sprints = self.jiraClinet.sprints(board.id)
        for sprint in sprints:
            logging.info('delete sprint %s' % (sprint.name))
            sprint.delete()

    def getProject(self, name):
        projects = self.jiraClinet.projects()
        #logging.info("get project %s" %(name))
        for project in projects:
            #logging.info("project %s" %(project.name))
            if (name == project.name):
                return project

        return None
        #return self.jiraClinet.create_project(key='SCRUM', name=name, assignee='yujiawang', type="Software", template_name='Scrum')

    def getBoard(self, project, name):
        boards = self.jiraClinet.boards()
        for board in boards:
            if (name == board.name):
                logging.info("board:%s id:%d" % (board.name, board.id))
                return board

        return self.jiraClinet.create_board(name, [project.id])

    def createSprint(self, board, name, startDate=None, endDate=None):
        logging.info("==== create sprint[%s] in board[%s] ====>" %
                     (name, board.name))
        sprint = self.jiraClinet.create_sprint(name, board.id, startDate,
                                               endDate)
        return sprint

    def getSprint(self, board_id, sprint_name):
        sprints = self.jiraClinet.sprints(board_id)
        for sprint in sprints:
            if sprint.name == sprint_name:
                return sprint
        return None

    def createEpicTask(self, project, sprint, summary, description, assignee,
                       participant, duedate):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'issuetype': {
                'id': issuetypes['Epic']
            },
            'customfield_10002': summary,  # epic 名称
            'summary': summary,
            'description': description,
            "customfield_10004": sprint.id,  # sprint
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant,
            'customfield_10302': '2018-08-24T05:41:00.000+0800'
        }

        logging.info(duedate)

        logging.info(issue_dict)  #juse for debug
        issue = self.jiraClinet.create_issue(issue_dict)
        self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']])
        logging.info(
            "===> add epic task[%s key:%s] to sprint [%s]" %
            (issue.raw['fields']['summary'], issue.raw['key'], sprint.name))
        #dumpIssue(issue) #juse for debug
        return issue

    def createTask(self, project, sprint, epic, summary, description, assignee,
                   participant):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'issuetype': {
                'id': issuetypes['Task']
            },
            'summary': summary,
            'description': description,
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant
        }

        issue = self.jiraClinet.create_issue(issue_dict)
        logging.info("==> add task[%s key:%s] link epic [%s key: %s]" %
                     (issue.raw['fields']['summary'], issue.raw['key'],
                      epic.raw['fields']['summary'], epic.raw['key']))
        self.jiraClinet.add_issues_to_epic(epic.id, [issue.raw['key']])
        self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']])
        return issue

    def createSubTask(self, project, parent, summary, description, assignee,
                      participant):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'parent': {
                'key': parent.raw['key']
            },
            'issuetype': {
                'id': issuetypes['Sub-Task']
            },
            'summary': summary,
            'description': description,
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant
        }
        issue = self.jiraClinet.create_issue(issue_dict)
        logging.info("=> add sub task[%s key:%s] to task [%s key: %s]" %
                     (issue.raw['fields']['summary'], issue.raw['key'],
                      parent.raw['fields']['summary'], parent.raw['key']))
        return issue
Example #54
0
#!/usr/bin/env python
import requests, json
import os
from jira import JIRA
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

options = {'verify': False, 'server': 'https://jira.name.com'}
WORK_DIR = os.path.dirname(os.path.realpath(__file__))

CREDENTIAL_FILE = 'credentials.json'
with open(WORK_DIR + '/' + CREDENTIAL_FILE, 'r') as file:
    credential = json.load(file)
jira = JIRA(options,
            basic_auth=(credential["jira"]["username"],
                        credential["jira"]['password']))
user_name = credential["jira"]["username"]
user_pass = credential["jira"]['password']

new_issue = jira.create_issue(project={'key': 'PROJECTNAME'},
                              summary='Test Summary',
                              description='Look into this one',
                              issuetype={'name': 'Incident'},
                              customfield_10321={'value': 'prod'})
Example #55
0
import os

# count the site
username = os.environ.get('JIRA_USER')
password = os.environ.get('JIRA_PASS')
jira_url = os.environ.get('JIRA_URL')

jira = JIRA(jira_url, basic_auth=(username, password))


with open("patching.yml", 'r') as stream:
    tasks = yaml.load(stream)

parent = jira.issue(tasks['parent'])
print(parent.key)

for task in tasks['stages']:
    subtask_dict = {
        'project' : { 'key': 'OP' },
        'summary' : task,
        'description' : '',
        'issuetype' : { 'name' : 'Sub-task' },
        'parent' : { 'id' : parent.key},
        'components' : [{ 'id' :'12510'}], # Security
        'customfield_13692' : 'none', # sitename
        'customfield_10795' : [ { 'id' :'10717'}], # ACE
    }

    child = jira.create_issue(fields=subtask_dict)
    print(task)
Example #56
0
class JiraTestManager:
    """Instantiate and populate the JIRA instance with data for tests.

    Attributes:
        CI_JIRA_ADMIN (str): Admin user account name.
        CI_JIRA_USER (str): Limited user account name.
        max_retries (int): number of retries to perform for recoverable HTTP errors.
        initialized (bool): if init was successful.
    """

    __shared_state: Dict[Any, Any] = {}

    def __init__(self,
                 jira_hosted_type=os.environ.get("CI_JIRA_TYPE", "Server")):
        """Instantiate and populate the JIRA instance"""
        self.__dict__ = self.__shared_state

        if not self.__dict__:
            self.initialized = False
            self.max_retries = 5
            self._cloud_ci = False

            if jira_hosted_type and jira_hosted_type.upper() == "CLOUD":
                self.set_jira_cloud_details()
                self._cloud_ci = True
            else:
                self.set_jira_server_details()

            jira_class_kwargs = {
                "server": self.CI_JIRA_URL,
                "logging": False,
                "validate": True,
                "max_retries": self.max_retries,
            }

            self.set_basic_auth_logins(**jira_class_kwargs)

            if not self.jira_admin.current_user():
                self.initialized = True
                sys.exit(3)

            # now we need to create some data to start with for the tests
            self.create_some_data()

        if not hasattr(self, "jira_normal") or not hasattr(self, "jira_admin"):
            pytest.exit("FATAL: WTF!?")

        if self._cloud_ci:
            self.user_admin = self.jira_admin.search_users(
                query=self.CI_JIRA_ADMIN)[0]
            self.user_normal = self.jira_admin.search_users(
                query=self.CI_JIRA_USER)[0]
        else:
            self.user_admin = self.jira_admin.search_users(
                self.CI_JIRA_ADMIN)[0]
            self.user_normal = self.jira_admin.search_users(
                self.CI_JIRA_USER)[0]
        self.initialized = True

    def set_jira_cloud_details(self):
        self.CI_JIRA_URL = "https://pycontribs.atlassian.net"
        self.CI_JIRA_ADMIN = os.environ["CI_JIRA_CLOUD_ADMIN"]
        self.CI_JIRA_ADMIN_PASSWORD = os.environ["CI_JIRA_CLOUD_ADMIN_TOKEN"]
        self.CI_JIRA_USER = os.environ["CI_JIRA_CLOUD_USER"]
        self.CI_JIRA_USER_PASSWORD = os.environ["CI_JIRA_CLOUD_USER_TOKEN"]
        self.CI_JIRA_ISSUE = os.environ.get("CI_JIRA_ISSUE", "Bug")

    def set_jira_server_details(self):
        self.CI_JIRA_URL = os.environ["CI_JIRA_URL"]
        self.CI_JIRA_ADMIN = os.environ["CI_JIRA_ADMIN"]
        self.CI_JIRA_ADMIN_PASSWORD = os.environ["CI_JIRA_ADMIN_PASSWORD"]
        self.CI_JIRA_USER = os.environ["CI_JIRA_USER"]
        self.CI_JIRA_USER_PASSWORD = os.environ["CI_JIRA_USER_PASSWORD"]
        self.CI_JIRA_ISSUE = os.environ.get("CI_JIRA_ISSUE", "Bug")

    def set_basic_auth_logins(self, **jira_class_kwargs):
        if self.CI_JIRA_ADMIN:
            self.jira_admin = JIRA(
                basic_auth=(self.CI_JIRA_ADMIN, self.CI_JIRA_ADMIN_PASSWORD),
                **jira_class_kwargs,
            )
            self.jira_sysadmin = JIRA(
                basic_auth=(self.CI_JIRA_ADMIN, self.CI_JIRA_ADMIN_PASSWORD),
                **jira_class_kwargs,
            )
            self.jira_normal = JIRA(
                basic_auth=(self.CI_JIRA_USER, self.CI_JIRA_USER_PASSWORD),
                **jira_class_kwargs,
            )
        else:
            raise RuntimeError(
                "CI_JIRA_ADMIN environment variable is not set/empty.")

    def _project_exists(self, project_key: str) -> bool:
        """True if we think the project exists, else False.

        Assumes project exists if unknown Jira exception is raised.
        """
        try:
            self.jira_admin.project(project_key)
        except JIRAError as e:  # If the project does not exist a warning is thrown
            if "No project could be found" in str(e):
                return False
            LOGGER.exception("Assuming project '%s' exists.", project_key)
        return True

    def _remove_project(self, project_key):
        """Ensure if the project exists we delete it first"""

        wait_between_checks_secs = 2
        time_to_wait_for_delete_secs = 40
        wait_attempts = int(time_to_wait_for_delete_secs /
                            wait_between_checks_secs)

        # TODO(ssbarnea): find a way to prevent SecurityTokenMissing for On Demand
        # https://jira.atlassian.com/browse/JRA-39153
        if self._project_exists(project_key):
            try:
                self.jira_admin.delete_project(project_key)
            except Exception:
                LOGGER.exception("Failed to delete '%s'.", project_key)

        # wait for the project to be deleted
        for _ in range(1, wait_attempts):
            if not self._project_exists(project_key):
                # If the project does not exist a warning is thrown
                # so once this is raised we know it is deleted successfully
                break
            sleep(wait_between_checks_secs)

        if self._project_exists(project_key):
            raise TimeoutError(
                " Project '{project_key}' not deleted after {time_to_wait_for_delete_secs} seconds"
            )

    def _create_project(self,
                        project_key: str,
                        project_name: str,
                        force_recreate: bool = False) -> int:
        """Create a project and return the id"""

        if not force_recreate and self._project_exists(project_key):
            pass
        else:
            self._remove_project(project_key)
            create_attempts = 6
            for _ in range(create_attempts):
                try:
                    if self.jira_admin.create_project(project_key,
                                                      project_name):
                        break
                except JIRAError as e:
                    if "A project with that name already exists" not in str(e):
                        raise e
        return self.jira_admin.project(project_key).id

    def create_some_data(self):
        """Create some data for the tests"""

        # jira project key is max 10 chars, no letter.
        # [0] always "Z"
        # [1-6] username running the tests (hope we will not collide)
        # [7-8] python version A=0, B=1,..
        # [9] A,B -- we may need more than one project
        """ `jid` is important for avoiding concurrency problems when
        executing tests in parallel as we have only one test instance.

        jid length must be less than 9 characters because we may append
        another one and the Jira Project key length limit is 10.
        """

        self.jid = get_unique_project_name()

        self.project_a = self.jid + "A"  # old XSS
        self.project_a_name = "Test user={} key={} A".format(
            getpass.getuser(),
            self.project_a,
        )
        self.project_b = self.jid + "B"  # old BULK
        self.project_b_name = "Test user={} key={} B".format(
            getpass.getuser(),
            self.project_b,
        )
        self.project_sd = self.jid + "C"
        self.project_sd_name = "Test user={} key={} C".format(
            getpass.getuser(),
            self.project_sd,
        )

        self.project_a_id = self._create_project(self.project_a,
                                                 self.project_a_name)
        self.project_b_id = self._create_project(self.project_b,
                                                 self.project_b_name,
                                                 force_recreate=True)

        sleep(1)  # keep it here as often Jira will report the
        # project as missing even after is created

        project_b_issue_kwargs = {
            "project": self.project_b,
            "issuetype": {
                "name": self.CI_JIRA_ISSUE
            },
        }
        self.project_b_issue1_obj = self.jira_admin.create_issue(
            summary="issue 1 from %s" % self.project_b,
            **project_b_issue_kwargs)
        self.project_b_issue1 = self.project_b_issue1_obj.key

        self.project_b_issue2_obj = self.jira_admin.create_issue(
            summary="issue 2 from %s" % self.project_b,
            **project_b_issue_kwargs)
        self.project_b_issue2 = self.project_b_issue2_obj.key

        self.project_b_issue3_obj = self.jira_admin.create_issue(
            summary="issue 3 from %s" % self.project_b,
            **project_b_issue_kwargs)
        self.project_b_issue3 = self.project_b_issue3_obj.key
Example #57
0
options = {
    'server': cfg['servercfg']['server'],
}


jira = JIRA(options, basic_auth=(cfg['servercfg']['user'],cfg['servercfg']['pwd']))

print("connected")

issue_dict = {
    'project': 'SD',
    'summary': 'New issue from jira-python',
    'description': 'Look into this one',
    'issuetype': {'name': 'Incidente'},
    'customfield_12100': 'Zabbix',
    'customfield_12106': '4',
    'customfield_12107': '32',
    'customfield_12108': '224',
    'customfield_11402': '9999',

}
new_issue = jira.create_issue(fields=issue_dict)

print(new_issue)

#Elemento 12108 = 224
#Descrição do Serviço  12107= 32
#Categoria 12106 = 4
#Solicitante 12100
Example #58
0
gcissues_list = []

#-----guarda os objetos issue em uma lista 
for issue in pjissues:
    pjissues_list.append(str(issue))
    
gcquery = 'project = GESTCONFIG and status not in (Resolved,Closed,Verification,Rejected) and Referência is null'
gcissues = jira.search_issues(gcquery)


cont = 0

for issue in pjissues:
    issue = jira.issue(pjissues_list[cont])
    project = issue.fields.project.key
    description = issue.fields.description
    summary = issue.fields.summary  
    print('teste jira :|'+project+' | '+summary+' | '+description)
    new_issue = jira.create_issue(project='', summary=summary,
                              description=description, issuetype={'name': 'Task'})
    issue.update(customfield_10151=str(new_issue.key))
    cont = cont+1


print(pjissues_list)
#-------adiciona issue na base interna pela quantidade de referencias nulls na base externa


#    
  #após criar issue na base interna , editar o issue externo e adicionar o ID da interno como referencia
Example #59
0
class TestIssues(unittest.TestCase):
    def setUp(self):
        self.jira = JIRA(options=dict(server=TEST_URL, verify=False), basic_auth=(TEST_USERNAME, TEST_PASSWORD))
        self.issue1 = self.jira.create_issue(
            project='KB',
            summary='Test-1',
            issuetype={'name': 'Bug'},
        )
        self.issue2 = self.jira.create_issue(
            project='KB',
            summary='Test-2',
            issuetype={'name': 'Bug'},
        )

    def tearDown(self):
        issues = self.jira.search_issues('project = "KB" AND summary ~ "Test*"', fields=['key'])
        for _ in issues:
            _.delete()

    def assert_single_attachment(self):
        # TODO - Find how to test this automatically
        pass

    def assert_single_comment_with(self, text):
        comments = self.jira.comments(self.issue1.key)
        self.assertEqual(len(comments), 1)
        self.assertIn(text, comments[0].body)

    def test_new(self):
        result = CliRunner().invoke(topcli, ['issue', 'new', 'KB', 'task', 'Test-new'])
        self.assertEqual(result.exit_code, 0)
        issues = self.jira.search_issues('project = "KB" AND summary ~ "Test-new"', fields=['key', 'summary'])
        self.assertEqual(len(issues), 1)
        self.assertIn(issues[0].key, result.output)

    def test_transition(self):
        result = CliRunner().invoke(topcli, ['issue', 'transition', self.issue1.key, 'Done'])
        self.assertEqual(result.exit_code, 0)

    def test_assign(self):
        result = CliRunner().invoke(topcli, ['issue', 'assign', self.issue1.key, TEST_USERNAME])
        self.assertEqual(result.exit_code, 0)
        assignee = self.jira.issue(self.issue1.key, fields=['assignee']).fields.assignee
        self.assertEqual(assignee.key, TEST_USERNAME)

    def test_unassign(self):
        result = CliRunner().invoke(topcli, ['issue', 'assign', self.issue1.key, TEST_USERNAME])
        result = CliRunner().invoke(topcli, ['issue', 'unassign', self.issue1.key])
        self.assertEqual(result.exit_code, 0)
        assignee = self.jira.issue(self.issue1.key, fields=['assignee']).fields.assignee
        self.assertIsNone(assignee)

    def test_attach_file(self):
        with CliRunner().isolated_filesystem() as dir_path:
            with open('data.txt', 'w') as f:
                print('abc', file=f)
            result = CliRunner().invoke(topcli, ['issue', 'attach', self.issue1.key, 'data.txt'])
            self.assertEqual(result.exit_code, 0)
            self.assert_single_attachment()

    def test_comment_args(self):
        result = CliRunner().invoke(topcli, ['issue', 'comment', self.issue1.key, 'Comment', 'from args'])
        self.assertEqual(result.exit_code, 0)
        self.assert_single_comment_with('Comment from args')

    def test_comment_file(self):
        with CliRunner().isolated_filesystem() as dir_path:
            with open('comment.txt', 'w') as f:
                print('Comment from file', file=f)
            result = CliRunner().invoke(topcli, ['issue', 'comment', self.issue1.key, 'comment.txt'])
            self.assertEqual(result.exit_code, 0)
            self.assert_single_comment_with('Comment from file')

    def test_comment_prompt(self):
        result = CliRunner().invoke(topcli, ['issue', 'comment', self.issue1.key], input='Comment from prompt\n')
        self.assertEqual(result.exit_code, 0)
        self.assert_single_comment_with('Comment from prompt')

    def test_comment_stdin(self):
        result = CliRunner().invoke(topcli, ['issue', 'comment', self.issue1.key, '-'], input='Comment\nfrom\nstdin')
        self.assertEqual(result.exit_code, 0)
        self.assert_single_comment_with('Comment\nfrom\nstdin')

    def test_link(self):
        result = CliRunner().invoke(topcli, ['issue', 'link', self.issue1.key, self.issue2.key, '-t', 'duplicates'])
        self.assertEqual(result.exit_code, 0)
        links = self.jira.issue(self.issue1.key, fields=['issuelinks']).fields.issuelinks
        self.assertEqual(len(links), 1)
        self.assertEqual(links[0].outwardIssue.key, self.issue2.key)
        self.assertEqual(links[0].type.outward, 'duplicates')

    def test_unlink(self):
        result = CliRunner().invoke(topcli, ['issue', 'link', self.issue1.key, self.issue2.key, '-t', 'duplicates'])
        self.assertEqual(result.exit_code, 0)
        result = CliRunner().invoke(topcli, ['issue', 'unlink', self.issue1.key, self.issue2.key])
        links = self.jira.issue(self.issue1.key, fields=['issuelinks']).fields.issuelinks
        self.assertEqual(len(links), 0)

    def test_search_issue(self):
        result = CliRunner().invoke(topcli, ['issue', 'search'])
        self.assertEqual(result.exit_code, 0)
        self.assertIn('KB-1', result.output)
        self.assertIn('KB-2', result.output)
        self.assertIn('KB-3', result.output)
Example #60
0
class JiraOperations(object):
    """ Base class for interaction with JIRA """
    def __init__(self, config):
        # do not print excess warnings
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        # JIRA configuration from config.json/DDB
        self.config = config
        # JIRA url
        self.server = self.config.jira.server
        # JIRA established session
        self.session = None

        if self.config.jira.enabled:
            self.login_oauth()
        else:
            logging.debug("JIRA integration is disabled")

    @property
    def current_user(self):
        """ :return: JIRA user name, used for connection establishing """
        return self.session.current_user()

    def login_oauth(self):
        """
        Establish JIRA connection using oauth

        :return: boolean, if connection was successful.
        """
        if not self.config.jira.credentials:
            logging.error("Failed to login jira (empty credentials)")
            return False

        try:
            self.session = JIRA(options={
                'server': self.server,
                'verify': False
            },
                                oauth=self.config.jira.credentials["oauth"])
        except JIRAError:
            logging.exception(
                f"Failed to create oauth session to {self.server}")
            return False

        logging.debug(
            f'JIRA session to {self.server} created successfully (oauth)')
        return True

    def login_basic(self):
        """
        Establish JIRA connection using basic authentication

        :return: boolean, if connection was successful.
        """
        if not self.config.jira.credentials:
            logging.error("Failed to login jira (empty credentials)")
            return False

        username = self.config.jira.credentials["basic"]["username"]
        password = self.config.jira.credentials["basic"]["password"]
        options = {'server': self.server, 'verify': False}

        try:
            self.session = JIRA(options, basic_auth=(username, password))
        except Exception:
            logging.exception(
                f"Failed to create basic session to {self.server}")
            return False

        logging.debug(
            f'JIRA session to {self.server} created successfully (basic)')
        return True

    def ticket_url(self, ticket_id):
        """ :return: URL to `ticket_id` """
        return f"{self.server}/browse/{ticket_id}"

    def ticket_assignee(self, ticket_id):
        """
        :param ticket_id: JIRA ticket
        :return: name of current assignee for ticket
        """
        ticket = self.session.issue(ticket_id)
        return ticket.fields.assignee.name

    def find_valid_assignee(self, project, assignees):
        """
        Check what record from given list of possible assignees can be used as assignee for given project.

        :param project: name of Jira project to perform check against
        :param assignees: list of possible assignees
        :return:
        """
        for assignee in assignees:
            if assignee is None:
                continue

            try:
                users = self.session.search_assignable_users_for_projects(
                    assignee, project)
            except Exception:
                continue

            # check only exact matches
            if len(users) == 1:
                return users[0].name
        return None

    def create_ticket(self, issue_data):
        """
        Create a JIRA ticket

        :param issue_data: a dict containing field names and the values to use
        """
        resp = self.session.create_issue(fields=issue_data)
        logging.debug(f"Created jira ticket {self.ticket_url(resp.key)}")
        return resp.key

    def create_issue_link(self, inward_issue, outward_issue):
        """
        Linking JIRA tickets with 'relates to' link

        :return: boolean, if linking was successful
        """
        if not (inward_issue or outward_issue):
            return False

        try:
            # JIRA comes with default types of links:
            #  1) relates to / relates to,
            #  2) duplicates / is duplicated by,
            #  3) blocks / is blocked by
            #  4) clones / is cloned by
            link_type = "relates to"
            self.session.create_issue_link(type=link_type,
                                           inwardIssue=inward_issue,
                                           outwardIssue=outward_issue)
        except Exception:
            logging.exception(
                f"Failed to create issue link {inward_issue} -> {outward_issue}"
            )
            return False

        logging.debug(f"Created issue link {inward_issue} -> {outward_issue}")
        return True

    def assign_user(self, ticket_id, assinee_name):
        """
        Assign `ticket_id` to `assinee_name`.

        :return: boolean, if assigning was successful
        """
        if not (ticket_id or assinee_name):
            return False

        try:
            issue = self.session.issue(ticket_id)
            issue.update(assignee={'name': assinee_name})
        except Exception:
            logging.exception(
                f"Failed to assign {ticket_id} to {assinee_name}")
            return False

        logging.debug(f"Assigned {ticket_id} to {assinee_name}")
        return True

    def update_ticket(self, ticket_id, updated_issue_data):
        """
        Update JIRA ticket fields as in self.create_ticket(), but for existing ticket

        :param ticket_id: ticket Id to update
        :param updated_issue_data: a dict containing field names and the values to use

        :return: boolean, if updating was successful
        """
        try:
            issue = self.session.issue(ticket_id)
            issue.update(updated_issue_data)
        except Exception:
            logging.exception(f"Failed to update {ticket_id}")
            return False

        logging.debug(f"Updated {ticket_id}")
        return True

    def add_comment(self, ticket_id, comment):
        """
        Add comment to JIRA ticket

        :param ticket_id: ticket Id to add comment to
        :param comment: comment text

        :return: boolean, if operation was successful
        """
        if ticket_id and comment:
            try:
                self.session.add_comment(ticket_id, comment)
            except Exception:
                logging.exception(f"Failed to add comment to {ticket_id}")
                return False
        return True

    def close_issue(self, ticket_id):
        """
        Transition of ticket to `Closed` state. It checks if issue can be transitioned to `Closed` state.

        :param ticket_id: ticket Id to close

        :return: nothing
        """
        issue = self.session.issue(ticket_id)
        if issue.fields.status.name == "Closed":
            logging.debug(f"{ticket_id} is already closed")
            return

        for transition in self.session.transitions(issue):
            if transition['name'] == 'Close Issue':
                self.session.transition_issue(ticket_id, transition['id'])
                logging.debug(f"Closed {ticket_id}")
                break
        else:
            logging.error(f"{self.ticket_url(ticket_id)} can't be closed")
            return

    def resolve_issue(self, ticket_id):
        """
        Transition of ticket to `Resolved` state. It checks if issue can be transitioned to `Resolved` state.

        :param ticket_id: ticket Id to resolve

        :return: nothing
        """
        issue = self.session.issue(ticket_id)
        if issue.fields.status.name == "Resolved":
            logging.debug(f"{ticket_id} is already resolved")
            return

        for transition in self.session.transitions(issue):
            if transition['name'] == 'Resolve Issue':
                self.session.transition_issue(ticket_id, transition['id'])
                logging.debug(f"Resolved {ticket_id}")
                break
        else:
            logging.error(f"{self.ticket_url(ticket_id)} can't be resolved")
            return

    def reopen_issue(self, ticket_id):
        """
        Transition of ticket to `Reopen Issue` state. It checks if issue can be transitioned to `Reopen Issue` state.

        :param ticket_id: ticket Id to reopen

        :return: nothing
        """
        issue = self.session.issue(ticket_id)
        if issue.fields.status.name in ["Open", "Reopened"]:
            logging.debug(f"{ticket_id} is already opened")
            return

        for transition in self.session.transitions(issue):
            if transition['name'] == 'Reopen Issue':
                self.session.transition_issue(ticket_id, transition['id'])
                logging.debug(f"Reopened {ticket_id}")
                break
        else:
            logging.error(f"{self.ticket_url(ticket_id)} can't be reopened")
            return

    def add_attachment(self, ticket_id, filename, text):
        """
        Add text as attachment with filename to JIRA ticket

        :param ticket_id: ticket Id to add attachment to
        :param filename: label for attachment
        :param text: attachment text

        :return: attachment object
        """
        attachment = io.StringIO(text)
        filename = filename.replace(':', '-')
        return self.session.add_attachment(issue=ticket_id,
                                           attachment=attachment,
                                           filename=filename)

    @staticmethod
    def build_tags_table(tags):
        """
        Build JIRA table from AWS tags dictionary

        :param tags: dict with tags

        :return: str with JIRA table
        """
        if not tags:
            return ""

        desc = f"*Tags*:\n"
        desc += f"||Key||Value||\n"
        for key, value in tags.items():
            desc += f"|{key}|{empty_converter(value)}|\n"
        return desc