Creating the Python script

The Python script for bug system integration is called review_action.py. This script needs to

  • identify your bug tracking system
  • define the actions that need to take place for the integration
  • specify the issue information you want to send to the bug tracker

Optionally, you can also

  • customize the text for the 'Create a ticket' button to appear on the issue details page
  • display a hyperlink on the issue details page to the bug entry in your bug tracking system, as well as showing comments from the bug tracker
  • include diagnostic messages in your script

Example: Creating a connection to Bugzilla

In this example, the review_action.py Python script specifies a connection to the open-source bug tracking system Bugzilla when the user clicks the 'Send to Bugzilla' button in Static Code Analysis. As is typical for the review_action.py script, it defines the issue data that should be sent to Bugzilla, such as the issue ID, the checker that found the issue, the checker message, and the issue's URL. (For a list of the predefined variables for the script, see Using predefined variables.)

The script also includes definition #ui.name to define the custom name for the button, Send to Bugzilla, instead of the default value (Create a ticket). The set_bug_id method specifies a link to Bugzilla, which will become a hyperlink to the issue's corresponding Bugzilla bug, and the success_msg method defines a diagnostic message.

Copy
import sys
import re
import json
import urllib.request
import urllib.error

class InputData:
    """
    Represents the input JSON structure.
    Fields:
    - issue: dict
    - username: str
    """
    def __init__(self, data):
        self.issue = data.get('issue', {})
        self.username = data.get('username', '')

class OutputData:
    """
    Represents the output JSON structure.
    Fields:
    - bugTrackerId: str
    - comment: str
    - status: str
    """
    def __init__(self, output_path):
        self.output_path = output_path
        self.data = {}

    def update(self, key, value):
        self.data[key] = value
        with open(self.output_path, 'w', encoding='utf-8') as f:
            json.dump(self.data, f)

def main():
    input_path = sys.argv[1]
    output_path = sys.argv[2]

    with open(input_path, 'r', encoding='utf-8') as f:
        data = json.load(f)

    input_data = InputData(data)
    output_data = OutputData(output_path)

    issue = input_data.issue
    history = issue.get('statusHistory', [])
    username = input_data.username

    def set_bug_id(id):
        output_data.update("bugTrackerId", id)

    def set_comment(comment):
        output_data.update("comment", comment)

    def success(status_msg):
        output_data.update("status", status_msg)

    def fail(s):
        raise RuntimeError(s)

    def bugzillaAPIRequest():
        host = "https://api-dev.bugzilla.mozilla.org/test/latest/bug?username=prezioso@gmail.com&password=klocwork"
        product = "FoodReplicator"
        component = "Salt"
        version = "1.0"
        target = "---"
        summary = "%s | %s | %s | %s | %s | %s | %s" % (
            issue.get("id", ""), issue.get("code", ""), issue.get("severity", ""),
            issue.get("severityCode", ""), issue.get("status", ""), issue.get("url", ""), issue.get("project", ""))
        op_sys = "Linux"
        platform = "All"

        jdata = json.dumps({
            "product": product,
            "component": component,
            "version": version,
            "target": target,
            "summary": summary,
            "opsys": op_sys,
            "platform": platform
        })
        jdata_bytes = jdata.encode('utf-8')

        req = urllib.request.Request(host, jdata_bytes, {'Content-Type': 'application/json', 'Accept': 'application/json'})

        try:
            resp = urllib.request.urlopen(req)
            content = resp.read().decode('utf-8')
            if not content:
                fail("Empty response from server")
            obj = json.loads(content)
            ref = obj.get('ref', '')
            id = obj.get('id', '')
            set_bug_id(id)
            return "%s" % (ref)
        except urllib.error.HTTPError as e:
            print(f"HTTPError: {e.code}", file=sys.stderr)
            if e.code == 201:
                content = e.read().decode('utf-8')
                if not content:
                    fail("Empty response from server")
                obj = json.loads(content)
                ref = obj.get('ref', '')
                id = obj.get('id', '')
                set_bug_id(id)
                return "%s" % (ref)
            else:
                fail("HTTP error: %s" % e)
        except Exception as e:
            fail("Request failed: %s" % e)
        return "fail"

    success_msg = bugzillaAPIRequest()

    if (success_msg == "fail"):
        fail("ERROR - HTTP POST REQUEST")

    success(success_msg)

if __name__ == "__main__":
    main()

Notes:

  • The latest review_action.py script has been updated to Python 3 syntax. Please update your old scripts and remove any legacy Python 2 code. Any future modifications should be based on the updated Python 3 structure to ensure compatibility and support.

  • The issue object is now a JSON object. You can access issue fields by specifying issue.get("id").

  • The history object is JSON array of history objects. You can access history fields by specifying history[0]['owner'].

  • The input_data in the script is in the following form:

Copy
{
"issue": {
"id": 95,
"groupId": 0,
"name": "Buffer Overflow - Array Index Out of Bounds",
"message": "Array \u0027i\u0027 of size 10 may use index value(s) 10",
"file": "C:\\Users\\dkumar\\Downloads\\projs\\c\\c_proj\\testFile.c",
"line": 8,
"code": "ABV.GENERAL",
"severity": "Critical",
"severityCode": 1,
"supportLevel": "Klocwork Certified",
"supportLevelCode": 1,
"bugTrackerId": "12345",
"state": "New",
"status": "Analyze",
"lastUpdateDate": 1753821247663,
"owner": "unowned",
"project": "ff",
"buildName": "build_2",
"url": "http://localhost:8080/review/insight-review.html#issuedetails_goto:problemid\u003d95,project\u003dff,view_id\u003d1",
"statusHistory": [
  {
        "date": 1753821247663,
        "userid": "dkumar",
        "comment": "Bug created successfully.",
    "status": "new",
    "owner":"smith"
  },
  {
        "date": 1753783959710,
        "userid": "dkumar",
        "comment": "Fix",
    "status": "new",
    "owner":"john"
  }
]
},
"username": "john"
 }

After the script has been installed, the issue window looks like the following display, with the BUG ID field showing the hyperlink to the issue's Bugzilla entry.

Image:bug_tracker_11.png

Example: Integrating with Rational Team Concert

The following example provides a walkthrough of the steps you need to do in order to integrate your Klocwork environment with Rational Team Concert.

How to navigate RTC xml documents

To create a work item in RTC you must first identify the relevant URL and project key by requesting a series of xml documents from the RTC server . The steps will be similar to:

  1. Fetch the xml root services document from the RTC REST API: http(s)://yourRTCserver:port/ccm/rootservices
  2. Extract the Services Catalog URL which is the "rdf:resource" attribute in "oslc_cm:cmServiceProviders"
            <oslc_cm:cmServiceProviders
                     xmlns:oslc_cm="http://open-services.net/xmlns/cm/1.0/"
                     rdf:resource="http(s):// yourRTCserver:port /ccm/oslc/workitems/catalog" 
            />
  3. Find the relevant project’s service provider URL (attribute "rdf:resource" of element "oslc_disc:services") in the Service Catalog by identifying the project’s title (element dc:title of element oslc_disc:ServiceProvider).
    The URL contains a unique project key (e.g. _Day3sOHaEeO1tdkD6QbZUQ).
  4. Extract the appropriate service factory (<dc:title>Location for creation of change requests</dc:title> of element “oslc_cm:factory” of element “oslc_cm:changeRequests oslc” ) URL ("oslc_cm:url") from the service provider URL
            <oslc_cm:changeRequests oslc_cm:version="1.0">
                            …
                            <oslc_cm:factory>
                                    >dc:title<Location for creation of change requests>/dc:title<
                                    >oslc_cm:url< http(s):// yourRTCserver:port /ccm/oslc/contexts/_Day3sOHaEeO1tdkD6QbZUQ/workitems</oslc_cm:url>
                            </oslc_cm:factory>
                            …
            </oslc_cm:changeRequests>
  5. Post the work item to the factory URL. For example a JSON encoded work item could be:
    {
                    "dc:title":"Klocwork ID 42: Suspicious dereference of pointer in function call before NULL check: cvs\\src\\checkin.c",
                    
                    "dc:description":"This issue was detected by Klocwork static code analysis.
                     \n
                     \t\t\nId: 42    URL: http(s)://yourKlocworkServer:port/review/kw-review.htm#issuedetails_goto:problemid=42,project=CVS,view_id=1
                     \nExported to RTC by: jchapman
                     \n
                     \nFile: cvs\\src\\checkin.c
                     \n
                     \nChecker: RNPD.CALL
                     \nType: Suspicious dereference of pointer in function call before NULL check
                     \nMessage: Suspicious dereference of pointer 'options' by passing argument 1 to function 'strcmp' at line 63 before NULL check at line 76
                     \nSeverity: Critical(1)
                     \nStatus: Analyze
                     \nState: Existing
                     \nOwner: azukich
                     \nLast Update: No Updates
                     \nHistory: \n",
                            
                            "dc:type": "task",
                            
                            "oslc_cm:priority": " http(s):// yourRTCserver:port /ccm/oslc/enumerations/_Day3sOHaEeO1tdkD6QbZUQ/priority/priority.literal.l4"
    }
  6. If successful, the URL to the work item will in “Location” within the response header and the work item ID will be in the “dc:identifier” element of the response.

How to handle authentication

By default the RTC server is setup to use forms based authentication so it is necessary to look for an authentication response request when posting anything to the RTC server. For example:

Copy
import urllib.request
import urllib.parse
import urllib.error

targetUrl = "..."  # your target URL
data = b"..."      # your data as bytes
hostbase = "..."   # your host base
rtc_username = "..."  # your username
rtc_password = "..."  # your password

req = urllib.request.Request(targetUrl, data, {'Content-Type': 'application/json', 'Accept': "application/xml"})
try:
    response = urllib.request.urlopen(req)
    if response.info().get('X-com-ibm-team-repository-web-auth-msg') == 'authrequired':
        login_data = urllib.parse.urlencode({'j_username': str(rtc_username), 'j_password': str(rtc_password)}).encode('utf-8')
        reqLogon = urllib.request.Request(
            hostbase + "/j_security_check",
            login_data,
            {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': "application/json"}
        )
        response = urllib.request.urlopen(reqLogon)
        if response.info().get('X-com-ibm-team-repository-web-auth-msg') == 'authfailed':
            raise RuntimeError("RTC logon failed")
        else:
            if data is not None:
                response = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
    eData = e.read().decode('utf-8')
    if hasattr(e, 'code'):
        if e.code == 201:
            # process response
            pass
        elif e.code == 400:
            # process error message
            pass

How to handle field values

Field value enumerators (e.g. “oslc_cm:priority”) are defined on a per project basis and can be customized by the RTC administrator. To discover the configured values for a particular project, use your browser and the project’s unique project key to query the server. For example:

http(s):// yourRTCserver:port/ccm/oslc/enumerations/_Day3sOHaEeO1tdkD6QbZUQ/priority

Need help? Contact the Static Code Analysis Professional Services Team.

The Static Code Analysis Professional Services Team has experience creating basic scripts for Bugzilla, JIRA, and IBM Rational Team Concert. The scripts can be customized for your installation. Contact the Static Code Analysis Professional Services Team for help.

Customizing the 'Create a ticket' button

Placing the review_action.py script in the projects_root/config directory causes Static Code Analysis to display the 'Create a ticket' button on the issue details pages in Static Code Analysis for the project. (You may need to refresh the browser to see the link.) If you want to specify a different name for the button, add the following line to your script:

#ui.name:<my custom button>

For example:

#ui.name:Send to Bugzilla

After the script has been installed, the issue window shows the new button:

Image:bug_tracker_31.png

You can put Japanese characters in the button name by including UTF-8 encoding in the review_action.py script.

Including diagnostic messages

If the bug report is filed successfully when the 'Create a ticket' button is clicked, the 'Ticket created' message pops up on the Static Code Analysis window. If it fails, 'Bug reporting failed' appears. To provide custom diagnostic messages for success and failure, use these methods in the Python script:

  • success(custom_message)
  • fail(custom_message)

If you call the fail() method in the script, it will interrupt further script execution.

Placing the script in projects_root

When you've completed your review_action.py script, place it in the projects_root/config directory for the project.

Available variables

The review_action.py script receives the following variables as input in a JSON structure:

variable.field Usage
username the name of the user who activated the script with the Create a ticket button
issue a Python object of class Issue containing information about the script in the following fields:
issue.id the identifier of the issue in Static Code Analysis
issue.groupId the identifier of the group that an issue belongs to in Static Code Analysis. A zero value indicates that group calculations are turned off and this variable should not be used.
issue.name the name of the issue from the Static Code Analysis issue details page
issue.message the checker message for the issue in Static Code Analysis
issue.file the file in which the issue occurs
issue.code the name of the checker that found that issue
issue.severity the severity of the issue in textual form (not numeric)
issue.severityCode the severity of the issue in numeric form
issue.state the state of the issue - Existing or Fixed
issue.status the status of the issue, such as Fix or Analyze
issue.lastUpdateDate the time of the last update in milliseconds
issue.owner the owner of the issue
issue.project the name of the Klocwork project
issue.url the URL of the issue in Static Code Analysis
issue.statusHistory an array with citing history events. Each event is an object of class StatusHistoryEvent with the following fields:
  • date - date of the event in milliseconds
  • userid - user name of the event creator
  • owner - new issue owner
  • status - new issue status
  • comment - comment created in the process of citing
issue.bugTrackerId the bug tracker ID of the issue in Static Code Analysis