Request Tracker Boot Camp Presentation
Request Tracker Boot Camp Presentation
Summer 2009
Todays agenda
Introductions RT History Getting up and running Point-and-click conguration How to customize RT Customizing the web interface Data Model Internationalization Reporting Approvals and workow Authentication Debugging
Introduction
A Brief History of RT
RT 0.9 (1996)!
Designed for use at a single company 2 sysadmins 30 users
RT 1.0 (1999)!
Same as RT 0.9 (+ a bit more courage)! Used at hundeds of companies Dozens of CSRs Thouands of requests per day Intense guilt
RT 2.0 (2001)!
Total rewrite Just after Jesse escaped Microsoft DBIx::SearchBuilder Abstraction Multiple DBs supported Whole new UI No more frames Keywords
10
RT 3.0 (2003)!
Overhauled web interface Extension mechanisms Internationalization Custom fields Cleaner internals Tests
11
RT 3.2 (2004)!
New search UI Spreadsheet / RSS output Outgoing mail preview and logging UI improvements Attributes for random metadata No major structural changes More tests
12
RT 3.4 (2005)!
Reimplemented Custom Fields Custom fields on users, groups transactions Generalized Transaction system Faster, Faster, Faster Prettier Even more tests
13
RT 3.6 (2006)!
All CSS layout and styling Customizable homepage Built in charts and reports Ticket "reminders" Comprehensive test coverage Cleaner code
14
RT 3.8 (2008)!
More user preferences Timezones Theme Ticket history order New configuration system Internals cleanup Even more tests PGP support
15
RT 3.8 (continued)!
Favorite tickets Ticket relationship graphs Branded queues iCal feeds Dashboards
RT 4.0!
The future
RT 4.0
Never trust a software vendor who talks about the future...
Setting up RT
Download and Install
19
Configuring RT
Point and Click
Custom Fields
Custom Fields
Helpdesk Hostname, OS, Browser, Site Bug Tracking Severity, OS, Category, "Broken In", "Fixed In" Property Rental Requests Smoking?, Beds, Pets, Special Needs
Scrips
Scrips
Condition / Action / Template Scrip Ideas: OnCreate NotifyManager WithTemplate:ManagerNotify OnResolve NotifyRequestor WithTemplate:HappynessSurvey OnOwnerChange NotifyOwner WithTemplate:AssignmentNotifyWithHistory OnCreate NotifyRequestor WithTemplate:SelfServiceNewPasswd
Conditions
On Create On Transaction On Correspond On Comment On Status Change
Actions
AutoReply Notify Requestors Notify Owner Notify AdminCCs as Comment Notify Requestors and CCs Notify Requestors, CCs, and Admin CCs Notify Other Recipients Create Tickets Open Tickets as Comment User Dened
Templates
Blank AutoReply Transaction Correspondence Comment Status Change Resolved User Dened
Customized Scrips
Custom Actions, Templates, and Conditions In Database On Disk Flexibility Perl RT's objects
Custom Templates
Templates are Text::Template Objects They can be plain text They can contain Perl They are not sandboxed Customizing the AutoReply
{$Transaction->Content()}
Template Tweaks
$Ticket, $Transaction $Ticket->QueueObj, $Transaction->CreatorObj Message Headers {$Transaction->Message->First->Headers} Real Code:
Thanks for contacting FooCorp Support! It's { use LWP::Simple; get("http://foocorp/weatherword"); } here in SomeCity today, but that won't stop us from answering your question within 48 business hours...
Custom Condition
Code snippet that returns true or false Controls whether an action is run For example: OnEmergency
if ($self->TransactionObj->Subject =~ /emergency/i && $self->TicketObj->Transactions->Count == 1) { 1; } else { 0; }
Custom Action
PageSysadmins Prepare
my $hour = (localtime)[2]; if ($hour > 8 && $hour < 18) { return 0; } else { return 1; }
Cleanup/Commit
use Net::SMS; my $sms = Net::SMS->new(); $sms->msgPin("+1 555 123 1234"); $sms->msgFrom("RT"); $sms->msgText($self->TemplateObj->MIMEObj->body->as_string); $sms->msgSend()!
Example One
EmergencyPage Template
Subject: 911 {$Ticket->Subject}
Example Two
OnCreate EscalatePriorityIfBoss WithTemplate:Blank EscalatePriorityIfBoss Action: Prepare
($self->TransactionObj->CreatorObj->EmailAddress =~ /president\@whitehouse.gov/i) ? 1 : 0;
Commit
$self->TicketObj->SetPriority(99);
Saved Searches
Any query Any output format Personal or Shared
Custom Homepage
Users can customize their own Superusers can change the default Pre-written portlets Any saved search Graphs and Charts (from 3.6.3)
Dashboards
Similar to custom homepages Shareable with groups Bookmarkable URLs Scheduled email delivery
How It Works
MTA pipes message to rt-mailgate On any system error, rt-mailgate returns a "Temporary Failure" error to the MTA rt-mailgate sends message to RT Server via HTTP Server processes message and creates or updates ticket Server returns a success or failure message
Debugging rt-mailgate
# /opt/rt3/bin/rt-mailgate \ --url http://localhost/ \ --queue general \ --debug \ --action correspond < /tmp/msg /tmp/msg: From: root@localhost Subject: just testing This is a message
Advanced Options
MTAs support "+ notation" to pass information to MUAs by appending +data to an email address. --extension 'queue' [email protected] 'ticket' [email protected] 'action' [email protected]
Mail Extensions
RT::Extension:: CommandByEmail ExtractSubjectTagOnTransaction
Self Service
Let your users help you do your job
Self Service
RT provides a "SelfService" interface for end-users Automatically displayed for unprivileged users See new/open/stalled/resolved tickets Create new tickets Password needed to access it Use external authentication for RT Send users their passwords when they first create a ticket
The RT Commandline
The RT CLI
Simple perl script Usually lives in /opt/rt3/bin Can run locally or remotely Uses HTTP (or HTTPS)! Uses TicketSQL for Searching Talks to RT's "REST" interface Scriptable
bin/rt
rt ls "Priority > 5 and Status='new'" rt ls "queue='perl5' and (Status='new' or Status='open')" rt edit ticket/312 set queue=spam status=deleted rt comment -m 'this is a comment' 151
standalone_httpd
RT Application Server Just like mod_perl or FastCGI Pure perl Can be easily profiled Can be run under perl's debugger
Developer Mode
Primarily useful for web development Mason refreshes on the fly Components (Web UI bits)! Perl Libraries
Mason Blocks
<%args> $variable => 'Default value' </%args> <h1>This is a page!</h1> <%init> # This block runs before any page output </%init> <h1> This is page content, too</h1>
Example Customization
/Ticket/Elements/ShowBasics
% if ($Ticket->TimeEstimated) { <tr> <td class="label time estimated"><&|/l&>Estimated</&>:</td> <td class="value time estimated"><& ShowTime, minutes => $Ticket>TimeEstimated &></td> </tr> % } % if ($Ticket->TimeWorked) { <tr> <td class="label time worked"><&|/l&>Worked</&>:</td> <td class="value time worked"><& ShowTime, minutes => $Ticket>TimeWorked &></td> </tr> % }
Example Customization
/Ticket/Elements/ShowBasics
% # if ($Ticket->TimeEstimated) { <tr> <td class="label time estimated"><&|/l&>Estimated</&>:</td> <td class="value time estimated"><& ShowTime, minutes => $Ticket>TimeEstimated &></td> </tr> % # } % # if ($Ticket->TimeWorked) { <tr> <td class="label time worked"><&|/l&>Worked</&>:</td> <td class="value time worked"><& ShowTime, minutes => $Ticket>TimeWorked &></td> </tr> % # }
RT Web Callbacks
Insert Mason code inside at hook points Add things to pages and menus Set widget content ...all without changing core RT code
Local code "registers" itself by living in the right place in the filesystem RT includes all components matching:
{share,local}/html/Callbacks/*/ Ticket/Update.html/AfterTitle
RT::Extension:: MenubarSearches
Small example of callbacks Jump to active tickets in each queue Inserts a menu item with a callback
rt.cpan.org
Extensions
RT::BugTracker RT::BugTracker::Public RT::Extension::rt_cpan_org RT::Authen::PAUSE RT::Authen::Bitcard RT::Authen::OpenID
MasonX::Profiler
Figuring out how the Web UI fits together
MasonX::Profiler
Foundry/Home/MyRequests.html BEGINS /Elements/SetupSessionCookie 0.0610 /Callbacks/Foundry/autohandler/Auth 0.0003 /Elements/Callback 0.0242 /Elements/Callback 0.0016 /Foundry/Elements/Top 0.0604 /Foundry/Elements/Tab 0.0194 /Foundry/Elements/Header 0.1375 /Foundry/Elements/Tabs 0.0037 /Elements/Callback 0.0294 /Elements/Footer 0.0308 /autohandler 2.9179 /Foundry/Home/MyRequests.html ENDS
Activating MasonX::Profiler
RT_SiteConfig.pm
use MasonX::Profiler; @MasonParameters = ( preamble => 'my $p = MasonX::Profiler->new($m,$r);' );
Database
RT is composed of over a dozen types of related objects Building your own relational database out of BDB or flat files on disks isn't our idea of fun Organizations want to be able to use their own tools to query RT RT connects to a SQL backend with an objectrelational mapper
Schema Diagram
DBIx::SearchBuilder
It's an object-relational mapper It hides SQL from your application It lets you transparently treat your database as perl objects Most RT objects are subclasses of DBIx::SearchBuilder or DBIx::SearchBuilder::Record It's a dessert topping and a floor wax
SearchBuilder is Easy
$t = new RT::Ticket( $RT::SystemUser ); $t->Load( 42 ); print $t->Subject;
Record Objects
Create my $user = RT::User->new($RT::SystemUser); ($id, $msg) = $user->Create(Name => 'jrv', EmailAddress => '[email protected]'); Load $user->LoadByCols(EmailAddress => '[email protected]'); Read print $user->Name; Update $user->SetName('jesse'); Delete $user->Delete();
Collection Objects
Every record has a corresponding DBIx::SearchBuilder
'collection' object
Collection Objects
The standard methods: Limit a result set Limit(); Iterate Next(); Sort and Order OrderBy(); Page RowsPerPage();
API
RT's API is its core objects All RT tools use the same API we export to the world rt-crontool Web frontend Database setup tools
Boilerplate Code
Simple samples
RT Tool Boilerplate
#!/usr/bin/perl -w use strict; use lib qw(/opt/rt3/local/lib /opt/rt3/lib); use RT; # Load the config file RT::LoadConfig(); # Connect to the database and get RT::SystemUser # loaded RT::Init();
Resolve a ticket
[...boilerplate...] use RT::Interface::CLI qw(GetCurrentUser loc); use RT::Ticket; my $CurrentUser = GetCurrentUser(); die loc("No RT user found.") unless ($CurrentUser->Id); my $ticketid = shift @ARGV; my $ticket = RT::Ticket->new($CurrentUser); $ticket->Load($ticketid); die loc("Ticket not found") unless ($ticket->Id); my ($trans, $msg) = $ticket->SetStatus('resolved'); print $msg;
Overlay Classes
Cleanly customize core classes
Authentication
RT has its own internal authentication system RT needs to have a user object for any user before they're allowed to access RT You can tie RT into your single sign on RT::Authen:: Bitcard OpenID
ACL System
ACLs can apply to any DBIx::SB::Record Any Record object type can define what rights it supports Rights can be granted to any user or group Other systems that drop on top of RT can use the ACL system
Delegation
Supports basic delegation of rights Doesn't support "partial" delegation of a given right Doesn't support "re-delegation of rights" When a user's right to do something is revoked, delegates also have right revoked
Internationalization
I18N
Building Blocks
UTF8/Unicode Locale::MakeText Locale::MakeText::Lexicon
Overview
Write Code Extract Strings to Message Catalog Translate Message Catalog
String Extraction
Core $self->loc("Created ticket [_1]", $ticket->Id); Code In Mason loc("Created ticket [_1]", $ticket->Id); Text In Mason <&|/l, $ticket->Id&>Created ticket [_1]</&> Getting the strings into the .po files tool/extract-message-catalog
Localization
L10N
Gotchas
Corporatization
Localization can be useful just within an organization. Every organization has its own jargon It's a "request", not a "ticket" PO files can be "overlaid" just like libraries
Reporting
Finding, Counting, and Extracting Tickets
Important Classes
RT::Attachment RT::CustomField RT::Link RT::Queue RT::Ticket RT::Transaction RT::User
Collection Classes
RT::Attachments RT::CustomFields RT::Links RT::Queues RT::Tickets RT::Transactions RT::Users
Finding Tickets
Limit DBIx::SearchBuilder-Like TicketSQL SQL-like query language Direct Database Access Try to avoid this if you can
TicketSQL
A SQL WHERE clause style syntax for searching for tickets Basic Component: Field + Operation + Value Boolean Logic and Grouping AND / OR / ( )
TicketSQL: Usage
my $ts = RT::Tickets->new( $CurrentUser ); $ts->FromSQL( q[Queue = "perl5"] );
TicketSQL: Examples
Queue = "perl5" AND ( "Status" = "new" OR Status = "open" )! Queue = "parrot" AND "CF.{Priority}" = "High" Queue = "perl5" AND Owner = "jhi" AND Created < "2003-05-29"
Things in the Premium queue, or high priority things in the support queue, or things in the support queue more than two days old.
(Queue = "support" AND Priority> 50) OR (Queue ="Premium") OR (Queue ="support" and Created < 'two days ago')!
Counting Results
$users->Count()!
Sorting Results
$users->OrderBy( FIELD => "Name", ORDER => "ASC" );
Report Structure
Query What records do I want? Renderer How do I want to output them?
Writing Queries
Finding tickets
Current Status
my %data = (); my $q = "perl5"; for my $status ( qw[new open stalled resolved] ) { my $search = new RT::Tickets($User); $search->LimitQueue( VALUE => $q ); $search->LimitStatus( VALUE => $status ); my $c = $search->Count; $data{$status} = $c; }
Writing Renderers
Loop over all the data and print it out formatted: <table> <tr><th>Name</th><th>Email</th></tr> % while( my $u = $users->Next ) { <tr><td><% $u->Name %></td> <td><% $u->EmailAddress %></td></tr> %} </table>
Categorized Tickets
Organize by Custom Field
Patch Status
Conference Organizing
Code Snippet
<%perl> my $cfs = RT::CustomFields->new($RT::SystemUser); $cfs->LimitToQueue("perl5"); while (my $cf = $cfs->Next) { my $cfn = $cf->Name(); my $cf_values = $cf->Values; while (my $cfo = $cf_values->Next()) { my $cfv = $cfo->Name(); my $query = qq[Queue = "perl5" AND "cf.{$cfn}" = "$cfv" AND ( Status = "New" OR Status = "Open")]; my $z = new RT::Tickets( $CurrentUser ); $z->FromSQL( $query ); </%perl> # ... renderer ... % } %}
Graphing
Pie Chart Imager::Graph
my @status = qw[new open stalled]; for my $status (@status) { my $search = new RT::Tickets($User); $search->FromSQL(q[Queue="perl5" and Status="$status"]); push @data, $search->Count; } use Imager::Graph::Pie; my $pie = Imager::Graph::Pie->new; my $img1 = $pie->draw(data=>\@data); $img1->write(file=>"out.png");
Graphing (II)!
Values Over Time RRDtool
Ical Export
BEGIN:VCALENDAR CALSCALE:GREGORIAN VERSION:2.0 %while (my $ticket = $tix->Next) { % my $start = Date::ICal->new( epoch => $ticket->DueObj->Unix); BEGIN:VEVENT SUMMARY:#<%$ticket->Id%>: <%$ticket->Subject%> DTSTART;VALUE=DATE-TIME:<% $start->ical%> DTEND;VALUE=DATE-TIME:<% $start->ical %> END:VEVENT % } END:VCALENDAR
Reporting Extensions
RT::Extension::ActivityReports RTx::Statistics
Approval Basics
Create tickets based on a template Dependency Chains Often used for "Managerial Approval" Workflow Modeling "a ticket depends on its approvals"
Example Approval
OnCreate / Create Tickets Template: ===Create-Ticket: ManagerApproval Subject: {$TOP->Subject} Approval Queue: ___Approvals Type: approval Depended-On-By: {$TOP->Id} Content: Please review {$TOP->OwnerObj->Name}'s ticket number {$TOP->Id} for approval. ENDOFCONTENT
Enforcement
RT requires all dependencies to be resolved before a Ticket can be resolved
CreateTicket Templates
===Create-Ticket: title Fields: Queue, Subject, Status, Due, Starts, Started, Resolved, Owner, Requestor, CC, AdminCC, TimeWorked, TimeEstimated, TimeLeft, InitialPriority, Type, DependsOn, DependedOnBy,RefersTo, ReferredToBy, Members, MemberOf, Content, ContentType, CustomField-<id#>
Important Fields
Queue Type Ticket Approval Content ENDOFCONTENT
Approval Notifications
Approvals queue configured by default To edit, you need to get there manually $RT::WebUrl/Admin/Queues/Scrips.html?id=2
Basics
RT has a built in authentication system Users Groups Delegation of Rights
Unified Authentication
Simplify your life Centralized Administration Simplify your users' lives One username One password
mod_auth_*
LDAP Kerberos PAM NT Domain NDS Lotus Notes Radius
RT_SiteConfig.pm
Trust REMOTE_USER Set($WebExternalAuth, 1); Fallback to RT database Set($WebFallbackToInternalAuth, 1); Autocreate Users Set($WebExternalAuto, 1);
Apache Side
Different for every module. Basic Format is <Location /rt3> <Limit GET POST> AuthType Basic AuthName "MyCo RT" AuthUserFile /etc/httpd/rt-passwd require valid-user </Limit> </Location>
MySQL Auth
<Location /rt3> SetHandler perl-script PerlHandler RT::Mason AuthName perl.org AuthType Basic AuthMySQLHost localhost AuthMySQLDB userdb AuthMySQLUser userdbuser AuthMySQLPassword userdbpass
RT as an Authentication Source
Users table Name Password (MD5)! Suitable for mod_auth_mysql Easy to use elsewhere
RT Auth Ideas
mod_auth_rt* Authen::RT* pam_rt* Overlay RT::User->IsPassword()! * denotes figment of Robert's imagination
Logging (I)!
A _lot_ of information gets logged RT_SiteConfig.pm: Set($LogToSyslog, Set($LogToScreen, Set($LogToFile, Set($LogDir, '' ); 'error' ); 'debug' ); '/opt/rt3/var/log' );
Set($LogToFileNamed, 'rt.log' );
Logging (II)!
Levels: debug, info, notice, warning, error, critical, alert, emergency Use: $RT::Logger->warning("beware of dog");
Database Tuning
MySQL
my.cnf key_buffer=256M table_cache=256 sort_buffer=2M record_buffer=2M thread_cache=8 thread_concurrency=4 Higher is better to a point MySQL 5.1 isnt ready
Postgres
Tell it to use more RAM VACUUM ANALYZE Improves query analyzer Query Analyzer highly configurable Newer versions are better Use 8.1.3 or newer
Query Tuning
Postgres and MySQL have very different query optimizers One sub-optimal query can take a disproportionate amount of time Use query logging to identify bottlenecks
MySQL: Logging
to enable Query Logging my.cnf: log safe_mysqld: --log output: `hostname`.log to enable Slow Query Log my.cnf: log-slow-queries my.cnf: long_query_time seconds (default: 10)! safe_mysqld: --log-slow-queries output: `hostname`-slow.log
MySQL: Explain
How a SELECT is processed mysql> explain SELECT * FROM Scrips WHERE id = '13'; +--------+-------+---------------+---------+---------+-------+------+-------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +--------+-------+---------------+---------+---------+-------+------+-------+ | Scrips | const | PRIMARY | PRIMARY | 4 | const | 1 | | +--------+-------+---------------+---------+---------+-------+------+-------+ 1 row in set (0.09 sec)! Good: where, index Bad: temporary, filesort MySQL Documentation, Chapter 5
Postgres: Logging
Postgresql.conf log_statement = true log_duration = true log_timestamp = true
Postgres: Explain
Postgres' explain works like MySQL's. Different results
Oracle
Take your DBA out to lunch ..often
Thanks!
Contributing
[email protected] If you find bugs, everyone wins if you report them [email protected] Lots of other folks use and hack on RT. Join the mailing list to share tips and tricks.
Recommended Reading
RT Essentials Embedding Perl in HTML with Mason perldoc RT::StyleGuide perldoc RT::Ticket perldoc RT::Ticket_Overlay