Möchte man programmatisch – beispielsweise mit PL/SQL – im Oracle Cloud Object Storage zum Beispiel einen Bucket anlegen, löschen oder Compartments auflisten, kann man dies jetzt auch mit der REST-API Erweiterung des Package DBMS_CLOUD bewerkstelligen. Dabei liefert die DBMS_CLOUD REST-API nicht nur eine Schnittstelle für die Oracle Cloud Infrastruktur (OCI), sondern auch für Amazon Web Services, Azure Cloud, Google BigQuery und weiteren.

Übrigens für diejenigen, die APEX nutzen: Mit APEX gibt es ebenfalls eine Integration für Oracle Cloud Infrastructure Web Credentials, um mit Oracle Cloud Object Storage zu arbeiten. Möchte man mehr dazu erfahren, kann man dazu folgenden älteren Artikel von Adrian Png im Oracle Magazine finden: Better File Storage in Oracle Cloud. Dort wird ausführlich beschrieben, wie man einfach mit den REST-APIs des Oracle Cloud Object Storage in APEX interagieren kann.

Aber zurück zu DBMS_CLOUD – wozu hat man DBMS_CLOUD eigentlich bisher verwendet? Die Packages DBMS_CLOUD und DBMS_CLOUD_ADMIN bieten wichtige Schnittstellen und Erweiterungen für das Arbeiten mit Autonomous Database. Mit wachsender Funktionalität der Autonomous Database werden auch diese Packages erweitert. Im Handbuch im Abschnitt Autonomous Database Supplied Package Reference kann man Details zu den einzelnen Funktionen und Prozeduren nachlesen.

Folgende Liste zeigt nur einige Beispiele für Funktionen und Prozeduren von DBMS_CLOUD:

COPY_DATA zum Kopieren von Dateien aus dem Object Storage in Autonomous Database 
CREATE_CREDENTIAL, DROP_CREDENTIAL, UPDATE_CREDENTIAL
  zum Speichern, Löschen und Ändern von Cloud Object Storage Credentials
CREATE_EXTERNAL_TABLE (CREATE_ETXERNAL_PART_TABLE, CREATE_HYBRID_PART_TABLE)
  zum Erzeugen von External Tables (auch partitioniert bzw. hybrid) für Dateien in der Cloud
– VALIDATE_EXTERNAL_TABLE (VALIDATE_EXTERNAL_PART_TABLE, VALIDATE_HYBRID_PART_TABLE)
  Validieren der External Table
DELETE_FILELIST_FILES Löscht bzw. listet Dateien in dem angegebenen Verzeichnis
– DELETE_OBJECT,  LIST_OBJECTS Löschen und Auflisten von Objekten aus dem Cloud Objekt Storage
GET_OBJECT Listet Objekte aus dem Object Storage und kopiert sie in Autonomous Database
PUT_OBJECT Kopiert eine Datei aus der Autonomous Database in den Cloud Object Storage 

Wie kann man nun programmatisch mit DBMS_CLOUD einen Bucket im Objektstore anlegen? Das Ganze funktioniert in zwei Schritten:

  1. Authentifizierung gegenüber dem Cloud Objekt Store mit DBMS_CLOUD.CREATE_CREDENTIAL
  2. Verwendung der REST API mit DBMS_CLOUD.SEND_REQUEST 

Da es sich hierbei um “native” Oracle Cloud Infrastructure Operationen handelt, müssen die Credentials OCI spezifische Parameter wie User OCID, Tenancy OCID, privater Key und Fingerprint enthalten. Folgendes Beispiel zeigt eine Verwendung.
Hinweis: Bitte unbedingt beachten, dass beim Kopieren und Einfügen des Private Keys keine zusätzlichen Umbrüche oder Sonderzeichen entstehen.

-- Löschen des Credentials
execute dbms_cloud.drop_credential('CRED_USEXTEND');
-- Anlegen des Credentials
begin DBMS_CLOUD.CREATE_CREDENTIAL (credential_name => 'CRED_USEXTEND',  user_ocid => 'ocid1.user.oc1..aaaaaaaajjo6ll3nsokpwdxxxxxxxxxxxxxxxxxxrijhbjxq', tenancy_ocid => 'ocid1.tenancy.oc1..aaaaaaaap7sgxxxxxxxxxx343ztr6ydz6fscx6nuka', private_key => '-----BEGIN RSA PRIVATE KEMIIEpAIBAAKCA..-----END RSA PRIVATE KEY-----', fingerprint => '9c:3f:d2:5b:49:1b:20:92:4e:4a:5d:cc:d7:d4:c1:66');
end;
/

Hinweis: Informationen zur Generierung eines Private/Public Key Paares und Fingerprints erhält man im Abschnitt How to Generate an API Signing Key aus der OCI Dokumentation. Nach der Erzeugung kann man die Credentials wie üblich mit ALTER DATABASE in der Datenbank bekanntgeben.

alter database property set default_credential = 'ADMIN.CRED_USEXTEND';

Alle Credentials lassen sich mit DBA_CREDENTIALS auflisten.

select credential_name, enabled, comments from dba_credentials;

CREDENTIAL_NAME ENABLED COMMENTS
--------------- ------- -------------------------------------------------------------------------
CREDENTIAL_US1  TRUE    {"comments":"Created via DBMS_CLOUD.create_credential"}
CREDENTIAL_US2  TRUE    {"comments":"Created via DBMS_CLOUD.create_credential"}
CRED_US         TRUE    {"comments":"Created native credential via DBMS_CLOUD.create_credential"}
CRED_USEXTEND   TRUE    {"comments":"Created native credential via DBMS_CLOUD.create_credential"}

Im nächsten Schritt wird die Funktion SEND_REQUEST des Package DBMS_CLOUD mit folgenden Parametern verwendet.

DBMS_CLOUD.SEND_REQUEST (
credential_name IN VARCHAR2, -- hier CRED_USEXTEND
uri             IN VARCHAR2, -- passende URI, die zum Request passt
method          IN VARCHAR2, -- HTTP Methode GET, PUT, POST, HEAD, DELETE mit DBMS_CLOUD
headers         IN CLOB DEFAULT NULL, -- Headers der Cloud native API in JSON Format
body            IN BLOB DEFAULT NULL) -- Body des PUT oder POST Request in JSON Format
RETURN DBMS_CLOUD_TYPES.resp;

Die Informationen zur verwendeten URI findet man in der OCI Dokumentation im Kapitel APIs and Endpoints – in unserem Fall unter Objectstorage im Abschnitt CREATE BUCKET. Es handelt sich; um den Endpoint https://objectstorage.eu-frankfurt-1.oraclecloud.com und die Methode POST /n/{namespaceName}/b/. Der Body erfordert die Übergabe des neuen Bucket Namens ‘name’ und der Compartment ID ‘compartmentId’. Somit sieht das Anlegen eines Buckets mit Namen TESTUS folgendermaßen aus:


DECLARE
  resp DBMS_CLOUD_TYPES.resp;
BEGIN
  resp := DBMS_CLOUD.send_request(
  credential_name => 'CRED_USEXTEND',
  uri             => 'https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/oraseemeadesandbox/b/',
  method          => DBMS_CLOUD.METHOD_POST,
  body            => UTL_RAW.cast_to_raw(
            JSON_OBJECT('name' value 'TESTUS',
                        'compartmentId' value 'ocid1.compartment.oc1..aaaaaaaaz77ryqv6xwxxxxa'))); 
END;
/

Den Erfolg kann man danach einfach in der graphischen Cloud Konsole im Bereich Object Storage überprüfen.

Um den Bucket wieder zu löschen, reicht folgender Aufruf aus. Die DELETE Methode erfordert außer dem Credentialnamen und der URI keine weiteren Informationen. Das folgende Beispiel löscht den Bucket TESTUS wieder.

DECLARE
  resp DBMS_CLOUD_TYPES.resp;
BEGIN
  resp := DBMS_CLOUD.send_request(
  credential_name => 'CRED_USEXTEND',
  uri    => 'https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/oraseemeadesandbox/b/TESTUS',
  method => DBMS_CLOUD.METHOD_DELETE);
END;
/

Auch hier kann man das Ergebnis natürlich wieder in der Cloud Konsole überprüfen.

Im letzten Beispiel listen wir alle Compartments auf. Dazu wird die GET Methode mit DBMS_CLOUD.METHOD_GET verwendet. Die Endpoints, URIs und die entsprechenden Parameter findet sich in der Identity and Access Management Service API. Der Endpoint lautet hier https://identity.eu-frankfurt-1.oraclecloud.com/ und die Methode ist GET /20160918/compartments/. Sie erfordert die Information über die Root Compartment ID. Die Header Informationen bestehen aus einem JSON Objekt mit Wert ‘list-compartments’ für die ‘opc-request-id’. Der vollständige Aufruf sieht dann folgendermaßen aus.

SET SERVEROUTPUT ON
DECLARE
  DBMS_CLOUD_TYPES.resp;
  root_compartment_ocid VARCHAR2(512) := 'ocid1.tenancy.oc1..aaaaaaaap7sg74i4ibhxz7ymmvxxxxxka';
BEGIN
  resp := DBMS_CLOUD.send_request(
  credential_name => 'CRED_USEXTEND',
  uri     => 'https://identity.eu-frankfurt-1.oraclecloud.com/20160918/compartments?compartmentId='||
              root_compartment_ocid,
  method  => DBMS_CLOUD.METHOD_GET,
  headers => JSON_OBJECT('opc-request-id' value 'list-compartments'));
  dbms_output.put_line('Body: ' || DBMS_CLOUD.get_response_text(resp));
END;
/

Die Ausgabe ist im JSON Format und gibt ausführliche Informationen über die Inhalte der Compartments. Ein Ausschnitt aus dem Ergebnis sieht dann so aus:

body: [ {   "id" : "ocid1.compartment.oc1..aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxhfv2a",   "compartmentId" : "ocid1.tenancxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxa",   "name" : "AutonomousPlatform",   "description" : "AutonomousPlatform ",   "timeCreated" : "2019-07-24T11:17:24.350Z",   "freeformTags" : { },   "definedTags" : {     "Operation" : {       "CreationDate" : "2019-07-24T11:17:24.289Z",       "CreatedBy" : "ocid1.saml2idp.oc1..aaaaaaaaq7g335nqsjimza3xpib67lribjdzadesisqjaywawvkww3tlpuvq/marcus.schroeder@oracle.com"     }   },   "lifecycleState" : "ACTIVE" }, {   "id" : "ocid1.compartment.oc1..aaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxg5qkrdbix56q",   "compartmentId" : "ocid1.tenancxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxa",   "name" : "BI",   "description" : "Compartment for the BI Group",   "timeCreated" : "2018-12-07T11:16:32.630Z",   "freeformTags" : { },   "definedTags" : { },   "lifecycleState" : "ACTIVE" }, { ...

Detaillierte Informationen und Beispiele zur DBMS_CLOUD REST API finden sich in der Oracle Cloud Infrastructure Dokumentation unter diesem Link