Beispiel #1
0
def _main():
    try:
        global verbose_flag
        args = parse_args()
        if args.verbose:
            verbose_flag = True
        success = True
        auth_path = args.auth_path

    except Exception as e:
        print("e=%s" % e)
    if success:
        auth = open(auth_path)
        info = json.load(auth)
        name = info['user']
        password = info['password']
        #print( name,password)
        jira = JIRA('http://jira.fihbdc.com:8080', basic_auth=(name, password))
        issue_dict = {
            'project': {
                'id': '11301'
            },  # <JIRA Project: key='BT1', name='B130DL', id='11301'>
            'summary': args.title,
            'description': args.description,
            'issuetype': {
                'name': 'SW Bug'
            },
            'components': [{
                'name': 'Stability'
            }],
            'customfield_10412': {
                'value': 'Auto-MTBF'
            },
            'customfield_10201': [{
                'id': '10205'
            }],  #value='China/Beijing', id='10205' value='USA', id='10240'
            'customfield_10501': {
                'value': 'Yes'
            },  #FIH can reporeduce
            'customfield_10700': {
                'value': args.HW_version
            },  #PVT,EVT or DVT
            'customfield_10503': {
                'id': '10816'
            },  #frequency Occasional(10%-49%)
            'customfield_10411': {
                'value': args.severity
            },  #value='Critical', id='10616'  value='Serious', id='10617'
            'customfield_10202': [{
                'id': '10311'
            }],  #value='China Unicom', id='10291' value='CMCC', id='10311'
            'customfield_10400': args.build_version,  #SW version
            # 'attachment':r'/mnt/d/MTBF/BT1/0.096/testTC90BluetoothTethering_20200406_020504.zip'
        }
        new_issue = jira.create_issue(fields=issue_dict)
        if args.attachment_path:
            jira.add_attachment(issue=new_issue,
                                attachment=args.attachment_path)
        print(new_issue.key)
Beispiel #2
0
def createBugOfJira(summaryStr, descriptionStr, developer, ccGroup):
    issue_dict = jiraConfig.boss_bug_issue_dict
    issue_dict['summary'] = summaryStr
    issue_dict['description'] = descriptionStr
    issue_dict['assignee']['name'] = developer
    issue_dict['customfield_12103'] = [{'name': ccGroup}]
    jira = JIRA(jiraConfig.JIRA_URL, basic_auth=jiraConfig.JIRA_AUTH)
    jira.DEFAULT_OPTIONS['headers']['Content-Type'] = 'application/json'
    issueKey = jira.create_issue(fields=issue_dict)
    jira.add_attachment(issue=issueKey, attachment='/tmp/log.txt')
    return issueKey
Beispiel #3
0
def update_jira_ticket(jira_ticket_nr, jira_report_comment,
                       jira_report_attachment_path):
    jira = JIRA(basic_auth=('USER', 'PASS'),
                options={
                    'server': 'https://JIRA-IP',
                    'verify': False
                })
    issue = jira.issue(jira_ticket_nr)
    jira.add_comment(issue, jira_report_comment)
    with open(jira_report_attachment_path, "rb") as report_to_upload:
        jira.add_attachment(issue=issue, attachment=report_to_upload)
Beispiel #4
0
class Jira():
	def __init__(self):
		jira_options = { 'server': jira.com, 'verify': False }	
		try:
			self.jira = JIRA(options=jira_options, basic_auth=(username, password))
			self.jira._session.proxies={'http':proxy.com, 'https':proxy.com}
		except:
			eprint("ERROR: cannot create JIRA object.  Is Jira down?")
			traceback.print_exc()
		self.ticket = None

	#@ret: (Fri Feb  3, 'Night', Shaver)
	def find_shift(self):
		date = time.asctime()[0:11]
		hour = int(time.asctime()[11:13])
		if hour >= 16 and hour < 23: 
			return (date, 'Swing')
		elif hour >= 8 and hour < 16: 
			return (date, 'Morning')
		else: 
			return (date, 'Night')

	def create_ticket(self):
		date = self.find_shift()
		summary = "evident.IO new alerts for " + date[1] + ' ' + date[0]
		jira_payload = {
						'project' : {'key' : team},
						'issuetype' : {'name' : 'Other'},
						'customfield_11802' : 	{ #This is the "Requesting Team"
												'id' : id_num,	#this is needed
												'key' : team,	#this is also needed
												'name' : team_name
												},
						'summary' : summary,
						'description': "Evident.IO new alerts for " + date[1] + " " + date[0]
						}
		try:
			new_ticket = self.jira.create_issue(fields=jira_payload)
			self.ticket = new_ticket.key
			print("created ticket: " + new_ticket.key)
		except:
			print("ERROR: cannot create new jira ticket")
			traceback.print_exc()

	def add_attachment(self):
		shutil.make_archive('evident_reports', 'zip', 'evident_reports')
		
		try:
			with open('evident_reports.zip', 'rb') as ifile:
				self.jira.add_attachment(self.ticket, ifile)
				print( self.ticket + ': attaching zipped reports directory')
		except:
			print("ERROR: unable to add zipped reports directory to ticket " + self.ticket)
			traceback.print_exc()
Beispiel #5
0
def createTaskOfJira(summaryStr, descriptionStr, jobName):
    if jobName == "stariboss-helm":
        issue_dict = jiraConfig.boss_issue_dict
    else:
        issue_dict = jiraConfig.ott_issue_dict
    issue_dict['summary'] = summaryStr
    issue_dict['description'] = descriptionStr
    jira = JIRA(jiraConfig.JIRA_URL, basic_auth=jiraConfig.JIRA_AUTH)
    jira.DEFAULT_OPTIONS['headers']['Content-Type'] = 'application/json'
    issueKey = jira.create_issue(fields=issue_dict)
    jira.add_attachment(issue=issueKey, attachment='/tmp/commitMessage.txt')
    return issueKey
Beispiel #6
0
def create_issue(log_info, ):
    """
    :param log_info: (account,password)
    :param project: 项目名称   eg: SQEAP https://jira.nevint.com/browse/SQEAP-483
    :param issuretype: 选择范围:Bug、Story、Epic、Task、Sub-task  具体看项目
    :param priority: 选择范围:P1 - Critical、P2 - High、P3 - Medium、P4 - Low、P5 - Undetermined
    :param summary: issue标题
    :param versions:iusse对应版本 Affects Version/s  选择范围见新建issue Affects Version选项
    :param customfield_10903: 发现方式  How Found 选择范围见新建issue How Found选项
    :param components:选择范围见新建issue Component选项
    :param assignee: 指派人账号(邮箱前缀)
    :param description: issue描述
    :param attachment: 附件的本地连接
    :param fix_day: 解决问题所需天数(工作日)
    :return:
    """
    options = {'server': 'https://jira.xxx.com/'}
    jira = JIRA(options, basic_auth=log_info)
    now = datetime.datetime.now().date()
    fix_day = 3
    end_day = now + datetime.timedelta(days=1)
    while fix_day > 0:  # 在创建日基础上增加预期的工作日
        end_day += datetime.timedelta(days=1)
        if end_day.weekday() not in [0, 1, 2, 3, 4]:
            continue
        else:
            fix_day -= 1
    issue_dict = {
        'project': {'key': 'SQEAP'},  # 项目
        'issuetype': {'name': 'Bug'},  # 问题类型
        'priority': {'name': 'P3 - Medium'},  # 优先级
        'summary': '自动化创建jira测试',  # 问题主题
        'versions': [{'name': '123'}],  # 解决版本
        'customfield_10903': {'value': 'Automation'},  # 发现方式
        'components': [{"name": "aaa"}],  # 相关模块
        'assignee': {'name': 'xxx'},  # 经办人
        'description': '自动化创建jira测试',  # 问题描述
        'customfield_10300': str(now),  # 计划开始时间
        'customfield_10405': str(end_day),  # 计划结束时间
    }
    rsp = jira.create_issue(issue_dict)
    issue = jira.issue(rsp)
    jira.add_attachment(issue=issue, attachment='pytest_learn.zip')
    jira.close()
Beispiel #7
0
def test_jira_api(args):
    logger.info(str(args))
    if not isinstance(g.identity, AnonymousIdentity):
        raw_ua = request.headers.get('User-Agent') or ''
        ua = user_agents.parse(raw_ua)
        args['os'] = ua.os.family
        args['browser'] = ua.browser.family
        args['device'] = ua.device.family
        args['ua'] = raw_ua
    from jira import JIRA

    HOST = current_app.config['JIRA_HOST']
    BASIC_AUTH_USER = current_app.config['BASIC_AUTH_USER']
    BASIC_AUTH_PASSWORD = current_app.config['BASIC_AUTH_PASSWORD']
    PROJECT = current_app.config['PROJECT']
    EPIC = current_app.config['EPIC']
    # 权限
    jira = JIRA(HOST, basic_auth=(BASIC_AUTH_USER, BASIC_AUTH_PASSWORD))
    # 新建issue
    new_issue = jira.create_issue(project=PROJECT,
                                  summary=args['summary'],
                                  description=args.get('description', ''),
                                  issuetype={'name': args['type']})
    # 添加issue到epic
    jira.add_issues_to_epic(EPIC, [str(new_issue.key)])
    # 添加额外信息到comment
    display_list = ['summary', 'description', 'type', 'file']
    comment_body = ''.join(
        ['%s:%s \n' % (k, args[k]) for k in args if k not in display_list])
    jira.add_comment(new_issue.key, comment_body) if comment_body else ""
    # 添加附件
    data = request.files.getlist("file")
    current_app.logger.info(type(data))
    if data:
        for file_data in data:
            current_app.logger.info(file_data.filename)
            binary = file_data.stream.read()
            if binary:
                jira.add_attachment(issue=new_issue,
                                    attachment=binary,
                                    filename=str(uuid.uuid1()) + '.' +
                                    file_data.filename.split(".")[-1])
Beispiel #8
0
class Jira:
    def __init__(self):
        self.user = "******"
        self.apikey = "your_jira_api_key"
        self.server = "https://your_emakina_jira_instance.com"
        self.project = "your_project_name"
        self.options = {'server': self.server}
        self.jira = JIRA(self.options, basic_auth=(self.user, self.apikey))

    def create_issue(self, summary, description, timestamp):
        self.jira.project(self.project)
        self.jira.create_issue(project=self.project,
                               summary=f"{summary} {timestamp}",
                               description=f"{description}",
                               issuetype={"name": "Task"})

    def add_attachment_to_ticket(self, filename):
        self.jira.add_attachment(
            issue=self.jira.search_issues(f'project={self.project}')[0],
            attachment=f"{filename}")
def jira_setup(project,
               assignee,
               title,
               description,
               filename='',
               due=1,
               comment=''):
    '''This code will Create Jira ticket and return the Issue Key of the ticket Created. '''

    apiKey = '*********************'  #apiKey you will find in Jira page
    jira = JIRA(basic_auth=('*****@*****.**', apiKey),
                options={"server": 'https://abcdef.xyz.net'})
    duedate = str((datetime.date.today() +
                   datetime.timedelta(days=due)).strftime('%Y-%m-%d'))
    issue_list = [{
        'project': {
            'key': project
        },
        'summary': title,
        'description': description,
        'issuetype': {
            'name': 'Task'
        },
        'assignee': {
            'name': assignee
        },
        'priority': {
            'name': 'High'
        },
        'duedate': duedate
    }]
    issues = jira.create_issues(issue_list)
    issueKey = issues[0]['issue'].key
    print(f'{issueKey} created')
    if filename:
        with open(filename, 'rb') as f:
            jira.add_attachment(issue=issueKey, attachment=f)
    if comment:
        jira.add_comment(issue=issueKey, body=comment)
        print(f'Comment added to {issueKey}')
    return issueKey
Beispiel #10
0
def createjira():
    title = "%s Repository Security" % (searchrepo)
    auth_jira = JIRA('https://jira.$ENTERPRISE.com',
                     basic_auth=(jirausername, jirapassword))
    issue_dict = {
        'project': 'CTM',
        'summary': title,
        'description': 'Please contact MIR for attachment password',
        'issuetype': {
            'name': 'Security'
        },
        'duedate': '2019-09-17'
    }
    new_issue = auth_jira.create_issue(fields=issue_dict)
    print "Created: https://jira.$ENTERPRISE.com/browse/%s" % (new_issue.key)
    f = open("findings.txt", "w+")
    f.writelines(["%s\n" % item for item in resulttext])
    f.close()
    pyminizip.compress("findings.txt", None, "findings.zip", "password1", 0)
    auth_jira.add_attachment(issue=new_issue.key, attachment='findings.zip')
    os.remove("findings.txt")
    os.remove("findings.zip")
    print "Uploaded findings to %s" % (new_issue.key)
    sys.exit()
    try:
        jira.search_issues('project=' +
                           all_issue_dict.get('project').get('key'))
    except:
        print 'Jira project key ', str(
            jkey
        ), 'may not existed, please create the project name and key in Jira before ticket submission.'
        exit()
    check_cid = jira.search_issues('project=' +
                                   all_issue_dict.get('project').get('key') +
                                   ' and labels=CID:' + str(cid))

    if not check_cid:
        print 'Submit Jira ticket for CID ' + str(
            cid) + ' in Jira project with key ' + str(
                all_issue_dict.get('project').get('key'))
        issues = jira.issue(jira.create_issue(fields=all_issue_dict))
        jira.add_attachment(issue=issues, attachment='' + pdf)
        jira.add_attachment(issue=issues, attachment='' + csv)
    else:
        print 'CID ' + str(cid) + ' existed in Jira project with key ' + str(
            all_issue_dict.get('project').get('key'))

    #issues = jira.issue(jira.create_issue(fields=all_issue_dict))
    #print(issues.fields.attachment)

    #print all_issue_dict
    #break
print 'time taken after Jira execution is ', datetime.datetime.now() - begin
Beispiel #12
0
class JiraTicket:
	def __init__(self, attachments):
		jira_options = { 'server': server.com, 'verify': False }	
		try:
			self.jira = JIRA(options=jira_options, basic_auth=(username, password))
			self.jira._session.proxies={'http':'proxy.com', 'https':'proxy.com'}
		except:
			eprint("ERROR: cannot create JIRA object.  Is Jira down?")
			traceback.print_exc()

		self.comments = [] #populated in add_comments(), global needed for email
		self.attachments = attachments
		self.ticket = "" #populated in create_ticket()

	def create_ticket(self):
		date = find_shift()
		summary = date[0] + "phishing indicators and IOC's - " + date[1] 
		description = 	"""
						helpful information
						"""
		jira_payload = {
						'project' : {'key' : team},
						'issuetype' : {'name' : 'Other'},
						'customfield_11802' : 	{ #This is the "Requesting Team"
												'id' : id_num,	#this is needed
												'key' : team,	#this is also needed
												'name' : team_name
												},
						'summary' : summary,
						'description': "Daily phishing indicators and IOC's from " + date[1] + " " + date[0] + description
						}

		try:
			new_ticket = self.jira.create_issue(fields=jira_payload)
			self.ticket = new_ticket.key
			print("created ticket: " + new_ticket.key)			
		except:
			eprint("ERROR: cannot create new Jira ticket")
			traceback.print_exc()

	def add_comments(self, phishing_data):
		for data in phishing_data:
			#this comment_list will be used in the email as well
			#*<text>* is bold in Jira
			comment_list = []
			comment_list.append('*Subject*: ' + data['subject'])
			comment_list.append('*Sender*: ' + data['sender'].replace('@', '(@)')) #unlink emails
			comment_list.append('*Date Received*: ' + data['date received'])
			comment_list.append('*Domain*: ' + data['domain'].replace('http', 'hxxp').replace('[.]', '.').replace('.', '[.]') + '\n') #unlink URL's

			#IOC's are multiline and come as it's own list, must iterate through
			#don't join, it will remove all multiline formatting
			comment_list.append('*Indicators of Compromise*')
			for IOC in data['indicators of compromise']:
				comment_list.append(IOC)

			comment = '\n'.join(comment_list) #This is our comment for Jira
			#this is extra stuff added for the email
			self.comments.append(comment + '<br><br>')
			self.comments.append('-------------------------------------<br><br>')

			try:
				if not DEBUG:
					self.jira.add_comment(self.ticket, comment)
				print('adding comments to ticket')
			except:
				eprint("ERROR: unable to add comments to Jira ticket")
				traceback.print_exc()

	def add_attachment(self):
		for name in self.attachments:
			try:
				with open('temp/' + name, 'rb') as ifile:
					self.jira.add_attachment(self.ticket, ifile)
					print( self.ticket + ': attaching ' + name)
			except:
				eprint("ERROR: unable to add attachment " + name + " to ticket " + self.ticket)
				traceback.print_exc()
Beispiel #13
0
class Ticket(object):
    """Class responisble for creating ticket/issue on specified platform."""
    def __init__(self, **kwargs):
        #Update the allowed_keys with the values passed from __main__.py.
        allowed_keys = {
            'config_info', 'group_name', 'description', 'summary', 'upload',
            'user'
        }
        self.__dict__.update((key, False) for key in allowed_keys)
        self.__dict__.update((key, value) for key, value in kwargs.items()
                             if key in allowed_keys)
        self.priority = 'Top'
        self.real_groups = ('project_1-jira', 'project_2-jira',
                            'project_3-jira', 'mnt-issue_log')

        jira_cloud = 'atlassian.net'
        host_name = str(self.config_info[self.group_name]['host'])

        #Connect to Jira service.
        if jira_cloud in host_name:
            try:
                self.jira = JIRA(
                    self.config_info[self.group_name]['host'],
                    basic_auth=(self.config_info[self.group_name]['user'],
                                self.config_info[self.group_name]['api_key']))
                print("Connecting to JIRA cloud.")
            except Exception as e:
                print(e)
        else:
            try:
                self.jira = JIRA(
                    self.config_info[self.group_name]['host'],
                    auth=(self.config_info[self.group_name]['user'],
                          self.config_info[self.group_name]['passwd']))
                print("Connecting to Jira Server.")
            except Exception as e:
                print(e)

    def create_jira_Ticket(self):
        """Creating jira ticket."""
        print('Creating Ticket')
        #Information from both config and user input for creating ticket in Jira.
        issue_fields = {
            'project': {
                'key': self.config_info[self.group_name]['project']
            },
            'summary':
            self.config_info[self.group_name]['component'] + ' - ' +
            self.summary,
            'description':
            self.description,
            'issuetype': {
                'name': self.config_info[self.group_name]['issue_level']
            },
            'priority': {
                'name': self.config_info[self.group_name]['priority']
            },
        }

        #Creating the actual ticket.
        try:
            jira_issue = self.jira.create_issue(fields=issue_fields)
            print("Jirra issue: {}".format(jira_issue))
        except Exception as e:
            print(e)

        #Check self.upload for file to upload.
        if not self.upload:
            pass
        else:
            self.upload_attachment(jira_issue)

    def upload_attachment(self, jira_issue):
        #If specified, file upload to jira ticket just created.
        try:
            self.jira.add_attachment(issue=jira_issue, attachment=self.upload)
        except Exception as e:
            print(e)
Beispiel #14
0
class JiraAPI(object):
    def __init__(self,
                 hostname=None,
                 username=None,
                 password=None,
                 path="",
                 debug=False,
                 clean_obsolete=True,
                 max_time_window=12,
                 decommission_time_window=3):
        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.excluded_tickets = []
        self.JIRA_REOPEN_ISSUE = "Reopen Issue"
        self.JIRA_CLOSE_ISSUE = "Close Issue"
        self.JIRA_RESOLUTION_OBSOLETE = "Obsolete"
        self.JIRA_RESOLUTION_FIXED = "Fixed"
        self.template_path = 'vulnwhisp/reporting/resources/ticket.tpl'
        self.max_ips_ticket = 30
        self.attachment_filename = "vulnerable_assets.txt"
        self.max_time_tracking = max_time_window  #in months
        if path:
            self.download_tickets(path)
        else:
            self.logger.warn(
                "No local path specified, skipping Jira ticket download.")
        self.max_decommission_time = decommission_time_window  #in months
        # [HIGIENE] close tickets older than 12 months as obsolete (max_time_window defined)
        if clean_obsolete:
            self.close_obsolete_tickets()
        # deletes the tag "server_decommission" from those tickets closed <=3 months ago
        self.decommission_cleanup()

        self.jira_still_vulnerable_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 decommissioned, please add the label "*server_decommission*" to the ticket before closing it.
        - If when checking the vulnerability it looks like a false positive, _+please elaborate in a comment+_ and add the label "*false_positive*" before closing it; we will review it and report it to the vendor.
        
        If you have further doubts, please contact the Security Team.'''

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

        self.logger.info("Creating ticket for project {} title: {}".format(
            project, title[:20]))
        self.logger.debug("project {} has a component requirement: {}".format(
            project, components))
        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))

        if attachment_contents:
            self.add_content_as_attachment(new_issue, attachment_contents)

        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")

        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(" "))

            # we exclude from the vulnerabilities to report those assets that already exist with *risk_accepted*/*server_decommission*
            vuln = self.exclude_accepted_assets(vuln)

            # make sure after exclusion of risk_accepted assets there are still assets
            if vuln['ips']:
                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=ticketid,
                        comment=self.jira_still_vulnerable_comment)
                    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
                attachment_contents = []
                # if assets >30, add as attachment
                # create local text file with assets, attach it to ticket
                if len(vuln['ips']) > self.max_ips_ticket:
                    attachment_contents = vuln['ips']
                    vuln['ips'] = [
                        "Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment."
                        .format(assets=len(attachment_contents))
                    ]
                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']
                                   ],
                                   attachment_contents=attachment_contents)
            else:
                self.logger.info(
                    "Ignoring vulnerability as all assets are already reported in a risk_accepted ticket"
                )

        self.close_fixed_tickets(vulnerabilities)
        # we reinitialize so the next sync redoes the query with their specific variables
        self.all_tickets = []
        self.excluded_tickets = []
        return True

    def exclude_accepted_assets(self, vuln):
        # we want to check JIRA tickets with risk_accepted/server_decommission or false_positive labels sharing the same source
        # will exclude tickets older than 12 months, old tickets will get closed for higiene and recreated if still vulnerable
        labels = [
            vuln['source'], vuln['scan_name'], 'vulnerability_management',
            'vulnerability'
        ]

        if not self.excluded_tickets:
            jql = "{} AND labels in (risk_accepted,server_decommission, false_positive) AND NOT labels=advisory AND created >=startOfMonth(-{})".format(
                " AND ".join(["labels={}".format(label) for label in labels]),
                self.max_time_tracking)
            self.excluded_tickets = self.jira.search_issues(jql, maxResults=0)

        title = vuln['title']
        #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 vulnerability to risk_accepted tickets")
        assets_to_exclude = []
        tickets_excluded_assets = []
        for index in range(len(self.excluded_tickets)):
            checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(
                self.excluded_tickets[index])
            if title.encode('ascii') == checking_title.encode('ascii'):
                if checking_assets:
                    #checking_assets is a list, we add to our full list for later delete all assets
                    assets_to_exclude += checking_assets
                    tickets_excluded_assets.append(checking_ticketid)

        if assets_to_exclude:
            assets_to_remove = []
            self.logger.warn(
                "Vulnerable Assets seen on an already existing risk_accepted Jira ticket: {}"
                .format(', '.join(tickets_excluded_assets)))
            self.logger.debug("Original assets: {}".format(vuln['ips']))
            #assets in vulnerability have the structure "ip - hostname - port", so we need to match by partial
            for exclusion in assets_to_exclude:
                # for efficiency, we walk the backwards the array of ips from the scanners, as we will be popping out the matches
                # and we don't want it to affect the rest of the processing (otherwise, it would miss the asset right after the removed one)
                for index in range(len(vuln['ips']))[::-1]:
                    if exclusion == vuln['ips'][index].split(" - ")[0]:
                        self.logger.debug(
                            "Deleting asset {} from vulnerability {}, seen in risk_accepted."
                            .format(vuln['ips'][index], title))
                        vuln['ips'].pop(index)
            self.logger.debug("Modified assets: {}".format(vuln['ips']))

        return vuln

    def check_vuln_already_exists(self, vuln):
        '''
        This function compares a vulnerability with a collection of tickets.
        Returns [exists (bool), is equal (bool), ticketid (str), assets (array)]
        '''
        # 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)):
            checking_ticketid, checking_title, checking_assets = self.ticket_get_unique_fields(
                self.all_tickets[index])
            # added "not risk_accepted", as if it is risk_accepted, we will create a new ticket excluding the accepted assets
            if title.encode('ascii') == checking_title.encode(
                    'ascii') and not self.is_risk_accepted(
                        self.jira.issue(checking_ticketid)):
                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")
        assets = []
        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 Exception as e:
            self.logger.error(
                "Ticket IPs regex failed. Ticket ID: {}. Reason: {}".format(
                    ticketid, e))
            assets = []

        try:
            if not assets:
                #check if attachment, if so, get assets from attachment
                affected_assets_section = self.check_ips_attachment(ticket)
                if affected_assets_section:
                    assets = list(
                        set(
                            re.findall(
                                r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b",
                                affected_assets_section)))
        except Exception as e:
            self.logger.error(
                "Ticket IPs Attachment regex failed. Ticket ID: {}. Reason: {}"
                .format(ticketid, e))

        return ticketid, title, assets

    def check_ips_attachment(self, ticket):
        affected_assets_section = []
        try:
            fields = self.jira.issue(ticket.key).raw.get('fields', {})
            attachments = fields.get('attachment', {})
            affected_assets_section = ""
            #we will make sure we get the latest version of the file
            latest = ''
            attachment_id = ''
            if attachments:
                for item in attachments:
                    if item.get('filename') == self.attachment_filename:
                        if not latest:
                            latest = item.get('created')
                            attachment_id = item.get('id')
                        else:
                            if latest < item.get('created'):
                                latest = item.get('created')
                                attachment_id = item.get('id')
            affected_assets_section = self.jira.attachment(attachment_id).get()

        except Exception as e:
            self.logger.error(
                "Failed to get assets from ticket attachment. Ticket ID: {}. Reason: {}"
                .format(ticket, e))

        return affected_assets_section

    def clean_old_attachments(self, ticket):
        fields = ticket.raw.get('fields')
        attachments = fields.get('attachment')
        if attachments:
            for item in attachments:
                if item.get('filename') == self.attachment_filename:
                    self.jira.delete_attachment(item.get('id'))

    def add_content_as_attachment(self, issue, contents):
        try:
            #Create the file locally with the data
            attachment_file = open(self.attachment_filename, "w")
            attachment_file.write("\n".join(contents))
            attachment_file.close()
            #Push the created file to the ticket
            attachment_file = open(self.attachment_filename, "rb")
            self.jira.add_attachment(issue, attachment_file,
                                     self.attachment_filename)
            attachment_file.close()
            #remove the temp file
            os.remove(self.attachment_filename)
            self.logger.info("Added attachment successfully.")
        except:
            self.logger.error("Error while attaching file to ticket.")
            return False

        return True

    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))

        #for now, if a vulnerability has been accepted ('accepted_risk'), ticket is completely ignored and not updated (no new assets)

        #TODO when vulnerability accepted, create a new ticket with only the non-accepted vulnerable assets
        #this would require go through the downloaded tickets, check duplicates/accepted ones, and if so,
        #check on their assets to exclude them from the new ticket
        risk_accepted = False
        ticket_obj = self.jira.issue(ticketid)
        if self.is_ticket_resolved(ticket_obj):
            if self.is_risk_accepted(ticket_obj):
                return 0
            self.reopen_ticket(ticketid=ticketid,
                               comment=self.jira_still_vulnerable_comment)

        #First will do the comparison of assets
        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 = ''
        added = ''
        removed = ''
        #put a comment with the assets that have been added/removed
        for asset in difference:
            if asset in assets:
                if not added:
                    added = '\nThe following assets *have been newly detected*:\n'
                added += '* {}\n'.format(asset)
            elif asset in ticket_assets:
                if not removed:
                    removed = '\nThe following assets *have been resolved*:\n'
                removed += '* {}\n'.format(asset)

        comment = added + removed

        #then will check if assets are too many that need to be added as an attachment
        attachment_contents = []
        if len(vuln['ips']) > self.max_ips_ticket:
            attachment_contents = vuln['ips']
            vuln['ips'] = [
                "Affected hosts ({assets}) exceed Jira's allowed character limit, added as an attachment."
                .format(assets=len(attachment_contents))
            ]

        #fill the ticket description template
        try:
            tpl = template(self.template_path, vuln)
        except Exception as e:
            self.logger.error('Exception updating assets: {}'.format(str(e)))
            return 0

        #proceed checking if it requires adding as an attachment
        try:
            #update attachment with hosts and delete the old versions
            if attachment_contents:
                self.clean_old_attachments(ticket_obj)
                self.add_content_as_attachment(ticket_obj, attachment_contents)

            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 Exception as e:
            self.logger.error(
                "Error while trying up update ticket {ticketid}.\nReason: {e}".
                format(ticketid=ticketid, e=e))
        return 0

    def add_label(self, ticketid, label):
        ticket_obj = self.jira.issue(ticketid)

        if label not in [x.encode('utf8') for x 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 remove_label(self, ticketid, label):
        ticket_obj = self.jira.issue(ticketid)

        if label in [x.encode('utf8') for x in ticket_obj.fields.labels]:
            ticket_obj.fields.labels.remove(label)

            try:
                ticket_obj.update(fields={"labels": ticket_obj.fields.labels})
                self.logger.info(
                    "Removed label {label} from ticket {ticket}".format(
                        label=label, ticket=ticketid))
            except:
                self.logger.error(
                    "Error while trying to remove label {label} to ticket {ticket}"
                    .format(label=label, ticket=ticketid))
        else:
            self.logger.error(
                "Error: label {label} not in 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.
        Higiene clean up affects to all tickets created by the module, filters by label 'vulnerability_management'
        '''
        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_decommission" in labels:
                    self.logger.warn(
                        "Ticket {} server decommissioned, will be ignored".
                        format(ticket_obj))
                    return True
                elif "false_positive" in labels:
                    self.logger.warn(
                        "Ticket {} flagged false positive, 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, ignore_labels=False, comment=""):
        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) or ignore_labels):
                try:
                    if self.is_ticket_reopenable(ticket_obj):
                        error = self.jira.transition_issue(
                            issue=ticketid,
                            transition=self.JIRA_REOPEN_ISSUE,
                            comment=comment)
                        self.logger.info(
                            "Ticket {} reopened successfully".format(ticketid))
                        if not ignore_labels:
                            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 {} months old.
        If the vulnerability still exists, a new ticket will be opened.'''.format(
            self.max_time_tracking)

        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

    def decommission_cleanup(self):
        '''
        deletes the server_decomission tag from those tickets that have been 
        closed already for more than x months (default is 3 months) in order to clean solved issues
        for statistics purposes
        '''
        self.logger.info(
            "Deleting 'server_decommission' tag from tickets closed more than {} months ago"
            .format(self.max_decommission_time))

        jql = "labels=vulnerability_management AND labels=server_decommission and resolutiondate <=startOfMonth(-{})".format(
            self.max_decommission_time)
        decommissioned_tickets = self.jira.search_issues(jql, maxResults=0)

        comment = '''This ticket is having deleted the *server_decommission* tag, as it is more than {} months old and is expected to already have been decommissioned.
        If that is not the case and the vulnerability still exists, the vulnerability will be opened again.'''.format(
            self.max_decommission_time)

        for ticket in decommissioned_tickets:
            #we open first the ticket, as we want to make sure the process is not blocked due to
            #an unexisting jira workflow or unallowed edit from closed tickets
            self.reopen_ticket(ticketid=ticket, ignore_labels=True)
            self.remove_label(ticket, 'server_decommission')
            self.close_ticket(ticket, self.JIRA_RESOLUTION_FIXED, comment)

        return 0
Beispiel #15
0
def call_jira(request):
    try:
        # JIRA が import されていない場合は何もしない
        if imported_jira is None:
            rsp = {}
            return JsonResponse(rsp)
        # feed情報取得
        feed_file_name_id = request.GET['feed_id']
        package_id_arg = request.GET['package_id']
        feed = Feed.get_feeds_from_package_id(request.user, package_id_arg)

        # JIRA instance
        proxies = System.get_request_proxies()
        j = JIRA(server=SNSConfig.get_jira_host(),
                 proxies=proxies,
                 basic_auth=(SNSConfig.get_jira_username(),
                             SNSConfig.get_jira_password()))
        # issues作成
        issue = j.create_issue(project=SNSConfig.get_jira_project(),
                               summary=feed.title,
                               description=feed.post,
                               issuetype={'name': SNSConfig.get_jira_type()})
        # 添付があればそれもつける
        for attach_file in feed.files.all():
            file_path = Feed.get_attach_file_path(attach_file.package_id)
            j.add_attachment(issue=issue,
                             attachment=file_path,
                             filename=str(attach_file.file_name))

        # STIX添付
        stix_package = STIXPackage.from_xml(feed.stix_file_path)
        package_id = stix_package.id_
        stix_file_name = '%s.xml' % (package_id)
        j.add_attachment(issue=issue,
                         attachment=feed.stix_file_path,
                         filename=stix_file_name)

        # CSV添付
        # CSVの中身を取得する
        content = get_csv_content(feed_file_name_id)
        csv_attachment = io.StringIO()
        csv_attachment.write(content)
        csv_file_name = '%s.csv' % (package_id)
        j.add_attachment(issue=issue,
                         attachment=csv_attachment,
                         filename=csv_file_name)

        # PDF添付
        feed_pdf = FeedPDF(feed, stix_package)
        pdf_attachment = io.BytesIO()
        feed_pdf.make_pdf_content(pdf_attachment, feed)
        pdf_file_name = '%s.pdf' % (package_id)
        j.add_attachment(issue=issue,
                         attachment=pdf_attachment,
                         filename=pdf_file_name)

        # isssue番号返却
        url = SNSConfig.get_jira_host(
        ) + '/projects/' + SNSConfig.get_jira_project() + '/issues/' + str(
            issue)
        rsp = {
            'issues': str(issue),
            'url': url,
        }
        return JsonResponse(rsp)
    except Exception as e:
        traceback.print_exc()
        return HttpResponseServerError(str(e))
Beispiel #16
0
class JiraWrapper:
    JIRA_REQUEST = 'project={} AND labels in ({})'

    def __init__(self,
                 args,
                 url,
                 user,
                 password,
                 jira_project,
                 assignee,
                 check_functional_errors,
                 check_performance_degradation,
                 check_missed_thresholds,
                 performance_degradation_rate,
                 missed_thresholds_rate,
                 issue_type='Bug',
                 labels=None,
                 watchers=None,
                 jira_epic_key=None):
        self.valid = True
        self.args = args
        self.url = url
        self.password = password
        self.user = user
        self.check_functional_errors = check_functional_errors
        self.check_performance_degradation = check_performance_degradation
        self.check_missed_thresholds = check_missed_thresholds
        self.performance_degradation_rate = performance_degradation_rate
        self.missed_thresholds_rate = missed_thresholds_rate
        try:
            self.connect()
        except Exception:
            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 create_issue(self,
                     title,
                     priority,
                     description,
                     issue_hash,
                     attachments=None,
                     get_or_create=True,
                     additional_labels=None):
        _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': 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):
        issue = self.client.create_issue(fields=issue_data)
        print("Issue " + issue.key + " created." + " Description - " +
              issue_data['summary'])
        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

    @staticmethod
    def create_functional_error_description(error, arguments):
        title = "Functional error in test: " + str(arguments['simulation']) + ". Request \"" \
                + str(error['Request name']) + "\"."
        description = "{panel:title=" + title + \
                      "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n"
        description += "h3. Request description\n"
        if error['Request name']:
            description += "*Request name*: " + error['Request name'] + "\n"
        if error['Method']:
            description += "*HTTP Method*: " + error['Method'] + "\n"
        if error['Request URL']:
            description += "*Request URL*: " + error['Request URL'] + "\n"
        if error['Request_params'] and str(error['Request_params']) != " ":
            description += "*Request params*: " + str(
                error['Request_params'])[2:-2].replace(" ", "\n") + "\n"
        if error['Request headers']:
            description += "*Request headers*: {code}"
            headers = str(error['Request headers']).replace(": ", ":")
            for header in headers.split(" "):
                description += header + "\n"
            description += "{code}\n"
        description += "---- \n h3. Error description\n"
        if error['Error count']:
            description += "*Error count*: " + str(
                error['Error count']) + ";\n"
        if error['Response code']:
            description += "*Response code*: " + error['Response code'] + ";\n"
        if error['Error_message']:
            description += "*Error message*: {color:red}" + str(
                error['Error_message']) + "{color}\n"
        if error['Response']:
            if len(str(error['Response'])) < 55000:
                description += "---- \n h3. Response body {code:html}" + str(
                    error['Response']) + "{code}"
            else:
                description += "---- \n See response body attached.\n"
        description += "{panel}"
        return description

    @staticmethod
    def get_functional_error_hash_code(error, arguments):
        error_str = arguments['simulation'] + "_" + error['Request URL'] + "_" + str(error['Error_message']) + "_" \
                    + error['Request name']
        return hashlib.sha256(error_str.strip().encode('utf-8')).hexdigest()

    @staticmethod
    def create_performance_degradation_description(
            performance_degradation_rate, compare_with_baseline, arguments):
        title = "Performance degradation in test: " + str(
            arguments['simulation'])
        description = "{panel:title=" + title + \
                      "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n"
        description += "{color:red}" + "Test performance degradation is {}% compared to the baseline."\
            .format(performance_degradation_rate) + "{color} \n"
        description += "h3. The following requests are slower than baseline:\n"
        for request in compare_with_baseline:
            description += "\"{}\" reached {} ms by {}. Baseline {} ms.\n".format(
                request['request_name'], request['response_time'],
                arguments['comparison_metric'], request['baseline'])
        description += "{panel}"
        return description

    @staticmethod
    def create_missed_thresholds_description(missed_threshold_rate,
                                             compare_with_thresholds,
                                             arguments):
        title = "Missed thresholds in test: " + str(arguments['simulation'])
        description = "{panel:title=" + title + \
                      "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n"
        description += "{color:red}" + "Percentage of requests exceeding the threshold was {}%." \
            .format(missed_threshold_rate) + "{color} \n"
        for color in ['yellow', 'red']:
            colored = False
            for th in compare_with_thresholds:
                if th['threshold'] == color:
                    if not colored:
                        description += f"h3. The following {color} thresholds were exceeded:\n"
                        colored = True
                    appendage = calculate_appendage(th['target'])
                    description += f"\"{th['request_name']}\" {th['target']}{appendage} " \
                                   f"with value {th['metric']}{appendage} " \
                                   f"exceeded threshold of {th[color]}{appendage}\n"
        description += "{panel}"
        return description

    def report_errors(self, aggregated_errors):
        for error in aggregated_errors:
            issue_hash = self.get_functional_error_hash_code(
                aggregated_errors[error], self.args)
            title = "Functional error in test: " + str(self.args['simulation']) + ". Request \"" \
                    + str(aggregated_errors[error]['Request name']) + "\" failed with error message: " \
                    + str(aggregated_errors[error]['Error_message'])[0:100]
            description = self.create_functional_error_description(
                aggregated_errors[error], self.args)
            if len(str(aggregated_errors[error]['Response'])) < 55000:
                self.create_issue(title, 'Major', description, issue_hash)
            else:
                content = io.StringIO()
                content.write(str(aggregated_errors[error]['Response']))
                attachment = {
                    "binary_content": content,
                    "message": "response_body.txt"
                }
                self.create_issue(title, 'Major', description, issue_hash,
                                  [attachment])

    def report_performance_degradation(self, performance_degradation_rate,
                                       compare_with_baseline):
        issue_hash = hashlib.sha256("{} performance degradation".format(
            self.args['simulation']).strip().encode('utf-8')).hexdigest()
        title = "Performance degradation in test: " + str(
            self.args['simulation'])
        description = self.create_performance_degradation_description(
            performance_degradation_rate, compare_with_baseline, self.args)
        self.create_issue(title, 'Major', description, issue_hash)

    def report_missed_thresholds(self, missed_threshold_rate,
                                 compare_with_thresholds):
        issue_hash = hashlib.sha256("{} missed thresholds".format(
            self.args['simulation']).strip().encode('utf-8')).hexdigest()
        title = "Missed thresholds in test: " + str(self.args['simulation'])
        description = self.create_missed_thresholds_description(
            missed_threshold_rate, compare_with_thresholds, self.args)
        self.create_issue(title, 'Major', description, issue_hash)
Beispiel #17
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))
Beispiel #18
0
class JiraToolsAPI:
    def __init__(self, jira_server_link, username=None, password=None):
        """Initalizes the Jira API connector. If a username or password is not provided you will be prompted for it.

        args:
            jira_server_link (str): Link to the Jira server to touch API

        kwargs:
            username (str): Overwrites jira username prompt
            password (str): Overwrites jira password prompt

        return: None
        """
        self.jira_server_link = jira_server_link
        self.jira_options = {"server": self.jira_server_link}

        if username == None:
            username = input("Username: "******"Authenticated successfully with Jira with {self.username}")

    def create(self, data):
        """Create a single Jira ticket.

        args:
            data (dict): Fields required or needed to create the ticket.

        return (str): Ticket number / 'False' if fails
        """
        try:
            jira_ticket = self._JIRA.create_issue(fields=data)
            logging.info(
                f"Successfully created Jira issue '{jira_ticket.key}'")
            return jira_ticket.key

        except Exception as error:
            logging.debug(
                f"Failed to create Jira issue '{jira_ticket.key}'\n\n{error}\n\n"
            )
            return False

    def link(self, issue_from, issue_to, issue_link_name=None):
        """Link two issues together. Defaults to 'Relates' unless issue_link_name is specified.

        args:
            issue_from (str): Issue that will be linked from.
            issue_to (str): Issue that will be linked to.


        kwargs:
            issue_link_name (str): issue link name that should be applied.

        return (bool): Will return 'True' if it completed successfully.
        """
        try:
            self._JIRA.create_issue_link(issue_link_name, issue_from, issue_to)
            logging.info(
                f"Successfully created a '{issue_link_name}' link between '{issue_from}' and '{issue_to}'."
            )
            return True

        except Exception as error:
            logging.debug(
                f"Failed to create a link between '{issue_from}' and '{issue_to}'\n\n{error}\n\n"
            )
            return False

    def label(self, issue, labels):
        """Apply labels to a given issue.

        args:
            issue (str): Issue that labels will be applied to.
            labels (list): list of labels that should be applied to the issue.

        Return (bool): Will return 'True' if it completed successfully.
        """
        if type(labels) == list:
            try:
                issue_instance = self._JIRA.issue(issue)
                issue_instance.update(
                    fields={"labels": issue_instance.fields.labels + labels})
                logging.info(
                    f"Successfully added labels '{labels}' to '{issue}'")
                return True

            except Exception as error:
                logging.debug(
                    f"Failed to add labels '{labels}' to '{issue}'\n\n{error}\n\n"
                )
                return False

        else:
            raise ScriptError('A list must be passed to the labels argument')

    def comment(self, issue, comment):
        """Apply a comment to a given issue.

         args:
             issue (str): Issue that comment will be applied to.
             comment (str): comment that should be applied to the issue.

         return (bool): Will return 'True' if it completed successfully.
         """
        try:
            self._JIRA.add_comment(issue, comment)
            logging.info(
                f"Successfully added comment '{comment}' to '{issue}'")
            return True
        except Exception as error:
            logging.debug(
                f"Failed to add comment '{comment}' to '{issue}'\n\n{error}\n\n"
            )
            return False

    def log_work(self, issue, time_spent, comment=None):
        """Log work to a given issue.

        args:
            issue (str): Issue to log work.
            time_spent (str): Time that should be logged to the issue.

        kwargs:
            comment (str): Description of what this time represents.

        return (bool): Will return 'True' if it completed successfully.
        """
        try:
            if comment != None and type(comment) == str:
                self._JIRA.add_worklog(issue, time_spent, comment=comment)
            else:
                self._JIRA.add_worklog(issue, time_spent)
            logging.info(f"Successfully logged time to '{issue}'")
            return True

        except Exception as error:
            logging.info(
                f"Failed to log work to '{issue}' See debug logs for more.")
            logging.debug(f"\n{error}\n")
            return False

    def add_attachment(self, issue, attachment):
        """Attach file to Jira issue.

        args:
            issue (str): Issue name
            attachment (str): Location of file that should be attached.

        Return (bool): Will return 'True' if completed successfully
        """
        assert isinstance(issue, str)
        assert isinstance(attachment, str)

        try:
            self._JIRA.add_attachment(issue=issue, attachment=attachment)
            logging.info(f'Successfully attached document to "{issue}"')
            return True

        except Exception as error:
            logging.debug(
                f"Failed to attach document to '{issue}'\n\n{error}\n\n")
            return False

    def update_status(self,
                      id,
                      end_status,
                      transfer_statuses=[],
                      timeout_attempts=10):
        """Change issue to desired status.

        Due to the workflow features of Jira it might not be possible to transition
        directly to the wanted status, intermediary statuses might be required and
        this funcation allows for that using 'transfer_statuses'.

        args:
            id (str): Issue id for status update
            end_status (str): Name of status to update ticket to.

        kwargs:
            transfer_statuses (list): Ordered list of intermediary statuses
            timeout_attempts (num): Number of times before while loop times out.

        return (bool): Will return 'True' if completed successfully
        """
        while timeout_attempts != 0:
            transitions = self._JIRA.transitions(id)
            for transition in transitions:
                if transition['name'] == end_status:
                    jira_ticket = self._JIRA.transition_issue(
                        id, transition['id'])
                    logging.info(
                        f"Updated status of '{issue}' to '{end_status}'")
                    return True
                elif transition['name'] in transfer_statuses:
                    jira_ticket = self._JIRA.transition_issue(
                        id, transition['id'])
            timeout_attempts -= 1
        logging.debug(
            f"Failed to update status of '{id}' to end_status ({end_status})")
        return False

# 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"]))
Beispiel #20
0
class JiraTaskTool:
    def __init__(self, user, password):
        self.server = 'http://jiratask.hisense.com/'
        self.basic_auth = (user, password)
        self.jira_client = None

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

    def create_issue(self,
                     key,
                     issue_type,
                     summary,
                     assignee,
                     reporter,
                     info_dict=None):
        '''

        :param key:
        :param issue_type:
        :param assignee:
        :param info_dict:
        :return:
        '''

        if info_dict is None:
            info_dict = {}
        issue_dict = {
            'project': {
                'key': key
            },
            'issuetype': {
                'name': issue_type
            },
            'summary': summary,
            'assignee': {
                'name': assignee
            },
            'reporter': {
                'name': reporter
            },
        }
        issue_dict.update(info_dict)

        if self.jira_client == None:
            self.login()
        return self.jira_client.create_issue(issue_dict)

    def create_sjzlgl_issue(self,
                            summary,
                            product_model,
                            description,
                            priority,
                            source,
                            duedate,
                            probability,
                            assignee,
                            reporter,
                            key='SJZLGL',
                            issue_type='SW Bug',
                            product_line='内销',
                            develop_class='自研'):

        # SJZLGL库需要的字段
        info_dict = {
            'customfield_10002': product_model,
            'description': description,
            'priority': {
                'name': priority
            },
            'customfield_19200': {
                'value': source
            },
            'duedate': duedate,
            'customfield_10109': {
                'value': product_line
            },  # 产品线:内销
            'customfield_10110': {
                'value': develop_class
            },  # 开发级别:自研
            'customfield_10005': {
                'value': probability
            },  # 概率是什么
        }
        if self.jira_client == None:
            self.login()
        return self.create_issue(key, issue_type, summary, assignee, reporter,
                                 info_dict)

    def get_all_prj(self):
        '''
        获取所有的项目
        :return:
        '''
        return self.jira_client.projects()

    def get_one_prj(self, prj_key):
        '''
        根据key获取此项目的所有信息
        :return:
        '''
        return self.jira_client.project(prj_key)

    def get_issue(self, issue_key):
        '''
        根据issue_key获取issue信息
        :param issue_key:
        :return:
        '''
        return self.jira_client.issue(issue_key)

    def get_issue_subtasks(self, issue_key):
        '''
        根据issue_key获取子TASK信息
        :param issue_key:
        :return:
        '''
        return self.get_issue(issue_key).fields.subtasks

    def get_issue_subtasks_case_nums(self, issue_key):
        '''
        根据issue_key获取子TASK信息
        :param issue_key:
        :return:
        '''
        sub_tasks = self.get_issue(issue_key).fields.subtasks
        for i in sub_tasks:
            pass

    def get_issue_resolutiondate(self, issue_key):
        '''
        根据issue_key获取resolution更新的日期,此日期为 closed的日期,也就是解决日期
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['resolutiondate'][0:10]
        return self.get_issue(issue_key).fields.resolutiondate[0:10]

    def get_issue_result_xinxianglian(self, issue_key):
        '''
        获取问题编号的 处理结果; 无法分析为63600 ;新增问题为63602 ;
        :param issue_key:
        :return:
        '''
        # if self.get_issue(issue_key).raw['fields']['customfield_18500'] == None:
        #     return self.get_issue(issue_key).raw['fields']['customfield_18500']
        # else:
        #     return self.get_issue(issue_key).raw['fields']['customfield_18500']['value']

        if self.get_issue(issue_key).fields.customfield_18500 == None:
            return self.get_issue(issue_key).fields.customfield_18500
        else:
            return self.get_issue(issue_key).fields.customfield_18500.value

    def get_issue_result(self, issue_key):
        '''
        获取问题编号的 处理结果; 无法分析为63600 ;新增问题为63602 ;
        :param issue_key:
        :return:
        '''
        # if self.get_issue(issue_key).raw['fields']['customfield_18500'] == None:
        #     return self.get_issue(issue_key).raw['fields']['customfield_18500']
        # else:
        #     return self.get_issue(issue_key).raw['fields']['customfield_18500']['value']

        if self.get_issue(issue_key).fields.resolution == None:
            return self.get_issue(issue_key).fields.resolution
        else:
            return self.get_issue(issue_key).fields.resolution.name

    def get_issue_source(self, issue_key):
        '''
        获取问题编号的 问题来源; 信相连63406; 网络信息
        :param issue_key:
        :return:
        '''
        # if
        # return self.get_issue(issue_key).raw['fields']['customfield_17300']['value']
        # if self.get_issue(issue_key).raw['fields']['customfield_17300'] == None:
        #     return self.get_issue(issue_key).raw['fields']['customfield_17300']
        # else:
        #     return self.get_issue(issue_key).raw['fields']['customfield_17300']['value']
        if self.get_issue(issue_key).fields.customfield_17300 == None:
            return self.get_issue(issue_key).fields.customfield_17300
        else:
            return self.get_issue(issue_key).fields.customfield_17300.value

    def get_issue_type(self, issue_key):
        '''
        获取问题编号的 类型
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['issuetype']['name']
        return self.get_issue(issue_key).fields.issuetype.name

    def get_issue_key(self, issue_id):
        '''
        由ID 获取其对应的key
        :param issue_id:
        :return:
        '''
        return self.get_issue(issue_id).key

    def get_issue_project(self, issue_key):
        '''
        获取问题编号对应的 库
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['project']['key']
        return self.get_issue(issue_key).fields.project.key

    def get_issue_description(self, issue_key):
        '''
        获取问题常规描述
        :param issue_key:
        :return:
        '''
        return self.get_issue(issue_key).fields.description

    def get_issue_feedback(self, issue_key):
        '''
        获取问题编码对应的 问题描述
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['customfield_10815']
        return self.get_issue(issue_key).fields.customfield_10815

    def get_issue_test_category(self, issue_key):
        '''
        获取问题编码对应的 测试类别,如验收Acceptance
        :param issue_key:
        :return:
        '''
        if self.get_issue(issue_key).fields.customfield_10014 == None:
            return self.get_issue(issue_key).fields.customfield_10014
        else:
            return self.get_issue(issue_key).fields.customfield_10014.value

    def get_issue_versions(self, issue_key):
        '''
        获取影响版本
        :return:
        '''
        return self.get_issue(issue_key).fields.versions[0].name

    def get_issue_summary(self, issue_key):
        '''
        获取问题编码对应的 主题
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['summary']
        return self.get_issue(issue_key).fields.summary

    def get_issue_priority(self, issue_key):
        '''
        获取问题编码对应的 优先级
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['priority']['name']
        return self.get_issue(issue_key).fields.priority.name

    def get_issue_status(self, issue_key):
        '''
        获取问题编码对应的 问题状态
        :param issue_key:
        :return:
        '''
        # return self.get_issue(issue_key).raw['fields']['priority']['name']
        return self.get_issue(issue_key).fields.status.name

    def get_issue_probability(self, issue_key):
        if self.get_issue(issue_key).fields.customfield_10005 == None:
            return self.get_issue(issue_key).fields.customfield_10005
        else:
            return self.get_issue(issue_key).fields.customfield_10005.value

    def get_issues_closed_yesterday(self,
                                    issue_type=("Market Problem",
                                                "Market Problem")):
        '''
        获取昨天close的 market problem 问题;  且问题处理类型必须为 无法分析 或是 新增问题
        :param issue_type: 问题类型
        :return: issues 的列表
        '''
        today = CommonUtils.get_date_others(0)
        yesterday = CommonUtils.get_date_others(-1)  # -1是昨天;-2是前天
        # resolved 字段对应 resolutiondate 字段
        jql = 'issuetype in {} and (resolved >{} and resolved < {})'.format(
            issue_type, yesterday, today)  # [昨天,今天)
        issues = self.jira_client.search_issues(jql)
        issues_list = []  # 列表元素为字典, 每个字典是检索到的issue的具体信息,用于以后操作
        issues_list_use = []
        for item in issues:
            issue_key = item.key
            issues_list.append({'key': issue_key})
            i = len(issues_list) - 1  # 获取当前列表的长度
            issues_list[i]['project'] = self.get_issue_project(issue_key)
            issues_list[i]['summary'] = self.get_issue_summary(issue_key)
            issues_list[i]['feedback'] = self.get_issue_feedback(issue_key)
            issues_list[i]['type'] = self.get_issue_type(issue_key)
            issues_list[i]['priority'] = self.get_issue_priority(issue_key)
            issues_list[i]['status'] = self.get_issue_status(issue_key)
            issues_list[i]['result'] = self.get_issue_result_xinxianglian(
                issue_key)
            issues_list[i]['source'] = self.get_issue_source(issue_key)
            issues_list[i]['resolutiondate'] = self.get_issue_resolutiondate(
                issue_key)
            issues_list[i]['probability'] = self.get_issue_probability(
                issue_key)

        #  遍历列表,筛选出 新增问题 与 无法分析 的问题
        for item in issues_list:
            if ((item['result'] == '新增问题' or item['result'] == '无法分析')
                    and (item['source'] == '信相连' or item['source'] == '网络信息')):
                issues_list_use.append(item)

        return issues_list_use

    def get_issue_preuser2sw_yesterday(self):
        zero_yesterday = CommonUtils.get_date_time_others_zero(-1)
        last_yesterday = CommonUtils.get_date_time_others_last(-1)

        sql_preuser2sw = 'select issueid from changegroup where ID in (select groupid from changeitem where \
                         OLDSTRING="PreUser Bug" and NEWSTRING="SW Bug") and CREATED between %s and %s'

        issues = MysqlUtils.find_data_dic_2args(sql_preuser2sw, zero_yesterday,
                                                last_yesterday)
        issues_list = []  # 列表元素为字典, 每个字典是检索到的issue的具体信息,用于以后操作
        issues_list_use = []

        if issues:
            for item in issues:
                issue_key = str(item['issueid'])
                issues_list.append({'id': issue_key})
                i = len(issues_list) - 1  # 获取当前列表的长度
                issues_list[i]['project'] = self.get_issue_project(issue_key)
                issues_list[i]['summary'] = self.get_issue_summary(issue_key)
                issues_list[i]['description'] = self.get_issue_description(
                    issue_key)
                issues_list[i]['type'] = self.get_issue_type(issue_key)
                issues_list[i]['priority'] = self.get_issue_priority(issue_key)
                issues_list[i]['status'] = self.get_issue_status(issue_key)
                issues_list[i]['source'] = self.get_issue_source(issue_key)
                issues_list[i]['probability'] = self.get_issue_probability(
                    issue_key)
                issues_list[i]['versions'] = self.get_issue_versions(issue_key)
                issues_list[i]['key'] = self.get_issue_key(issue_key)

            #  遍历列表,筛选出 版本号为6开头的
            for item in issues_list:
                if (ReUtils.bool_version_6(item['versions'])):
                    issues_list_use.append(item)
        else:
            issues_list_use = []

        return issues_list_use

    def get_issue_yanshou_yesterday(self):
        '''
        获取昨天 测试类别为 验收;由 open 变为 resolved ; 且解决结果为 fiexed 与 Pendings的问题
        :return:
        '''
        zero_yesterday = CommonUtils.get_date_time_others_zero(-1)
        last_yesterday = CommonUtils.get_date_time_others_last(-1)

        sql_preuser2sw = 'select issueid from changegroup where ID in (select groupid from changeitem where \
                         OLDSTRING="Open" and NEWSTRING="Resolved") and CREATED between %s and %s'

        issues = MysqlUtils.find_data_dic_2args(sql_preuser2sw, zero_yesterday,
                                                last_yesterday)
        issues_list = []  # 列表元素为字典, 每个字典是检索到的issue的具体信息,用于以后操作
        issues_list_use = []

        if issues:
            for item in issues:
                try:
                    issue_key = str(item['issueid'])
                    issues_list.append({'id': issue_key})
                    i = len(issues_list) - 1  # 获取当前列表的长度
                    issues_list[i]['project'] = self.get_issue_project(
                        issue_key)
                    issues_list[i]['summary'] = self.get_issue_summary(
                        issue_key)
                    issues_list[i]['description'] = self.get_issue_description(
                        issue_key)
                    issues_list[i]['type'] = self.get_issue_type(issue_key)
                    issues_list[i]['priority'] = self.get_issue_priority(
                        issue_key)
                    issues_list[i]['status'] = self.get_issue_status(issue_key)
                    issues_list[i]['probability'] = self.get_issue_probability(
                        issue_key)
                    issues_list[i][
                        'test_category'] = self.get_issue_test_category(
                            issue_key)
                    issues_list[i]['key'] = self.get_issue_key(issue_key)
                    issues_list[i]['result'] = self.get_issue_result(issue_key)
                except:
                    # 有异常时不去考虑,继续下一个
                    continue

            #  遍历列表,筛选出 版本号为6开头的
            for item in issues_list:
                if ('test_category' in item):
                    if (item['test_category'] == "验收Acceptance"
                            or item['test_category']
                            == "市场反馈") and (item['result'] == "Fixed"
                                            or item['result'] == "Pending"):
                        issues_list_use.append(item)
        else:
            issues_list_use = []

        return issues_list_use

    def change_summary(self, key, summary):
        '''
        修改主题
        :param key:
        :param summary:
        :return:
        '''
        issue = self.jira_client.issue(key)
        issue.update(summary=summary)

    def change_des(self, key, des):
        '''
        修改描述
        :param key:
        :param des:
        :return:
        '''
        issue = self.jira_client.issue(key)
        issue.update(description=des)

    def change_reporter(self, key, reporter):
        '''
        :param self: 修改报告人
        :param key:关键字
        :param reporter: 指定新的报告人
        :return:
        '''
        issue = self.jira_client.issue(key)
        issue.update(reporter={'key': reporter})

    def change_assignee(self, key, assignee):
        '''
        修改经办人
        :param key:
        :param assignee:
        :return:
        '''
        issue = self.jira_client.issue(key)
        issue.update(assignee=assignee)

    def upload_attachment(self, key, attachment):
        '''
        上传附件
        :param key:
        :param attachment:
        :return:
        '''
        if not os.path.exists(attachment):
            return False
        try:
            issue = self.jira_client.issue(key)
            self.jira_client.add_attachment(issue=issue, attachment=attachment)
        except:
            print(traceback.format_exc())
            return False
import pysftp, glob
from jira import JIRA

srv = pysftp.Connection(host="", username="******", password="******")
authed_jira = JIRA('jira url', basic_auth=('username', 'password'))

issue = raw_input('Enter jira ID::')
files = srv.listdir('path1')
file = srv.listdir('path2')

for i, j in zip(files, file):
    srv.chdir('path1')
    print i
    print srv.get(i)
    srv.chdir('path2')
    print j
    print srv.get(j)

for opfiles in glob.glob('/'):
    authed_jira.add_attachment(issue, opfiles)

authed_jira.add_comment(issue, 'file uploaded')
                     "already exists and has latest entry. Skipping.")
                 created = True
                 break
             print(group, "is outdated. Updating ticket...")
             issueObj = jira.issue(issue.key)
             for attachment in issueObj.fields.attachment:
                 jira.delete_attachment(attachment.id)
             issueObj.update(
                 notify=False,
                 description=(str(count) + " unique problems as of " +
                              creation_date))
             issueObj.update(
                 notify=False,
                 fields={"customfield_10202": creation_date})
             jira.add_attachment(issueObj,
                                 filename,
                                 filename=group + ".csv")
             ticketCount += 1
             created = True
             break
     blockNum += 1
     if created == True:
         break
 #If we don't find the group, create a new ticket
 if created == False:
     print(group, "is new. Creating new ticket...")
     issuedict = {}
     issuedict['project'] = {'id': projectID}
     issuedict['summary'] = group
     issuedict['description'] = str(
         count) + " unique problems as of " + creation_date
        print("Found comments. Starting..")
        for comment in trello_comments:
            try:
                username = comment['memberCreator']['fullName']
            except KeyError:
                username = "******"
            print('Creating comment by ' + username + ' on ' + issue.key)
            jira.add_comment(issue, f"{username}:\n{comment['data']['text']}")

    # attachment fields: url, name
    trello_attachments, attachments = card.fetch_attachments(force=True), []
    if len(trello_attachments) != 0:
        print("Found attachments. Downloading...")
        for attachment in trello_attachments:
            if os.path.exists('attachments/' + card.id) is False:
                os.mkdir('attachments/' + card.id)
            try:
                urllib.request.urlretrieve(
                    attachment['url'],
                    'attachments/' + card.id + '/' + attachment["name"])
            except Exception:
                print("Failed to download attachment. Skipping")
            try:
                jira.add_attachment(
                    issue,
                    open('attachments/' + card.id + '/' + attachment["name"],
                         'rb'))
                print("Attached " + attachment["name"] + " to " + issue.key)
            except IOError:
                print("Failed to attach file. Skipping")
Beispiel #24
0
class JiraWrapper(object):
    JIRA_REQUEST = 'project={} AND labels in ({})'

    def __init__(self,
                 url,
                 user,
                 password,
                 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 = project.upper()
        print(self.projects)
        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 = [
                watchers.strip() for watchers 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)
        logging.info(
            f'  \u2713 {issue_data["issuetype"]["name"]} issue was created: {issue.key}'
        )
        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(f'  {issuetype} issue already exists: {issue.key}')
        else:
            issue = self.post_issue(issue_data)
            created = True
        return issue, created
Beispiel #25
0
class JiraWrapper(object):
    JIRA_REQUEST = 'project={} AND (description ~ "{}" OR labels in ({}))'

    def __init__(self, url, user, password, project, fields=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 = project.upper()
        if self.project not in self.projects:
            self.client.close()
            self.valid = False
            return
        self.fields = {}
        self.watchers = []
        if isinstance(fields, dict):
            if 'watchers' in fields.keys():
                self.watchers = [
                    item.strip() for item in fields.pop('watchers').split(",")
                ]
            all_jira_fields = self.client.fields()
            for key, value in fields.items():
                if value:
                    if isinstance(
                            value, str
                    ) and const.JIRA_FIELD_DO_NOT_USE_VALUE in value:
                        continue
                    jira_keys = [
                        item for item in all_jira_fields if item["id"] == key
                    ]
                    if not jira_keys:
                        jira_keys = [
                            item for item in all_jira_fields
                            if item["name"].lower() == key.lower().replace(
                                '_', ' ')
                        ]
                    if len(jira_keys) == 1:
                        jira_key = jira_keys[0]
                        key_type = jira_key['schema']['type']
                    else:
                        logging.warning(
                            f'Cannot recognize field {key}. This field will not be used.'
                        )
                        continue
                    if key_type in ['string', 'number', 'any'] or isinstance(
                            value, dict):
                        _value = value
                    elif key_type == 'array':
                        if isinstance(value, str):
                            _value = [
                                item.strip() for item in value.split(",")
                            ]
                        elif isinstance(value, int):
                            _value = [value]
                    else:
                        _value = {'name': value}
                    self.fields[jira_key['id']] = _value
        if not self.fields.get('issuetype', None):
            self.fields['issuetype'] = {'name': '!default_issuetype'}
        self.client.close()
        self.created_jira_tickets = list()

    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):
        def replace_defaults(value):
            if isinstance(value,
                          str) and const.JIRA_FIELD_USE_DEFAULT_VALUE in value:
                for default_key in default_fields.keys():
                    if default_key in value:
                        value = value.replace(default_key,
                                              default_fields[default_key])
            return value

        default_fields = {
            '!default_issuetype': 'Bug',
            '!default_summary': title,
            '!default_description': description,
            '!default_priority': priority
        }
        description = self.markdown_to_jira_markdown(description)
        issue_data = {
            'project': {
                'key': self.project
            },
            'issuetype': 'Bug',
            'summary': title,
            'description': description,
            'priority': {
                'name': priority
            }
        }
        fields = deepcopy(self.fields)
        for key, value in fields.items():
            if isinstance(value, str):
                if const.JIRA_FIELD_DO_NOT_USE_VALUE in value:
                    issue_data.pop(key)
                else:
                    issue_data[key] = replace_defaults(value)
            elif isinstance(value, list):
                for item in value:
                    value[value.index(item)] = replace_defaults(item)
                if issue_data.get(key):
                    issue_data[key].extend(value)
                else:
                    issue_data[key] = value
            elif isinstance(value, dict):
                for _key, _value in value.items():
                    value[_key] = replace_defaults(_value)
                issue_data[key] = value
            elif not key in issue_data:
                issue_data[key] = value
            else:
                logging.warning(
                    'field {} is already set and has \'{}\' value'.format(
                        key, issue_data[key]))
        _labels = []
        if additional_labels and isinstance(additional_labels, list):
            _labels.extend(additional_labels)
        if issue_data.get('labels', None):
            issue_data['labels'].extend(_labels)
        else:
            issue_data['labels'] = _labels
        jira_request = self.JIRA_REQUEST.format(issue_data["project"]["key"],
                                                issue_hash, 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
        try:
            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)
        except:
            if os.environ.get("debug", False):
                logging.error(format_exc())
        finally:
            self.created_jira_tickets.append({
                'description':
                issue.fields.summary,
                'priority':
                issue.fields.priority,
                'key':
                issue.key,
                'link':
                self.url + '/browse/' + issue.key,
                'new':
                created,
                'assignee':
                issue.fields.assignee,
                'status':
                issue.fields.status.name,
                'open_date':
                issue.fields.created
            })
        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):
        issue = self.client.create_issue(fields=issue_data)
        logging.info(
            f'  \u2713 {issue_data["issuetype"]["name"]} was created: {issue.key}'
        )
        return issue

    def get_or_create_issue(self, search_string, issue_data):
        issuetype = issue_data['issuetype']
        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:
                logging.error('  more then 1 issue with the same summary')
            else:
                logging.info(
                    f'  {issuetype["name"]} already exists: {issue.key}')
        else:
            issue = self.post_issue(issue_data)
            created = True
        return issue, created

    def add_comment_to_issue(self, issue, data):
        return self.client.add_comment(issue, data)

    def get_created_tickets(self):
        return self.created_jira_tickets
Beispiel #26
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 add_label(self, ticket_id, label):
        """
                add label to `ticket_id`.

                :return: boolean, if label update was successful
                """
        if not (ticket_id and label):
            return False

        try:
            issue = self.session.issue(ticket_id)
            issue.fields.labels.append(label)
            issue.update(fields={"labels": issue.fields.labels})

        except Exception:
            logging.exception(f"Failed to add {label} to {ticket_id}")
            return False

        logging.debug(f"Added label {label} to {ticket_id}")
        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 add_watcher(self, ticket_id, user):
        """
        Adding jira ticket watcher.
        
        :param ticket_id: jira ticket id 
        :param user: watcher user id
        :return: nothing
        """

        self.session.add_watcher(ticket_id, user)

    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
        """
        if not ticket_id:
            return

        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
def add_attachment(issue, attachment):
    jira_server = {'server': config.jira_server, 'verify': config.jira_verify}
    jira = JIRA(options=jira_server,
                basic_auth=(config.jira_user, config.jira_pass))
    jira.add_attachment(issue=issue, attachment=attachment)
Beispiel #28
0
postString = DeviceTest.split("\n", 1)[1]  #No Config Now
print(postString)
preconfig = '==========>Device Test Information<============' + '\n'
preconfig += postString
preconfig += '\n'
preconfig += '\n'

result = ParserRobotXML(xmlPath)

testCaseFail = result.getFailCases()

print(testCaseFail.count)
jira = JIRA(server=HOST, basic_auth=(UserName, Token))

for tc in testCaseFail:

    new_issue = jira.create_issue(project='MST',
                                  summary='[' + DeviceName + ']' + tc.testName,
                                  description=preconfig +
                                  tc.getStringTestCase(),
                                  issuetype={'name': 'Bug'})
    fileName = tc.captureScreen  #----
    if fileName != "":
        path = ReportPath + fileName
        print(path)
        # jira.add_attachment(issue=new_issue, attachment=path)
        with open(path, 'rb') as f:
            jira.add_attachment(issue=new_issue, attachment=f)

    # parent_issue = jira.issue('AUTO-135')
    # jira.create_issue_link('tests', new_issue, parent_issue, None)
class JiraIssues(object):
    APPLICATION = {"type": "www.hackerone.comr", "name": "Hacker One"}

    SCOPE = '''
h4.Scope
----
asset type: %(type)s
asset identifier: %(identifier)s\n'''

    DESCRIPTION = '''
h4.Report Info
----
Report State: %(state)s
Reporter: %(reporter)s
Assignee: %(assignee)s
Report Created: %(created)s
Report Last Activity: %(last_activity)s

h4.Weakness
----
name: %(name)s
description: %(w_description)s
id: %(id)s

h4.Severity
----
rating: %(rating)s
score: %(score)s'

h4.Description
----
%(description)s
'''

    def __init__(self, server, username, password, project):
        """Inits jira client.  This current setup requires a jira username to be setup with the appropriate
        permissions in the jira project

        :type server: string
        :param server: jira url

        :type username: string
        :param username: token

        :type password: string
        :param password: jira username password

        :type project: string
        :param project: jira project
        """
        self.__jira_server = server
        self.__username = username
        self.__password = password
        self.jira_project = project
        self._init_jira_client()

    def _init_jira_client(self):
        options = {'server': self.__jira_server}

        def create_custom_field(fields=None):
            url = self._get_url('field')
            r = self._session.post(url, data=json.dumps(fields))

            if r.status_code != 201:
                raise JIRAError(r.status_code, request=r)

            return r

        # Jira library doesn't have method for creating custom fields
        setattr(JIRA, 'create_custom_field', create_custom_field)

        self.jira_client = JIRA(options,
                                basic_auth=(self.__username, self.__password))

    def get_jira_projects(self):
        return self.jira_client.projects()

    def create_project(self, key, name, jira_type="Software"):
        return self.jira_client.create_project(key, name, jira_type)

    def get_jira_issue(self, report):
        """
        Return Jira Issue based on HackerOne Report issue_tracker_reference_id

        :type report: h1.models.Report
        :param report: hackerone report
        :return: Jira Issue
        """
        try:
            return self.jira_client.issue(report.issue_tracker_reference_id)
        except JIRAError as e:
            if e.text == "Issue Does Not Exist":
                return None
            else:
                raise

    @staticmethod
    def _get_jira_summary(report):
        return "%s - %s" % (report.id, report.title)

    def _get_jira_description(self, report):
        return self.DESCRIPTION % {
            'description': report.vulnerability_information,
            'reporter': report.reporter.name,
            'assignee':
            report.assignee.name if report.assignee is not None else "",
            'state': report.state,
            'created': report.created_at,
            'last_activity': report.last_activity_at,
            'name': report.weakness.name,
            'w_description': report.weakness.description,
            'id': report.weakness.external_id,
            'rating': report.severity.rating,
            'score': report.severity.score
        }

    def create_jira_issue(self, report):
        """
        Create Jira Issue
        https://developer.atlassian.com/server/jira/platform/jira-rest-api-example-create-issue-7897248/

        :type report: h1.models.Report
        :param report: hackerone report

        :type :return: string
        :return: Jira ID
        """
        issue_dict = {
            'project': {
                'key': self.jira_project
            },
            'summary': self._get_jira_summary(report),
            'description': self._get_jira_description(report),
            'issuetype': {
                'name': 'Bug'
            },
            'labels': ['hackerOne']
        }

        return self.jira_client.create_issue(fields=issue_dict, prefetch=True)

    def update_jira_issue(self, report, jira):
        fields = {}

        summary = self._get_jira_summary(report)

        if jira.fields.summary != summary:
            fields['summary'] = summary

        description = self._get_jira_description(report)

        if jira.fields.description != description:
            fields['description'] = description

        if fields:
            logging.info("Updating Existing Jira Issue: %s" % fields.keys())
            jira.update(fields=fields)

    def search_for_jira_issues(self, report_id):
        """
        Perform a Jira query search using JQL
        :param report_id: hacker one report id
        :return: returns jira issue match
        """
        return self.jira_client.search_issues(
            '''project = %s AND summary ~ "%s"''' %
            (self.jira_project, report_id),
            maxResults=1)

    def get_fields(self):
        return self.jira_client.fields()

    def create_custom_field(self, fields):
        return self.jira_client.create_custom_field(fields)

    def get_remote_links(self, jira):
        return self.jira_client.remote_links(jira)

    def add_remote_link(self, report, jira, relationship="Relates"):
        links = set()

        # note all rmeote links have to have a global id
        for link in self.get_remote_links(jira):
            if hasattr(link, 'globalId'):
                links.add(link.globalId)

        if report.id not in links:
            destination = {'url': report.html_url, 'title': report.title}
            return self.jira_client.add_remote_link(jira, destination,
                                                    report.id,
                                                    self.APPLICATION,
                                                    relationship)

    def add_simple_link(self, report, jira):
        """https://developer.atlassian.com/server/jira/platform/jira-rest-api-for-remote-issue-links/"""
        link = {'url': report.html_url, 'title': report.title}

        return self.jira_client.add_simple_link(jira, object=link)

    def add_jira_attachment(self, jira, attachment, filename):
        """Add H1 Attachment in Jira

        :param jira: Jira object that has attachments
        :param attachment: hacker one attachment object content
        :param filename: attachment file name
        :return: return
        """
        return self.jira_client.add_attachment(issue=jira.id,
                                               attachment=attachment,
                                               filename=filename)

    def create_comments(self, jira, comment):
        return self.jira_client.add_comment(jira, comment)
Beispiel #30
0
def h_list(request, **kwargs):
    global test_main

    if request.is_ajax():
        schema_name = settings_gen.objects.get(id=1).tenant_name
        j_file = "frontend/static/out/" + request.POST['tpid'] + "/log.html"
        #Connection first get user and pass from tab
        jadd = ""
        juser = ""
        jpass = ""

        jFile = False
        response = []

        errarg = ""
        connData = jra_settings.objects.all()
        for i in connData:
            jadd = i.j_address.strip()
            juser = i.j_user.strip()
            jpass = i.j_pass.strip()

        try:
            options = {'server': jadd}
            jira = JIRA(options, basic_auth=(juser, jpass))
            #if auth ok continue
            # Get the issue.
            try:
                issue = jira.issue(request.POST['jissue'])
                #If issue is ok add element:
                # Add a comment to the issue.
                if request.POST['jcom']:
                    jira.add_comment(issue, request.POST['jcom'])

                #add log file
                if str(request.POST['jfile'].strip()) == 'true':
                    try:
                        jira.add_attachment(issue, j_file)
                        jFile = True
                    except Exception as ef:
                        errarg = ef.args

            except Exception as ei:
                errarg = ei.args

        except Exception as e:
            errarg = e.args

        #check if error is nauthorized Access, too long for be displayed, i trunk it
        strerr = 'Unauthorized'
        if strerr in str(errarg):
            errarg = "(401, 'Unauthorized (401), Check Jira settings connection data.')"

        #Now if all is ok add new line to the table
        jra_ev = jra_history(j_tid=request.POST['tid'],
                             j_issue=request.POST['jissue'],
                             j_comment=request.POST['jcom'],
                             j_file=jFile,
                             dt=str(datetime.now()),
                             j_error=errarg)
        jra_ev.save()

        #Query for have already inserted jira records

        vallabel = {}
        vallabel['csrfmiddlewaretoken'] = request.POST['csrfmiddlewaretoken']
        vallabel['j_err'] = errarg
        response.append(vallabel)
        json = simplejson.dumps(response)
        return HttpResponse(json, content_type='application/json')

    else:
        pass

    #EXTRA AJAX, NORMAL BEHAVIOUR
    #First check if is first time with no threads, if is new, home redirect to active else continue

    a = t_threads.objects.all()[:1]
    #if not a : return HttpResponseRedirect('/')

    uGroup = request.user.groups.all()
    # menu_list = kwargs['menu']
    context = RequestContext(request)
    context_dict = {'all_test': test_main, 'uGroup': uGroup}
    response = render(request, 'base_history.html', context_dict)

    return response