[Bps-public-commit] rt-extension-aws-assets branch sync-utility updated. a2841594f394d5965e109278592c43533fbbacce

BPS Git Server git at git.bestpractical.com
Fri Feb 9 22:11:49 UTC 2024


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt-extension-aws-assets".

The branch, sync-utility has been updated
       via  a2841594f394d5965e109278592c43533fbbacce (commit)
       via  593318d868804fc02982dbb24b47d343720ea372 (commit)
       via  aa8917f2d0d8b887fc48a5de904addc2b8b22993 (commit)
      from  2b2677bf3c1859a96939b48b09d8bb0ea1e18e2e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit a2841594f394d5965e109278592c43533fbbacce
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Feb 9 15:58:56 2024 -0500

    Insert and update for RDS resources

diff --git a/bin/rt-import-aws-assets.in b/bin/rt-import-aws-assets.in
index c099d9b..ad285df 100644
--- a/bin/rt-import-aws-assets.in
+++ b/bin/rt-import-aws-assets.in
@@ -25,17 +25,17 @@ sub run{
     my %args = $class->process_args(@args);
 
     if ( $args{insert} ) {
-        my ($reservations, $token);
+        my ($aws_resources, $token);
 
         do {
-            ($reservations, $token) =
+            ($aws_resources, $token) =
                 RT::Extension::AWS::Assets::FetchMultipleAssetsFromAWS(
                     Region => $args{'region'},
                     ServiceType => $args{'type'},
-                    NextToken => $token );
+                    Token => $token );
 
                 RT::Extension::AWS::Assets::InsertAWSAssets(
-                    Reservations => $reservations,
+                    AWSResources => $aws_resources,
                     Region => $args{'region'},
                     ServiceType => $args{'type'},
                     CurrentUser => GetCurrentUser() );
@@ -44,17 +44,17 @@ sub run{
     }
 
     if ( $args{update} ) {
-        my ($reservations, $token);
+        my ($aws_resources, $token);
 
         do {
-            ($reservations, $token) =
+            ($aws_resources, $token) =
                 RT::Extension::AWS::Assets::FetchMultipleAssetsFromAWS(
                     Region => $args{'region'},
                     ServiceType => $args{'type'},
-                    NextToken => $token );
+                    Token => $token );
 
                 RT::Extension::AWS::Assets::UpdateAWSAssets(
-                    Reservations => $reservations,
+                    AWSResources => $aws_resources,
                     Region => $args{'region'},
                     ServiceType => $args{'type'},
                     CurrentUser => GetCurrentUser() );
diff --git a/lib/RT/Extension/AWS/Assets.pm b/lib/RT/Extension/AWS/Assets.pm
index 58c5968..8d144ce 100644
--- a/lib/RT/Extension/AWS/Assets.pm
+++ b/lib/RT/Extension/AWS/Assets.pm
@@ -87,9 +87,12 @@ sub FetchSingleAssetFromAWS {
     my $instance_obj;
     my $credentials = AWSCredentials();
 
+    my $method = 'DescribeInstances'; # Default for EC2
+    $method = 'DescribeDBInstances' if $args{'ServiceType'} eq 'RDS';
+
     eval {
         my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
-        my $res = $service->DescribeInstances(InstanceIds => [$args{'AWSID'}]);
+        my $res = $service->$method(InstanceIds => [$args{'AWSID'}]);
         $instance_obj = $res->Reservations->[0]->Instances->[0];
     };
 
@@ -100,9 +103,12 @@ sub FetchSingleAssetFromAWS {
     return $instance_obj;
 }
 
+# Token can be NextToken for EC2 or Marker for RDS
+
 sub FetchMultipleAssetsFromAWS {
     my %args = (
-        MaxResults => 5,
+        MaxResults => 20,
+        Token => undef,
         @_,
     );
 
@@ -116,28 +122,47 @@ sub FetchMultipleAssetsFromAWS {
         return;
     }
 
-    my $reservations;
+    my $aws_resources;
     my $credentials = AWSCredentials();
     my $res;
+    my $token;
 
-    eval {
-        my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
+    if ( $args{'ServiceType'} eq 'EC2' ) {
+        eval {
+            my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
 
-        if ( $args{'NextToken'} ) {
-            $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'}, NextToken => $args{'NextToken'});
-        }
-        else {
-            $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'});
-        }
+            if ( $args{'Token'} ) {
+                $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'}, NextToken => $args{'Token'});
+            }
+            else {
+                $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'});
+            }
 
-        $reservations = $res->Reservations;
-    };
+            $aws_resources = $res->Reservations;
+            $token = $res->NextToken;
+        };
+    }
+    elsif ( $args{'ServiceType'} eq 'RDS' ) {
+        eval {
+            my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
+
+            if ( $args{'Marker'} ) {
+                $res = $service->DescribeDBInstances(MaxRecords => $args{'MaxResults'}, Marker => $args{'Token'});
+            }
+            else {
+                $res = $service->DescribeDBInstances(MaxRecords => $args{'MaxResults'});
+            }
+
+            $aws_resources = $res->DBInstances;
+            $token = $res->Marker;
+        };
+    }
 
     if ( $@ ) {
         RT->Logger->error("RT-Extension-AWS-Assets: Failed call to AWS: " . $@);
     }
 
-    return ($reservations, $res->NextToken);
+    return ($aws_resources, $token);
 }
 
 =pod
@@ -149,8 +174,9 @@ Accept a loaded RT::Asset object and a Paws Instance object.
 sub UpdateAWSAsset {
     my $asset = shift;
     my $paws_obj = shift;
+    my $service = shift;
 
-    foreach my $aws_value ( @{ RT->Config->Get('AWSAssetsUpdateFields') } ) {
+    foreach my $aws_value ( @{ RT->Config->Get('AWSAssetsUpdateFields')->{$service} } ) {
 
         my $submethod;
         my $cf_name = $aws_value;
@@ -159,19 +185,20 @@ sub UpdateAWSAsset {
         my $method = $cf_name;
         $method =~ s/\s+//g;
 
+        # Fixup some special cases
+        if ( $service eq 'RDS' ) {
+            $method = 'DBInstanceIdentifier' if $cf_name eq 'Name';
+            $method = 'DBInstanceClass' if $cf_name eq 'Instance Type';
+        }
+
         my ($ret, $msg);
 
-        if ( $submethod && $submethod eq 'Tags' ) {
-            foreach my $tag ( @{ $paws_obj->Tags } ) {
+        if ( $submethod && ( $submethod eq 'Tags' || $submethod eq 'TagList' ) ) {
+            foreach my $tag ( @{ $paws_obj->$submethod } ) {
                 if ( $tag->Key eq $cf_name ) {
                     if ( $cf_name eq 'Name' ) {
                         # Name isn't a CF but a core asset field
                         ($ret, $msg) = $asset->SetName($tag->Value);
-
-                        if ( $msg && $msg =~ /That is already the current value/ ) {
-                            # Don't log an error for the "current value" message
-                            $ret = 1;
-                        }
                         last;
                     }
                     else {
@@ -181,6 +208,10 @@ sub UpdateAWSAsset {
                 }
             }
         }
+        elsif ( $method eq 'DBInstanceIdentifier' ) {
+            # Name isn't a CF but a core asset field
+            ($ret, $msg) = $asset->SetName($paws_obj->$method);
+        }
         elsif ( $submethod ) {
             ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => $paws_obj->$submethod->$method );
         }
@@ -193,6 +224,11 @@ sub UpdateAWSAsset {
             ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => $paws_obj->$method );
         }
 
+        if ( $msg && $msg =~ /That is already the current value/ ) {
+            # Don't log an error for the "current value" message
+            $ret = 1;
+        }
+
         unless ( $ret ) {
             RT->Logger->error("RT-Extension-AWS-Assets: unable to update CF $cf_name: $msg");
         }
@@ -208,21 +244,31 @@ sub InsertAWSAssets {
 
     my $catalog = RT->Config->Get('AWSAssetsInstanceCatalog');
 
-    for my $reservation ( @{ $args{'Reservations'} } ) {
-        my $instance = $reservation->Instances->[0];
+    for my $resource ( @{ $args{'AWSResources'} } ) {
+        my $resource_id;
+        my $instance;
+
+        if ( $args{'ServiceType'} eq 'EC2' ) {
+            $instance = $resource->Instances->[0];
+            $resource_id = $resource->Instances->[0]->InstanceId;
+        }
+        elsif ( $args{'ServiceType'} eq 'RDS' ) {
+            $instance = $resource;
+            $resource_id = $resource->DbiResourceId;
+        }
 
         # Load as system user to find all possible assets to avoid
         # trying to create a duplicate CurrentUser might not be able to see
         my $assets = RT::Assets->new( RT->SystemUser );
         my ($ok, $msg) = $assets->FromSQL("Catalog = '" . $catalog .
-            "' AND 'CF.{AWS ID}' = '" . $instance->InstanceId . "'");
+            "' AND 'CF.{AWS ID}' = '" . $resource_id . "'");
 
         # AWS ID is unique, so there should only ever be 1 or 0
         my $asset = $assets->First;
 
         # Search for an existing asset, next if found
         # Asset already exists, next
-        RT->Logger->debug("Asset for " . $instance->InstanceId . " exists, skipping")
+        RT->Logger->debug("Asset for " . $resource_id . " exists, skipping")
             if $asset and $asset->Id;
         next if $asset and $asset->Id;
 
@@ -249,21 +295,21 @@ sub InsertAWSAssets {
 
         ($ok, $msg) = $new_asset->Create(
             Catalog => RT->Config->Get('AWSAssetsInstanceCatalog'),
-            'CustomField-' . $aws_id_cf->Id => $instance->InstanceId,
+            'CustomField-' . $aws_id_cf->Id => $resource_id,
             'CustomField-' . $region_cf->Id => $args{'Region'},
             'CustomField-' . $service_type_cf->Id => $args{'ServiceType'},
         );
 
         if ( not $ok ) {
-            RT->Logger->error('Unable to create new asset for instance ' . $instance->InstanceId . ' ' . $msg);
+            RT->Logger->error('Unable to create new asset for instance ' . $resource_id . ' ' . $msg);
             next;
         }
         else {
-            RT->Logger->debug('Created asset ' . $new_asset->Id . ' for instance ' . $instance->InstanceId);
+            RT->Logger->debug('Created asset ' . $new_asset->Id . ' for instance ' . $resource_id);
         }
 
         # Call UpdateAWSAsset to load remaining CFs
-        UpdateAWSAsset($new_asset, $instance);
+        UpdateAWSAsset($new_asset, $instance, $args{'ServiceType'});
     }
     return;
 }
@@ -275,24 +321,34 @@ sub UpdateAWSAssets {
 
     my $catalog = RT->Config->Get('AWSAssetsInstanceCatalog');
 
-    for my $reservation ( @{ $args{'Reservations'} } ) {
-        my $instance = $reservation->Instances->[0];
+    for my $resource ( @{ $args{'AWSResources'} } ) {
+        my $resource_id;
+        my $instance;
+
+        if ( $args{'ServiceType'} eq 'EC2' ) {
+            $instance = $resource->Instances->[0];
+            $resource_id = $resource->Instances->[0]->InstanceId;
+        }
+        elsif ( $args{'ServiceType'} eq 'RDS' ) {
+            $instance = $resource;
+            $resource_id = $resource->DbiResourceId;
+        }
 
         # Load as system user to find all possible assets to avoid
         # trying to create a duplicate CurrentUser might not be able to see
         my $assets = RT::Assets->new( RT->SystemUser );
         my ($ok, $msg) = $assets->FromSQL("Catalog = '" . $catalog .
-            "' AND 'CF.{AWS ID}' = '" . $instance->InstanceId . "'");
+            "' AND 'CF.{AWS ID}' = '" . $resource_id . "'");
 
         # AWS ID is unique, so there should only ever be 1 or 0
         my $asset = $assets->First;
 
         unless ( $asset and $asset->Id ) {
-            RT->Logger->debug("No asset found for " . $instance->InstanceId . ", skipping");
+            RT->Logger->debug("No asset found for " . $resource_id . ", skipping");
             next;
         }
 
-        UpdateAWSAsset($asset, $instance);
+        UpdateAWSAsset($asset, $instance, $args{'ServiceType'});
         RT->Logger->debug('Updated asset ' . $asset->Id . ' ' . $asset->Name);
     }
     return;
commit 593318d868804fc02982dbb24b47d343720ea372
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Feb 9 14:23:46 2024 -0500

    Provide update mode for EC2 instances

diff --git a/bin/rt-import-aws-assets.in b/bin/rt-import-aws-assets.in
index ca17ac0..c099d9b 100644
--- a/bin/rt-import-aws-assets.in
+++ b/bin/rt-import-aws-assets.in
@@ -43,6 +43,25 @@ sub run{
         } while ( $token )
     }
 
+    if ( $args{update} ) {
+        my ($reservations, $token);
+
+        do {
+            ($reservations, $token) =
+                RT::Extension::AWS::Assets::FetchMultipleAssetsFromAWS(
+                    Region => $args{'region'},
+                    ServiceType => $args{'type'},
+                    NextToken => $token );
+
+                RT::Extension::AWS::Assets::UpdateAWSAssets(
+                    Reservations => $reservations,
+                    Region => $args{'region'},
+                    ServiceType => $args{'type'},
+                    CurrentUser => GetCurrentUser() );
+
+        } while ( $token )
+    }
+
     return;
 }
 
@@ -59,6 +78,11 @@ sub process_args {
         exit;
     }
 
+    if ( $opt{'insert'} && $opt{'update'} ) {
+        print "Running insert and update at the same time is not supported, exiting.\n";
+        exit;
+    }
+
     return %opt;
 }
 
diff --git a/lib/RT/Extension/AWS/Assets.pm b/lib/RT/Extension/AWS/Assets.pm
index e84766d..58c5968 100644
--- a/lib/RT/Extension/AWS/Assets.pm
+++ b/lib/RT/Extension/AWS/Assets.pm
@@ -268,6 +268,36 @@ sub InsertAWSAssets {
     return;
 }
 
+sub UpdateAWSAssets {
+    my %args = (
+        @_
+    );
+
+    my $catalog = RT->Config->Get('AWSAssetsInstanceCatalog');
+
+    for my $reservation ( @{ $args{'Reservations'} } ) {
+        my $instance = $reservation->Instances->[0];
+
+        # Load as system user to find all possible assets to avoid
+        # trying to create a duplicate CurrentUser might not be able to see
+        my $assets = RT::Assets->new( RT->SystemUser );
+        my ($ok, $msg) = $assets->FromSQL("Catalog = '" . $catalog .
+            "' AND 'CF.{AWS ID}' = '" . $instance->InstanceId . "'");
+
+        # AWS ID is unique, so there should only ever be 1 or 0
+        my $asset = $assets->First;
+
+        unless ( $asset and $asset->Id ) {
+            RT->Logger->debug("No asset found for " . $instance->InstanceId . ", skipping");
+            next;
+        }
+
+        UpdateAWSAsset($asset, $instance);
+        RT->Logger->debug('Updated asset ' . $asset->Id . ' ' . $asset->Name);
+    }
+    return;
+}
+
 # We don't have a catalog in the empty asset object when we want to
 # load the CFs, so create a custom version of the loader that accepts
 # catalog as a parameter.
commit aa8917f2d0d8b887fc48a5de904addc2b8b22993
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Feb 9 14:08:57 2024 -0500

    Create new assets based on EC2 instances

diff --git a/Makefile.PL b/Makefile.PL
index 4d5c7fd..2438bf5 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -5,6 +5,8 @@ RTx     'RT-Extension-AWS-Assets';
 license 'gpl_2';
 repository 'https://github.com/bestpractical/rt-extension-aws-assets';
 
+requires('Paws');
+
 requires_rt '5.0.0';
 rt_too_new '5.2.0';
 
diff --git a/bin/rt-import-aws-assets.in b/bin/rt-import-aws-assets.in
index 72cb1b9..ca17ac0 100644
--- a/bin/rt-import-aws-assets.in
+++ b/bin/rt-import-aws-assets.in
@@ -4,8 +4,78 @@
 use strict;
 use warnings;
 
-### after: use lib qw(@RT_LIB_PATH@);
-use lib qw(/opt/rt5/local/lib /opt/rt5/lib);
+package RT::AWS::Assets::Run;
+
+BEGIN {
+### after:     use lib qw(@RT_LIB_PATH@);
+use lib '/opt/rt5/local/lib /opt/rt5/lib';
+use RT;
+RT::LoadConfig();
+RT::Init();
+}
+
+use RT::Interface::CLI  qw(GetCurrentUser loc);
+use RT::Extension::AWS::Assets;
+
+__PACKAGE__->run(@ARGV) unless caller;
+
+sub run{
+    my ($class, @args) = @_;
+
+    my %args = $class->process_args(@args);
+
+    if ( $args{insert} ) {
+        my ($reservations, $token);
+
+        do {
+            ($reservations, $token) =
+                RT::Extension::AWS::Assets::FetchMultipleAssetsFromAWS(
+                    Region => $args{'region'},
+                    ServiceType => $args{'type'},
+                    NextToken => $token );
+
+                RT::Extension::AWS::Assets::InsertAWSAssets(
+                    Reservations => $reservations,
+                    Region => $args{'region'},
+                    ServiceType => $args{'type'},
+                    CurrentUser => GetCurrentUser() );
+
+        } while ( $token )
+    }
+
+    return;
+}
+
+sub process_args {
+    require Getopt::Long;
+    local @ARGV = @_;
+
+    my %opt;
+    Getopt::Long::GetOptions( \%opt, 'help|h', 'insert|i', 'update|u', 'catalog=s', 'type=s', 'region=s', 'debug|d' );
+
+    if ( delete $opt{help} ) {
+        require Pod::Usage;
+        Pod::Usage::pod2usage( { verbose => 2 } );
+        exit;
+    }
+
+    return %opt;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+rt-import-aws-assets - import asset data from AWS
+
+=head1 SYNOPSIS
+
+    rt-import-aws-assets
+
+=head1 DESCRIPTION
+
+Access your AWS account using the aws-cli utility to pull in data and
+add it to RT assets.
 
-use RT::Interface::CLI qw(Init GetCurrentUser);
-my %opt;
diff --git a/html/Callbacks/RT-Extension-AWS-Assets/Asset/Display.html/BeforeProcessArguments b/html/Callbacks/RT-Extension-AWS-Assets/Asset/Display.html/BeforeProcessArguments
new file mode 100644
index 0000000..2119617
--- /dev/null
+++ b/html/Callbacks/RT-Extension-AWS-Assets/Asset/Display.html/BeforeProcessArguments
@@ -0,0 +1,31 @@
+<%init>
+
+my ($result, $message);
+
+if ( $ARGSRef->{'AWSUpdate'} && $ARGSRef->{'AWSUpdate'} eq 'true' ) {
+
+    # Make sure we have required params
+    if ( $AssetObj->FirstCustomFieldValue('AWS ID')
+         && $AssetObj->FirstCustomFieldValue('Service Type')
+         && $AssetObj->FirstCustomFieldValue('Region') ) {
+
+        RT::Extension::AWS::Assets::ReloadFromAWS($AssetObj);
+        $result = 1;
+        $message = 'Asset information updated from AWS.';
+    }
+    else {
+        $result = 0;
+        $message = 'Set AWS ID, Service Type, and Region to load resource information from AWS.';
+    }
+}
+
+if ( $message ) {
+    push @{$ActionsRef}, $message;
+}
+
+</%init>
+<%args>
+$ARGSRef
+$AssetObj
+$ActionsRef
+</%args>
diff --git a/html/Callbacks/RT-Extension-AWS-Assets/Elements/Tabs/Privileged b/html/Callbacks/RT-Extension-AWS-Assets/Elements/Tabs/Privileged
new file mode 100644
index 0000000..edfff70
--- /dev/null
+++ b/html/Callbacks/RT-Extension-AWS-Assets/Elements/Tabs/Privileged
@@ -0,0 +1,22 @@
+<%init>
+
+# Assets only
+return unless $Path =~ m{^/Asset/};
+my $id = $DECODED_ARGS->{'id'};
+return unless $id;
+
+my $asset = LoadAsset($id);
+return unless $asset->Id;
+
+if ( $asset->CurrentUserHasRight('ModifyAsset') ){
+    PageMenu()->child( 'actions' )->child(
+        'update_from_aws' => title => loc( 'Update from AWS' ),
+        path => '/Asset/Display.html?AWSUpdate=true;id=' . $id,
+    );
+}
+
+</%init>
+<%args>
+$Path
+$Has_Query
+</%args>
diff --git a/lib/RT/Extension/AWS/Assets.pm b/lib/RT/Extension/AWS/Assets.pm
index b3cb56c..e84766d 100644
--- a/lib/RT/Extension/AWS/Assets.pm
+++ b/lib/RT/Extension/AWS/Assets.pm
@@ -2,6 +2,11 @@ use strict;
 use warnings;
 package RT::Extension::AWS::Assets;
 
+use Paws;
+use Paws::Credential::Explicit;
+
+use Data::Printer;
+
 our $VERSION = '0.01';
 
 =head1 NAME
@@ -42,6 +47,243 @@ Add this line:
 
 =back
 
+=head1 METHODS
+
+=cut
+
+sub ReloadFromAWS {
+    my $asset = shift;
+
+    my $res_obj = FetchSingleAssetFromAWS(
+                      AWSID => $asset->FirstCustomFieldValue('AWS ID'),
+                      ServiceType => $asset->FirstCustomFieldValue('Service Type'),
+                      Region => $asset->FirstCustomFieldValue('Region'));
+
+    UpdateAWSAsset($asset, $res_obj);
+}
+
+
+sub AWSCredentials {
+    my $credentials = Paws::Credential::Explicit->new(
+        access_key => RT->Config->Get('AWS_ACCESS_KEY'),
+        secret_key => RT->Config->Get('AWS_SECRET_KEY'),
+    );
+    return $credentials;
+}
+
+sub FetchSingleAssetFromAWS {
+    my %args = @_;
+
+    unless ( $args{'AWSID'} ) {
+        RT->Logger->error('RT-Extension-AWS-Assets: No AWS ID found.');
+        return;
+    }
+
+    unless ( $args{'ServiceType'}) {
+        RT->Logger->error('RT-Extension-AWS-Assets: No Service Type found.');
+        return;
+    }
+
+    my $instance_obj;
+    my $credentials = AWSCredentials();
+
+    eval {
+        my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
+        my $res = $service->DescribeInstances(InstanceIds => [$args{'AWSID'}]);
+        $instance_obj = $res->Reservations->[0]->Instances->[0];
+    };
+
+    if ( $@ ) {
+        RT->Logger->error("RT-Extension-AWS-Assets: Failed call to AWS: " . $@);
+    }
+
+    return $instance_obj;
+}
+
+sub FetchMultipleAssetsFromAWS {
+    my %args = (
+        MaxResults => 5,
+        @_,
+    );
+
+    unless ( $args{'Region'} ) {
+        RT->Logger->error('RT-Extension-AWS-Assets: No Region found.');
+        return;
+    }
+
+    unless ( $args{'ServiceType'}) {
+        RT->Logger->error('RT-Extension-AWS-Assets: No Service Type found.');
+        return;
+    }
+
+    my $reservations;
+    my $credentials = AWSCredentials();
+    my $res;
+
+    eval {
+        my $service = Paws->service($args{'ServiceType'}, credentials => $credentials, region => $args{'Region'});
+
+        if ( $args{'NextToken'} ) {
+            $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'}, NextToken => $args{'NextToken'});
+        }
+        else {
+            $res = $service->DescribeInstances(MaxResults => $args{'MaxResults'});
+        }
+
+        $reservations = $res->Reservations;
+    };
+
+    if ( $@ ) {
+        RT->Logger->error("RT-Extension-AWS-Assets: Failed call to AWS: " . $@);
+    }
+
+    return ($reservations, $res->NextToken);
+}
+
+=pod
+
+Accept a loaded RT::Asset object and a Paws Instance object.
+
+=cut
+
+sub UpdateAWSAsset {
+    my $asset = shift;
+    my $paws_obj = shift;
+
+    foreach my $aws_value ( @{ RT->Config->Get('AWSAssetsUpdateFields') } ) {
+
+        my $submethod;
+        my $cf_name = $aws_value;
+        ($submethod, $cf_name) = split(':', $aws_value) if $aws_value =~ /:/;
+
+        my $method = $cf_name;
+        $method =~ s/\s+//g;
+
+        my ($ret, $msg);
+
+        if ( $submethod && $submethod eq 'Tags' ) {
+            foreach my $tag ( @{ $paws_obj->Tags } ) {
+                if ( $tag->Key eq $cf_name ) {
+                    if ( $cf_name eq 'Name' ) {
+                        # Name isn't a CF but a core asset field
+                        ($ret, $msg) = $asset->SetName($tag->Value);
+
+                        if ( $msg && $msg =~ /That is already the current value/ ) {
+                            # Don't log an error for the "current value" message
+                            $ret = 1;
+                        }
+                        last;
+                    }
+                    else {
+                        ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => $tag->Value );
+                        last;
+                    }
+                }
+            }
+        }
+        elsif ( $submethod ) {
+            ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => $paws_obj->$submethod->$method );
+        }
+        elsif ( $cf_name eq 'Platform' ) {
+            # Paws currently defaults Platform to Windows. All of our systems are
+            # currently "Linux/UNIX" so set that.
+            ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => "Linux/UNIX" );
+        }
+        else {
+            ($ret, $msg) = $asset->AddCustomFieldValue( Field => $cf_name, Value => $paws_obj->$method );
+        }
+
+        unless ( $ret ) {
+            RT->Logger->error("RT-Extension-AWS-Assets: unable to update CF $cf_name: $msg");
+        }
+    }
+
+    return;
+}
+
+sub InsertAWSAssets {
+    my %args = (
+        @_
+    );
+
+    my $catalog = RT->Config->Get('AWSAssetsInstanceCatalog');
+
+    for my $reservation ( @{ $args{'Reservations'} } ) {
+        my $instance = $reservation->Instances->[0];
+
+        # Load as system user to find all possible assets to avoid
+        # trying to create a duplicate CurrentUser might not be able to see
+        my $assets = RT::Assets->new( RT->SystemUser );
+        my ($ok, $msg) = $assets->FromSQL("Catalog = '" . $catalog .
+            "' AND 'CF.{AWS ID}' = '" . $instance->InstanceId . "'");
+
+        # AWS ID is unique, so there should only ever be 1 or 0
+        my $asset = $assets->First;
+
+        # Search for an existing asset, next if found
+        # Asset already exists, next
+        RT->Logger->debug("Asset for " . $instance->InstanceId . " exists, skipping")
+            if $asset and $asset->Id;
+        next if $asset and $asset->Id;
+
+        # Try to create a new asset with AWS ID, Region, Service Type
+        my $new_asset = RT::Asset->new($args{'CurrentUser'});
+
+        my $aws_id_cf = LoadCustomFieldByIdentifier($new_asset, 'AWS ID', $catalog, $args{'CurrentUser'});
+        unless ( $aws_id_cf && $aws_id_cf->Id ) {
+            RT->Logger->error('Unable to load AWS ID CF for asset');
+            next;
+        }
+
+        my $region_cf = LoadCustomFieldByIdentifier($new_asset, 'Region', $catalog, $args{'CurrentUser'});
+        unless ( $region_cf && $region_cf->Id ) {
+            RT->Logger->error('Unable to load Region CF for asset');
+            next;
+        }
+
+        my $service_type_cf = LoadCustomFieldByIdentifier($new_asset, 'Service Type', $catalog, $args{'CurrentUser'});
+        unless ( $service_type_cf && $service_type_cf->Id ) {
+            RT->Logger->error('Unable to load Service Type CF for asset');
+            next;
+        }
+
+        ($ok, $msg) = $new_asset->Create(
+            Catalog => RT->Config->Get('AWSAssetsInstanceCatalog'),
+            'CustomField-' . $aws_id_cf->Id => $instance->InstanceId,
+            'CustomField-' . $region_cf->Id => $args{'Region'},
+            'CustomField-' . $service_type_cf->Id => $args{'ServiceType'},
+        );
+
+        if ( not $ok ) {
+            RT->Logger->error('Unable to create new asset for instance ' . $instance->InstanceId . ' ' . $msg);
+            next;
+        }
+        else {
+            RT->Logger->debug('Created asset ' . $new_asset->Id . ' for instance ' . $instance->InstanceId);
+        }
+
+        # Call UpdateAWSAsset to load remaining CFs
+        UpdateAWSAsset($new_asset, $instance);
+    }
+    return;
+}
+
+# We don't have a catalog in the empty asset object when we want to
+# load the CFs, so create a custom version of the loader that accepts
+# catalog as a parameter.
+
+sub LoadCustomFieldByIdentifier {
+    my $asset = shift;
+    my $field = shift;
+    my $catalog = shift;
+    my $current_user = shift;
+
+    my $cf = RT::CustomField->new( $current_user );
+    $cf->SetContextObject( $asset );
+    my ($ok, $msg) = $cf->LoadByNameAndCatalog( Name => $field, Catalog => $catalog );
+    return $cf;
+}
+
 =head1 AUTHOR
 
 =for html <p>All bugs should be reported via email to <a
-----------------------------------------------------------------------

Summary of changes:
 Makefile.PL                                        |   2 +
 bin/rt-import-aws-assets.in                        | 102 ++++++-
 .../Asset/Display.html/BeforeProcessArguments      |  31 ++
 .../Elements/Tabs/Privileged                       |  22 ++
 lib/RT/Extension/AWS/Assets.pm                     | 328 +++++++++++++++++++++
 5 files changed, 481 insertions(+), 4 deletions(-)
 create mode 100644 html/Callbacks/RT-Extension-AWS-Assets/Asset/Display.html/BeforeProcessArguments
 create mode 100644 html/Callbacks/RT-Extension-AWS-Assets/Elements/Tabs/Privileged


hooks/post-receive
-- 
rt-extension-aws-assets


More information about the Bps-public-commit mailing list