# $Id: Bugzilla.pm,v 1.6 2008/08/25 06:39:28 thoger Exp $ # Bugzilla interface # Lubomir Kundrak package Libexig::Bugzilla; use XMLRPC::Lite; use warnings; use strict; # Instantialize a Bugzilla connection sub new { my $class = shift; my $self = shift; # Login credentials if ($self->{username} and $self->{password}) { $self->{creds} = [$self->{username}, $self->{password}]; } else { die 'Need username and password if not dryrun' unless $self->{dryrun}; $self->{creds} = []; } # XMLRPC endpoint $self->{url} = 'https://bugzilla.redhat.com/xmlrpc.cgi' unless $self->{url}; $self->{rpc} = new XMLRPC::Lite ( proxy => $self->{url}, encoding => 'UTF-8', ) or die 'Could not create a RPC instnace'; if ($self->{debug}) { use Data::Dumper; } bless $self, $class; return $self; } # Get list of owners of a package from Bugzilla sub owners { my $self = shift; my $component = shift; print STDERR "Getting list of owners\n" if $self->{debug}; # Call bugzilla my $call = $self->{rpc}->call('bugzilla.getCompInfo', $component); my $result = $call->result or die $call->faultstring; print STDERR Dumper ($result) if $self->{debug}; # Eliminate duplicates my %people; foreach my $instance (@{$result}) { # blacklist some EOLed products if ($instance->{'product'} eq 'Red Hat Linux' || $instance->{'product'} eq 'Red Hat Linux Beta' || $instance->{'product'} eq 'Red Hat Public Beta' || $instance->{'product'} eq 'Red Hat Raw Hide' || $instance->{'product'} eq 'Fedora Legacy' || $instance->{'product'} eq 'eCos' || $instance->{'product'} eq 'eCos runtime kernel' || $instance->{'product'} =~ /^Red Hat Powertools/ || $instance->{'product'} =~ /^Stronghold /) { next; } # XXX: Add also 'initialqa'? $people{$instance->{initialowner}} = 1 if defined $instance->{initialowner}; # Add initial CC list if any foreach my $cc (@{ $instance->{'initialcclist'} }) { $people{$cc} = 1; } } return keys %people; } # Create a bug (unless dryrun) and return its ID sub file_bug { my $self = shift; return 0 if $self->{dryrun}; print STDERR "Creating a bug\n" if $self->{debug}; my $call = $self->{rpc}->call('bugzilla.createBug', shift, @{$self->{creds}}); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to createBug: '.Dumper($result) if $self->{debug}; return $result->[0]; } # Get bugs sub get_bugs { my $self = shift; my $bugs = shift or die 'No bugs to fetch!'; my $columns = shift; $columns = [] unless ($columns); # The default set # sort out aliases and bug ids my @bugs_ids; my @bugs_aliases; foreach my $bug (@{ $bugs }) { if ($bug =~ /^[0-9]+$/) { push @bugs_ids, $bug; } else { push @bugs_aliases, $bug; } } # query bugs ids and aliases separately my @results; if (scalar(@bugs_ids) > 0) { my $call = $self->{rpc}->call('bugzilla.runQuery', { 'bug_id' => \@bugs_ids, 'bug_status' => [], 'column_list' => $columns, }, @{$self->{creds}}); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to ID runQuery: '.Dumper($result) if $self->{debug}; if (scalar(@bugs_ids) != scalar(@{ $result->{bugs} })) { die "Bugzilla returned unexpected number of results!"; } push @results, @{ $result->{bugs} }; } if (scalar(@bugs_aliases) > 0) { my $call = $self->{rpc}->call('bugzilla.runQuery', { 'bug_status' => [], 'column_list' => $columns, 'field0-0-0' => 'alias', 'type0-0-0' => 'anywordssubstr', 'value0-0-0' => join(" ", @bugs_aliases), }, @{$self->{creds}}); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to Alias runQuery: '.Dumper($result) if $self->{debug}; if (scalar(@bugs_aliases) != scalar(@{ $result->{bugs} })) { die "Bugzilla returned unexpected number of results!"; } push @results, @{ $result->{bugs} }; } return \@results; } # Add blockers (unless dryrun) to a bug sub add_blockers { my $self = shift; return 0 if $self->{dryrun}; my $bug = shift or die 'No blocker!'; my $parents = shift or die 'No bug to block!'; my $call = $self->{rpc}->call('bugzilla.updateDepends', $bug, { 'blocked' => $parents, 'action' => 'add', }, @{$self->{creds}}, 1); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to updateDepends: '.Dumper($result) if $self->{debug}; return undef; } # Add comment - wrapper around bugzilla addComment XMLRPC method # # Mandatory arguments: # bugid, comment # Optional arguments: # isprivate, timestamp, worktime, bz_gid, private_in_it, nomail sub add_comment { my $self = shift; my $bug = shift or die 'No bug!'; my $comment = shift or die 'No comment!'; if ($self->{dryrun}) { print STDERR 'Would add following comment to bug: #'.$bug."\n"; print STDERR "$comment\n"; return 0; } my $call = $self->{rpc}->call('bugzilla.addComment', $bug, $comment, @{$self->{creds}}, @_); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to addComment: '.Dumper($result) if $self->{debug}; return undef; } # Add private comment to a bug # # Arguments: # bugid, comment sub add_private_comment { my $self = shift; my $bug = shift; my $comment = shift; $self->add_comment($bug, $comment, 1); } # Close bug - wrapper around bugzilla closeBug XMLRPC method # # Mandatory arguments: # bugid, resolution # Optional arguments: # dupeid, fixedin, comment, isprivate, private_in_it, nomail sub close_bug { my $self = shift; my $bug = shift or die 'No bug!'; my $resolution = shift or die 'No resolution!'; if ($self->{dryrun}) { print STDERR 'Would close bug #'.$bug.' as: '.$resolution."\n"; return 0; } my $call = $self->{rpc}->call('bugzilla.closeBug', $bug, $resolution, @{$self->{creds}}, @_); my $result = $call->result or die $call->faultstring; print STDERR 'Bugzilla answered to closeBug: '.Dumper($result) if $self->{debug}; return undef; } # Close bug with comment # # Mandatory arguments: # bugid, resulution, comment # Optional arguments: # newfixedin, dupeid sub close_bug_with_comment { my $self = shift; my $bug = shift; my $resolution = shift; my $comment = shift or die 'No comment!'; my $fixedin = shift; my $dupeid = shift; $self->close_bug($bug, $resolution, $dupeid, $fixedin, $comment); } # Call arbitrary Bugzilla XMLRPC method # # Arguments: method, method-specific arguments sub rpccall { my $self = shift; my $method = shift or die 'No XMLRPC method specified!'; print "Calling bugzilla.$method with arguments:\n", Dumper(\@_) if $self->{'debug'}; my $call = $self->{rpc}->call('bugzilla.'.$method, @_); my $result = $call->result; if (!defined($result)) { print STDERR "XMLRPC call to bugzilla.$method failed:\n"; print STDERR $call->faultstring; return undef; } print "Bugzilla answer to bugzilla.$method:\n", Dumper($result), "\n" if $self->{'debug'}; return $result; } 1;