diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d52e0314f1..25fd59873f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_. +==================== +2.2.8 - 2019-05-07 +==================== + +Added +----- +* Support for the Tokyo (NRT) region +* A sample demonstrating how to find, stop and report on instances that have been improperly tagged is available on `GitHub `__. +* A sample demonstrating adding and deleting an API key is available on `GitHub `__. +* New services to showoci.py on `GitHub `__. + +Fixed +----- +* Updated example for Streaming service to address issue with encoding in Python 3 is available on `GitHub `__. + ==================== 2.2.7 - 2019-04-16 ==================== diff --git a/README.rst b/README.rst index eede868e6b..065ab0d50a 100644 --- a/README.rst +++ b/README.rst @@ -79,7 +79,7 @@ Full documentation, including prerequisites and installation and configuration i API reference can be found `here`__. __ https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/index.html -__ https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/index.html +__ https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/landing.html A downloadable version of the documentation is include with in the release zip, which can be found `here`__. diff --git a/docs/api/announcements_service/models/oci.announcements_service.models.NotificationFollowupDetails.rst b/docs/api/announcements_service/models/oci.announcements_service.models.NotificationFollowupDetails.rst deleted file mode 100644 index 299c8d7b98..0000000000 --- a/docs/api/announcements_service/models/oci.announcements_service.models.NotificationFollowupDetails.rst +++ /dev/null @@ -1,11 +0,0 @@ -NotificationFollowupDetails -=========================== - -.. currentmodule:: oci.announcements_service.models - -.. autoclass:: NotificationFollowupDetails - :show-inheritance: - :special-members: __init__ - :members: - :undoc-members: - :inherited-members: \ No newline at end of file diff --git a/docs/api/index.rst b/docs/api/index.rst index 24316b1cfc..2f4c9c56e1 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,811 +1,7 @@ -.. _api-reference: - -.. raw:: html - - - Single Page Reference ~~~~~~~~~~~~~~~~~~~~~~ +This page has been deprecated. -===================== -Announcements Service -===================== - --------- - Client --------- - -.. autoclass:: oci.announcements_service.announcement_client.AnnouncementClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.announcements_service.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -===== -Audit -===== - --------- - Client --------- - -.. autoclass:: oci.audit.audit_client.AuditClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.audit.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -=========== -Autoscaling -=========== - --------- - Client --------- - -.. autoclass:: oci.autoscaling.auto_scaling_client.AutoScalingClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.autoscaling.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -====== -Budget -====== - --------- - Client --------- - -.. autoclass:: oci.budget.budget_client.BudgetClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.budget.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -================ -Container Engine -================ - --------- - Client --------- - -.. autoclass:: oci.container_engine.container_engine_client.ContainerEngineClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.container_engine.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============= -Core Services -============= - ---------- - Clients ---------- - - -Block Storage -============= - -.. autoclass:: oci.core.blockstorage_client.BlockstorageClient - :members: - :noindex: - - -Compute -======= - -.. autoclass:: oci.core.compute_client.ComputeClient - :members: - :noindex: - - -Compute Management -================== - -.. autoclass:: oci.core.compute_management_client.ComputeManagementClient - :members: - :noindex: - - -Virtual Network -=============== - -.. autoclass:: oci.core.virtual_network_client.VirtualNetworkClient - :members: - :noindex: - - --------- - Models --------- - -.. automodule:: oci.core.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -======== -Database -======== - --------- - Client --------- - -.. autoclass:: oci.database.database_client.DatabaseClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.database.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -=== -DNS -=== - --------- - Client --------- - -.. autoclass:: oci.dns.dns_client.DnsClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.dns.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -===== -Email -===== - --------- - Client --------- - -.. autoclass:: oci.email.email_client.EmailClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.email.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============ -File Storage -============ - --------- - Client --------- - -.. autoclass:: oci.file_storage.file_storage_client.FileStorageClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.file_storage.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============ -Healthchecks -============ - --------- - Client --------- - -.. autoclass:: oci.healthchecks.health_checks_client.HealthChecksClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.healthchecks.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -======== -Identity -======== - --------- - Client --------- - -.. autoclass:: oci.identity.identity_client.IdentityClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.identity.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============== -Key Management -============== - ---------- - Clients ---------- - - -Kms Crypto -========== - -.. autoclass:: oci.key_management.kms_crypto_client.KmsCryptoClient - :members: - :noindex: - - -Kms Management -============== - -.. autoclass:: oci.key_management.kms_management_client.KmsManagementClient - :members: - :noindex: - - -Kms Vault -========= - -.. autoclass:: oci.key_management.kms_vault_client.KmsVaultClient - :members: - :noindex: - - --------- - Models --------- - -.. automodule:: oci.key_management.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============= -Load Balancer -============= - --------- - Client --------- - -.. autoclass:: oci.load_balancer.load_balancer_client.LoadBalancerClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.load_balancer.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -========== -Monitoring -========== - --------- - Client --------- - -.. autoclass:: oci.monitoring.monitoring_client.MonitoringClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.monitoring.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -============== -Object Storage -============== - --------- - Client --------- - -.. autoclass:: oci.object_storage.object_storage_client.ObjectStorageClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.object_storage.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -=== -Ons -=== - ---------- - Clients ---------- - - -Notification Control Plane -========================== - -.. autoclass:: oci.ons.notification_control_plane_client.NotificationControlPlaneClient - :members: - :noindex: - - -Notification Data Plane -======================= - -.. autoclass:: oci.ons.notification_data_plane_client.NotificationDataPlaneClient - :members: - :noindex: - - --------- - Models --------- - -.. automodule:: oci.ons.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -================ -Resource Manager -================ - --------- - Client --------- - -.. autoclass:: oci.resource_manager.resource_manager_client.ResourceManagerClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.resource_manager.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -=============== -Resource Search -=============== - --------- - Client --------- - -.. autoclass:: oci.resource_search.resource_search_client.ResourceSearchClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.resource_search.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -========= -Streaming -========= - ---------- - Clients ---------- - - -Stream Admin -============ - -.. autoclass:: oci.streaming.stream_admin_client.StreamAdminClient - :members: - :noindex: - - -Stream -====== - -.. autoclass:: oci.streaming.stream_client.StreamClient - :members: - :noindex: - - --------- - Models --------- - -.. automodule:: oci.streaming.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -==== -Waas -==== - --------- - Client --------- - -.. autoclass:: oci.waas.waas_client.WaasClient - :members: - :noindex: - --------- - Models --------- - -.. automodule:: oci.waas.models - :special-members: __init__ - :members: - :undoc-members: - :imported-members: - :inherited-members: - :noindex: - - -================ - Upload Manager -================ - -.. module:: oci.object_storage - :noindex: - -.. autoclass:: UploadManager - :special-members: __init__ - :members: - :noindex: - -============= - Base Client -============= - -.. module:: oci.base_client - -.. autoclass:: BaseClient - :members: call_api, request - -======== - Config -======== - -.. module:: oci.config - :noindex: - -.. autofunction:: from_file - :noindex: - -.. autofunction:: validate_config - :noindex: - -.. module:: oci.regions - :noindex: - -.. autofunction:: is_region - :noindex: - -.. autofunction:: endpoint_for - :noindex: - - -============ - Exceptions -============ - -.. automodule:: oci.exceptions - :members: - :noindex: - -========= - Signing -========= - -.. module:: oci.signer - :noindex: - -.. autofunction:: load_private_key_from_file - :noindex: - -.. autofunction:: load_private_key - :noindex: - -.. autoclass:: Signer - :noindex: - -===================== - Additional Signers -===================== - -.. module:: oci.auth.signers - :noindex: - -.. autoclass:: SecurityTokenSigner - :special-members: __init__ - :members: - :noindex: - -.. autoclass:: X509FederationClientBasedSecurityTokenSigner - :special-members: __init__ - :members: - :noindex: - -.. autoclass:: InstancePrincipalsSecurityTokenSigner - :special-members: __init__ - :members: - :noindex: - -============================ -X509 Certificate Retrievers -============================ - -.. module:: oci.auth.certificate_retriever - :noindex: - -.. autoclass:: UrlBasedCertificateRetriever - :special-members: __init__ - :members: - :noindex: - -.. autoclass:: PEMStringCertificateRetriever - :special-members: __init__ - :members: - :noindex: - -.. autoclass:: FileBasedCertificateRetriever - :special-members: __init__ - :members: - :noindex: - -==================================== -X509 Certificate Federation Client -==================================== - -.. module:: oci.auth.federation_client - :noindex: - -.. autoclass:: X509FederationClient - :special-members: __init__ - :members: - :noindex: - -.. module:: oci.auth.session_key_supplier - :noindex: - -.. autoclass:: SessionKeySupplier - :special-members: __init__ - :members: - :noindex: - -=========== - Utilities -=========== - -.. module:: oci.util - :noindex: - -.. autofunction:: to_dict - :noindex: - -.. autoclass:: Sentinel - :noindex: - -=========== - Waiters -=========== - -.. module:: oci - :noindex: - -.. autofunction:: wait_until - :noindex: - -=========== - Pagination -=========== - -.. module:: oci.pagination - :noindex: - -.. autofunction:: list_call_get_all_results - :noindex: - -.. autofunction:: list_call_get_up_to_limit - :noindex: - -.. autofunction:: list_call_get_all_results_generator - :noindex: - -.. autofunction:: list_call_get_up_to_limit_generator - :noindex: - -========= -Request -========= -.. module:: oci.request - :noindex: - -.. autoclass:: Request - :members: - :undoc-members: - :noindex: - -========= -Response -========= -.. module:: oci.response - :noindex: - -.. autoclass:: Response - :members: - :undoc-members: - :noindex: \ No newline at end of file +* :doc:`Home <../index>` +* :doc:`API Reference ` \ No newline at end of file diff --git a/docs/api/landing.rst b/docs/api/landing.rst index cf8d5db254..487ec5b4f7 100644 --- a/docs/api/landing.rst +++ b/docs/api/landing.rst @@ -44,9 +44,6 @@ API Reference * :doc:`Utilities ` * :doc:`Waiters ` -.. rubric:: Single Page Reference - -* :doc:`Single Page Reference ` .. toctree:: :hidden: @@ -82,4 +79,3 @@ API Reference upload_manager utilities waiters - index \ No newline at end of file diff --git a/examples/add_API_key.py b/examples/add_API_key.py new file mode 100644 index 0000000000..325ab91ab6 --- /dev/null +++ b/examples/add_API_key.py @@ -0,0 +1,118 @@ +import oci +import sys +import os.path +from hashlib import md5 +from codecs import decode +from time import sleep + + +# This script provides a basic example of how to upload and delete an API key +# using the Python SDK. +# +# This script accepts one argument: +# +# * The path to the public API key to upload. +# +# The script relies on having a config file for the other variables. +# More information on the config file can be found here: +# https://docs.cloud.oracle.com/iaas/Content/API/Concepts/sdkconfig.htm +# +# Note, you must already have an API key associated with your user to call the +# APIs so this example shows you how to add a second key, or you could create +# a new user and use the steps here to upload a key to that user. +# +# More information on Keys and API Signing can be found here: +# https://docs.cloud.oracle.com/iaas/Content/API/Concepts/apisigningkey.htm +# + + +def get_fingerprint(key): + + # There should be more error checking here, but to keep the example simple + # the error checking has been omitted. + m = md5() + + # Strip out the parts of the key that are not used in the fingerprint + # computation. + key = key.replace(b'-----BEGIN PUBLIC KEY-----\n', b'') + key = key.replace(b'\n-----END PUBLIC KEY-----', b'') + + # The key is base64 encoded and needs to be decoded before getting the md5 + # hash + decoded_key = decode(key, "base64") + m.update(decoded_key) + hash = m.hexdigest() + + # Break the hash into 2 character parts. + length = 2 + parts = list(hash[0 + i:length + i] for i in range(0, len(hash), length)) + + # Join the parts with a colon seperator + fingerprint = ":".join(parts) + + return fingerprint + + +def is_key_already_uploaded(keys, fingerprint): + for key in keys: + if key.fingerprint == fingerprint: + return True + + return False + + +# Check there are enough arguments +if len(sys.argv) != 2: + raise RuntimeError('This script expects an argument of a path to the public API key') + +# Verify the path to the public key exists +public_key_path = sys.argv[1] +if not os.path.exists(public_key_path): + raise RuntimeError('This argument must be a valid path to a public API key') + +# Open the key file and store the contents +with open(public_key_path, 'rb') as f: + public_key = f.read().strip() + +# Get the fingerprint for the key +fingerprint = get_fingerprint(public_key) +print("Fingerprint of API key: {}".format(fingerprint)) + +# Read the config file and initialize the identity client +config = oci.config.from_file() +identity = oci.identity.IdentityClient(config) + +# Check to see if this key is already associated with the user. +if is_key_already_uploaded(identity.list_api_keys(config['user']).data, fingerprint): + print("Key with fingerprint {} has already been added to user".format(fingerprint)) + sys.exit() + +# Initialize the CreateApiKeyDetails model +key_details = oci.identity.models.CreateApiKeyDetails(key=public_key.decode()) + +# Upload the key +response = identity.upload_api_key(config['user'], key_details) + +# Results of uploading key +print("Results of uploading key") +print(response.data) + +# Check to see if the key is in the list of keys. +# This is pretty crude and just checks that the fingerprint is in the list +# of keys. If you want to do something similar you should also check that +# the lifecycle_state is "ACTIVE" +while True: + if is_key_already_uploaded(identity.list_api_keys(config['user']).data, fingerprint): + print("Sucessfully uploaded API key {}".format(public_key_path)) + break + sleep(2) + +# Delete key and wait a bit. +identity.delete_api_key(config['user'], fingerprint) +sleep(2) + +# Verify the key has been deleted. +if not is_key_already_uploaded(identity.list_api_keys(config['user']).data, fingerprint): + print("Deleted uploaded API key.") +else: + print("Failed to delete uploaded API key.") diff --git a/examples/showoci/CHANGELOG.rst b/examples/showoci/CHANGELOG.rst index c725a34b9f..390f15fa7a 100644 --- a/examples/showoci/CHANGELOG.rst +++ b/examples/showoci/CHANGELOG.rst @@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_. +==================== +19.4.23 - 2019-04-23 +==================== + +Added +----- +* Added Autonomous Database Whitelist IPs +* Added Identity - Cost Tracking Tags +* Added Budgets +* Added Compute Autoscaling +* Add OS Version to the compute summary +* Add Reboot migration alert + +Fixed / Changed +--------------- +* Display Volume Backups with 1 line instead of 3 lines +* Fix load balancer pathroute error when output to JSON + ==================== 19.4.14 - 2019-04-14 ==================== diff --git a/examples/showoci/README.md b/examples/showoci/README.md index 1db2aa0d30..1856206d72 100644 --- a/examples/showoci/README.md +++ b/examples/showoci/README.md @@ -15,7 +15,8 @@ Modules Included: - oci.email.EmailClient - oci.container_engine.ContainerEngineClient - oci.streaming.StreamAdminClient - +- oci.budget.BudgetClient +- oci.autoscaling.AutoScalingClient ## OCI User Requirement Required OCI IAM user with read only privileges: @@ -44,7 +45,8 @@ pip3 install oci oci-cli ### Installation on OCI VM with Oracle Cloud Developer Image ``` -All Installed, just run the config below +Default is python 2.7 , please run as below: +python showoci.py ``` ### Installation on OCI VM with Oracle Linux 7 @@ -110,8 +112,8 @@ Execute ``` $ ./showoci.py -usage: showoci [-h] [-a] [-ani] [-n] [-i] [-c] [-cn] [-o] [-l] [-d] [-f] [-e] - [-s] [-rm] [-so] [-mc] [-nr] [-t PROFILE] [-p PROXY] +usage: showoci [-h] [-a] [-ani] [-b] [-n] [-i] [-c] [-cn] [-o] [-l] [-d] [-f] + [-e] [-s] [-rm] [-so] [-mc] [-nr] [-t PROFILE] [-p PROXY] [-rg REGION] [-cp COMPART] [-cf CONFIG] [-jf JOUTFILE] [-js] [-cachef SERVICEFILE] [-caches] [--version] @@ -119,6 +121,7 @@ optional arguments: -h, --help show this help message and exit -a Print All Resources -ani Print All Resources but identity + -b Print Budgets -n Print Network -i Print Identity -c Print Compute @@ -144,7 +147,6 @@ optional arguments: -caches Output Cache to screen (JSON format) --version show program's version number and exit - ``` ## Below example of reports from few tenancies @@ -333,6 +335,24 @@ Compartment gse00000000 (root): Group Map :DemoUsers <-> demousers Group Map :OCI_Administrators <-> Administrators +############################## +# Cost Tracking Tags # +############################## +--> Project.Billing + Desc :Billing Cost + Created :2019-04-03 12:38 + +############################## +# Budgets # +############################## +--> DemoBudget for Compartment: Demo (MONTHLY) + Costs : Spent: 0.0, Forcasted: 0.0 , Time Computed: 2019-04-17 17:45 + Created : 2019-04-17 15:57, Total Alert Rules: 0 + +--> BudgetForAdiCompartment for Compartment: Adi (MONTHLY) + Costs : Spent: 0.0, Forcasted: 0.0 , Time Computed: 2019-04-17 17:45 + Created : 2019-04-17 13:29, Total Alert Rules: 2 + ########################################################################################## # Region us-ashburn-1 # ########################################################################################## @@ -488,6 +508,16 @@ Compartment gse00000000 (root): ADs : fHBa:US-ASHBURN-AD-3, fHBa:US-ASHBURN-AD-2 Config: AdiInstanceConfig - VM.Standard2.1 +############################## +# Compute Autoscaling # +############################## +--> AutoScallingConfig (ENABLED) + Resource : instancePool: Adi-Instance-Pool + Policy : auto-scaling-policy-20190417-1627 (threshold) + Capacity : Initial = 1, Min = 1, Max = 2 + Rule : CHANGE_COUNT_BY -1 when CPU_UTILIZATION LT 20 + Rule : CHANGE_COUNT_BY 1 when CPU_UTILIZATION GT 80 + ############################## # Compute Custom Images # ############################## @@ -502,22 +532,17 @@ Compartment gse00000000 (root): ############################## # Boot Volume Backups # ############################## ---> demohost (Boot Volume), - Auto-backup for 2018-08-01 04:00:00 via policy: bronze - Type : INCREMENTAL, SCHEDULED, 2018-08-01 16:27 -> 2019-08-01 23:07 - Size : 47gb , Stored 1gb ---> demohost (Boot Volume), - Auto-backup for 2018-09-01 04:00:00 via policy: bronze - Type : INCREMENTAL, SCHEDULED, 2018-09-01 07:50 -> 2019-09-01 14:30 - Size : 47gb , Stored 1gb +--> demohost (Boot Volume), 47gb , Stored 1gb, AUTO (bronze), INCR, SCHEDU, 2018-08-01 16:27 -> 2019-08-01 23:07 +--> demohost (Boot Volume), 47gb , Stored 1gb, AUTO (bronze), INCR, SCHEDU, 2018-09-01 07:50 -> 2019-09-01 14:30 +--> demohost (Boot Volume), 47gb , Stored 1gb, AUTO (bronze), INCR, SCHEDU, 2018-10-01 09:02 -> 2019-10-01 15:42 ############################## # Block Volume Backups # ############################## ---> Adi_50G ( Source TERMINATED ) - Adi_Backup_50G - Type : FULL, MANUAL, 2018-10-22 02:52 -> Keep - Size : 50gb , Stored 1gb ---> Adi_50G ( Source TERMINATED ) - Auto-backup for 2018-11-01 04:00:00 via policy: bronze - Type : INCREMENTAL, SCHEDULED, 2018-11-01 04:40 -> 2019-11-01 11:20 - Size : 50gb , Stored 1gb +--> Adi_50G (Source TERMINATED), 50gb , Stored 1gb, Adi_Backup_50G, FULL, MANUAL, 2018-10-22 02:52 -> Keep +--> Adi_50G (Source TERMINATED), 50gb , Stored 1gb, AUTO (bronze), INCR, SCHEDU, 2018-11-01 04:40 -> 2019-11-01 11:20 +--> Adi_50G (Source TERMINATED), 50gb , Stored 1gb, AUTO (bronze), INCR, SCHEDU, 2018-12-01 05:53 -> 2019-12-01 12:33 +--> Adi_50G (Source TERMINATED), 50gb , Stored 1gb, AUTO (bronze), FULL, SCHEDU, 2019-01-01 06:23 -> 2023-12-31 06:23 ############################## # Databases # @@ -678,53 +703,52 @@ Compartment gse00000000 (root): ########################################################################### # Summary - Compartment generic # ########################################################################### -Compute - Block Storage (gb) - 3547 -Compute - VM.Standard1.1 - 1 -Compute - VM.Standard2.1 - 7 -Compute - VM.Standard2.4 - 1 -Object Storage - BV Backups (gb) - 1276 -Object Storage - Buckets (gb) - 216 +Compute - Block Storage (gb) - 3547 +Compute - Oracle Linux - VM.Standard1.1 - 1 +Compute - Oracle Linux - VM.Standard2.1 - 7 +Compute - Oracle Linux - VM.Standard2.4 - 1 +Object Storage - BV Backups (gb) - 1276 +Object Storage - Buckets (gb) - 216 ########################################################################### # Summary - Compartment npdb # ########################################################################### -Database - Exadata.Half2.184 - 1 -Object Storage - Buckets (gb) - 6152 +Database - Exadata.Half2.184 - 1 +Object Storage - Buckets (gb) - 6152 ########################################################################### # Summary - Compartment npebs # ########################################################################### -Compute - Block Storage (gb) - 14121 -Compute - VM.Standard2.1 - 16 -Compute - VM.Standard2.2 - 28 -Compute - VM.Standard2.4 - 6 -Compute - VM.Standard2.8 - 5 -File Storage (gb) - 2617 -Load Balancer 100Mbps - 10 -Object Storage - BV Backups (gb) - 2806 -Object Storage - Images (gb) - 1862 +Compute - Block Storage (gb) - 14121 +Compute - Oracle Linux - VM.Standard2.1 - 16 +Compute - Oracle Linux - VM.Standard2.4 - 6 +Compute - Oracle Linux - VM.Standard2.8 - 5 +Compute - Windows - VM.Standard2.2 - 28 +File Storage (gb) - 2617 +Load Balancer 100Mbps - 10 +Object Storage - BV Backups (gb) - 2806 +Object Storage - Images (gb) - 1862 ########################################################################################## # Summary Total # ########################################################################################## -Compute - Block Storage (gb) - 17668 -Compute - VM.Standard1.1 - 1 -Compute - VM.Standard2.1 - 23 -Compute - VM.Standard2.2 - 28 -Compute - VM.Standard2.4 - 7 -Compute - VM.Standard2.8 - 5 -Database - Exadata.Half2.184 - 1 -File Storage (gb) - 2617 -Load Balancer 100Mbps - 10 -Object Storage - BV Backups (gb) - 4082 -Object Storage - Buckets (gb) - 6368 -Object Storage - Images (gb) - 1862 +Compute - Block Storage (gb) - 17668 +Compute - Oracle Linux - VM.Standard1.1 - 1 +Compute - Oracle Linux - VM.Standard2.1 - 23 +Compute - Oracle Linux - VM.Standard2.4 - 7 +Compute - Oracle Linux - VM.Standard2.8 - 5 +Compute - Windows - VM.Standard2.2 - 28 +Database - Exadata.Half2.184 - 1 +File Storage (gb) - 2617 +Load Balancer 100Mbps - 10 +Object Storage - BV Backups (gb) - 4082 +Object Storage - Buckets (gb) - 6368 +Object Storage - Images (gb) - 1862 ``` ## Below example JSON report on us-ashburn-1 region, compartment Adi without identity ``` - > showoci -t gse00015259 -ani -js -rg us-ashburn-1 -cp Adi ############################################################ @@ -1399,4 +1423,4 @@ Processing... # Completed Successfully at 2019-04-05 08:15:16 # ########################################################################################## -``` \ No newline at end of file +``` diff --git a/examples/showoci/showoci.py b/examples/showoci/showoci.py index 447b31aae3..68ddc73e2f 100644 --- a/examples/showoci/showoci.py +++ b/examples/showoci/showoci.py @@ -3,12 +3,10 @@ # Copyright(c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # showoci.py # -# @Created On : Mar 17 2019 -# @Last Updated: Apr 14 2019 -# @author : Adi Zohar -# @Version : 19.4.14 +# @author: Adi Zohar # # Supports Python 2.7 and above, Python 3 recommended +# With Python 2, execute using - python showoci.py # # coding: utf-8 ########################################################################## @@ -30,19 +28,28 @@ ########################################################################## # # Modules Included: -# identity , virtual_network, compute , block_storage, file_storage -# object_storage, database, load_balancer, email -# resource_management, ContainerEngine, Streams +# - oci.core.VirtualNetworkClient +# - oci.core.ComputeClient +# - oci.core.ComputeManagementClient +# - oci.core.BlockstorageClient +# - oci.file_storage.FileStorageClient +# - oci.object_storage.ObjectStorageClient +# - oci.database.DatabaseClient +# - oci.identity.IdentityClient +# - oci.load_balancer.LoadBalancerClient +# - oci.email.EmailClient +# - oci.container_engine.ContainerEngineClient +# - oci.streaming.StreamAdminClient +# - oci.budget.BudgetClient +# - oci.autoscaling.AutoScalingClient # # Modules Not Yet Covered: -# Monitoring -# Notifications -# WaasClient -# HealthChecksClient -# DnsClient -# BudgetClient -# AutoScalingClient -# AnnouncementClient +# - oci.ons.NotificationDataPlaneClient +# - oci.waas.WaasClient +# - oci.monitoring.MonitoringClient +# - oci.healthchecks.HealthChecksClient +# - oci.dns.DnsClient +# - oci.announcements_service.AnnouncementClient # ########################################################################## from __future__ import print_function @@ -54,7 +61,7 @@ import argparse import datetime -version = "19.4.14" +version = "19.4.23" ########################################################################## # execute_extract @@ -150,6 +157,10 @@ def execute_extract(): ############################################ complete_message = return_error_message(data.get_service_errors(), data.get_service_warnings(), data.error) + # if reboot migration + if data.get_service_reboot_migration() > 0: + output.print_header(str(data.get_service_reboot_migration()) + " Reboot Migration Alert for Compute", 0) + # print completion output.print_header("Completed " + complete_message + " at " + str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), 0) @@ -182,6 +193,7 @@ def set_parser_arguments(): parser = argparse.ArgumentParser() parser.add_argument('-a', action='store_true', default=False, dest='all', help='Print All Resources') parser.add_argument('-ani', action='store_true', default=False, dest='allnoiam', help='Print All Resources but identity') + parser.add_argument('-b', action='store_true', default=False, dest='budgets', help='Print Budgets') parser.add_argument('-n', action='store_true', default=False, dest='network', help='Print Network') parser.add_argument('-i', action='store_true', default=False, dest='identity', help='Print Identity') parser.add_argument('-c', action='store_true', default=False, dest='compute', help='Print Compute') @@ -216,7 +228,7 @@ def set_parser_arguments(): if not (result.all or result.allnoiam or result.network or result.identity or result.compute or result.object or result.load or result.database or result.file or result.email or result.orm or result.container or - result.streams): + result.streams or result.budgets): parser.print_help() @@ -276,6 +288,9 @@ def set_service_extract_flags(cmd): if cmd.all or cmd.allnoiam or cmd.streams: prm.read_streams = True + if cmd.all or cmd.allnoiam or cmd.budgets: + prm.read_budgets = True + if cmd.noroot: prm.read_root_compartment = False diff --git a/examples/showoci/showoci_data.py b/examples/showoci/showoci_data.py index 145c0dc007..c9146d4efd 100755 --- a/examples/showoci/showoci_data.py +++ b/examples/showoci/showoci_data.py @@ -1,11 +1,8 @@ ########################################################################## # Copyright(c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. -# showocy_data.py +# showoci_data.py # -# @Created On : Mar 17 2019 -# @Last Updated: Apr 14 2019 -# @author : Adi Zohar -# @Version : 19.4.14 +# @author: Adi Zohar # # Supports Python 2.7 and above, Python 3 recommended # @@ -67,6 +64,12 @@ def process_oci_data(self): identity_data = {'type': "identity", 'data': self.service.get_identity()} self.data.append(identity_data) + # run on budgets module + if self.service.flags.read_budgets: + budgets_data = {'type': "budgets", 'data': self.service.get_budgets()} + self.data.append(budgets_data) + + # run on compartments if self.service.flags.is_loop_on_compartments: # pointer to Tenancy in cache @@ -128,6 +131,13 @@ def get_service_warnings(self): return self.service.warning + ########################################################################## + # get service reboot migration + ########################################################################## + def get_service_reboot_migration(self): + + return self.service.reboot_migration_counter + ########################################################################## # print print error ########################################################################## @@ -983,28 +993,29 @@ def __get_core_block_volume(self, volume_id, compartment_name): self.__print_error("__get_core_block_volume", e) ########################################################################## - # get compute boot volume + # get compute boot volume or volume ########################################################################## - def __get_core_block_volume_boot_backup(self, region_name, compartment): + def __get_core_block_volume_boot_backup(self, region_name, compartment, volume_name, service_name): data = [] try: - backups = self.service.search_multi_items(self.service.C_BLOCK, self.service.C_BLOCK_BOOTBACK, 'region_name', region_name, 'compartment_id', compartment['id']) + backups = self.service.search_multi_items(self.service.C_BLOCK, service_name, 'region_name', region_name, 'compartment_id', compartment['id']) for backup in backups: value = {} if backup['backup_lifecycle_state'] != "AVAILABLE": - value['name'] = backup['backup_name'] + " ( Source " + backup['backup_lifecycle_state'] + " )" + value['name'] = backup['backup_name'] + " (Source " + backup['backup_lifecycle_state'] + ")" else: - value['name'] = backup['backup_name'] + ", " + value['name'] = backup['backup_name'] value['desc'] = backup['display_name'] - value['type'] = backup['type'] + ", " + backup['source_type'] + ", " + backup['time_created'][0:16] + " -> " + backup['expiration_time'][0:16] - value['size'] = (str(backup['size_in_gbs']) + "gb " + ", Stored " + str(backup['unique_size_in_gbs']) + "gb") + value['type'] = backup['type'][0:4] + ", " + backup['source_type'][0:6] + ", " + backup['time_created'][0:16] + " -> " + backup['expiration_time'][0:16] + value['size'] = (str(backup['size_in_gbs']).rjust(3) + "gb " + ", Stored " + str(backup['unique_size_in_gbs']).rjust(3) + "gb") value['sum_info'] = 'Object Storage - BV Backups (gb)' value['sum_size_gb'] = (str(backup['unique_size_in_gbs'])) - value['boot_volume_id'] = str(backup['boot_volume_id']) + value[volume_name] = str(backup[volume_name]) + value['id'] = str(backup[volume_name]) data.append(value) @@ -1013,7 +1024,7 @@ def __get_core_block_volume_boot_backup(self, region_name, compartment): return data except Exception as e: - self.__print_error("__get_core_compute_images", e) + self.__print_error("__get_core_block_volume_boot_backup", e) return data ########################################################################## @@ -1137,39 +1148,6 @@ def __get_core_block_volume_groups(self, region_name, compartment): self.__print_error("__get_core_block_volume_groups", e) return data - ########################################################################## - # get compute boot volume - ########################################################################## - def __get_core_block_volume_backup(self, region_name, compartment): - - data = [] - try: - backups = self.service.search_multi_items(self.service.C_BLOCK, self.service.C_BLOCK_VOLBACK, 'region_name', region_name, 'compartment_id', compartment['id']) - - for backup in backups: - value = {} - - if backup['backup_lifecycle_state'] != "AVAILABLE": - value['name'] = backup['backup_name'] + " ( Source " + backup['backup_lifecycle_state'] + " )" - else: - value['name'] = backup['backup_name'] - value['desc'] = backup['display_name'] - value['type'] = backup['type'] + ", " + backup['source_type'] + ", " + backup['time_created'][0:16] + " -> " + backup['expiration_time'][0:16] - value['size'] = (str(backup['size_in_gbs']) + "gb " + ", Stored " + str(backup['unique_size_in_gbs']) + "gb") - value['sum_info'] = 'Object Storage - BV Backups (gb)' - value['sum_size_gb'] = (str(backup['unique_size_in_gbs'])) - value['volume_id'] = str(backup['volume_id']) - - data.append(value) - - if len(data) > 0: - return sorted(data, key=lambda k: k['name']) - return data - - except Exception as e: - self.__print_error("__get_core_block_volume_block_backup", e) - return data - ########################################################################## # print compute instances ########################################################################## @@ -1181,11 +1159,12 @@ def __get_core_compute_instances(self, region_name, compartment): for instance in instances: inst = {'id': instance['id'], 'name': instance['shape'] + " - " + instance['display_name'] + " - " + instance['lifecycle_state'], - 'sum_info': 'Compute', 'sum_shape': instance['shape'], + 'sum_info': 'Compute', 'sum_shape': instance['image_os'][0:14] + " - " + instance['shape'], 'availability_domain': instance['availability_domain'], 'fault_domain': instance['fault_domain'], 'time_maintenance_reboot_due': str(instance['time_maintenance_reboot_due']), 'image': instance['image'], 'image_id': instance['image_id'], + 'image_os': instance['image_os'], 'console_id': instance['console_id'], 'console': instance['console'], 'time_created': instance['time_created'], 'defined_tags': instance['defined_tags'], 'freeform_tags': instance['freeform_tags']} @@ -1250,8 +1229,8 @@ def __get_core_compute_images(self, region_name, compartment): for image in images: value = {'id': image['id'], - 'desc': image['display_name'] + " - " + image['operating_system'] + " - " + image[ - 'size_in_gbs'] + "gb - Base: " + image['base_image_name'], + 'desc': image['display_name'].ljust(24) + " - " + image['operating_system'] + " - " + image[ + 'size_in_gbs'].rjust(3) + "gb - Base: " + image['base_image_name'], 'sum_info': 'Object Storage - Images (gb)', 'sum_size_gb': image['size_in_gbs'], 'time_created': image['time_created'], 'defined_tags': image['defined_tags'], 'freeform_tags': image['freeform_tags']} @@ -1317,6 +1296,35 @@ def __get_core_compute_instance_pool(self, region_name, compartment): self.__print_error("__get_core_compute_instance_pool", e) return data + ########################################################################## + # get compute autoscaling + ########################################################################## + def __get_core_compute_autoscaling(self, region_name, compartment): + + data = [] + try: + + autos = self.service.search_multi_items(self.service.C_COMPUTE, self.service.C_COMPUTE_AUTOSCALING, 'region_name', region_name, 'compartment_id', compartment['id']) + + for auto in autos: + value = {'id': auto['id'], + 'name': str(auto['display_name']) + " (" + ("ENABLED" if auto['is_enabled'] else "DISABLED") + ")", + 'time_created': auto['time_created'], + 'compartment_name': auto['compartment_name'], + 'compartment_id': auto['compartment_id'], + 'resource_id': auto['resource_id'], + 'resource_name': auto['resource_name'], + 'resource_type': auto['resource_type'], + 'policies': auto['policies'] + } + data.append(value) + + return data + + except Exception as e: + self.__print_error("__get_core_compute_autoscaling", e) + return data + ########################################################################## # Compute ########################################################################## @@ -1345,6 +1353,10 @@ def __get_core_compute_main(self, region_name, compartment): if len(data) > 0: return_data['instance_pool'] = data + data = self.__get_core_compute_autoscaling(region_name, compartment) + if len(data) > 0: + return_data['autoscaling'] = data + data = self.__get_core_block_volume_groups(region_name, compartment) if len(data) > 0: return_data['volume_groups'] = data @@ -1357,11 +1369,11 @@ def __get_core_compute_main(self, region_name, compartment): if len(data) > 0: return_data['volume_not_attached'] = data - data = self.__get_core_block_volume_boot_backup(region_name, compartment) + data = self.__get_core_block_volume_boot_backup(region_name, compartment, 'boot_volume_id', self.service.C_BLOCK_BOOTBACK) if len(data) > 0: return_data['boot_volume_backup'] = data - data = self.__get_core_block_volume_backup(region_name, compartment) + data = self.__get_core_block_volume_boot_backup(region_name, compartment, 'volume_id', self.service.C_BLOCK_VOLBACK) if len(data) > 0: return_data['volume_backup'] = data @@ -1606,6 +1618,7 @@ def __get_database_autonomous_databases(self, region_name, compartment): 'connection_strings': str(dbs['connection_strings']), 'sum_info': "Autonomous Database (OCPUs) - " + dbs['license_model'], 'sum_count': str(dbs['sum_count']), 'sum_info_storage': "Autonomous Database (tb)", 'sum_size_tb': str(dbs['data_storage_size_in_tbs']), 'backups': self.__get_database_autonomous_backups(dbs['backups']), + 'whitelisted_ips': dbs['whitelisted_ips'], 'defined_tags': dbs['defined_tags'], 'freeform_tags': dbs['freeform_tags']} data.append(value) diff --git a/examples/showoci/showoci_output.py b/examples/showoci/showoci_output.py index 20239e130e..23d85ebc7c 100755 --- a/examples/showoci/showoci_output.py +++ b/examples/showoci/showoci_output.py @@ -1,11 +1,8 @@ ########################################################################## # Copyright(c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. -# showocy_output.py +# showoci_output.py # -# @Created On : Mar 17 2019 -# @Last Updated: Apr 14 2019 -# @author : Adi Zohar -# @Version : 19.4.14 +# @author: Adi Zohar # # Supports Python 2.7 and above, Python 3 recommended # @@ -60,6 +57,10 @@ def print_data(self, data, print_version=False): self.__print_identity_main(d['data']) has_data = True + elif d['type'] == "budgets": + self.__print_budgets_main(d['data']) + has_data = True + elif d['type'] == "region": self.__print_region_data(d['region'], d['data']) has_data = True @@ -225,6 +226,24 @@ def __print_identity_dynamic_groups(self, dynamic_groups): except Exception as e: self.__print_error("__print_identity_dynamic_groups", e) + ########################################################################## + # Print Cost Tracking Tags + ########################################################################## + def __print_identity_cost_tracking_tags(self, tags): + try: + if not tags: + return + self.print_header("Cost Tracking Tags", 2) + + for tag in tags: + print(self.taba + tag['tag_namespace_name'] + "." + tag['name']) + print(self.tabs + "Desc :" + tag['description']) + print(self.tabs + "Created :" + tag['time_created'][0:16]) + print("") + + except Exception as e: + self.__print_error("__print_identity_cost_tracking_tags", e) + ########################################################################## # Identity Module ########################################################################## @@ -243,6 +262,8 @@ def __print_identity_main(self, data): self.__print_identity_policies(data['policies']) if 'providers' in data: self.__print_identity_providers(data['providers']) + if 'cost_tracking_tags' in data: + self.__print_identity_cost_tracking_tags(data['cost_tracking_tags']) except Exception as e: self.__print_error("__print_identity_data", e) @@ -583,7 +604,7 @@ def __print_load_balancer_details(self, load_balance_obj): print(self.tabs + "Path Route : " + prs['name']) if 'path_routes' in prs: for p in prs['path_routes']: - print(self.tabs + " : Backend: " + str(p.backend_set_name) + ', Path: ' + p.path) + print(self.tabs + " : Backend: " + str(p['backend_set_name']) + ', Path: ' + p['path']) # Hostnames if 'hostnames' in lb: @@ -789,13 +810,15 @@ def __print_database_db_autonomous(self, dbs): for db in dbs: print(self.taba + db['name']) if 'cpu_core_count' in db: - print(self.tabs + "Cores : " + str(db['cpu_core_count'])) + print(self.tabs + "Cores : " + str(db['cpu_core_count'])) if 'data_storage_size_in_tbs' in db: - print(self.tabs + "Size TB : " + str(db['data_storage_size_in_tbs'])) + print(self.tabs + "Size TB : " + str(db['data_storage_size_in_tbs'])) if 'db_name' in db: - print(self.tabs + "DB Name : " + db['db_name']) + print(self.tabs + "DB Name : " + db['db_name']) if 'time_created' in db: - print(self.tabs + "Created : " + db['time_created']) + print(self.tabs + "Created : " + db['time_created']) + if 'whitelisted_ips' in db: + print(self.tabs + "Allowed IPs : " + db['whitelisted_ips']) # print backups if db['backups']: @@ -890,6 +913,26 @@ def __print_streams_main(self, streams): except Exception as e: self.__print_error("__print_streams_main", e) + ########################################################################## + # Budgets + ########################################################################## + def __print_budgets_main(self, budgets): + + try: + if not budgets: + return + + self.print_header("Budgets", 2) + + for budget in budgets: + print(self.taba + budget['display_name'] + " for Compartment: " + budget['compartment_name'] + " (" + budget['reset_period'] + ")") + print(self.tabs + "Costs : Spent: " + budget['actual_spend'] + ", Forcasted: " + budget['forecasted_spend'], ", Time Computed: " + budget['time_spend_computed'][0:16]) + print(self.tabs + "Created : " + budget['time_created'][0:16] + ", Total Alert Rules: " + budget['alert_rule_count']) + print("") + + except Exception as e: + self.__print_error("__print_budgets_main", e) + ########################################################################## # Container ########################################################################## @@ -980,8 +1023,10 @@ def __print_core_compute_instances(self, instances): print(self.tabs2 + "VNIC: " + vnic['desc']) if 'console' in instance: - print(self.tabs2 + instance['console']) - print("") + if instance['console']: + print(self.tabs2 + instance['console']) + + print("") except Exception as e: self.__print_error("__print_core_compute_instances", e) @@ -1003,7 +1048,7 @@ def __print_core_compute_images(self, images): self.__print_error("__print_core_compute_images", e) ########################################################################## - # print compute images + # print compute pool ########################################################################## def __print_core_compute_instance_pool(self, pools): @@ -1021,6 +1066,34 @@ def __print_core_compute_instance_pool(self, pools): except Exception as e: self.__print_error("__print_core_compute_instance_pool", e) + ########################################################################## + # print compute autoscaling + ########################################################################## + def __print_core_compute_autoscaling(self, autos): + + try: + if len(autos) == 0: + return + + self.print_header("Compute Autoscaling", 2) + for auto in autos: + print(self.taba + auto['name']) + print(self.tabs + "Resource : " + auto['resource_type'] + ": " + auto['resource_name']) + + # Policies + for pol in auto['policies']: + print(self.tabs + "Policy : " + pol['display_name'] + " (" + pol['policy_type'] + ")") + print(self.tabs + " Capacity : Initial = " + pol['capacity_initial'] + ", Min = " + pol['capacity_min'] + ", Max = " + pol['capacity_max']) + + # Rules + for rule in pol['rules']: + print(self.tabs + " Rule : " + rule) + + print("") + + except Exception as e: + self.__print_error("__print_core_compute_autoscaling", e) + ########################################################################## # print compute images ########################################################################## @@ -1041,48 +1114,33 @@ def __print_core_compute_instance_configuration(self, configs): self.__print_error("__print_core_compute_instance_configuration", e) ########################################################################## - # print compute boot volume + # print compute boot volume or volumes ########################################################################## - def __print_core_compute_boot_volume_backup(self, backups): + def __print_core_compute_boot_volume_backup(self, backups, header): try: if len(backups) == 0: return - self.print_header("Boot Volume Backups", 2) + self.print_header(header, 2) prev_id = "" for backup in backups: - if prev_id and prev_id != backup['boot_volume_id']: + if prev_id and prev_id != backup['id']: print("") - print(self.taba + backup['name'] + " - " + backup['desc']) - print(self.tabs + self.tabs + "Type : " + backup['type']) - print(self.tabs + self.tabs + "Size : " + backup['size']) - prev_id = backup['boot_volume_id'] - except Exception as e: - self.__print_error("__print_core_compute_boot_volume_backup", e) + # if auto backup, print only the policy name + desc_name = backup['desc'] + if 'via policy' in backup['desc']: + start = backup['desc'].find('via policy') + 12 + desc_name = "AUTO (" + backup['desc'][start:] + ")" - ########################################################################## - # print compute block volume - ########################################################################## - def __print_core_compute_volume_backup(self, backups): + # print the line + print(self.taba + backup['name'] + ", " + backup['size'] + ", " + desc_name + ", " + backup['type']) - try: - if len(backups) == 0: - return - - self.print_header("Block Volume Backups", 2) - prev_id = "" - for backup in backups: - if prev_id and prev_id != backup['volume_id']: - print("") - print(self.taba + backup['name'] + " - " + backup['desc']) - print(self.tabs + self.tabs + "Type : " + backup['type']) - print(self.tabs + self.tabs + "Size : " + backup['size']) - prev_id = backup['volume_id'] + prev_id = backup['id'] except Exception as e: - self.__print_error("__print_core_compute_volume_backup", e) + self.__print_error("__print_core_compute_boot_volume_backup", e) ########################################################################## # print compute block volume Groups @@ -1152,6 +1210,9 @@ def __print_core_compute_main(self, data): if 'instance_pool' in data: self.__print_core_compute_instance_pool(data['instance_pool']) + if 'autoscaling' in data: + self.__print_core_compute_autoscaling(data['autoscaling']) + if 'images' in data: self.__print_core_compute_images(data['images']) @@ -1165,10 +1226,10 @@ def __print_core_compute_main(self, data): self.__print_core_compute_volume_groups(data['volume_group']) if 'boot_volume_backup' in data: - self.__print_core_compute_boot_volume_backup(data['boot_volume_backup']) + self.__print_core_compute_boot_volume_backup(data['boot_volume_backup'], "Boot Volume Backups") if 'volume_backup' in data: - self.__print_core_compute_volume_backup(data['volume_backup']) + self.__print_core_compute_boot_volume_backup(data['volume_backup'], "Block Volume Backups") except Exception as e: self.__print_error("__print_core_compute_main", e) @@ -1493,7 +1554,7 @@ def __summary_print_results(self, data, header, header_size): # sort and print for d in sorted(grouped_data, key=lambda i: i['type']): - print(d['type'].ljust(43)[0:42] + " - " + str(round(d['size'])).rjust(10)) + print(d['type'].ljust(46)[0:45] + " - " + str(round(d['size'])).rjust(10)) except Exception as e: self.__print_error("__summary_print_results", e) diff --git a/examples/showoci/showoci_service.py b/examples/showoci/showoci_service.py index 3ad64185e8..fdf31e5ff3 100755 --- a/examples/showoci/showoci_service.py +++ b/examples/showoci/showoci_service.py @@ -1,12 +1,8 @@ - ########################################################################## # Copyright(c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. -# showocy_service.py +# showoci_service.py # -# @Created On : Mar 17 2019 -# @Last Updated: Apr 14 2019 -# @author : Adi Zohar -# @Version : 19.4.14 +# @author: Adi Zohar # # Supports Python 2.7 and above, Python 3 recommended # @@ -41,6 +37,7 @@ class ShowOCIFlags(object): read_resource_management = False read_containers = False read_streams = False + read_budgets = False read_ManagedCompartmentForPaaS = True read_root_compartment = True @@ -76,7 +73,8 @@ def is_loop_on_compartments(self): self.read_email_distribution or self.read_resource_management or self.read_containers or - self.read_streams) + self.read_streams or + self.read_budgets) ############################################ # check if to load basic network (vcn+subnets) @@ -94,7 +92,7 @@ def is_load_basic_network(self): # class ShowOCIService ########################################################################## class ShowOCIService(object): - oci_compatible_version = "2.2.5" + oci_compatible_version = "2.2.7" ########################################################################## # Global Constants @@ -133,6 +131,7 @@ class ShowOCIService(object): C_IDENTITY_PROVIDERS = 'providers' C_IDENTITY_DYNAMIC_GROUPS = 'dynamic_groups' C_IDENTITY_USERS_GROUPS_MEMBERSHIP = 'users_groups_membership' + C_IDENTITY_COST_TRACKING_TAGS = 'cost_tracking_tags' # Compute Identifiers C_COMPUTE = 'compute' @@ -143,6 +142,7 @@ class ShowOCIService(object): C_COMPUTE_BOOT_VOL_ATTACH = 'instance_boot_vol_attach' C_COMPUTE_VOLUME_ATTACH = 'instance_volume_attach' C_COMPUTE_VNIC_ATTACH = 'instance_vnic_attach' + C_COMPUTE_AUTOSCALING = 'auto_scaling' # Block Storage Identifiers C_BLOCK = 'blockstorage' @@ -190,9 +190,14 @@ class ShowOCIService(object): C_STREAMS = "streams" C_STREAMS_STREAMS = "streams" - # Error flag + # budgets + C_BUDGETS = "budgets" + C_BUDGETS_BUDGETS = "budgets" + + # Error flag and reboot migration error = 0 warning = 0 + reboot_migration_counter = 0 ########################################################################## # Local Variables @@ -262,6 +267,15 @@ def get_availability_domains(self, region_name): ads = self.data[self.C_IDENTITY][self.C_IDENTITY_ADS] return [e for e in ads if e['region_name'] == region_name] + ########################################################################## + # return budget data + ########################################################################## + def get_budgets(self): + if self.C_BUDGETS in self.data: + if self.C_BUDGETS_BUDGETS in self.data[self.C_BUDGETS]: + return self.data[self.C_BUDGETS][self.C_BUDGETS_BUDGETS] + return [] + ########################################################################## # return subnet ########################################################################## @@ -424,8 +438,9 @@ def __load_print_cnt(self, cnt, start_time): ########################################################################## # print auth warning ########################################################################## - def __load_print_auth_warning(self, special_char="a"): - self.warning += 1 + def __load_print_auth_warning(self, special_char="a", increase_warning=True): + if increase_warning: + self.warning += 1 print(special_char, end="") ########################################################################## @@ -506,10 +521,25 @@ def __load_oci_region_data(self, region_name): if self.flags.read_object_storage: self.__load_object_storage_main() - # file storage - not exist in toronto yet - if region_name != "ca-toronto-1": - if self.flags.read_file_storage: - self.__load_file_storage_main() + # file storage + if self.flags.read_file_storage: + self.__load_file_storage_main() + + # containers + if self.flags.read_containers: + self.__load_container_main() + + # resource management + if self.flags.read_resource_management: + self.__load_resource_management_main() + + # if streams + if self.flags.read_streams: + self.__load_streams_main() + + # if budgets + if self.flags.read_budgets: + self.__load_budgets_main() # only available in US Regions if region_name == "us-ashburn-1" or region_name == "us-phoenix-1": @@ -518,19 +548,6 @@ def __load_oci_region_data(self, region_name): if self.flags.read_email_distribution: self.__load_email_main() - # resource management - only at us for now - if self.flags.read_resource_management: - self.__load_resource_management_main() - - # if streams - if self.flags.read_streams: - self.__load_streams_main() - - # containers - not exist in toronto yet - if region_name != "ca-toronto-1": - if self.flags.read_containers: - self.__load_container_main() - ########################################################################## # Identity Module ########################################################################## @@ -557,6 +574,7 @@ def __load_identity_main(self): self.__load_identity_dynamic_groups(identity, tenancy_id) self.__load_identity_policies(identity) self.__load_identity_providers(identity, tenancy_id) + self.__load_identity_cost_tracking_tags(identity, tenancy_id) print("") except oci.exceptions.RequestException: @@ -911,6 +929,46 @@ def __load_identity_dynamic_groups(self, identity, tenancy_id): except Exception as e: self.__print_error("__load_identity_dynamic_groups", e) + ########################################################################## + # load cost tracking tags + ########################################################################## + def __load_identity_cost_tracking_tags(self, identity, tenancy_id): + + data = [] + self.__load_print_status("Cost Tracking Tags") + start_time = time.time() + + try: + try: + tags = oci.pagination.list_call_get_all_results(identity.list_cost_tracking_tags, tenancy_id).data + except oci.exceptions.ServiceError as e: + if self.__check_service_error(e.code): + self.__load_print_auth_warning() + else: + raise + + # tag = oci.identity.models.Tag + for tag in tags: + dataval = {'tag_namespace_id': str(tag.tag_namespace_id), + 'tag_namespace_name': str(tag.tag_namespace_name), + 'id': str(tag.id), + 'name': str(tag.name), + 'description': str(tag.description), + 'is_retired': str(tag.is_retired), + 'time_created': str(tag.time_created), + 'is_cost_tracking': str(tag.is_cost_tracking) + } + data.append(dataval) + + # add to data + self.data[self.C_IDENTITY][self.C_IDENTITY_COST_TRACKING_TAGS] = data + self.__load_print_cnt(len(data), start_time) + + except oci.exceptions.RequestException: + raise + except Exception as e: + self.__print_error("__load_identity_cost_tracking_tags", e) + ########################################################################## # Load Identity Availability Domains ########################################################################## @@ -2154,6 +2212,11 @@ def __load_core_compute_main(self): if self.flags.proxy: virtual_network.base_client.session.proxies = {'https': self.flags.proxy} + # auto scaling + auto_scaling = oci.autoscaling.AutoScalingClient(self.config) + if self.flags.proxy: + auto_scaling.base_client.session.proxies = {'https': self.flags.proxy} + # reference to compartments compartments = self.get_compartment() @@ -2166,6 +2229,7 @@ def __load_core_compute_main(self): self.__initialize_data_key(self.C_COMPUTE, self.C_COMPUTE_INST_CONFIG) self.__initialize_data_key(self.C_COMPUTE, self.C_COMPUTE_INST_POOL) + self.__initialize_data_key(self.C_COMPUTE, self.C_COMPUTE_AUTOSCALING) self.__initialize_data_key(self.C_BLOCK, self.C_BLOCK_VOLGRP) self.__initialize_data_key(self.C_BLOCK, self.C_BLOCK_BOOT) @@ -2185,6 +2249,7 @@ def __load_core_compute_main(self): compute[self.C_COMPUTE_VNIC_ATTACH] += self.__load_core_compute_vnic_attach(compute_client, virtual_network, compartments) compute[self.C_COMPUTE_INST_CONFIG] += self.__load_core_compute_inst_config(compute_client, compute_manage, block_storage, compartments) compute[self.C_COMPUTE_INST_POOL] += self.__load_core_compute_inst_pool(compute_manage, compartments) + compute[self.C_COMPUTE_AUTOSCALING] += self.__load_core_compute_autoscaling(auto_scaling, compute_manage, compartments) print("") print("Block Storage...") @@ -2246,7 +2311,7 @@ def __load_core_compute_instances(self, compute, compartments): print(".", end="") # loop on array - # arr = oci.core.models.Instance() + # arr = oci.core.models.Instance for arr in arrs: if (arr.lifecycle_state == oci.core.models.Instance.LIFECYCLE_STATE_TERMINATED or arr.lifecycle_state == oci.core.models.Instance.LIFECYCLE_STATE_PROVISIONING or @@ -2264,11 +2329,24 @@ def __load_core_compute_instances(self, compute, compartments): 'console_id': "", 'console': "", 'console_connection_string': "", 'defined_tags': [] if arr.defined_tags is None else arr.defined_tags, 'freeform_tags': [] if arr.freeform_tags is None else arr.freeform_tags, - 'console_vnc_connection_string': "", 'image': "Not Found"} + 'console_vnc_connection_string': "", 'image': "Not Found", 'image_os': "Oracle Linux"} + + # if PaaS compartment assign Paas Image + if self.__if_managed_paas_compartment(compartment['name']): + val['image_os'] = "PaaS Image" + val['image'] = "PaaS Image" - # check image name + # mark reboot migration flag + if arr.time_maintenance_reboot_due is not None: + self.reboot_migration_counter += 1 + + # get image info try: - val['image'] = str(compute.get_image(arr.image_id).data.display_name) + # image = oci.core.models.Image + image = compute.get_image(arr.image_id).data + if image: + val['image'] = str(image.display_name) + val['image_os'] = str(image.operating_system) except Exception: pass @@ -2355,7 +2433,131 @@ def __load_core_compute_images(self, compute, compartments): return data ########################################################################## - # data compute read images + # compute auto scaling + ########################################################################## + def __load_core_compute_autoscaling(self, autoscaling, compute_manage, compartments): + + data = [] + cnt = 0 + start_time = time.time() + + try: + + self.__load_print_status("Autoscaling") + + # loop on all compartments + for compartment in compartments: + + if self.__if_managed_paas_compartment(compartment['name']): + print(".", end="") + continue + + autos = [] + try: + # pagination didn't work on auto scaling code + autos = oci.pagination.list_call_get_all_results(autoscaling.list_auto_scaling_configurations, compartment['id']).data + + except oci.exceptions.ServiceError as e: + if self.__check_service_error(e.code): + self.__load_print_auth_warning() + continue + raise + + print(".", end="") + + # loop on array + # arr = oci.autoscaling.models.AutoScalingConfigurationSummary + for auto in autos: + val = {'id': str(auto.id), 'display_name': str(auto.display_name), + 'cool_down_in_seconds': str(auto.cool_down_in_seconds), + 'is_enabled': auto.is_enabled, + 'time_created': str(auto.time_created), + 'compartment_name': str(compartment['name']), 'compartment_id': str(compartment['id']), + 'region_name': str(self.config['region']), + 'resource_id': "", + 'resource_type': "", + 'resource_name': "", + 'policies': [] + } + + ########################### + # get the resources + # if resource is oci.autoscaling.models.InstancePoolResource + ########################### + if auto.resource: + if isinstance(auto.resource, oci.autoscaling.models.InstancePoolResource): + val['resource_id'] = str(auto.resource.id) + val['resource_type'] = str(auto.resource.type) + + # get instance pool name + try: + pool_name = compute_manage.get_instance_pool(auto.resource.id).data.display_name + val['resource_name'] = str(pool_name) + except oci.exceptions.ServiceError as e: + if self.__check_service_error(e.code): + self.__load_print_auth_warning("p") + else: + raise + + ################## + # get the policy + ################## + try: + policies = autoscaling.list_auto_scaling_policies(auto.id).data + + # policy = oci.autoscaling.models.AutoScalingPolicySummary + for policy in policies: + + # read the proper policy which has the capacity and rules + # pol = oci.autoscaling.models.AutoScalingPolicy + # didn't add the rules for now. + pol = autoscaling.get_auto_scaling_policy(auto.id, policy.id).data + if pol: + valpol = {'id': str(pol.id), + 'display_name': str(pol.display_name), + 'policy_type': str(pol.policy_type), + 'time_created': str(pol.time_created), + 'capacity_min': str(pol.capacity.min), + 'capacity_max': str(pol.capacity.max), + 'capacity_initial': str(pol.capacity.initial), + 'rules': [] + } + + ############################## + # if policy is ThresholdPolicy + ############################## + if pol.policy_type == "threshold": + for rule in pol.rules: + valpol['rules'].append( + str(rule.action.type) + " " + + str(rule.action.value).ljust(3) + " when " + + str(rule.metric.metric_type) + " " + + str(rule.metric.threshold.operator) + " " + + str(rule.metric.threshold.value)) + + # add policy + val['policies'].append(valpol) + + except oci.exceptions.ServiceError as e: + if self.__check_service_error(e.code): + self.__load_print_auth_warning("l") + else: + raise + + data.append(val) + cnt += 1 + + self.__load_print_cnt(cnt, start_time) + return data + + except oci.exceptions.RequestException: + raise + except Exception as e: + self.__print_error("__load_core_compute_autoscaling", e) + return data + + ########################################################################## + # __load_core_compute_inst_config ########################################################################## def __load_core_compute_inst_config(self, compute, compute_manage, block_storage, compartments): @@ -2382,9 +2584,11 @@ def __load_core_compute_inst_config(self, compute, compute_manage, block_storage compartment['id'] ).data + # inst pool and inst config service often goes down, not marking warning + # for inst pool and inst config except oci.exceptions.ServiceError as e: if self.__check_service_error(e.code): - self.__load_print_auth_warning() + self.__load_print_auth_warning("a", False) continue raise @@ -2459,7 +2663,7 @@ def __load_core_compute_inst_config(self, compute, compute_manage, block_storage return data ########################################################################## - # data compute read images + # __load_core_compute_inst_pool ########################################################################## def __load_core_compute_inst_pool(self, compute_manage, compartments): @@ -2486,9 +2690,11 @@ def __load_core_compute_inst_pool(self, compute_manage, compartments): compartment['id'] ).data + # inst pool and inst config service often goes down, not marking warning + # for inst pool and inst config except oci.exceptions.ServiceError as e: if self.__check_service_error(e.code): - self.__load_print_auth_warning() + self.__load_print_auth_warning("a", False) continue raise @@ -3214,7 +3420,16 @@ def __load_load_balancers(self, load_balancer, compartments): datapath = [] for prs in arr.path_route_sets: pro = arr.path_route_sets[prs] - datapath.append({'name': pro.name, 'path_routes': pro.path_routes}) + + # get the path routes + array_path = [] + if pro.path_routes is not None: + for path_route in pro.path_routes: + array_path.append({'path': path_route.path, 'backend_set_name': path_route.backend_set_name}) + + # add the paths + datapath.append({'name': pro.name, 'path_routes': array_path}) + val['path_route'] = datapath # Hostnames @@ -4533,6 +4748,7 @@ def __load_database_autonomouns(self, database_client, compartments): 'defined_tags': [] if dbs.defined_tags is None else dbs.defined_tags, 'freeform_tags': [] if dbs.freeform_tags is None else dbs.freeform_tags, 'region_name': str(self.config['region']), + 'whitelisted_ips': "" if dbs.whitelisted_ips is None else str(', '.join(x for x in dbs.whitelisted_ips)), 'backups': self.__load_database_autonomouns_backups(database_client, dbs.id)} # license model @@ -4846,3 +5062,108 @@ def __load_streams_streams(self, stream_client, compartments): except Exception as e: self.__print_error("__load_streams_streams", e) return data + + ########################################################################## + # __load_budget_main + ########################################################################## + # + # OCI Classes used: + # + # class oci.budget.BudgetClient(config, **kwargs) + # + ########################################################################## + def __load_budgets_main(self): + + try: + print("Budgets...") + + # BudgetClient + budget_client = oci.budget.BudgetClient(self.config) + if self.flags.proxy: + budget_client.base_client.session.proxies = {'https': self.flags.proxy} + + # reference to tenancy + tenancy = self.get_tenancy() + + # add the key if not exists + self.__initialize_data_key(self.C_BUDGETS, self.C_BUDGETS_BUDGETS) + + # reference to stream + budget = self.data[self.C_BUDGETS] + + # append the data + budget[self.C_BUDGETS_BUDGETS] += self.__load_budgets_budgets(budget_client, tenancy['id']) + print("") + + except oci.exceptions.RequestException: + raise + except oci.exceptions.ServiceError: + raise + except Exception as e: + self.__print_error("__load_budgets_main", e) + + ########################################################################## + # __load_budgets_budgets + ########################################################################## + def __load_budgets_budgets(self, budget_client, tenancy_id): + + data = [] + cnt = 0 + start_time = time.time() + + try: + self.__load_print_status("Budgets") + + budgets = [] + try: + budgets = oci.pagination.list_call_get_all_results( + budget_client.list_budgets, + tenancy_id, + lifecycle_state=oci.budget.models.BudgetSummary.LIFECYCLE_STATE_ACTIVE + ).data + + except oci.exceptions.ServiceError as e: + if self.__check_service_error(e.code): + self.__load_print_auth_warning() + else: + raise + + print(".", end="") + + # budget = oci.budget.models.BudgetSummary + for budget in budgets: + val = {'id': str(budget.id), + 'target_compartment_id': str(budget.target_compartment_id), + 'compartment_name': "", + 'display_name': str(budget.display_name), + 'description': str(budget.description), + 'amount': str(budget.amount), + 'reset_period': str(budget.reset_period), + 'alert_rule_count': str(budget.alert_rule_count), + 'version': str(budget.version), + 'actual_spend': str(budget.actual_spend), + 'forecasted_spend': str(budget.forecasted_spend), + 'time_spend_computed': str(budget.time_spend_computed), + 'time_created': str(budget.time_created), + 'time_updated': str(budget.time_updated), + 'defined_tags': [] if budget.defined_tags is None else budget.defined_tags, + 'freeform_tags': [] if budget.freeform_tags is None else budget.freeform_tags, + 'region_name': str(self.config['region'])} + + # fill the comaprtment name + compartment = self.search_unique_item(self.C_IDENTITY, self.C_IDENTITY_COMPARTMENTS, 'id', str(budget.target_compartment_id)) + if compartment: + val['compartment_name'] = compartment['path'] + + # add the data + cnt += 1 + data.append(val) + + self.__load_print_cnt(cnt, start_time) + return data + + except oci.exceptions.RequestException: + raise + except Exception as e: + self.__print_error("__load_budgets_budgets", e) + return data diff --git a/examples/stop_untagged_instances.py b/examples/stop_untagged_instances.py new file mode 100644 index 0000000000..fd91f74c12 --- /dev/null +++ b/examples/stop_untagged_instances.py @@ -0,0 +1,205 @@ +# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + +# This example demonstrates how to find, stop and report on instances that have +# been improperly tagged. +# +# I recommend that you run this every 30 minutes and set the audit hours to 1. +# Example usage 'stop_untagged_instances.py --hours_to_audit 2 --tag_string CostCenter' +# This searches for any instance without the CostCenter tag applied, stops them, finds who started it and logs +# the results. + +import oci +import datetime +import argparse + + +# After determining all the events associated with th stopped instances, merge those event records (OCID and Email) +# with the existing list of instances_to_stop to add the email to the existing list, then write it to disk. +def join_lists(instance_list, events_list, filename): + for item in instance_list: + for item2 in events_list: + # when you find a match between the event_list & instances to stop, add the email to the creator column + if item['instance_ocid'] == item2['instance_ocid']: + item['creator'] = item2['principal_name'] + + # Write out this joined list to disk + try: + with open(filename, 'w') as f: + for item in instance_list: + f.write('{0},{1},{2},{3},{4},{5},{6},{7},{8}\n' + .format(item['stop_datetime'], item['region'], item['compartment_name'], item['instance_name'], + item['instance_shape'], item['time_created'], item['instance_ocid'], + item['compartment_ocid'], item['creator'])) + except Exception as e: + print('An unexpected Error Occured writing the file: {0}' .format(e)) + + +# This function stops the instance sent in as a parameter +def stop_resource(instance_id, region): + base_compute = oci.core.compute_client.ComputeClient(config) + base_compute.base_client.set_region(region) + + # Stop a running instance, but it is possible for it to fail so putting it in a try catch for common errors. + try: + if base_compute.get_instance(instance_id).data.lifecycle_state in 'RUNNING': + try: + print('\t\tStopping instance. Stop response code: {1}' + .format(instance_id, str(base_compute.instance_action(instance_id, 'STOP').status))) + except oci.exceptions.ServiceError as e: + print('\t\tStopping instance failed. {0}' .format(e)) + else: + print('\t\tThe instance was in the incorrect state to stop' .format(instance_id)) + except oci.exceptions.ServiceError as e: + print('\t\tStopping instance failed. {0}'.format(e)) + + +# determines the list of subscribed regions for the tenancy +def list_of_regions(): + identity = oci.identity.IdentityClient(config) + list_of_regions = [] + list_regions_response = identity.list_regions() + for r in list_regions_response.data: + list_of_regions.append(r.name) + return list_of_regions + + +# This function finds LaunchInstance audit events based on the instance OCID and compartment passed into it +# from the find_resources_wo_tags function. The LaunchInstance events give it the principal (usually email) of the +# 'person' who started the resource and joins it to the exisitng instances_to_stop so it now contains the email of who +# started it. +def find_audit_events(instances_to_stop, instance_stop_list, compartment_id_stop_list): + end_time = datetime.datetime.utcnow() + start_time = end_time + datetime.timedelta(hours=-int(audit_hours)) + list_of_audit_events = [] + events_list = [{'principal_name': '', 'instance_ocid': ''}] + audit = oci.audit.audit_client.AuditClient(config) + print('\nGetting list of audit events for the last {0} hour(s).' .format(audit_hours)) + + # Finding all Resources in a region + region_list = list_of_regions() + for region in region_list: + audit.base_client.set_region(region) + + for c in compartment_id_stop_list: + # clear out the audit events from a compartment + del list_of_audit_events[:] + list_events_response = oci.pagination.list_call_get_all_results(audit.list_events, compartment_id=c, + start_time=start_time, + end_time=end_time).data + list_of_audit_events.extend(list_events_response) + print('\t\tNumber of audit events in {0}\\{1}: {2}' .format(region, identity_client.get_compartment(c).data.name, len(list_of_audit_events))) + + for ae in list_of_audit_events: + if ae.event_name == 'LaunchInstance': + if ae.response_payload['id'] in instance_stop_list: + if len(ae.user_name) == 0: + principal_name = ae.principal_id.rsplit('/', 1)[-1] + else: + principal_name = ae.user_name + + events_line = {'principal_name': principal_name, + 'instance_ocid': ae.response_payload['id']} + + events_list.append(events_line) + print('\t\t\tFound a launch instance: {0}'.format(ae.response_payload['id'])) + join_lists(instances_to_stop, events_list, 'stop_untagged_instances_result.csv') + + +# This function looks for all compute instances using the RQS (search) service, then it filters that list by looking +# For any resources that are in the correct state and that are either missing tags entirely or not tag correctly. +# It records those which are going to be stopped and calls stop_resource with a list of all compute instances to be +# stopped. +def find_resources_wo_tags(instances_to_stop_list, search_string): + instances_stop_list = [] + compartment_id_stop_list = [] + search_client = oci.resource_search.ResourceSearchClient(config) + compute_client = oci.core.ComputeClient(config) + + # Finding all Resources in a region + region_list = list_of_regions() + for region in region_list: + print('Searching RQS to find mis-tagged resources in Region:{0}' .format(region)) + try: + search_client.base_client.set_region(region) + structured_search = oci.resource_search.models.StructuredSearchDetails( + query="query instance resources", + type='Structured', + matching_context_type=oci.resource_search.models.SearchDetails.MATCHING_CONTEXT_TYPE_NONE) + results = search_client.search_resources(structured_search) + except oci.exceptions.ServiceError as e: + print('\t\tRQS Search failed with Service Error: {0}' .format(e)) + except oci.exceptions.RequestException as e: + print('\t\tRQS Search failed w/ a Request exception. {0}' .format(e)) + + # Filtering from everything to just those not tagged. + # This method is needed to also find resources with no tags at all + filter_key = search_string + for result in results.data.items: + if (filter_key not in str(result.defined_tags)) or len(result.defined_tags) == 0: + if result.lifecycle_state in ('Provisioning', 'Starting', 'Running'): + compute_client.base_client.set_region(region) + try: + stopped_instances_line = {'stop_datetime': datetime.datetime.utcnow().replace(microsecond=0).isoformat(), + 'region': region, + 'compartment_name': identity_client.get_compartment(result.compartment_id).data.name, + 'instance_name': result.display_name, + 'instance_shape': compute_client.get_instance(result.identifier).data.shape, + 'time_created': result.time_created.replace(microsecond=0).isoformat(), + 'instance_ocid': result.identifier, + 'compartment_ocid': result.compartment_id, + 'creator': 'Not Found'} + instances_to_stop_list.append(stopped_instances_line) + instances_stop_list.append(result.identifier) + if result.compartment_id not in compartment_id_stop_list: + compartment_id_stop_list.append(result.compartment_id) + print('\t\t*Found an resource ({0}) without a tag. Stopping it.*' .format(result.display_name)) + stop_resource(result.identifier, region) + + except oci.exceptions.ServiceError as e: + print('\t\tThe instance ({0}) could not be retrieved. It may be a ghost Search entry.' + .format(result.display_name, e)) + + # Only find audit events for those compartments with a stopped instance + if len(instances_to_stop_list) != 0: + find_audit_events(instances_to_stop_list, instances_stop_list, compartment_id_stop_list) + + +# A helper function that processes the two arguments for this program. Tag_string is the string that is searched for. +# Hours to audit is the number of hours before now to search the audit log for. +def prep_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument('--tag_string', default='', help='The string in all tags that will be searched for.') + parser.add_argument('--hours_to_audit', default=1, help='The number of hours to search the audit logs for.') + args = parser.parse_args() + return args + + +if __name__ == "__main__": + # Starts a timer for the execution time. + print('Stop the Untagged v0.6') + start_time = datetime.datetime.now() + print('Start Time: {0}'.format(start_time.replace(microsecond=0).isoformat())) + + config = oci.config.from_file() + identity_client = oci.identity.IdentityClient(config) + + # Set the search_string and audit_hours from arguments + args = prep_arguments() + search_string = args.tag_string + audit_hours = args.hours_to_audit + + # Create a list of dictionaries that represents the final csv file that records all stopped records. + instances_to_stop = [] + instance_line = {'stop_datetime': 'stop_datetime', 'region': 'region', 'compartment_name': 'compartment_name', + 'instance_name': 'instance_name', 'instance_shape': 'instance_shape', + 'time_created': 'time_created', 'instance_ocid': 'instance_ocid ', + 'compartment_ocid': 'compartment_ocid', 'creator': 'creator'} + instances_to_stop.append(instance_line) + + # This is the main function that finds any instance that's not tagged with the search_string + find_resources_wo_tags(instances_to_stop, search_string) + + # Completes the program and shows the duration of the run + end_time = datetime.datetime.now() + print('\nEnd Time: {0}' .format(end_time.replace(microsecond=0).isoformat())) + print('Duration: {0}' .format((end_time - start_time))) diff --git a/examples/stream_example.py b/examples/stream_example.py index 623d891642..ce3e31209f 100644 --- a/examples/stream_example.py +++ b/examples/stream_example.py @@ -3,8 +3,8 @@ import oci import sys -import base64 import time +from codecs import encode, decode # ========================================================== # This file provides an example of basic streaming usage @@ -29,8 +29,8 @@ def publish_example_messages(client, stream_id): for i in range(100): key = "key" + str(i) value = "value" + str(i) - encoded_key = base64.b64encode(key) - encoded_value = base64.b64encode(value) + encoded_key = encode(key.encode(), "base-64").decode().strip() + encoded_value = encode(value.encode(), "base-64").decode().strip() message_list.append(oci.streaming.models.PutMessagesDetailsEntry(key=encoded_key, value=encoded_value)) print("Publishing {} messages to the stream {} ".format(len(message_list), stream_id)) @@ -74,8 +74,8 @@ def get_stream(admin_client, stream_id): def delete_stream(client, stream_id): print(" Deleting Stream {}".format(stream_id)) - # Stream deletion is an asynchronous operation, give it some time to complete. - client.delete_stream_and_wait_for_state(stream_id, oci.streaming.models.StreamSummary.LIFECYCLE_STATE_DELETED) + print(" Stream deletion is an asynchronous operation, give it some time to complete.") + client.delete_stream_and_wait_for_state(stream_id, wait_for_states=[oci.streaming.models.StreamSummary.LIFECYCLE_STATE_DELETED]) def get_cursor_by_partition(client, stream_id, partition): @@ -99,7 +99,8 @@ def simple_message_loop(client, stream_id, initial_cursor): # Process the messages print(" Read {} messages".format(len(get_response.data))) for message in get_response.data: - print("{}: {}".format(base64.b64decode(message.key), base64.b64decode(message.value))) + print("{}: {}".format(decode(message.key.encode(), "base64").decode(), + decode(message.value.encode(), "base64").decode())) # get_messages is a throttled method; clients should retrieve sufficiently large message # batches, as to avoid too many http requests. diff --git a/examples/tagging.py b/examples/tagging.py index 6b4eea8441..8ac791abbe 100644 --- a/examples/tagging.py +++ b/examples/tagging.py @@ -84,7 +84,7 @@ # correspond to the name of a tag within the specified namespace (and the namespace must exist). # # Resources where we can create/update tags will have the freeform_tags and defined_tags attributes. Consult the API -# documentation to see what these are (https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/index.html) +# documentation to see what these are (https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/landing.html) num_tries = 0 while True: # You may get a 404 if you create/reactivate a tag and try and use it straight away. If you have a delay/sleep between @@ -118,7 +118,7 @@ # We can also update tags on a resource. Note that this is a total replacement for any previously set freeform or defined tags. # # Resources where we can create/update tags will have the freeform_tags and defined_tags attributes. Consult the API -# documentation to see what these are (https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/index.html) +# documentation to see what these are (https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/landing.html) update_vcn_response = virtual_network.update_vcn( vcn_id, oci.core.models.UpdateVcnDetails( diff --git a/src/oci/load_balancer/load_balancer_client.py b/src/oci/load_balancer/load_balancer_client.py index 9f5a0c71b0..897802b0f9 100644 --- a/src/oci/load_balancer/load_balancer_client.py +++ b/src/oci/load_balancer/load_balancer_client.py @@ -740,7 +740,10 @@ def create_path_route_set(self, create_path_route_set_details, load_balancer_id, def create_rule_set(self, load_balancer_id, create_rule_set_details, **kwargs): """ CreateRuleSet - Creates a new rule set associated with the specified load balancer. + Creates a new rule set associated with the specified load balancer. For more information, see + `Managing Rule Sets`__. + + __ https://docs.cloud.oracle.com/Content/Balance/Tasks/managingrulesets.htm :param str load_balancer_id: (required) diff --git a/src/oci/load_balancer/models/add_http_request_header_rule.py b/src/oci/load_balancer/models/add_http_request_header_rule.py index 374d2943fe..aea9921dfe 100644 --- a/src/oci/load_balancer/models/add_http_request_header_rule.py +++ b/src/oci/load_balancer/models/add_http_request_header_rule.py @@ -10,7 +10,7 @@ class AddHttpRequestHeaderRule(Rule): """ An object that represents the action of adding a header to a request. - This rule applies only to HTTP or HTTP2 listeners. + This rule applies only to HTTP listeners. **NOTES:** diff --git a/src/oci/load_balancer/models/add_http_response_header_rule.py b/src/oci/load_balancer/models/add_http_response_header_rule.py index 434dd91b80..6e3fde9c7f 100644 --- a/src/oci/load_balancer/models/add_http_response_header_rule.py +++ b/src/oci/load_balancer/models/add_http_response_header_rule.py @@ -10,7 +10,7 @@ class AddHttpResponseHeaderRule(Rule): """ An object that represents the action of adding a header to a response. - This rule applies only to HTTP or HTTP2 listeners. + This rule applies only to HTTP listeners. **NOTES:** diff --git a/src/oci/load_balancer/models/extend_http_request_header_value_rule.py b/src/oci/load_balancer/models/extend_http_request_header_value_rule.py index 7bd3ecaa92..53bf09d75b 100644 --- a/src/oci/load_balancer/models/extend_http_request_header_value_rule.py +++ b/src/oci/load_balancer/models/extend_http_request_header_value_rule.py @@ -9,7 +9,7 @@ @init_model_state_from_kwargs class ExtendHttpRequestHeaderValueRule(Rule): """ - An object that represents the action of modifying a request header value. This rule applies only to HTTP or HTTP2 listeners. + An object that represents the action of modifying a request header value. This rule applies only to HTTP listeners. This rule adds a prefix, a suffix, or both to the header value. diff --git a/src/oci/load_balancer/models/extend_http_response_header_value_rule.py b/src/oci/load_balancer/models/extend_http_response_header_value_rule.py index f3622aa33d..cc517ece0d 100644 --- a/src/oci/load_balancer/models/extend_http_response_header_value_rule.py +++ b/src/oci/load_balancer/models/extend_http_response_header_value_rule.py @@ -9,7 +9,7 @@ @init_model_state_from_kwargs class ExtendHttpResponseHeaderValueRule(Rule): """ - An object that represents the action of modifying a response header value. This rule applies only to HTTP or HTTP2 listeners. + An object that represents the action of modifying a response header value. This rule applies only to HTTP listeners. This rule adds a prefix, a suffix, or both to the header value. diff --git a/src/oci/load_balancer/models/health_check_result.py b/src/oci/load_balancer/models/health_check_result.py index bd33f16a95..8a7d782b63 100644 --- a/src/oci/load_balancer/models/health_check_result.py +++ b/src/oci/load_balancer/models/health_check_result.py @@ -120,7 +120,7 @@ def source_ip_address(self): """ **[Required]** Gets the source_ip_address of this HealthCheckResult. The IP address of the health check status report provider. This identifier helps you differentiate same-subnet - (private) load balancers that report health check status. + load balancers that report health check status. Example: `10.0.0.7` @@ -135,7 +135,7 @@ def source_ip_address(self, source_ip_address): """ Sets the source_ip_address of this HealthCheckResult. The IP address of the health check status report provider. This identifier helps you differentiate same-subnet - (private) load balancers that report health check status. + load balancers that report health check status. Example: `10.0.0.7` diff --git a/src/oci/load_balancer/models/remove_http_request_header_rule.py b/src/oci/load_balancer/models/remove_http_request_header_rule.py index ccc81d160a..4ef3d87ffa 100644 --- a/src/oci/load_balancer/models/remove_http_request_header_rule.py +++ b/src/oci/load_balancer/models/remove_http_request_header_rule.py @@ -9,7 +9,7 @@ @init_model_state_from_kwargs class RemoveHttpRequestHeaderRule(Rule): """ - An object that represents the action of removing a header from a request. This rule applies only to HTTP or HTTP2 listeners. + An object that represents the action of removing a header from a request. This rule applies only to HTTP listeners. If the same header appears more than once in the request, the load balancer removes all occurances of the specified header. diff --git a/src/oci/load_balancer/models/remove_http_response_header_rule.py b/src/oci/load_balancer/models/remove_http_response_header_rule.py index 9171bcd868..0db62e2551 100644 --- a/src/oci/load_balancer/models/remove_http_response_header_rule.py +++ b/src/oci/load_balancer/models/remove_http_response_header_rule.py @@ -9,7 +9,7 @@ @init_model_state_from_kwargs class RemoveHttpResponseHeaderRule(Rule): """ - An object that represents the action of removing a header from a response. This rule applies only to HTTP or HTTP2 listeners. + An object that represents the action of removing a header from a response. This rule applies only to HTTP listeners. If the same header appears more than once in the response, the load balancer removes all occurances of the specified header. diff --git a/src/oci/load_balancer/models/rule_set.py b/src/oci/load_balancer/models/rule_set.py index 7858b621e3..58862ab955 100644 --- a/src/oci/load_balancer/models/rule_set.py +++ b/src/oci/load_balancer/models/rule_set.py @@ -10,7 +10,10 @@ class RuleSet(object): """ A named set of rules associated with a load balancer. Rules are objects that represent actions to apply to a listener, - such as adding, altering, or removing HTTP headers. + such as adding, altering, or removing HTTP headers. For more information, see + `Managing Rule Sets`__. + + __ https://docs.cloud.oracle.com/Content/Balance/Tasks/managingrulesets.htm """ def __init__(self, **kwargs): diff --git a/src/oci/regions.py b/src/oci/regions.py index 021c114836..591d1d8cdb 100644 --- a/src/oci/regions.py +++ b/src/oci/regions.py @@ -11,6 +11,7 @@ 'yyz': 'ca-toronto-1' } REGION_REALMS = { + 'ap-tokyo-1': 'oc1', 'us-phoenix-1': 'oc1', 'us-ashburn-1': 'oc1', 'eu-frankfurt-1': 'oc1', @@ -30,6 +31,7 @@ 'oc3': 'oraclegovcloud.com' } REGIONS = [ + "ap-tokyo-1", "us-phoenix-1", "us-ashburn-1", "eu-frankfurt-1", diff --git a/src/oci/version.py b/src/oci/version.py index fccd104160..dbfc4d7e14 100644 --- a/src/oci/version.py +++ b/src/oci/version.py @@ -1,4 +1,4 @@ # coding: utf-8 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. -__version__ = "2.2.7" +__version__ = "2.2.8" diff --git a/tests/testing_service_client.py b/tests/testing_service_client.py index 43f698fb45..62a64adfdd 100644 --- a/tests/testing_service_client.py +++ b/tests/testing_service_client.py @@ -1,6 +1,7 @@ # coding: utf-8 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. +import base64 import json import requests import uuid @@ -157,7 +158,7 @@ def validate_result(self, service_name, api_name, container_id, request, oci_res if api_name.lower().startswith('list'): data_field_name = 'items' response_dict = self.get_response_dictionary(oci_response, is_delete_operation, data_field_name) - response_json = util.make_dict_keys_camel_case(response_dict) + response_json = util.make_dict_keys_camel_case(response_dict, ['freeformTags', 'definedTags', 'metadata']) data['responseJson'] = json.dumps(response_json) data['responseClass'] = response_class @@ -185,7 +186,14 @@ def get_response_dictionary(self, oci_response, is_delete_operation, data_field_ if is_delete_operation: oci_response.data = {} - response_dict = oci_util.to_dict({data_field_name: oci_response.data}) + if data_field_name == 'stream': + # for binary data, decode them first then put into inputStream + response_dict = {"inputStream": str(base64.b64encode(oci_response.data.content).decode('utf-8')), + "contentLength": len(oci_response.data.content)} + else: + response_dict = oci_util.to_dict({data_field_name: oci_response.data}) + + # response_dict = oci_util.to_dict({data_field_name: oci_response.data}) response_dict['opcRequestId'] = self.build_request_id() response_dict['opcNextPage'] = oci_response.next_page @@ -273,3 +281,20 @@ def is_api_enabled(self, service_name, api_name): assert api_enabled_response is True or api_enabled_response is False, 'Received invalid response from testing service, should be true or false. Response: {}'.format(api_enabled_response) return api_enabled_response + + def get_endpoint(self, service_name, client_name, api_name): + # standardize service name to convention for Java SDK model namespaces (all lower case one word) + service_name = service_name.replace('_', '').lower() + + url = '{service_root_url}/endpoint'.format(service_root_url=SERVICE_ROOT_URL) + params = { + 'sessionId': self.session_id, + 'serviceName': service_name, + 'clientName': client_name, + 'lang': SERVICE_LANGUAGE, + 'apiName': api_name + } + + response = requests.get(url, params=params) + assert response.status_code == 200, response.content + return response.content.decode('UTF-8') diff --git a/tests/util.py b/tests/util.py index 4c0d4d5683..e929d257ad 100644 --- a/tests/util.py +++ b/tests/util.py @@ -160,6 +160,20 @@ def camel_to_snake_keys(dictionary): def camelize(to_camelize, uppercase_first_letter=False): + # some cases are not be able to handle by this method such as: ip will be changed to Ip + # here is a hard coded list for that cases. As of right now, haven't figured out a good way to handle it + # as the list need to be updated in the future with new spec changes + camelized_dict = { + 'create_ip_sec_connection_details': 'CreateIPSecConnectionDetails', + 'update_ip_sec_connection_details': 'UpdateIPSecConnectionDetails' + } + + if to_camelize in camelized_dict: + if uppercase_first_letter: + return camelized_dict[to_camelize] + else: + return camelized_dict[to_camelize][0].lower() + camelized_dict[to_camelize][1:] + if not to_camelize: return '' @@ -169,7 +183,9 @@ def camelize(to_camelize, uppercase_first_letter=False): return to_camelize[0].lower() + camelize(to_camelize, uppercase_first_letter=True)[1:] -def make_dict_keys_camel_case(original_obj): +# ignore_for_parent_keys will not convert the sub-nodes of that key in dictionary +# such as for defineTag the key is defined by user, we don't want to covert it to camel case +def make_dict_keys_camel_case(original_obj, ignore_for_parent_keys=None): if isinstance(original_obj, six.string_types): return original_obj @@ -182,14 +198,18 @@ def make_dict_keys_camel_case(original_obj): new_dict = {} for key, value in six.iteritems(original_obj): camelized_key = camelize(key) - new_dict[camelized_key] = make_dict_keys_camel_case(value) + + if ignore_for_parent_keys is not None and camelized_key in ignore_for_parent_keys: + new_dict[camelized_key] = value + else: + new_dict[camelized_key] = make_dict_keys_camel_case(value, ignore_for_parent_keys) return new_dict if isinstance(original_obj, abc.Iterable): new_list = [] for obj in original_obj: - new_list.append(make_dict_keys_camel_case(obj)) + new_list.append(make_dict_keys_camel_case(obj, ignore_for_parent_keys)) return new_list