diff --git a/icons/Delete.png b/icons/Delete.png new file mode 100644 index 0000000..7944045 Binary files /dev/null and b/icons/Delete.png differ diff --git a/icons/Download_from_the_Cloud.png b/icons/Download_from_the_Cloud.png new file mode 100644 index 0000000..2e21a54 Binary files /dev/null and b/icons/Download_from_the_Cloud.png differ diff --git a/src/AWSManager.fbp b/src/AWSManager.fbp index c3fd640..799ce2c 100644 --- a/src/AWSManager.fbp +++ b/src/AWSManager.fbp @@ -182,7 +182,7 @@ 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -237,11 +237,11 @@ - + EC2 0 - + 1 1 1 @@ -293,16 +293,16 @@ wxTAB_TRAVERSAL - + bSizer8 wxVERTICAL none - + 5 wxEXPAND 1 - + 1 1 1 @@ -556,8 +556,8 @@ - - + + 1 1 1 @@ -609,7 +609,7 @@ wxTAB_TRAVERSAL - + bSizer13 wxVERTICAL @@ -4336,7 +4336,7 @@ S3 - 0 + 1 1 1 @@ -4571,6 +4571,7 @@ + aws_s3_load_details @@ -4704,252 +4705,1118 @@ wxTAB_TRAVERSAL - - - - - - - - - - RDS - 0 - - 1 - 1 - 1 - 1 - 0 - panelRDS - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - panelRDS - 1 - - - protected - 1 - - Resizable - 1 - - ; ; forward_declare - 0 - - - - wxTAB_TRAVERSAL - - - bSizer82 - wxVERTICAL - none - - 5 - wxEXPAND - 1 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - 0 - - 0 - - 1 - m_splitter12 - 1 - - - protected - 1 - - Resizable - 0.0 - 0 - -1 - 1 - - wxSPLIT_VERTICAL - wxSP_3D - ; ; forward_declare - 0 - - - - - - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - panelRDSTree - 1 - - - protected - 1 - - Resizable - 1 - - ; ; forward_declare - 0 - - - - wxTAB_TRAVERSAL - + + 1 + wxBOTH + 0 + 1 + 0 - bSizer42 - wxVERTICAL + fgSizer15 + wxFLEX_GROWMODE_SPECIFIED none + 3 + 0 5 - wxALL|wxEXPAND + wxEXPAND 1 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 + + 4 + wxBOTH + 1 + + 0 - 1 - treeRDS - 1 - - - protected - 1 - - Resizable - 1 - - wxTR_DEFAULT_STYLE|wxTR_LINES_AT_ROOT|wxTR_TWIST_BUTTONS - ; ; forward_declare - 0 - - - - - + fgSizerS3Details + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Bucket Name + 0 + + 0 + + + 0 + + 1 + staticTextS3_Details_BucketName + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + textCtrlS3_Details_BucketName + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + Refresh + + 0 + + 0 + + + 0 + + 1 + buttonS3_Details_Refresh + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + aws_s3_refresh_bucket + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + Open in AWS + + 0 + + 0 + + + 0 + + 1 + buttonS3_Details_OpenMgmtConsole + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + aws_s3_open_mgmt_console + + + + + + 5 + wxEXPAND + 1 + + 2 + wxBOTH + 1 + 0 + 0 + + fgSizerS3Objects + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Objects + 0 + + 0 + + + 0 + + 1 + staticTextS3_Objects + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + treeCtrlS3_Objects + 1 + + + protected + 1 + + Resizable + 1 + + wxTR_DEFAULT_STYLE + ; ; forward_declare + 0 + + + + + aws_s3_selected_key + + S3 Operations + menuS3_Object + protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + Download + menuItemS3_DownloadObject + none + + + aws_s3_menu_download_object + + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + Delete + menuItemS3_DeleteObject + none + + + aws_s3_menu_delete_object + + + + + + + + 5 + wxEXPAND + 1 + + 3 + wxBOTH + 1 + + 0 + + fgSizerS3Upload + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + To upload a file, select a key to add the file into it. If you select a file object, the file will be overwritten. You can adjust the name, by just editing it. + 0 + + 0 + + + 0 + + 1 + staticTextS3_Upload_Details + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + 400 + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Upload file to S3 bucket + 0 + + 0 + + + 0 + + 1 + staticTextS3_Upload + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + textCtrlS3_SelectedKey + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Select an object or key to upload a new file. + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + Upload + + 0 + + 0 + + + 0 + + 1 + buttonS3_Upload + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + aws_s3_upload_file + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + 1 + wxSYS_COLOUR_HIGHLIGHT + 1 + + 0 + 0 + wxID_ANY + Drag and drop a file here to upload it to the selected key. + 0 + + 0 + + + 0 + + 1 + staticTextS3_Upload_DragZone + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + aws_s3_drop_file + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + + + + + + + + + + + + RDS + 0 + + 1 + 1 + 1 + 1 + 0 + panelRDS + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + panelRDS + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer82 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_splitter12 + 1 + + + protected + 1 + + Resizable + 0.0 + 0 + -1 + 1 + + wxSPLIT_VERTICAL + wxSP_3D + ; ; forward_declare + 0 + + + + + + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + panelRDSTree + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer42 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + treeRDS + 1 + + + protected + 1 + + Resizable + 1 + + wxTR_DEFAULT_STYLE|wxTR_LINES_AT_ROOT|wxTR_TWIST_BUTTONS + ; ; forward_declare + 0 + + + + + 5 @@ -5089,11 +5956,11 @@ - + Cloudfront - 1 - + 0 + 1 1 1 @@ -5145,16 +6012,16 @@ wxTAB_TRAVERSAL - + bSizer83 wxVERTICAL none - + 5 wxEXPAND 1 - + 1 1 1 @@ -5461,16 +6328,16 @@ wxTAB_TRAVERSAL - + bSizer132 wxVERTICAL none - + 5 wxEXPAND 1 - + 3 wxBOTH 1 @@ -5482,11 +6349,11 @@ none 0 0 - + 5 wxALL 0 - + 1 1 1 @@ -5544,11 +6411,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -5609,11 +6476,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -5684,11 +6551,11 @@ aws_cloudfront_refresh_distribution - + 5 wxALL 0 - + 1 1 1 @@ -5746,11 +6613,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -5811,11 +6678,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -5886,11 +6753,11 @@ aws_cloudfront_open_mgmt_console - + 5 wxALL 0 - + 1 1 1 @@ -5948,11 +6815,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6013,21 +6880,21 @@ - + 5 wxEXPAND 1 - + 0 protected 0 - + 5 wxALL 0 - + 1 1 1 @@ -6085,11 +6952,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6150,11 +7017,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -6225,11 +7092,11 @@ aws_cloudfront_invalidate - + 5 wxALL 0 - + 1 1 1 @@ -6287,11 +7154,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6352,21 +7219,21 @@ - + 5 wxEXPAND 1 - + 0 protected 0 - + 5 wxALL 0 - + 1 1 1 @@ -6424,11 +7291,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6489,21 +7356,21 @@ - + 5 wxEXPAND 1 - + 0 protected 0 - + 5 wxALL 0 - + 1 1 1 @@ -6561,11 +7428,11 @@ -1 - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6623,11 +7490,11 @@ - + 5 wxEXPAND 1 - + 0 protected 0 @@ -6647,7 +7514,7 @@ - + 0 wxAUI_MGR_DEFAULT @@ -6675,7 +7542,8 @@ - + showConfig + 1 wxBOTH diff --git a/src/aws_cloudfront.py b/src/aws_cloudfront.py index 7341586..021fdbc 100644 --- a/src/aws_cloudfront.py +++ b/src/aws_cloudfront.py @@ -1,10 +1,12 @@ import boto3 +import aws_session_handler import json # get all cloudfront distributions and return a list of distributions def get_cloudfront_distributions(): - cloudfront = boto3.client('cloudfront') + session = aws_session_handler.get_session() + cloudfront = session.client('cloudfront') distributions = cloudfront.list_distributions() distributions_list = [] for distribution in distributions['DistributionList']['Items']: @@ -14,20 +16,23 @@ def get_cloudfront_distributions(): # get information about a cloudfront distribution def get_cloudfront_distribution(distribution_id): - cloudfront = boto3.client('cloudfront') + session = aws_session_handler.get_session() + cloudfront = session.client('cloudfront') distribution = cloudfront.get_distribution(Id=distribution_id) return distribution # invalidate a cloudfront distribution def invalidate_cloudfront_distribution(distribution_id, paths): - cloudfront = boto3.client('cloudfront') + session = aws_session_handler.get_session() + cloudfront = session.client('cloudfront') response = cloudfront.create_invalidation(DistributionId=distribution_id, InvalidationBatch={'Paths': {'Quantity': len(paths), 'Items': paths}, 'CallerReference': 'awsmanager'}) return json.dumps(response, indent=2, default=str) # load tags of a cloudfront distribution def get_cloudfront_distribution_tags(distribution_id): - cloudfront = boto3.client('cloudfront') + session = aws_session_handler.get_session() + cloudfront = session.client('cloudfront') tags = cloudfront.list_tags_for_resource(Resource=distribution_id) return tags diff --git a/src/aws_ec2.py b/src/aws_ec2.py index 62c3a06..9d49c48 100644 --- a/src/aws_ec2.py +++ b/src/aws_ec2.py @@ -1,23 +1,27 @@ import boto3 +import aws_session_handler # get information about an ec2 instance def get_ec2_instance(region, instance_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) instance = ec2.describe_instances(InstanceIds=[instance_id]) return instance['Reservations'][0]['Instances'][0] # get all information about a volumeid def get_ec2_volume(region, volume_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) volume = ec2.describe_volumes(VolumeIds=[volume_id]) return volume['Volumes'][0] # get all ec2 instances of a region and return a list of instances def get_ec2_instances(region): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) instances = ec2.describe_instances() instances_list = [] for reservation in instances['Reservations']: @@ -28,23 +32,27 @@ def get_ec2_instances(region): # start a given instance def start_ec2_instance(region, instance_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) ec2.start_instances(InstanceIds=[instance_id]) # stop a given instance def stop_ec2_instance(region, instance_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) ec2.stop_instances(InstanceIds=[instance_id]) # reboot a given instance def reboot_ec2_instance(region, instance_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) ec2.reboot_instances(InstanceIds=[instance_id]) # terminate a given instance def terminate_ec2_instance(region, instance_id): - ec2 = boto3.client('ec2', region_name=region) + session = aws_session_handler.get_session() + ec2 = session.client('ec2', region_name=region) ec2.terminate_instances(InstanceIds=[instance_id]) diff --git a/src/aws_lambda.py b/src/aws_lambda.py index b8e6591..ad7a175 100644 --- a/src/aws_lambda.py +++ b/src/aws_lambda.py @@ -1,10 +1,12 @@ import boto3 +import aws_session_handler import json # get all lambda functions of a region and return a list of functions def get_lambda_functions(region): - lambda_client = boto3.client('lambda', region_name=region) + session = aws_session_handler.get_session() + lambda_client = session.client('lambda', region_name=region) functions = lambda_client.list_functions() functions_list = [] for lambdafunction in functions['Functions']: @@ -14,13 +16,15 @@ def get_lambda_functions(region): # get information about a lambda function def get_lambda_function(region, function_name): - lambda_client = boto3.client('lambda', region_name=region) + session = aws_session_handler.get_session() + lambda_client = session.client('lambda', region_name=region) lambdafunction = lambda_client.get_function(FunctionName=function_name) return lambdafunction # invoke a lambda function def invoke_lambda_function(region, function_name, payload): - lambda_client = boto3.client('lambda', region_name=region) + session = aws_session_handler.get_session() + lambda_client = session.client('lambda', region_name=region) response = lambda_client.invoke(FunctionName=function_name, Payload=payload) return json.dumps(response, indent=2, default=str) diff --git a/src/aws_rds.py b/src/aws_rds.py index 2f7279e..473463c 100644 --- a/src/aws_rds.py +++ b/src/aws_rds.py @@ -1,9 +1,11 @@ import boto3 +import aws_session_handler # get all rds databases of a region and return a list of rds databases def get_rds_databases(region): - rds = boto3.client('rds', region_name=region) + session = aws_session_handler.get_session() + rds = session.client('rds', region_name=region) databases = rds.describe_db_instances() databases_list = [] for database in databases['DBInstances']: diff --git a/src/aws_s3.py b/src/aws_s3.py index 4ecfe53..9f2016c 100644 --- a/src/aws_s3.py +++ b/src/aws_s3.py @@ -1,11 +1,44 @@ import boto3 +import aws_session_handler # get all s3 buckets of a region and return a list of buckets def get_s3_buckets(region): - s3 = boto3.client('s3', region_name=region) + session = aws_session_handler.get_session() + s3 = session.client('s3', region_name=region) buckets = s3.list_buckets() buckets_list = [] for bucket in buckets['Buckets']: buckets_list.append(bucket) return buckets_list + + +# get all objects of a bucket +def get_s3_bucket_objects(region, bucket_name): + s3 = boto3.client('s3', region_name=region) + objects = s3.list_objects_v2(Bucket=bucket_name) + objects_list = [] + for obj in objects['Contents']: + objects_list.append(obj) + return objects_list + + +# download an object from a bucket into given file +def download_object(region, bucket_name, object_name, file_name): + session = aws_session_handler.get_session() + s3 = session.client('s3', region_name=region) + s3.download_file(bucket_name, object_name, file_name) + + +# upload a file to a bucket +def upload_file(region, bucket_name, file_name, object_name): + session = aws_session_handler.get_session() + s3 = session.client('s3', region_name=region) + s3.upload_file(file_name, bucket_name, object_name) + + +# delete an object from a bucket +def delete_object(region, bucket_name, object_name): + session = aws_session_handler.get_session() + s3 = session.client('s3', region_name=region) + s3.delete_object(Bucket=bucket_name, Key=object_name) diff --git a/src/aws_session_handler.py b/src/aws_session_handler.py new file mode 100644 index 0000000..6ace08c --- /dev/null +++ b/src/aws_session_handler.py @@ -0,0 +1,20 @@ +import boto3 +import settings + + +# use the selected profile, if no profile is given, use the default profile or the provided credentials +def get_session(): + awslogin = settings.read_config() + aws_access_key_id = awslogin.get("aws_access_key_id", None) + aws_secret_access_key = awslogin.get("aws_secret_access_key", None) + aws_session_token = awslogin.get("aws_session_token", None) + aws_profile = awslogin.get("aws_profile", None) + if aws_access_key_id and aws_secret_access_key: + session = boto3.Session( + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + ) + elif aws_profile: + session = boto3.Session(profile_name=aws_profile) + return session diff --git a/src/awsmanager.py b/src/awsmanager.py index 2c72b34..32c9abc 100644 --- a/src/awsmanager.py +++ b/src/awsmanager.py @@ -41,6 +41,10 @@ def __init__(self, parent): self.m_auinotebook1.SetPageBitmap(self.m_auinotebook1.FindPage(self.panelS3), iconsaws.arch_amazon_simple_storage_service_48.GetBitmap().ConvertToImage().Rescale(16, 16).ConvertToBitmap()) self.m_auinotebook1.SetPageBitmap(self.m_auinotebook1.FindPage(self.panelCloudfront), iconsaws.arch_amazon_cloudfront_48.GetBitmap().ConvertToImage().Rescale(16, 16).ConvertToBitmap()) + # add the icons to the menu items of S3 + self.menuItemS3_DownloadObject.SetBitmap(icons.download_from_the_cloud.GetBitmap().ConvertToImage().Rescale(16, 16).ConvertToBitmap()) + self.menuItemS3_DeleteObject.SetBitmap(icons.delete.GetBitmap().ConvertToImage().Rescale(16, 16).ConvertToBitmap()) + def awsmanagerClose(self, event): self.Close() @@ -107,9 +111,33 @@ def aws_lambda_invoke(self, event): def aws_lambda_open_mgmt_console(self, event): ui_aws_lambda.aws_lambda_open_mgmt_console(self, event) + def aws_s3_load_details(self, event): + ui_aws_s3.aws_s3_load_details(self, event) + def aws_s3_reload(self, event): ui_aws_s3.aws_s3_reload(self, event) + def aws_s3_refresh_bucket(self, event): + ui_aws_s3.aws_s3_refresh_bucket(self, event) + + def aws_s3_open_mgmt_console(self, event): + ui_aws_s3.aws_s3_open_mgmt_console(self, event) + + def aws_s3_selected_key(self, event): + ui_aws_s3.aws_s3_selected_key(self, event) + + def aws_s3_upload_file(self, event): + ui_aws_s3.aws_s3_upload_file(self, event) + + def aws_s3_drop_file(self, event): + ui_aws_s3.aws_s3_drop_file(self, event) + + def aws_s3_menu_download_object(self, event): + ui_aws_s3.aws_s3_menu_download_object(self, event) + + def aws_s3_menu_delete_object(self, event): + ui_aws_s3.aws_s3_menu_delete_object(self, event) + def aws_rds_reload(self, event): ui_aws_rds.aws_rds_reload(self, event) diff --git a/src/configuration_ui.py b/src/configuration_ui.py index 6b7437b..dec4fbd 100644 --- a/src/configuration_ui.py +++ b/src/configuration_ui.py @@ -1,5 +1,6 @@ # importing wx files import wx + # import the newly created GUI file import gui @@ -18,27 +19,48 @@ def __init__(self, parent): gui.dialogAbout.SetIcon(self, icons.settings.GetIcon()) def showConfig(self, event): + # read all available profiles + profiles = settings.get_profiles() + # set the values + self.comboBoxAwsProfile.Clear() + self.comboBoxAwsProfile.Append(profiles) + # read all available regions + regions = settings.get_regions() + print(regions) + # set the values + self.comboBoxAwsDefaultRegion.Clear() + self.comboBoxAwsDefaultRegion.Append(regions) + # get the config config = settings.read_config() # set the values - self.textAwsAccessKeyId.SetValue(config['aws_access_key_id']) - self.textAwsSecretAccessKey.SetValue(config['aws_secret_access_key']) - self.textAwsSessionToken.SetValue(config['aws_session_token']) - self.comboBoxAwsProfile.SetValue(config['aws_profile']) - self.comboBoxAwsRegion.SetValue(config['region']) + self.textAwsAccessKeyId.SetValue(config["aws_access_key_id"]) + self.textAwsSecretAccessKey.SetValue(config["aws_secret_access_key"]) + self.textAwsSessionToken.SetValue(config["aws_session_token"]) + self.comboBoxAwsProfile.SetValue(config["aws_profile"]) + self.comboBoxAwsDefaultRegion.SetValue(config["region"]) self.Layout() self.Fit() def saveConfig(self, event): # save the config - settings.save_config('aws_access_key_id', self.textAwsAccessKeyId.GetValue()) - settings.save_config('aws_secret_access_key', self.textAwsSecretAccessKey.GetValue()) - settings.save_config('aws_session_token', self.textAwsSessionToken.GetValue()) - settings.save_config('aws_profile', self.comboBoxAwsProfile.GetValue()) - settings.save_config('region', self.comboBoxAwsRegion.GetValue()) + settings.save_config("aws_access_key_id", self.textAwsAccessKeyId.GetValue()) + settings.save_config( + "aws_secret_access_key", self.textAwsSecretAccessKey.GetValue() + ) + settings.save_config("aws_session_token", self.textAwsSessionToken.GetValue()) + settings.save_config("aws_profile", self.comboBoxAwsProfile.GetValue()) + settings.save_config("region", self.comboBoxAwsDefaultRegion.GetValue()) # close the dialog self.Close() def closeConfig(self, event): self.Close() + + def reloadAwsProfiles(self, event): + # read all available profiles + profiles = settings.get_profiles() + # set the values + self.comboBoxAwsProfile.Clear() + self.comboBoxAwsProfile.Append(profiles) diff --git a/src/gui.py b/src/gui.py index 38215a6..47519a3 100644 --- a/src/gui.py +++ b/src/gui.py @@ -422,6 +422,108 @@ def __init__( self, parent ): self.panelS3Tree.Layout() bSizer41.Fit( self.panelS3Tree ) self.panelS3Details = wx.Panel( self.m_splitter11, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + fgSizer15 = wx.FlexGridSizer( 3, 1, 0, 0 ) + fgSizer15.AddGrowableCol( 0 ) + fgSizer15.AddGrowableRow( 1 ) + fgSizer15.SetFlexibleDirection( wx.BOTH ) + fgSizer15.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + fgSizerS3Details = wx.FlexGridSizer( 0, 4, 0, 0 ) + fgSizerS3Details.AddGrowableCol( 1 ) + fgSizerS3Details.SetFlexibleDirection( wx.BOTH ) + fgSizerS3Details.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + self.staticTextS3_Details_BucketName = wx.StaticText( self.panelS3Details, wx.ID_ANY, u"Bucket Name", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.staticTextS3_Details_BucketName.Wrap( -1 ) + + fgSizerS3Details.Add( self.staticTextS3_Details_BucketName, 0, wx.ALL, 5 ) + + self.textCtrlS3_Details_BucketName = wx.TextCtrl( self.panelS3Details, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + fgSizerS3Details.Add( self.textCtrlS3_Details_BucketName, 1, wx.ALL|wx.EXPAND, 5 ) + + self.buttonS3_Details_Refresh = wx.Button( self.panelS3Details, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, 0 ) + fgSizerS3Details.Add( self.buttonS3_Details_Refresh, 0, wx.ALL, 5 ) + + self.buttonS3_Details_OpenMgmtConsole = wx.Button( self.panelS3Details, wx.ID_ANY, u"Open in AWS", wx.DefaultPosition, wx.DefaultSize, 0 ) + fgSizerS3Details.Add( self.buttonS3_Details_OpenMgmtConsole, 0, wx.ALL, 5 ) + + + fgSizer15.Add( fgSizerS3Details, 1, wx.EXPAND, 5 ) + + fgSizerS3Objects = wx.FlexGridSizer( 0, 2, 0, 0 ) + fgSizerS3Objects.AddGrowableCol( 1 ) + fgSizerS3Objects.AddGrowableRow( 0 ) + fgSizerS3Objects.SetFlexibleDirection( wx.BOTH ) + fgSizerS3Objects.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + self.staticTextS3_Objects = wx.StaticText( self.panelS3Details, wx.ID_ANY, u"Objects", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.staticTextS3_Objects.Wrap( -1 ) + + fgSizerS3Objects.Add( self.staticTextS3_Objects, 0, wx.ALL, 5 ) + + self.treeCtrlS3_Objects = wx.TreeCtrl( self.panelS3Details, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TR_DEFAULT_STYLE ) + self.menuS3_Object = wx.Menu() + self.menuItemS3_DownloadObject = wx.MenuItem( self.menuS3_Object, wx.ID_ANY, u"Download", wx.EmptyString, wx.ITEM_NORMAL ) + self.menuS3_Object.Append( self.menuItemS3_DownloadObject ) + + self.menuItemS3_DeleteObject = wx.MenuItem( self.menuS3_Object, wx.ID_ANY, u"Delete", wx.EmptyString, wx.ITEM_NORMAL ) + self.menuS3_Object.Append( self.menuItemS3_DeleteObject ) + + self.treeCtrlS3_Objects.Bind( wx.EVT_RIGHT_DOWN, self.treeCtrlS3_ObjectsOnContextMenu ) + + fgSizerS3Objects.Add( self.treeCtrlS3_Objects, 1, wx.ALL|wx.EXPAND, 5 ) + + + fgSizer15.Add( fgSizerS3Objects, 1, wx.EXPAND, 5 ) + + fgSizerS3Upload = wx.FlexGridSizer( 0, 3, 0, 0 ) + fgSizerS3Upload.AddGrowableCol( 1 ) + fgSizerS3Upload.SetFlexibleDirection( wx.BOTH ) + fgSizerS3Upload.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + + fgSizerS3Upload.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + self.staticTextS3_Upload_Details = wx.StaticText( self.panelS3Details, wx.ID_ANY, u"To upload a file, select a key to add the file into it. If you select a file object, the file will be overwritten. You can adjust the name, by just editing it.", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.staticTextS3_Upload_Details.Wrap( 400 ) + + fgSizerS3Upload.Add( self.staticTextS3_Upload_Details, 1, wx.ALL|wx.EXPAND, 5 ) + + + fgSizerS3Upload.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + self.staticTextS3_Upload = wx.StaticText( self.panelS3Details, wx.ID_ANY, u"Upload file to S3 bucket", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.staticTextS3_Upload.Wrap( -1 ) + + fgSizerS3Upload.Add( self.staticTextS3_Upload, 0, wx.ALL, 5 ) + + self.textCtrlS3_SelectedKey = wx.TextCtrl( self.panelS3Details, wx.ID_ANY, u"Select an object or key to upload a new file.", wx.DefaultPosition, wx.DefaultSize, 0 ) + fgSizerS3Upload.Add( self.textCtrlS3_SelectedKey, 1, wx.ALL|wx.EXPAND, 5 ) + + self.buttonS3_Upload = wx.Button( self.panelS3Details, wx.ID_ANY, u"Upload", wx.DefaultPosition, wx.DefaultSize, 0 ) + fgSizerS3Upload.Add( self.buttonS3_Upload, 0, wx.ALL, 5 ) + + + fgSizerS3Upload.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + self.staticTextS3_Upload_DragZone = wx.StaticText( self.panelS3Details, wx.ID_ANY, u"Drag and drop a file here to upload it to the selected key.", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.staticTextS3_Upload_DragZone.Wrap( -1 ) + + self.staticTextS3_Upload_DragZone.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_HIGHLIGHT ) ) + self.staticTextS3_Upload_DragZone.DragAcceptFiles( True ) + + fgSizerS3Upload.Add( self.staticTextS3_Upload_DragZone, 1, wx.ALL|wx.EXPAND, 5 ) + + + fgSizerS3Upload.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + + fgSizer15.Add( fgSizerS3Upload, 1, wx.EXPAND, 5 ) + + + self.panelS3Details.SetSizer( fgSizer15 ) + self.panelS3Details.Layout() + fgSizer15.Fit( self.panelS3Details ) self.m_splitter11.SplitVertically( self.panelS3Tree, self.panelS3Details, 0 ) bSizer81.Add( self.m_splitter11, 1, wx.EXPAND, 5 ) @@ -429,7 +531,7 @@ def __init__( self, parent ): self.panelS3.SetSizer( bSizer81 ) self.panelS3.Layout() bSizer81.Fit( self.panelS3 ) - self.m_auinotebook1.AddPage( self.panelS3, u"S3", False, wx.NullBitmap ) + self.m_auinotebook1.AddPage( self.panelS3, u"S3", True, wx.NullBitmap ) self.panelRDS = wx.Panel( self.m_auinotebook1, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) bSizer82 = wx.BoxSizer( wx.VERTICAL ) @@ -576,7 +678,7 @@ def __init__( self, parent ): self.panelCloudfront.SetSizer( bSizer83 ) self.panelCloudfront.Layout() bSizer83.Fit( self.panelCloudfront ) - self.m_auinotebook1.AddPage( self.panelCloudfront, u"Cloudfront", True, wx.NullBitmap ) + self.m_auinotebook1.AddPage( self.panelCloudfront, u"Cloudfront", False, wx.NullBitmap ) gSizer1.Add( self.m_auinotebook1, 1, wx.EXPAND |wx.ALL, 5 ) @@ -604,7 +706,15 @@ def __init__( self, parent ): self.buttonLambda_RefreshFunction.Bind( wx.EVT_BUTTON, self.aws_lambda_refresh_function ) self.buttonLambda_OpenMgmtConsole.Bind( wx.EVT_BUTTON, self.aws_lambda_open_mgmt_console ) self.buttonLambda_Invoke.Bind( wx.EVT_BUTTON, self.aws_lambda_invoke ) + self.treeS3.Bind( wx.EVT_TREE_ITEM_ACTIVATED, self.aws_s3_load_details ) self.buttonReloadS3.Bind( wx.EVT_BUTTON, self.aws_s3_reload ) + self.buttonS3_Details_Refresh.Bind( wx.EVT_BUTTON, self.aws_s3_refresh_bucket ) + self.buttonS3_Details_OpenMgmtConsole.Bind( wx.EVT_BUTTON, self.aws_s3_open_mgmt_console ) + self.treeCtrlS3_Objects.Bind( wx.EVT_TREE_ITEM_ACTIVATED, self.aws_s3_selected_key ) + self.Bind( wx.EVT_MENU, self.aws_s3_menu_download_object, id = self.menuItemS3_DownloadObject.GetId() ) + self.Bind( wx.EVT_MENU, self.aws_s3_menu_delete_object, id = self.menuItemS3_DeleteObject.GetId() ) + self.buttonS3_Upload.Bind( wx.EVT_BUTTON, self.aws_s3_upload_file ) + self.staticTextS3_Upload_DragZone.Bind( wx.EVT_DROP_FILES, self.aws_s3_drop_file ) self.buttonReloadRDS.Bind( wx.EVT_BUTTON, self.aws_rds_reload ) self.treeCloudfront.Bind( wx.EVT_TREE_ITEM_ACTIVATED, self.aws_cloudfront_load_details ) self.buttonReloadCloudfront.Bind( wx.EVT_BUTTON, self.aws_cloudfront_reload ) @@ -668,9 +778,33 @@ def aws_lambda_open_mgmt_console( self, event ): def aws_lambda_invoke( self, event ): event.Skip() + def aws_s3_load_details( self, event ): + event.Skip() + def aws_s3_reload( self, event ): event.Skip() + def aws_s3_refresh_bucket( self, event ): + event.Skip() + + def aws_s3_open_mgmt_console( self, event ): + event.Skip() + + def aws_s3_selected_key( self, event ): + event.Skip() + + def aws_s3_menu_download_object( self, event ): + event.Skip() + + def aws_s3_menu_delete_object( self, event ): + event.Skip() + + def aws_s3_upload_file( self, event ): + event.Skip() + + def aws_s3_drop_file( self, event ): + event.Skip() + def aws_rds_reload( self, event ): event.Skip() @@ -701,6 +835,9 @@ def m_splitter11OnIdle( self, event ): self.m_splitter11.SetSashPosition( 0 ) self.m_splitter11.Unbind( wx.EVT_IDLE ) + def treeCtrlS3_ObjectsOnContextMenu( self, event ): + self.treeCtrlS3_Objects.PopupMenu( self.menuS3_Object, event.GetPosition() ) + def m_splitter12OnIdle( self, event ): self.m_splitter12.SetSashPosition( 0 ) self.m_splitter12.Unbind( wx.EVT_IDLE ) @@ -849,6 +986,7 @@ def __init__( self, parent ): self.Centre( wx.BOTH ) # Connect Events + self.Bind( wx.EVT_SHOW, self.showConfig ) self.buttonReloadAwsProfile.Bind( wx.EVT_BUTTON, self.reloadAwsProfiles ) self.buttonSave.Bind( wx.EVT_BUTTON, self.saveConfig ) self.buttonCancel.Bind( wx.EVT_BUTTON, self.cancelConfig ) @@ -858,6 +996,9 @@ def __del__( self ): # Virtual event handlers, override them in your derived class + def showConfig( self, event ): + event.Skip() + def reloadAwsProfiles( self, event ): event.Skip() diff --git a/src/helper.py b/src/helper.py index dbcbffb..a5f8242 100644 --- a/src/helper.py +++ b/src/helper.py @@ -2,7 +2,7 @@ import json import logging -VERSION = "v2024-06-09" +VERSION = "v2024-07-07" UPDATEURL = 'https://api.github.com/repos/dseichter/AWSManager/releases/latest' RELEASES = 'https://github.com/dseichter/AWSManager/releases' NAME = 'AWSManager' diff --git a/src/icons.py b/src/icons.py index b0c2bab..ca0dd50 100644 --- a/src/icons.py +++ b/src/icons.py @@ -80,6 +80,111 @@ index.append('cancel') catalog['cancel'] = cancel +#---------------------------------------------------------------------- +delete = PyEmbeddedImage( + b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARzQklU' + b'CAgICHwIZIgAAAfWSURBVHic7Z27jxxFEId/NesXEuKMhIxlATohY5HZlogg4IgcEGAJ4dAp' + b'IZAhIyKERYZJCPgTyMhIQCaADPmwkIWNkBYOix0hGWOfAXO7XQQzs/Pornl27cN0BXcz/aqe' + b'r6arq3t2Z4EgQYIECRIkSJAgQYIECRLk/yS07A4AwKG3r2zeN/QhgFMANpXVjQFsH4z4rX8+' + b'OD1W1tUoSzdACv8KgMMLVn37YMSnl22EaJnKAeA+4xIWDx8ADqe6lypLNwAYW8vTTSeXpjuV' + b'fcvuACjamHtCiorp1XLglh6TwACbcmLxfH7Mm536qiCLmQPe/fFFmNlhMCWTLPEmmDahP+G2' + b'lTGIx2Aap8fbiEa38d4zX2kr9muACzdOgfgkGJsg3loxyH0lM85lEMZg+g4XT2z7anyYAd75' + b'4RUg2gLMKTBteejP+gjxZSDaBsxlvP/sZ72b6d2BCzcuAfxG7/oPlNBHuHjizT41B0RBAX4u' + b'/VkMMcDP/es+aNKfxQADRGeDEYCEQXS2b21vUdDpL259TaDnfbUHLv0TE9mRbSVxXaYzx6Ej' + b'L8yMb75/+bEXXNW6ireFGIFiLw2xC8hqgJ8Lwc+1wqMBGBzTkAG1TPBcPZUUzmX1DNB7BEjg' + b'LSjSSSFJGXzePE3s3H7icQTQhITbRaiwEPBNbiY5rfNJrubNKo4AbtcpC0p3NzNPXjj4VIyn' + b'+Q4eDWAwiiPM5AIPAngAYMYU0eq5oBFMLF28F/BcU6AFeDGicepwZ2bZMxp5GwHeHsjsTTfK' + b'd0USLxeulTEnxaUUx0mhGQaY2V3A0mG3lRzaum0dwoUxg7k0PeORoxs7QunO4s0AV8/QPTD2' + b'fIBnVMFXM5XBpx2oggcANrz77XO056jVS7w+kjTAjg/wqANv5/gHDxt81hZ5XIQBng2QREKF' + b'21MTfMUr5djc4PNR5ei4BN7SARiPizDA8zNhNjwhylfD1sVUy5f/WJlOt2yBZTGvrvlipvNu' + b'l04NvEVAgGcDECXxcelud4hX8I065MzW4Evp/tYAgO8RAI4BWmHw7K7aCnySQNEqG4Bp4tqO' + b'Wwh4CXqa2U6HOz1LYAZ4lV1QMjwLcVCdL2oBvnS/Lhy86zoAM1rhEUBsYhClHZZ9hCp4yb87' + b'2uoCPpNoleeA6b5RPJo69oPqWK4J+KwI82h1XVA03ZgAt/KEFQIvh8TtwGf/rh/b+FUo1Uu8' + b'LsSunqF7QLIdkbka61oLiYtaPHE1r3JSr6NQkrELj9sQgMKno41JtiOcUOanBWw14J23dRN4' + b'1+lQ8HmGV/cDaHw8vbhXIoFnO29eXATP3cBbOhiZq+oMPtPNfrchAI2Pp7MdJy978dSsQ6rK' + b'1WKrbwBijpHuB63K4knWIVW1wGcH3l2QdwMYopiWBr5FRFMo1gF8Ip63ogEVFyQ/nF92DO9o' + b'zllJGlXrMQcQJo6Ou6UleIE1FgV+nkz+oyD/BphRjKjB/0rgpbu95qQzeEeFRvB5+hqMgGgU' + b'M0/deesLHgBjNFsDA5iH/o2jvyrLi67gawoOBd8E3a6Sn1x/+nGv2xCAwkLs2ktHdgEky/W+' + b'i6dKDudN2ZLWsauWK8g6XFW4XDA5vOt7GwLQ+qI2m50snPQB3gnMF3h2gbd1GIUICFAygGHE' + b'g8DzAsHXdLCYSgr7QIDaN+XThxatfHwL/14oNtTHS/5dDHuzBKURoPWqgonzgnxFNI5KauCz' + b'Q4VVMKBnANsFOU7WAXx2qjUHqBiAiWKyPrw5ALyHGL5Zh9Sd5DrWag7gGeL8A3LrDT4Tomh9' + b'RgBGNIHJXw+zzMVTZ/BSZxVWwYCSAabRNN5v5E/I6YB3h1yDwadZM14jF3Rgl2I+6MjwBb7k' + b'HfqBz3VIvqxcZXzn6E2hN4NEZSF27dyRXTDyZbtjYZOkM7osnrLiDEchUYerGFu6iwUcX/64' + b'i9f9b0MAiu+MY+Yd3+ArpfPDzuBdHRayGGCj434A3Zf2xergXVU9gi8kqkzAgOJL+5gQ55Fo' + b'Nx/fefEkTaxVRy7qcHcuO9RaBQOab000mID8gm8d0XgCn6fruSA1A2RfZmu6UwGP4BtCSXdG' + b'fiD3VWcRBmi6oMxvykwWFsNLHcwOGt0ZtXwNQw9RM4BhxNIMvwrgrQekog69fSBA0wARJlHx' + b'5bUlt9wPfG43yZfVDLge4PNzf68mqIreHJBu34r+vXDaDnwNXSXwhRw1A6itA/7co3iZi6dc' + b'R+UjAU4dEB6BJgq0tiEARQP8fu7ILpj3LPBYAHi0B293JleQLgTvaG1DAMqvr2fCjgieqwnN' + b'4LkRPJfBizrs9HluBj5LUoyAAO3fDzD5doTLG5WTmsHbGaiAb9LhznOCn1+Dv/fDuUT19wPK' + b'2xGOfEAgm+bXTLrZQd0cn+sQNTSHq8ojQNUAJDzIHgp+YETTSkd+6Pd7wVXRHQHgSfFdorXg' + b'hfk2r+gBfAsdVnXFrWhA+ydMOIo525BbN/C57rUeAdepK/gSECXwEnRHgwb7bria8CXqvyFz' + b'/NPf7gJ4eJ7gATwL6UUFQ8EDADPf/uX8k4+6mvIl6j9jxeDXgDYxvBVMDo/hnTocIXFlGcwA' + b'DPPfM9CrrS5ygKgb4Kdzxz7n2YGnwPwJGHfmGVYMXyG0CPCwwTPzH2zwMfYfOn7z/BNfdr/i' + b'IEGCBAkSJEiQIEGCBAkSJIgs/wGKIXp9X+ZuwAAAAABJRU5ErkJggg==') +index.append('delete') +catalog['delete'] = delete + +#---------------------------------------------------------------------- +download_from_the_cloud = PyEmbeddedImage( + b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARzQklU' + b'CAgICHwIZIgAAAtsSURBVHic7Zx7jFx1Fcc/5+7M7M5uy0Lb1QIt7S70oTyiwfgKxoiBliql' + b'NJIIARQ1QGggviCExFDR0hcoCAb7cttSKBbbkBj+EDWYGFSkQIVSoWpr290ttLLbfXRnZ3bu' + b'7/jH3XnfO4+dO7NLvN+/9t575ne+53vO7/x+987chQABAgQIECBAgAABAgQIECBAgAABAgT4' + b'f4BMNAEvzHtqpAN79GMNyiUGvVTQi0HmAKgCKAonVDklcEpVjwKvqco+EWvvv74x9eSEBlAm' + b'JlUCFm4fXA56jSJLBGaAjontICV87nHq76wDwBj2iegetaxnDt3S+s/aMh8/JjwBC3fG5pJM' + b'fk+N3iQirc5ZN+Gd87nHhcJnJyl9SXlVLdmgVuuT/7lFRvyOoRpMWAI+2jk0Uxv0foXbM2er' + b'Fb5wjBxbpV/RrapNa4/c1nK82hj8wIQkYMH2wdsFXQ8yxTlTY+HzxgAzaKvcf+z4tEdZKWac' + b'YfiCuibggh3DsxqMvU3gcueMP8JXYptjB29im1uO3tH2aqWx+IW6JWDh9sHlinYKcoZ7tYKb' + b'mHWYHQnbyI3dd0x/toJwfINVDycLtw88BOwW5AzVQkEdMXTs7/QVsm0z1wptHTvNG7NwjHzb' + b'MbuIJbpr9hP/XVWD0Eui5jNg4fbBncBXy9qtpK+59e5arQdZtsrOrhVtN3hH4z9qloBZuzTa' + b'Ehv6DegXs8/7I3zmfL5tda1NQfXJ7hUf+hoieSPVBqFaDdwcG3wBuCx1XGvhK7EtmiTkprMf' + b'P5k4Dt/yis1P1GQNWLB14NcyJn52j08dp+DWu916fMo2ewzv9cDbttz1R0S/OfOx937ghxal' + b'4HsLmr9t4D6BVVXfvbrY1rst2bD4xJ0f/i01hK8JWLBt6BJV8yoQqs2iWc/1ABTtT5jIJX3f' + b'nnaUGsHXBMzbOrBf0AuhxrsVait8vq1CD0a7QI8r0qUif0+Y6O6B77b2UiV8S8C8zoG7RXTd' + b'xAlfia0/SVL09wZ+pfHos333TutnHPAlAXM7h2aGSB4SJJomV0ZlOX/XN0nlrz+VxGBiimwa' + b'sSJrhr/TVtFDPl92QSGSqxzx63b3mrri4s9tB+Tlr5BXxraSGCQqcFeTHf/3Weu772allq1r' + b'1TOgo7N/XgMcrMfdq6qioyNgkqjJ82cJIhaEm8gOayJ2XKq618ANA/fMLvlFUNUzQIzeXVid' + b'btXiJmiKdOmKV1U0PoSJDWIPncKc7sMe6sMe6sWc7sUM9WHHhjDx4YIxcv17VXwlMXjbAojI' + b'Jyzk4Bnrum4uqV8pg2K44GfayJRT76tKyxjFNLFMcJnzudcqWwhNPIYdG2RxRyP3XX4u82Y0' + b'5Xz24MkYP/xdF384kkCapiLhphL+az87VDEqeuPAPeftxAPVzYApA9c54ntXVqlqyVzLtR27' + b'kp4damxIJli35LwC8QHmt0VZddV5mEQcNbbP68H4ZgdgifL01NXHrvWSsKoEGGMW1WpKF7Ql' + b'o6ixaZsS9uQz96xGVA1qjMuYXsKn/NUuBhHdM/XBo0vdOFeVAIXP1Vz4vOosi1XNdlzjjwHR' + b'p5vXHj0nn+24noZ2bOhtVcv6iqBzUqRznI0Rzj0u1o9LrwfliZ+xlTxepf258Sq0LbamlLBt' + b'kaRuBa7M/kRFM6Bjc+8N7Zv6XqBBTono5uqrJVVxuaSLfUNWHrwrvlAktyeymTHybd2Lorz7' + b'ERGuiP74yHXZTMtKQMem/iXtm/peB3lKhCv8JJ09RvHH1uUwxUUIqG1bquxGUER/wsrD6V1E' + b'0RY0a3P/tAj2DjBXTcSULvBXjGwer/wW5jlmnWNAmRUNcXsMHoEiM2Duxv5FEczbIFdNiscG' + b'ZGyLwbviCwWtewwpW+We1HnXBLRv6VsqlnlelbbJQjp3zGLwY8dVPIYU1ypa69mRHx1eBi4J' + b'mLu5fzE2u1Ft8JP0uKsFcvyVQrapnzeCfsdrqV4PeWtA++a+OWqSexAJ5RPJFyITDOTb5guW' + b'bZt7rbJeWu4uKH821j8GL3/ZvnQJK9XKXYSN7hCR6GQlXQ5UdeweYPLG4BzKlLB16NJ0AuZu' + b'ev9WVS7Dw+FkIF1WLtKzfnLGkDuGftpJwC6NaF/fqtRvkSYt6fI3ojWPoRJbz3hVPhICmNPX' + b'd6uIzpiswheO643UTJH0sVsMXtzcbd25VR+vKvPHFlu9N9dBtaQrT5IaA8k4xrbJ6iPjXg9M' + b'bKCQlwhYDWhDIyJSIoZKhPfSrHgMgp4Tmv1E70Uieq73IM5ANa+WRAyTiKGjw6C5zaYS4VUV' + b'+3Sfqz8QiDQjIRuNtPgQQyXCZ86njhXODYllLnefNs4H/Be+kLQaG2OS6OgwP7+2nes/3sZ4' + b'MbD6M57XOl9+lzv3HAYrBMZGLGtChE/ZqmpTSJUvCBMjfIa0Ol+4qHLl/DOpFT5/QWsuNy3k' + b'leFWOoZKbF2TpAxaanRuXlbSxs6CltuPNYt0uY8N3B9TZGyRBqyGMISiLN/2Nv0jSfxGfyzJ' + b'zTveQRsaUQkhlpWO1y2G1LFbDNXGm9FQ+ixE2vKFTw3iJny1pN2IAGi4CSvSzBsnbJZu+QdD' + b'cbuooJWgP5ZkyYb9vP5eEmlsgXCjS6GluNWu0AoKW8xBCzXT89tN+c86/CMtImg4ikRaeONE' + b'kmWd/iRhKG6zZMN+9p1IIpFmNJT53VBxkYo9Aa5S+LRmst9SJVlcpGLC+0taBIhEIdLC3p5R' + b'rvlldUkYitt8eeN+9p0YhXAzGm52Ej3uGCp5AlxM+LFrhpctVemprfCVkU5tFZ0kJFi65cC4' + b'kpAS/289CQi3OGMi44ohw83b1k2zlK1blwCFxugfLRHtnnjhC2/ciESRRmcmLN18gOFE+UkY' + b'itt8aYMjvkQy4rvFkPLvFkNt49W9rJzdaxnVHi8hvIiUQ9qNSGbM0qRBnJbR2MLe4wmWbSkv' + b'CSnxXznuiK/h5ix/k6jQVDYCWIj1JzfhJ0NbEgENO2vCX7oTLCsxE4YTGfEJtziLuuQLQV1j' + b'8JjhpxhtfRrAitvW87kO3ISfONLO7qgZws38uTvumYThhM3STW+N9fxmZzFP9/xi/txioIIY' + b'KBlDAZRf8NDM0wBW713Tu4zypm97W5+Ezx7T2R01Q7iFl7riXLP5ACOjmf+xMZywuXrjW7x0' + b'bMTZaoZTPX+8MXjbFgqfsS0pvIMRkpFHUwchABF2q+HibCuvvp17nE2w0DafRLZt7jU32/wx' + b'xx6kobx0bJhPPvwa3798Ngnb8NMXuzg0QLrnZ550at6YXv5rE68HHmB9+7upAwE4a0NvayQW' + b'P4TItIkmXSpJagyMxjCjcbBHHduGMDQ0Qjg69oBtUgoPygFWL7gw+5QF0HfbtH5VVtfslttl' + b'So934Ucsp8ojUaSxGSJTxnp+M4gUaYO1j6EolH4arIJfSOe8oNH2SM8xVGZNpoovz59bxXv5' + b'c+Pl5a/Kis+GsRaxZt4L+adzfxekoatRE5tMFV/cn//bRMUrhpRtvoTlQG9yEx9cXlGa8XD3' + b'UhV5TkCqrxav6ixd8ZXY1nt2lA3VUURu5sEFz3iZuL4jNv3h7hXA44VkajGlx5+kSSu889F+' + b'RK/lwYUvFjPzfElv2kM9i0B3AR7/Yqyewley/kyw8M7n30Eii1jVcaSUZdG3JFvXvXe+JYnn' + b'QC5KDz1phffyV0fhVXtQWc2aBY+XNnZQ+jXVlWqd2dzzdTAPAGO/nsjx+gES3stftcJzCEvW' + b'MyCdPDYvXslHy39PeOXhpqnRhhWislyEz/olfCW29UhShdiHkXWsme/5HnApjO9F7bUnp04x' + b'w4sF+RTQoartItoO0vpBnR0loXoa5DVE/4rhFTT8MmvPr9n/EQoQIECAAAECBAgQIECAAAEC' + b'BAgQIECAAAECBAgQIECAAB98/A8DIpRkVa3vWwAAAABJRU5ErkJggg==') +index.append('download_from_the_cloud') +catalog['download_from_the_cloud'] = download_from_the_cloud + #---------------------------------------------------------------------- get_help = PyEmbeddedImage( b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARzQklU' diff --git a/src/requirements.txt b/src/requirements.txt index b5a929c..97ff7f6 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,4 +1,4 @@ wxPython==4.2.1 urllib3==2.2.2 -pyinstaller==6.8.0 -boto3==1.34.138 +pyinstaller==6.9.0 +boto3==1.34.140 diff --git a/src/settings.py b/src/settings.py index 891fcd5..679c411 100644 --- a/src/settings.py +++ b/src/settings.py @@ -1,43 +1,76 @@ import json +import boto3 +import os -CONFIGFILE = 'config.json' +CONFIGFILE = "config.json" def create_config(): # create the config file if it does not exist try: - with open(CONFIGFILE, 'r') as f: + with open(CONFIGFILE, "r") as f: data = json.load(f) except FileNotFoundError: - with open(CONFIGFILE, 'w') as f: - f.write('{}') + with open(CONFIGFILE, "w") as f: + f.write("{}") - with open(CONFIGFILE, 'r') as f: + with open(CONFIGFILE, "r") as f: data = json.load(f) - if 'aws_access_key_id' not in data: - data['aws_access_key_id'] = '' - if 'aws_secret_access_key' not in data: - data['aws_secret_access_key'] = '' - if 'aws_session_token' not in data: - data['aws_session_token'] = '' - if 'aws_profile' not in data: - data['aws_profile'] = '' - if 'region' not in data: - data['region'] = 'eu-central-1' - - with open(CONFIGFILE, 'w') as f: + if "aws_access_key_id" not in data: + data["aws_access_key_id"] = "" + if "aws_secret_access_key" not in data: + data["aws_secret_access_key"] = "" + if "aws_session_token" not in data: + data["aws_session_token"] = "" + if "aws_profile" not in data: + data["aws_profile"] = "" + if "region" not in data: + data["region"] = "eu-central-1" + + with open(CONFIGFILE, "w") as f: json.dump(data, f, indent=4, sort_keys=True) def read_config(): - with open(CONFIGFILE, 'r') as f: + with open(CONFIGFILE, "r") as f: return json.load(f) def save_config(key, value): - with open(CONFIGFILE, 'r') as f: + with open(CONFIGFILE, "r") as f: data = json.load(f) data[key] = value - with open(CONFIGFILE, 'w') as f: + with open(CONFIGFILE, "w") as f: json.dump(data, f, indent=4, sort_keys=True) + + +# get a list of all AWS profiles of the credentials file +def get_profiles(): + profiles = [] + if os.name == "posix": # Linux or macOS + credentials_file = os.path.expanduser("~/.aws/credentials") + elif os.name == "nt": # Windows + credentials_file = os.path.expanduser( + os.path.join(os.environ["USERPROFILE"], ".aws", "credentials") + ) + else: + raise OSError("Unsupported operating system") + + with open(credentials_file, "r") as f: + lines = f.readlines() + for line in lines: + if line.startswith("["): + profile = line.strip().strip("[]") + profiles.append(profile) + return profiles + + +# get a list of all AWS regions using the boto3 library +def get_regions(): + ec2 = boto3.client("ec2") + regions = ec2.describe_regions() + region_list = [] + for region in regions["Regions"]: + region_list.append(region["RegionName"]) + return region_list diff --git a/src/ui_aws_ec2.py b/src/ui_aws_ec2.py index 9103073..8d2d31f 100644 --- a/src/ui_aws_ec2.py +++ b/src/ui_aws_ec2.py @@ -69,11 +69,11 @@ def aws_ec2_load_details(self, event): self.textCtrlEC2_PublicIpAddress.SetValue(ec2_instance.get('PublicIpAddress', '')) self.textCtrlEC2_Architecture.SetValue(ec2_instance.get('Architecture', '')) # get the tags - self.propertyGridEC2_tags.Clear() + self.propertyGridEC2_Tags.Clear() # add the tags to the property grid for tag in ec2_instance['Tags']: self.propertyGridEC2_Tags.Append(wx.propgrid.StringProperty(tag['Key'], tag['Key'], tag['Value'])) - self.propertyGridEC2_tags.Refresh() + self.propertyGridEC2_Tags.Refresh() # get volume information and add it to the grid self.gridEC2_Volumes.ClearGrid() volumes = ec2_instance['BlockDeviceMappings'] diff --git a/src/ui_aws_s3.py b/src/ui_aws_s3.py index a17dd7e..6957976 100644 --- a/src/ui_aws_s3.py +++ b/src/ui_aws_s3.py @@ -4,6 +4,11 @@ import aws_s3 import settings import iconsaws +import webbrowser +import os + +NEWKEY_DEFAULT = 'Select an object or key to upload a new file.' +NEWFILE_DEFAULT = 'Drag and drop a file here to upload it to the selected key.' def aws_s3_reload(self, event): @@ -17,7 +22,158 @@ def aws_s3_reload(self, event): root = self.treeS3.AddRoot('Buckets') for bucket in buckets: bucket_name = bucket['Name'] - bucket_creation_date = bucket['CreationDate'] - item = self.treeS3.AppendItem(root, bucket_name + ' (' + bucket_creation_date.strftime('%Y-%m-%d %H:%M:%S') + ')') + item = self.treeS3.AppendItem(root, bucket_name) self.treeS3.SetItemImage(item, s3_buckets, wx.TreeItemIcon_Normal) self.treeS3.ExpandAll() + + +# Load all details +def aws_s3_load_details(self, event): + def add_paths_to_tree_ctrl(treectrl, paths): + root = treectrl.GetRootItem() + if not root: # If there's no root item, create one. + root = treectrl.AddRoot("Root") + + for path in paths: + components = path.split('/') + parent = root + for component in components: + parent = find_or_create_node(treectrl, parent, component) + + def find_or_create_node(treectrl, parent, component): + child, cookie = treectrl.GetFirstChild(parent) + while child.IsOk(): + if treectrl.GetItemText(child) == component: + return child + child, cookie = treectrl.GetNextChild(parent, cookie) + + # If the component does not exist, create it. + return treectrl.AppendItem(parent, component) + + # get the selected item + item = self.treeS3.GetSelection() + if not item: + return + # get the text of the selected item + bucket_name = self.treeS3.GetItemText(item) + # get the bucket name + self.textCtrlS3_Details_BucketName.SetValue(bucket_name) + + # load the objects of the bucket + objects = aws_s3.get_s3_bucket_objects(settings.read_config()['region'], bucket_name) + # clear the tree control + self.treeCtrlS3_Objects.DeleteAllItems() + self.treeCtrlS3_Objects.AddRoot('/') + for obj in objects: + object_name = obj['Key'] + add_paths_to_tree_ctrl(self.treeCtrlS3_Objects, [object_name]) + + self.treeCtrlS3_Objects.ExpandAll() + + +def aws_s3_get_full_path(self, item): + path = [] + while item: + path.insert(0, self.treeCtrlS3_Objects.GetItemText(item)) + item = self.treeCtrlS3_Objects.GetItemParent(item) + text = '/'.join(path) + # remove all leading slashes + text = text.lstrip('/') + + # if the item has a child node, add a / at the end, because this is a folder + if self.treeCtrlS3_Objects.ItemHasChildren(self.treeCtrlS3_Objects.GetSelection()): + text += '/' + + return text + + +def aws_s3_selected_key(self, event): + # get the selected item + item = self.treeCtrlS3_Objects.GetSelection() + if not item: + return + # read the full path of the selected item + text = aws_s3_get_full_path(self, item) + + self.textCtrlS3_SelectedKey.SetLabel(text) + + +def aws_s3_refresh_bucket(self, event): + aws_s3_load_details(self, event) + + +def aws_s3_open_mgmt_console(self, event): + # get the selected item + item = self.treeS3.GetSelection() + if item: + # get the text of the selected item + text = self.treeS3.GetItemText(item) + # get the bucket name + bucket_name = text.split(' ')[0] + # open the management console + webbrowser.open('https://s3.console.aws.amazon.com/s3/buckets/' + bucket_name + '?region=' + settings.read_config()['region']) + + +def aws_s3_upload_file(self, event): + # check first, if default text is still in the text control. If so, show a message box and return + if self.staticTextS3_Upload_DragZone.GetLabel() == NEWFILE_DEFAULT or self.textCtrlS3_SelectedKey.GetValue() == NEWKEY_DEFAULT: + wx.MessageBox("Please drop a file and select/enter a key before uploading.", "Warning", wx.OK | wx.ICON_WARNING) + return + + file_name_with_extension = os.path.basename(self.staticTextS3_Upload_DragZone.GetLabel()) + target_key = self.textCtrlS3_SelectedKey.GetValue() + source_file = self.staticTextS3_Upload_DragZone.GetLabel() + + # if both are set, upload the file + aws_s3.upload_file(settings.read_config()['region'], self.textCtrlS3_Details_BucketName.GetValue(), source_file, target_key + file_name_with_extension) + # show a message box that the file was uploaded + wx.MessageBox("File uploaded successfully.", "Success", wx.OK | wx.ICON_INFORMATION) + # clear the text control + self.staticTextS3_Upload_DragZone.SetLabel(NEWFILE_DEFAULT) + self.textCtrlS3_SelectedKey.SetValue(NEWKEY_DEFAULT) + # refresh the bucket + aws_s3_load_details(self, event) + + +def aws_s3_drop_file(self, event): + # read the dropped file + file_path = event.GetFiles()[0] + # show the file in the static text + self.staticTextS3_Upload_DragZone.SetLabel(file_path) + + +def aws_s3_menu_download_object(self, event): + # get the selected item + item = self.treeCtrlS3_Objects.GetSelection() + if not item: + return + # read the full path of the selected item + s3object = aws_s3_get_full_path(self, item) + filename = self.treeS3.GetItemText(item) + # ask the user where to save the file + dlg = wx.FileDialog(self, "Save File", defaultFile=filename, wildcard="All files (*.*)|*.*", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + if dlg.ShowModal() == wx.ID_OK: + # download the object + aws_s3.download_object(settings.read_config()['region'], self.textCtrlS3_Details_BucketName.GetValue(), s3object, dlg.GetPath()) + # show a message box that the object was downloaded + wx.MessageBox("Object downloaded successfully.", "Success", wx.OK | wx.ICON_INFORMATION) + dlg.Destroy() + + +def aws_s3_menu_delete_object(self, event): + # get the selected item + item = self.treeCtrlS3_Objects.GetSelection() + if not item: + return + # read the full path of the selected item + s3object = aws_s3_get_full_path(self, item) + # ask the user if he really wants to delete the object + dlg = wx.MessageDialog(self, f"Are you sure you want to delete the object '{s3object}'?", "Delete Object", wx.YES_NO | wx.ICON_QUESTION) + if dlg.ShowModal() == wx.ID_YES: + # delete the object + aws_s3.delete_object(settings.read_config()['region'], self.textCtrlS3_Details_BucketName.GetValue(), s3object) + # show a message box that the object was deleted + wx.MessageBox("Object deleted successfully.", "Success", wx.OK | wx.ICON_INFORMATION) + # refresh the bucket + aws_s3_load_details(self, event) + dlg.Destroy()