RT 6.0.3 har et egnet regelverktøy under Admin → Utløsere. Løsningen bør være én samlet, køspesifikk utløser med handlingen User Defined. Flere overlappende flytteregler kan konkurrere om samme sak.
Ingen regel er opprettet eller aktivert i RT. Kontoen som ble kontrollert, fikk en ShowScrips/ModifyScrips-feil under globale handlinger. RT-drift må bekrefte rettighetene og installere og teste utkastet.
Den tidligere saken RT 7127382 beskriver samme behov. Den angir it-hjelp som mottaks- og felleskø, SE* som it-sentrum og BL05 som it-ovre-blindern. Saken viser også til en CSV-tabell som nå gir 404. scala_koekart.csv er et foreløpig, rekonstruert køkart basert på denne saken og historiske RT-saker.
Foreslått RT-oppsett
- La Scala-meldingene komme direkte til
it-hjelp. Bruk omdirigering som bevarer avsenderen, ikke vanlig videresending. - Opprett en køspesifikk utløser med navnet Rute Scala-alarm etter byggkode på
it-hjelp. - Bruk disse innstillingene:
- Betingelse:
On Create Via Email, dersom RT-drift bekrefter at UiOs lokale betingelse er egnet. Ellers brukes den dokumenterte kjerne-betingelsenOn Create; koden avgrenser videre på kø, avsender og innhold. - Handling:
User Defined - Forberedelseskode:
return 1; - «Custom action commit code»: scala_rt_ruting_action.pl
- Mal:
Blank - Nivå/Stage:
Normal(internverdiTransactionCreate), ikkeBatch - Status under test: deaktivert med utløserens egen aktiv/deaktivert-kontroll
- Sortering: før eventuelle
On Create-varsler dersom køen styrer mottakere, mal eller avsender
- Betingelse:
- Test med utgående e-post slått av. Aktiver først etter godkjent køkart og kontroll av faktiske Scala-meldinger.
Rutingskategorier
| Kategori | Signaturer | Kø |
|---|---|---|
| Lokal spiller eller maskinvare | heartbeat overdue, RunICIsDown, ApplicationTerimatedAbnormally, PlayerEventWatchdogTimerExpired | Geografisk kø fra spillerkode |
| Lokal avspilling | ScriptPlayedIncorrectly med Error 24 eller 304.23 | Geografisk kø fra spillerkode |
| Helseopplasting | SSHealthNonrecoverableErrorUploadingHealthLogs | Geografisk kø når spillerkoden er kjent, ellers it-hjelp |
| Sentralt innhold eller plan | MediaFileMissing, NetworkClientPlanUnreadable, Error 314 eller teksten invalid code point | it-hjelp |
| Lisens | RunICInvalidLicense | it-hjelp |
| Ikke klassifisert | Error 824.1 og andre ukjente Scala-signaturer | it-hjelp |
| Ukjent byggkode | Kjent lokal feil uten bekreftet geografisk treff | it-hjelp |
ApplicationTerimatedAbnormally er skrevet slik av Scala og må beholdes i filteret.
Foreløpige kontrolltilfeller
| Spiller og feil | Forventet kø |
|---|---|
se27-01-1001.skilt.uio.no, lokal feil | it-sentrum |
bl16-01-102-hoyre, lokal feil | it-ovre-blindern |
bl18-03-3601.skilt.uio.no, lokal feil | it-nedre-blindern |
bl20-01-101.skilt.uio.no, lokal feil | it-nedre-blindern |
GA02 Domus Odontologica, lokal feil | it-gaustad-geitmyrsveien |
ga24-015n006.skilt.uio.no, lokal feil | it-lvb |
Kjent spiller med RunICInvalidLicense | it-hjelp |
| Kjent spiller med Error 314 | it-hjelp |
DigEx_3.Etg Mellom C og D | it-hjelp |
Før aktivering
- Gjenopprett eller erstatt den tidligere, komplette byggkode–kø-tabellen.
- Avklar blant annet
GA06; den er ikke lagt inn fordi riktig geografisk kø ikke kunne bekreftes. - Bruk
Player.BuildingIDforan vertsnavnprefikset når feltet finnes og verdien er kvalitetssikret. Bruk eierarbeidsgruppe eller lokasjon først når en godkjent kobling til kø foreligger. - Bekreft at alle målkøene bruker kompatibel livssyklus og status.
- Test at Exchange/MTA bevarer
From: noreply_UiO@atea-signage.no. Koden avviser avvik og lar saken stå iit-hjelp. - La Exchange/MTA kontrollere betrodd transport og SPF, DKIM eller DMARC. RT-koden alene autentiserer ikke domenet.
- Avklar de nøyaktige fleksifeltene før tema
Maskinvareog fritekstverdienScalalegges til. - Legg deduplisering og terskler i en egen fase. Dette utkastet gjør bare første ruting.
- Lokale feil: spiller-ID + alarmsignatur + feilkode innen én time.
- Sentrale feil: alarmsignatur + feilkode + medie-/plan-/kanalnavn på tvers av spillere.
- Error 304.23 og helseopplastingsfeil: oppfølging først ved minst to treff innen én time, dersom driftsdata bekrefter at enkeltfeil er forbigående.
- Samme lokale signatur i minst to geografiske områder innen én time: samle eller eskaler til
it-hjelp.
Teknisk grunnlag
- rt-mailgate for RT 6.0.3: velger kø fra adresse eller parameter, ikke fra meldingsinnhold.
- Egendefinerte betingelser og handlinger: mønster for User Defined-handlinger.
- RT::Scrip:
Blank-mal, nivå og egendefinerte kodefelt. - RT::Transaction, RT::Ticket og RT::Queue: API-ene utkastet bruker.
Koden er syntakskontrollert med Perl (syntax OK), kjørt mot ti lokale rutingstester og statisk kontrollert mot RT 6.0.3-API-et. Testene ligger i test_scala_rt_ruting_action.pl. Den må fortsatt funksjonstestes i et RT 6.0.3-testmiljø før aktivering.'
spillerregel,rt_koe,status,kilde,merknad
^SE,it-sentrum,dokumentert,RT 7127382,Tidligere bestilling sier at alle SE-koder skal hit
^BL05,it-ovre-blindern,dokumentert,RT 7127382,Eksplisitt eksempel i tidligere bestilling
^BL16,it-ovre-blindern,verifisert,RT 7441925,AV-sak for Georg Morgenstiernes hus
^BL18,it-nedre-blindern,verifisert,RT 7231766 og 7019636,Historiske lokale saker
^BL20,it-nedre-blindern,verifisert,RT 7401074,AV-sak for Helga Engs hus
^GA02,it-gaustad-geitmyrsveien,verifisert,RT 7479225,AV-sak for Domus Odontologica
^GA24,it-lvb,verifisert,RT 7472499 og 7633324,Digital skilting og lokal sak for Livsvitenskapsbygget
.*,it-hjelp,fallback,RT 7127382,Ukjent byggkode eller generisk feiltest_scala_rt_ruting_action.pl
use strict;
use warnings;
use utf8;
package MockAddress;
sub new { bless { address => $_[1] }, $_[0] }
sub address { $_[0]->{address} }
package MockQueueObject;
sub new { bless { ticket => $_[1] }, $_[0] }
sub Name { $_[0]->{ticket}->{queue} }
package MockTicket;
sub new { bless $_[1], $_[0] }
sub Subject { $_[0]->{subject} }
sub Id { 1001 }
sub QueueObj { MockQueueObject->new($_[0]) }
sub IsWatcher {
my ($self, %args) = @_;
return $self->{requestor_ok}
&& $args{Type} eq 'Requestor'
&& lc($args{Email}) eq lc('noreply_UiO@atea-signage.no');
}
sub SetQueue {
my ($self, $queue) = @_;
$self->{queue} = $queue;
return (1, 'ok');
}
package MockTransaction;
sub new { bless $_[1], $_[0] }
sub Addresses {
my ($self) = @_;
return { From => [ MockAddress->new($self->{from}) ] };
}
sub Content { $_[0]->{body} }
package MockScripAction;
sub new { bless $_[1], $_[0] }
sub TicketObj { $_[0]->{ticket} }
sub TransactionObj { $_[0]->{transaction} }
package RT;
sub SystemUser { 'system-user' }
package RT::Queue;
sub new { bless {}, $_[0] }
sub Load {
my ($self, $name) = @_;
$self->{name} = $name;
return ($name, 'ok');
}
sub Id { $_[0]->{name} }
package RT::Logger;
sub warning { 1 }
sub error { 1 }
sub info { 1 }
package main;
my $action_path = 'rt-filterutkast/scala_rt_ruting_action.pl';
open my $action_file, '<:encoding(UTF-8)', $action_path
or die "Kan ikke lese $action_path: $!\n";
local $/;
my $action_code = <$action_file>;
close $action_file;
eval 'sub run_action { my ($self) = @_;' . "\n"
. $action_code . "\n}";
die "Kompileringsfeil i handlingen: $@" if $@;
my $scala_header = <<'SCALA';
Scala Content Manager on uio.atea-signage.no received an error from one of its players:
SCALA
my @tests = (
{
name => 'Error 314 er sentral selv med kjent spiller',
subject => 'se27 Reported at Scala [ScriptPlayedIncorrectly]',
body => $scala_header . "Player Name: se27-04-4219.skilt.uio.no\nProblem 6: Script played incorrectly.\nError 314: Text contains an invalid code point\n",
expected => 'it-hjelp',
},
{
name => 'Error 24 går til Nedre Blindern',
subject => 'bl18 Reported at Scala [ScriptPlayedIncorrectly]',
body => $scala_header . "Player Name: bl18-03-3601.skilt.uio.no\nProblem 6: Script played incorrectly.\nError 24: Not enough memory\n",
expected => 'it-nedre-blindern',
},
{
name => 'DirectSound-feilkode finnes midt på linjen',
subject => 'bl20 Reported at Scala [ScriptPlayedIncorrectly]',
body => $scala_header . "Player Name: bl20-05-502.skilt.uio.no\nProblem 6: Script played incorrectly.\nDirectSound Error 304.23: DSERR_BUFFERLOST\n",
expected => 'it-nedre-blindern',
},
{
name => 'Watchdog går til Nedre Blindern',
subject => 'bl20 Reported at Scala [PlayerEventWatchdogTimerExpired]',
body => $scala_header . "Player Name: bl20-01-101.skilt.uio.no\nWatchdog detected that Player failed to complete playing.\nError 702.37: timeout\n",
expected => 'it-nedre-blindern',
},
{
name => 'Heartbeat i GA24 går til LVB',
subject => 'A heartbeat is overdue from a Scala player',
body => $scala_header . "Player Name: ga24-035s014.skilt.uio.no\n",
expected => 'it-lvb',
},
{
name => 'Error 824.1 er ikke klassifisert',
subject => 'se27 Reported at Scala [ScriptPlayedIncorrectly]',
body => $scala_header . "Player Name: se27-04-4219.skilt.uio.no\nProblem 6: Script played incorrectly.\nError 824.1: ukjent\n",
expected => 'it-hjelp',
},
{
name => 'Ukjent byggkode får fallback',
subject => 'ga06 Reported at Scala [RunICIsDown]',
body => $scala_header . "Player Name: ga06-01-001.skilt.uio.no\nRunICIsDown\n",
expected => 'it-hjelp',
},
{
name => 'Ukjent signatur får fallback selv med kjent spiller',
subject => 'se27 Reported at Scala [UnknownAlarm]',
body => $scala_header . "Player Name: se27-04-4219.skilt.uio.no\nUnknownAlarm\n",
expected => 'it-hjelp',
},
{
name => 'BuildingID brukes før spillerkode',
subject => 'ukjent spiller Reported at Scala [RunICIsDown]',
body => $scala_header . "Player Name: ukjent-spiller\nPlayer.BuildingID: GA02\nRunICIsDown\n",
expected => 'it-gaustad-geitmyrsveien',
},
{
name => 'Feil avsender flytter ikke saken',
subject => 'se27 Reported at Scala [RunICIsDown]',
body => $scala_header . "Player Name: se27-04-4219.skilt.uio.no\nRunICIsDown\n",
from => 'annen@example.org',
requestor_ok => 0,
expected => 'it-hjelp',
},
);
my $failures = 0;
for my $test (@tests) {
my $ticket = MockTicket->new({
subject => $test->{subject},
queue => 'it-hjelp',
requestor_ok => $test->{requestor_ok} // 1,
});
my $transaction = MockTransaction->new({
body => $test->{body},
from => $test->{from} // 'noreply_UiO@atea-signage.no',
});
my $self = MockScripAction->new({
ticket => $ticket,
transaction => $transaction,
});
run_action($self);
if ($ticket->{queue} eq $test->{expected}) {
print "OK: $test->{name}\n";
}
else {
$failures++;
print "FEIL: $test->{name}: fikk $ticket->{queue}, ventet $test->{expected}\n";
}
}
die "$failures test(er) feilet\n" if $failures;
print scalar(@tests) . " tester bestått\n";
# Utkast for RT 6.0.3.
# Bruk som «Custom action commit code» for «User Defined» i en utløser som bare kjøres:
# - i mottakskøen it-hjelp
# - med «On Create Via Email», eller «On Create» dersom RT-drift velger kjernen
#
# Ikke aktiver før køkartet og en ekte Scala-melding er testet av RT-drift.
my $ticket = $self->TicketObj;
my $txn = $self->TransactionObj;
my $subject = $ticket->Subject // '';
my $common_queue = 'it-hjelp';
# Forsvar i dybden: Handlingen skal bare kunne flytte saker fra mottakskøen.
return 1 unless lc($ticket->QueueObj->Name // '') eq lc($common_queue);
# Billig avgrensning før vi leser meldingsinnholdet.
return 1 unless $subject =~ /(?:heartbeat(?:\s+is)?\s+overdue|Reported at|Scala)/i;
# Kontroller både meldingsavsenderen og RTs registrerte innmelder.
my $expected = 'noreply_UiO@atea-signage.no';
my $addresses = $txn->Addresses || {};
my @from = @{ $addresses->{From} || [] };
my $from_ok = @from == 1
&& defined $from[0]->address
&& lc($from[0]->address) eq lc($expected);
my $requestor_ok = $ticket->IsWatcher(
Type => 'Requestor',
Email => $expected,
);
unless ($from_ok && $requestor_ok) {
my $seen = join ', ', map { $_->address // '' } @from;
RT::Logger->warning(
'Scala-ruting hoppet over sak ' . $ticket->Id
. "; From='$seen'; forventet '$expected'"
);
return 1;
}
my $body = $txn->Content(Type => 'text/plain') // '';
return 1 unless $body
=~ /Scala Content Manager on uio\.atea-signage\.no/i;
my ($player) = $body =~ /^Player Name:\s*([^\r\n]+)/mi;
my ($building_id) = $body =~ /^(?:Player\.BuildingID|BuildingID):\s*([^\r\n]+)/mi;
my ($problem_no) = $body =~ /^Problem\s+(\d+):/mi;
my ($error_code) = $body =~ /\bError\s+([0-9]+(?:\.[0-9]+)?):/mi;
$player //= '';
$building_id //= '';
$problem_no //= '';
$error_code //= '';
$player =~ s/^\s+|\s+$//g;
$building_id =~ s/^\s+|\s+$//g;
my $destination = $common_queue;
my $signal_text = "$subject\n$body";
# Sentrale feil skal ikke fordeles etter spillerens plassering.
my $is_generic =
$signal_text =~ /\b(?:MediaFileMissing
|NetworkClientPlanUnreadable
|RunICInvalidLicense)\b/ix
|| $error_code eq '314'
|| $signal_text =~ /invalid code point/i;
# Bare dokumenterte, lokale signaturer kan flyttes til geografisk kø.
my $is_local =
$signal_text =~ /heartbeat(?:\s+is)?\s+overdue/i
|| $signal_text =~ /\b(?:RunICIsDown
|ApplicationTerimatedAbnormally
|PlayerEventWatchdogTimerExpired
|SSHealthNonrecoverableErrorUploadingHealthLogs)\b/ix
|| $signal_text =~ /Watchdog detected that Player failed to complete/i
|| (
$signal_text =~ /(?:\bScriptPlayedIncorrectly\b|played incorrectly)/i
&& ($error_code eq '24' || $error_code eq '304.23')
);
my $category = $is_generic ? 'felles'
: $is_local ? 'lokal'
: 'ukjent';
# Foreløpig køkart. Se scala_koekart.csv for kilde og status.
my @geographic_queues = (
[ qr/^SE/i, 'it-sentrum' ],
[ qr/^BL(?:05|16)\b/i, 'it-ovre-blindern' ],
[ qr/^BL(?:18|20)\b/i, 'it-nedre-blindern' ],
[ qr/^GA02\b/i, 'it-gaustad-geitmyrsveien' ],
[ qr/^GA24\b/i, 'it-lvb' ],
);
if ($is_local && !$is_generic) {
ROUTING_KEY:
for my $routing_key (grep { length } ($building_id, $player)) {
for my $rule (@geographic_queues) {
if ($routing_key =~ $rule->[0]) {
$destination = $rule->[1];
last ROUTING_KEY;
}
}
}
}
# Ukjent byggkode og generiske feil blir i felleskøen.
return 1 if lc($ticket->QueueObj->Name // '') eq lc($destination);
my $queue = RT::Queue->new(RT->SystemUser);
my ($queue_id, $queue_message) = $queue->Load($destination);
unless ($queue_id) {
RT::Logger->error(
"Scala-ruting: finner ikke kø '$destination': $queue_message"
);
return 1;
}
my ($ok, $message) = $ticket->SetQueue($queue->Id);
unless ($ok) {
RT::Logger->error(
'Scala-ruting: kunne ikke flytte sak ' . $ticket->Id
. " til '$destination': $message"
);
return 1;
}
RT::Logger->info(
'Scala-ruting: sak ' . $ticket->Id
. "; kategori='$category'; spiller='$player'; bygg='$building_id'"
. "; problem='$problem_no'; feil='$error_code'"
. "; kø='$destination'"
);
return 1;