]>
Commit | Line | Data |
---|---|---|
320f0092 MKG |
1 | =head1 Automating Tasks in RT |
2 | ||
3 | As RT tickets are created, worked on, and resolved, there are sometimes | |
4 | updates or notifications that have defined rules and could be automatic. | |
5 | These might include increasing ticket priority over time so tickets don't | |
6 | get lost, resolving old tickets that haven't had any activity for a period of | |
7 | time, or sending email notifications based on some ticket criteria like | |
8 | being 3 days old and still having a status of new. | |
9 | ||
10 | The tool for automating RT tasks is L<rt-crontool>. It's designed to be | |
11 | run from the cron scheduler and accepts a set of parameters that define | |
12 | what RT objects it should operate on and what it should do. The sections | |
13 | below describe some common L<rt-crontool> tasks as examples of the | |
14 | different ways you can automate tasks. | |
15 | ||
16 | All of the options for L<rt-crontool> are documented with the tool itself: | |
17 | ||
18 | $ perldoc bin/rt-crontool | |
19 | ||
20 | and on the Best Practical web site. | |
21 | ||
22 | =head2 Running C<rt-crontool> | |
23 | ||
24 | As you'll see in the examples below, this tool gives full access to RT. | |
25 | To manage the scope of changes that could be performed by the tool, we | |
26 | recommended creating a dedicated unix user with limited privileges for | |
27 | this purpose. Then create a user in RT with just enough access to | |
28 | perform the changes you need to automate, and set the "Unix login" field | |
29 | of the RT user to the username of the unix user you created. See the | |
30 | L<rt-crontool> documentation for more information. | |
31 | ||
32 | =head2 Testing Tips | |
33 | ||
34 | When setting up a new automated crontool job, keep in mind that you might be | |
35 | modifying a large number of tickets, especially the first time you run it. | |
36 | Changes to tickets can trigger scrips just like the same change made via | |
37 | the user interface. For example, changing the status to resolved will trigger | |
38 | the 'On Resolve' scrips, which often means sending email. Depending on the | |
39 | modification, you could end up sending a lot of email or triggering other | |
40 | actions. | |
41 | ||
42 | You can test your TicketSQL search queries in the RT web interface | |
43 | (using the Advanced tab of ticket search), and use bulk update if you | |
44 | want to prepare things for your new automated job. You can also disable | |
45 | scrips which you wish to avoid, or turn off outgoing mail with the | |
46 | L<RT_Config.pm/"$MailCommand"> option. This can be useful if you want to | |
47 | clean up older tickets without sending notifications to requestors for | |
48 | tickets that were resolved years ago. | |
49 | ||
50 | To help with debugging, the C<--verbose> option will give you more output. | |
51 | The C<--log> option accepts all of the valid log levels for RT and allows | |
52 | you to change the logging level just for the automated job. While testing, | |
53 | it's often convenient to set: | |
54 | ||
55 | --log debug | |
56 | ||
57 | to see what's happening. | |
58 | ||
59 | =head1 A Simple Search | |
60 | ||
61 | Starting with a simple example, this command performs a search and | |
62 | displays output, but doesn't do anything to the returned tickets. | |
63 | This can be useful for safely testing search criteria. | |
64 | ||
65 | bin/rt-crontool --search RT::Search::FromSQL \ | |
66 | --search-arg "Owner = 'root'" \ | |
67 | --action RT::Action \ | |
68 | --verbose \ | |
69 | --log debug | |
70 | ||
71 | The C<--search> argument sets the search module RT should use, in this | |
72 | case L<RT::Search::FromSQL> which processes TicketSQL. The second | |
73 | argument, C<--search-arg>, is the search query to use. These are | |
74 | the same queries you create in the RT search interface, so can use | |
75 | the RT web UI to refine your queries before setting up your job. | |
76 | ||
77 | The C<--action> argument is set to L<RT::Action> which is the base class | |
78 | for RT actions. Since this class doesn't perform any action itself, this | |
79 | command will just output the results of the TicketSQL search. | |
80 | ||
81 | =head1 Auto-resolve Aged Tickets | |
82 | ||
83 | You can auto-set status based on any criteria you can define in | |
84 | a TicketSQL statement. For example, this command will resolve all | |
85 | active tickets that haven't been acted on in a month or more: | |
86 | ||
87 | bin/rt-crontool --search RT::Search::FromSQL \ | |
88 | --search-arg "(Status != 'resolved' AND Status != 'rejected') \ | |
89 | AND LastUpdated <= '1 month ago'" \ | |
90 | --action RT::Action::SetStatus \ | |
91 | --action-arg resolved | |
92 | ||
93 | The search is similar to the previous example with a slightly more | |
94 | complicated search argument. Note that since LastUpdated is treated as | |
95 | a timestamp (which increases over time) C<LastUpdated <= '1 month ago'> | |
96 | means "the timestamp when it was updated is before the timestamp one | |
97 | month ago" and not "updated less than a month ago." | |
98 | ||
99 | The C<--action> in this case uses the L<RT::Action::SetStatus> module | |
100 | with an C<--action-arg> of C<resolved>. For each of the tickets | |
101 | returned from the search query, the status is set to resolved. When | |
102 | setting up automated tasks, you can use actions provided as part of RT, | |
103 | actions available from extensions, or actions you create yourself. | |
104 | ||
105 | As noted previously, the normal RT rules apply when running actions | |
106 | with L<rt-crontool>, so for this example applicable 'On Resolve' | |
107 | scrips will run. If a ticket has unresolved dependencies, it will | |
108 | log an error since tickets can't be resolved until dependencies are | |
109 | resolved. Also, the status argument must be valid for the lifecycle of | |
110 | the selected tickets, and the transition must be allowed. | |
111 | ||
112 | =head1 Commenting and Corresponding on a Ticket | |
113 | ||
114 | The following command records a comment on all tickets returned from the | |
115 | query -- in this case, tickets that are new and unowned after 3 days. | |
116 | ||
117 | bin/rt-crontool --search RT::Search::FromSQL \ | |
118 | --search-arg "Owner = 'Nobody' AND Status = 'new' \ | |
119 | AND Created < '3 days ago'" \ | |
120 | --action RT::Action::RecordComment \ | |
121 | --template 'Unowned tickets' | |
122 | ||
123 | The L<RT::Action::RecordComment> action does just that, it records a | |
124 | comment just like replying to a comment email or commenting in the | |
125 | RT UI. It uses the global RT template defined by C<--template>, so you | |
126 | could put whatever you like in that template. For example: | |
127 | ||
128 | Subject: {$Ticket->id} new and unowned | |
129 | RT-Send-Cc: support-backup@example.com | |
130 | ||
131 | Ticket {$Ticket->id} is still new and unowned after 3 days! | |
132 | ||
133 | You can set up a similar command to send a reply rather than a comment | |
134 | using the L<RT::Action::RecordCorrespondence> module. | |
135 | ||
136 | =head1 Sending Notifications | |
137 | ||
138 | While the example above sends notifications as a side-effect of recording | |
139 | a comment, you can also send notifications directly. | |
140 | ||
141 | bin/rt-crontool --search RT::Search::FromSQL \ | |
142 | --search-arg "(Status != 'resolved' AND Status != 'rejected') \ | |
143 | AND Queue = 'Project Work'" \ | |
144 | --condition RT::Condition::Overdue \ | |
145 | --action RT::Action::NotifyGroup \ | |
146 | --action-arg 'project-manager@example.com' \ | |
147 | --template 'Overdue task' | |
148 | ||
149 | This example shows the C<--condition> argument and the | |
150 | L<RT::Condition::Overdue> module, which returns true if the current | |
151 | time (the time the cron job is running) is past the Due date on the | |
152 | ticket. Like the C<--action> argument, you can use conditions | |
153 | provided with RT, added from extensions, or conditions you have | |
154 | created. | |
155 | ||
156 | L<RT::Action::NotifyGroup>, despite the "Group" in the name, can accept a | |
157 | bare email address or list of addresses as the action argument and it will | |
158 | send mail to them. A combination of email addresses and group names separated | |
159 | by commas also works. RT usernames are valid unless they conflict with group | |
160 | names. | |
161 | ||
162 | The action sends email, but unlike comment and correspond above, it | |
163 | doesn't record a transaction in the ticket history. | |
164 | ||
165 | =head1 Escalating Priority | |
166 | ||
167 | RT has a built-in ticket priority system with priority values from | |
168 | 0 to 99. Depending on how you configure your queues, you can set 1 as the | |
169 | top priority with lower numbers meaning more important, or 99 can be the | |
170 | top priority with higher numbers meaning more important. You can set this | |
171 | in your queue configuration at Tools -> Configuration -> Queues. On the queue | |
172 | configuration page, set "Priority starts at" and "Over time, priority moves | |
173 | toward". | |
174 | ||
175 | Whichever scheme you choose, RT's L<RT::Action::EscalatePriority> can | |
176 | escalate the priority over time so tickets that are closer to their due | |
177 | date and are still not resolved have priority escalated automatically. | |
178 | ||
179 | This command escalates tickets in a designated queue: | |
180 | ||
181 | bin/rt-crontool --search RT::Search::ActiveTicketsInQueue \ | |
182 | --search-arg "General" \ | |
183 | --action RT::Action::EscalatePriority | |
184 | ||
185 | The C<--search-arg> is the name of the queue in which to escalate tickets. | |
186 | As shown in previous examples, you can also set your criteria using a | |
187 | TicketSQL query as well: | |
188 | ||
189 | bin/rt-crontool --search RT::Search::FromSQL \ | |
190 | --search-arg "(Status='new' OR Status='open') AND Due > 'Jan 1, 1970'" \ | |
191 | --action RT::Action::EscalatePriority | |
192 | ||
193 | This example will find new and open tickets in all queues, but will skip tickets | |
194 | with no explicit due dates set. Maybe you only want to bump the priority on tasks | |
195 | that have to be done by a certain date. | |
196 | ||
197 | L<RT::Action::LinearEscalate> is an alternative escalation module that | |
198 | handles the "Due date not set" condition for you. It also offers some | |
199 | configuration options to control whether a transaction is recorded on the | |
200 | ticket and whether LastUpdated is modified. | |
201 | ||
202 | =head1 Transactions | |
203 | ||
204 | Many actions and conditions are also used in RT in scrips and may require | |
205 | a transaction in addition to a ticket. For such cases, L<rt-crontool> | |
206 | provides a C<--transaction> argument to designate a transaction. Valid | |
207 | values are C<first>, C<last>, and C<all> and these are relative to the | |
208 | current ticket being processed. C<first> and C<last> are the first and | |
209 | last transaction on the ticket. Be careful with the C<all> option since | |
210 | it will run the action on all transactions for the ticket. | |
211 | ||
212 | Since actions and conditions can be used in different contexts, you | |
213 | may need to provide a transaction object even if it doesn't seem | |
214 | necessary for your automated job. If you're seeing errors about | |
215 | a missing transaction, setting C<--transaction> to C<first> or | |
216 | C<last> is usually safe and will resolve the error. | |
217 | ||
218 | You can also target specific transaction types with C<--transation-type>. | |
219 | This argument accepts one or more transaction types as a comma-separated | |
220 | list. | |
221 | ||
222 | Using these options together, you can set up a command that sets the | |
223 | appropriate transaction object for your conditions and actions. For | |
224 | example, if you had an action you wanted to perform based on the content | |
225 | of the last reply on stalled tickets, you could do something like: | |
226 | ||
227 | bin/rt-crontool --search RT::Search::FromSQL \ | |
228 | --search-arg "Status = 'stalled' AND Queue = 'General'" \ | |
229 | --action RT::Action::CheckLastCorrespond \ | |
230 | --transaction last \ | |
231 | --transaction-type Correspond | |
232 | ||
233 | ||
234 | =cut |