diff --git a/.travis.yml b/.travis.yml index a9d23b6ea7f25..6e6f6a00aeaa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,22 @@ sudo: required - language: python cache: directories: - - $HOME/.cache/pip - - $HOME/dd-agent + - "$HOME/.cache/pip" + - "$HOME/.cache/oracle" + - "$HOME/dd-agent" - vendor/cache - python: - - "2.7" - + - '2.7' git: depth: 3 - branches: only: - master - services: - docker - matrix: fast_finish: true - env: global: - NOSE_FILTER="not windows and not fixme" @@ -36,6 +30,10 @@ env: - EXTRAS_BRANCH=master - JMXFETCH_URL="https://dd-jmxfetch.s3.amazonaws.com" - REQ_LOCALS="$TRAVIS_BUILD_DIR,$HOME/dd-agent,$HOME/integrations-extras" + - ORACLE_DIR="$HOME/.cache/oracle" + - ORACLE_HOME="$ORACLE_DIR/instantclient" + - secure: B6zL+2Zi0sjcPPSU2G0J/wxHxuQ89gA8UlrhINI13bZ6euoeMiZCr0Bhh/eXlqjezj8uawaN3ux/dQNG5/u3zMk4/HHESlaPvoxG5Gocw0nh5mhqiAuYabNDOL3YWCVw53WXpDD0f64jg6rl6be4m3gU6HYiZteKFNkC00Kndg3nL+CTSvhJVVYeLSu4R6GKdPBsKwZVSibHFg1oRnBCFHzfHoQwTxPtin6AjmKu750h0hl29sfP/vC7CErVkHtyANT/L5p6Venvz7x6gLMnx6+7vEQCAHRO1OAfzInYLHj1i8vRvKgH7QjvAJVPgToLcwxXujE5ZAEhmqdnfRf23/+7/I9Ne+l9SgOh4uRVPVe8z0Jb9fJRsw508zj1z502DBvH07Dy52JSLF9xK2zQ/5SzAUoeUre1z33WLE82KcpHGg4QJme4zXRXxVNtmdWSIa8+UT/K2M1CK30kzxHr7b7tzTK+7QBNx2mZNZUQLcHvlMTi+NGJmP212ogn+zMJ827bBZvNzLq7Rs345RAPeXja+2znfLIiguRG4wrP804hvh9fUJY0psJc85iurSW8JcGHHJwy+IUGOJYEEf55EZMaoECNiq0RU+EdABykiJiP5bAhpF530f1KfUYFX6vXVoFM4rXxDxad39+DpbQ5XRguKk+KdmUiH5Hhb7qulzU= + - secure: pm42Zj8DnzUkbZ9Ne+HeyUzPaFj3nVMjZ2cVvtxfx3ERfJPVeXSjZSZSNagMORYNe3VngGARCutfoedokHqXC4b21rrsqNhy43wCwdyVHQQKroAfwUAS3A9mPny58LrG8RToklvGJPwHKE8Klwg4mAehK0j3jJ7OYh9hsszK+bgF9g3ygV0ZwNS7Jej8fSfAH5WOMsqAwn57doXZcrmvgwpDJDQfQuoJdEn4aQ0TYf9dSuebS9CuWTZEzUegrbjQU/EYuL3T3QBzHsG5C0efoHNgfebfRW5PAfGjfHgzD00IdB8LWlKJGxyIk6381rWLC7rQqlkMly22vv/hL/qnnhcJmvd/CpB3OgVerIqG6NVibhmK+66wKZd2j7kj9jkUyv/hcKsm7F31cAZjzN+nm6VQasgx3LD2ENI7nwqtzRIZrrbUvvTtfkXJsw65SRShhxsLkO+Tzn5bWKaOX5gR84JGpr1IRA3JS3Yal5ZaVsZefQ4bO5Ipkd8sTR7+ozetMTPy/an5IwoaK3fMzPOz1L6YeKZakrQVX0/BjSmaKRWV9PRK6r5dBIGozepWopkxiQpTvMuDVHdYSuKSkEVPlva4U1b+WyAoFF+nebPWJjIg8TQZOyYaO5vXYI6tn7UkknMWaOKC3H61x6WxhsmoszJ+wMOSVo4kuxBNZOwRt2k= matrix: - TRAVIS_FLAVOR=default - TRAVIS_FLAVOR=activemq_xml FLAVOR_VERSION=5.11.1 @@ -90,6 +88,7 @@ env: - TRAVIS_FLAVOR=nginx FLAVOR_VERSION=1.11.8 - TRAVIS_FLAVOR=nginx FLAVOR_VERSION=1.10.2 - TRAVIS_FLAVOR=openstack + - TRAVIS_FLAVOR=oracle FLAVOR_VERSION=latest - TRAVIS_FLAVOR=pgbouncer FLAVOR_VERSION=latest - TRAVIS_FLAVOR=php_fpm FLAVOR_VERSION=5.5 - TRAVIS_FLAVOR=postgres FLAVOR_VERSION=latest @@ -107,14 +106,13 @@ env: - TRAVIS_FLAVOR=sqlserver FLAVOR_VERSION=latest - TRAVIS_FLAVOR=statsd - TRAVIS_FLAVOR=supervisord FLAVOR_VERSION=3.3.0 - - TRAVIS_FLAVOR=tcp_check + - TRAVIS_FLAVOR=tcp_check - TRAVIS_FLAVOR=tokumx - TRAVIS_FLAVOR=tomcat FLAVOR_VERSION=latest - TRAVIS_FLAVOR=twemproxy FLAVOR_VERSION=latest - TRAVIS_FLAVOR=varnish FLAVOR_VERSION=4.1.7 - TRAVIS_FLAVOR=varnish FLAVOR_VERSION=5.0.0 # END OF TRAVIS MATRIX - install: - bundle install - bundle package @@ -123,12 +121,17 @@ install: - echo "$HOME/dd-agent/" > $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")/datadog-agent.pth - if [ -e ~/dd-agent/requirements.txt ]; then pip install -r ~/dd-agent/requirements.txt; fi - bundle exec rake setup_agent_libs - + - sudo apt-get -qqy install npm + - pip install pexpect==4.2.1 + - mkdir -p $ORACLE_DIR + - $TRAVIS_BUILD_DIR/oracle/ci/resources/get_instantclient.py --agree=yes + - echo "$ORACLE_HOME" | sudo tee /etc/ld.so.conf.d/oracle_instantclient.conf + - sudo ldconfig + - if [ ! -e $ORACLE_HOME/libclntsh.so ]; then ln -s $ORACLE_HOME/libclntsh.so.12.1 $ORACLE_HOME/libclntsh.so; fi script: - bundle exec rake prep_travis_ci - bundle exec rake ci:run - bundle exec rake requirements - # we should clean generated files before we save the cache # We don't want to save .pyc files # Since clobber only cleans the project directory, @@ -136,6 +139,5 @@ script: before_cache: - rake clobber - find $HOME/.cache/pip $HOME/dd-agent -name *.pyc -delete - after_script: - if [[ $(docker ps -a -q) ]]; then docker stop $(docker ps -a -q); fi diff --git a/Rakefile b/Rakefile index 3fa01ae9a2965..d92e734789834 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,7 @@ unless ENV['CI'] ENV['INTEGRATIONS_DIR'] = File.join(rakefile_dir, 'embedded') ENV['PIP_CACHE'] = File.join(rakefile_dir, '.cache/pip') ENV['VOLATILE_DIR'] = '/tmp/integration-sdk-testing' + ENV['ORACLE_DIR'] = "#{ENV['VOLATILE_DIR']}/oracle" ENV['CONCURRENCY'] = ENV['CONCURRENCY'] || '2' ENV['NOSE_FILTER'] = ENV['NOSE_FILTER'] || 'not windows' ENV['RUN_VENV'] = 'true' diff --git a/oracle/CHANGELOG.md b/oracle/CHANGELOG.md new file mode 100644 index 0000000000000..f2b25fdb02e7d --- /dev/null +++ b/oracle/CHANGELOG.md @@ -0,0 +1,8 @@ +# CHANGELOG - oracle + +1.0.0 / Unreleased +================== + +### Changes + +* [FEATURE] adds oracle integration. diff --git a/oracle/README.md b/oracle/README.md new file mode 100644 index 0000000000000..19b5f0193a1f4 --- /dev/null +++ b/oracle/README.md @@ -0,0 +1,53 @@ +# Oracle Integration + +## Overview + +Get metrics from oracle service in real time to: + +* Visualize and monitor oracle states +* Be notified about oracle failovers and events. + +## Installation + +Install the `dd-check-oracle` package manually or with your favorite configuration manager + +We cannot ship the oracle `instantclient` libraries with the agent or the standalone check due to licensing issues. Although the required `cx_Oracle` python library will be bundled, you will still need to install the `instantclient` for it to work (hard-requirement). The steps to do so would trypically be: + +``` +mkdir -p /opt/oracle/ && cd /opt/oracle/ +# Download Oracle Instant Client (example dir: /opt/oracle) +unzip /opt/oracle/instantclient-basic-linux.x64-12.1.0.2.0.zip +unzip /opt/oracle/instantclient-sdk-linux.x64-12.1.0.2.0.zip +export ORACLE_HOME=/opt/oracle/instantclient/ +``` +From this point we'll need to make sure the relevant oracle libs are in the `LD_LIBRARY_PATH`: + +``` +if [ ! -e $ORACLE_HOME/libclntsh.so ]; then ln -s $ORACLE_HOME/libclntsh.so.12.1 $ORACLE_HOME/libclntsh.so; fi +echo "$ORACLE_HOME" | sudo tee /etc/ld.so.conf.d/oracle_instantclient.conf +sudo ldconfig +``` + +That should make the oracle `instantclient` dynamic libs be reachable in the host system `LD_LIBRARY_PATH` and the python package `cx_Oracle`. + +Please do not hesitate to contact support or open an issue should you encounter any problems. + +## Configuration + +Edit the `oracle.yaml` file to point to your server and port, set the masters to monitor + +## Validation + +When you run `datadog-agent info` you should see something like the following: + + Checks + ====== + + oracle + ----------- + - instance #0 [OK] + - Collected 18 metrics, 0 events & 1 service checks + +## Compatibility + +The oracle check is currently compatible with the linux and darwin-based OS diff --git a/oracle/check.py b/oracle/check.py new file mode 100644 index 0000000000000..6f65c03c38ff9 --- /dev/null +++ b/oracle/check.py @@ -0,0 +1,124 @@ +# (C) Datadog, Inc. 2010-2017 +# All rights reserved +# Licensed under Simplified BSD License (see LICENSE) + +# stdlib + +# 3rd party +try: + import cx_Oracle +except Exception as e: + cx_Oracle = None + +# project +from checks import AgentCheck + +EVENT_TYPE = SOURCE_TYPE_NAME = 'oracle' + + +class Oracle(AgentCheck): + + SERVICE_CHECK_NAME = 'oracle.can_connect' + SYS_METRICS = { + 'Buffer Cache Hit Ratio': 'oracle.buffer_cachehit_ratio', + 'Cursor Cache Hit Ratio': 'oracle.cursor_cachehit_ratio', + 'Library Cache Hit Ratio': 'oracle.library_cachehit_ratio', + 'Shared Pool Free %': 'oracle.shared_pool_free', + 'Physical Reads Per Sec': 'oracle.physical_reads', + 'Physical Writes Per Sec': 'oracle.physical_writes', + 'Enqueue Timeouts Per Sec': 'oracle.enqueue_timeouts', + 'GC CR Block Received Per Second': 'oracle.gc_cr_receive_time', + 'Global Cache Blocks Corrupted': 'oracle.cache_blocks_corrupt', + 'Global Cache Blocks Lost': 'oracle.cache_blocks_lost', + 'Logons Per Sec': 'oracle.logons', + 'Average Active Sessions': 'oracle.active_sessions', + 'Long Table Scans Per Sec': 'oracle.long_table_scans', + 'SQL Service Response Time': 'oracle.service_response_time', + 'User Rollbacks Per Sec': 'oracle.user_rollbacks', + 'Total Sorts Per User Call': 'oracle.sorts_per_user_call', + 'Rows Per Sort': 'oracle.rows_per_sort', + 'Disk Sort Per Sec': 'oracle.disk_sorts', + 'Memory Sorts Ratio': 'oracle.memory_sorts_ratio', + 'Database Wait Time Ratio': 'oracle.database_wait_time_ratio', + 'Enqueue Timeouts Per Sec': 'oracle.enqueue_timeouts', + 'Session Limit %': 'oracle.session_limit_usage', + 'Session Count': 'oracle.session_count', + 'Temp Space Used': 'oracle.temp_space_used', + } + + def check(self, instance): + if not cx_Oracle: + msg = """Cannot run the Oracle check until the Oracle instant client is available: + http://www.oracle.com/technetwork/database/features/instant-client/index.html + You will also need to ensure the `LD_LIBRARY_PATH` is also updated so the libs + are reachable. + """ + self.log.error(msg) + return + + self.log.debug('Running cx_Oracle version {0}'.format(cx_Oracle.version)) + server, user, password, service, tags = self._get_config(instance) + + if not server or not user: + raise Exception("Oracle host and user are needed") + + con = self._get_connection(server, user, password, service) + + self._get_sys_metrics(con, tags) + + self._get_tablespace_metrics(con) + + def _get_config(self, instance): + self.server = instance.get('server', None) + user = instance.get('user', None) + password = instance.get('password', None) + service = instance.get('service_name', None) + tags = instance.get('tags', None) + return (self.server, user, password, service, tags) + + def _get_connection(self, server, user, password, service): + self.service_check_tags = [ + 'server:%s' % server + ] + connect_string = '{0}/{1}@//{2}/{3}'.format(user, password, server, service) + try: + con = cx_Oracle.connect(connect_string) + self.log.debug("Connected to Oracle DB") + self.service_check(self.SERVICE_CHECK_NAME, AgentCheck.OK, + tags=self.service_check_tags) + except Exception, e: + self.service_check(self.SERVICE_CHECK_NAME, AgentCheck.CRITICAL, + tags=self.service_check_tags) + self.log.error(e) + raise + return con + + def _get_sys_metrics(self, con, tags): + query = "SELECT METRIC_NAME, VALUE, BEGIN_TIME FROM GV$SYSMETRIC " \ + "ORDER BY BEGIN_TIME" + cur = con.cursor() + cur.execute(query) + for row in cur: + metric_name = row[0] + metric_value = row[1] + if metric_name in self.SYS_METRICS: + self.gauge(self.SYS_METRICS[metric_name], metric_value, tags=tags) + + def _get_tablespace_metrics(self, con): + query = "SELECT TABLESPACE_NAME, sum(BYTES), sum(MAXBYTES) FROM sys.dba_data_files GROUP BY TABLESPACE_NAME" + cur = con.cursor() + cur.execute(query) + for row in cur: + tablespace_tag = 'tablespace:%s' % row[0] + used = float(row[1]) + size = float(row[2]) + if (used >= size): + in_use = 100 + elif (used == 0) or (size == 0): + in_use = 0 + else: + in_use = used / size * 100 + + self.gauge('oracle.tablespace.used', used, tags=[tablespace_tag]) + self.gauge('oracle.tablespace.size', size, tags=[tablespace_tag]) + self.gauge('oracle.tablespace.in_use', in_use, tags=[tablespace_tag]) diff --git a/oracle/ci/SQL-LICENSE.txt b/oracle/ci/SQL-LICENSE.txt new file mode 100644 index 0000000000000..3b5669f639b77 --- /dev/null +++ b/oracle/ci/SQL-LICENSE.txt @@ -0,0 +1,36 @@ +LICENSE AGREEMENT FOR CX_ORACLE + +Copyright 2016, 2017, Oracle and/or its affiliates. All rights reserved. + +Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. + +Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, +Canada. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the disclaimer that follows. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the names of the copyright holders nor the names of any contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +*AS IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Computronix is a registered trademark of Computronix (Canada) Ltd. + diff --git a/oracle/ci/oracle.rake b/oracle/ci/oracle.rake new file mode 100644 index 0000000000000..7982afe60dfc7 --- /dev/null +++ b/oracle/ci/oracle.rake @@ -0,0 +1,83 @@ +require 'ci/common' + +def oracle_version + ENV['FLAVOR_VERSION'] || 'latest' +end + +def oracle_repo + 'sath89/oracle-12c' +end + +def oracle_rootdir + "#{ENV['INTEGRATIONS_DIR']}/oracle_#{oracle_version}" +end + +def oracle_volume + "#{ENV['ORACLE_DIR']}/data" +end + +container_name = 'dd-test-oracle' +container_port = 1_521 +container_port_8080 = 80_80 + +namespace :ci do + namespace :oracle do |flavor| + task before_install: ['ci:common:before_install'] do + `docker kill $(docker ps -q --filter name=#{container_name}) || true` + `docker rm $(docker ps -aq --filter name=#{container_name}) || true` + end + + task install: ['ci:common:install'] do + use_venv = in_venv + install_requirements('oracle/requirements.txt', + "--cache-dir #{ENV['PIP_CACHE']}", + "#{ENV['VOLATILE_DIR']}/ci.log", use_venv) + sh %(mkdir -p #{oracle_volume}) + sh %(docker run --shm-size=1024MB -p #{container_port}:1521 -p #{container_port_8080}:8080 \ + --name #{container_name} -v #{__dir__}/resources:/docker-entrypoint-initdb.d -v #{oracle_volume}:/u01/app/oracle \ + -e WEB_CONSOLE=false -d #{oracle_repo}:#{oracle_version}) + end + + task before_script: ['ci:common:before_script'] do + Wait.for container_port + Wait.for container_port_8080 + # it can be a loooong wait (600s TO)... + wait_on_docker_logs(container_name, 600, 'Database ready to use') + end + + task script: ['ci:common:script'] do + this_provides = [ + 'oracle' + ] + Rake::Task['ci:common:run_tests'].invoke(this_provides) + end + + task before_cache: ['ci:common:before_cache'] + + task cleanup: ['ci:common:cleanup'] do + `docker kill $(docker ps -q --filter name=#{container_name}) || true` + `docker rm $(docker ps -aq --filter name=#{container_name}) || true` + end + + task :execute do + exception = nil + begin + %w(before_install install before_script).each do |u| + Rake::Task["#{flavor.scope.path}:#{u}"].invoke + end + Rake::Task["#{flavor.scope.path}:script"].invoke + Rake::Task["#{flavor.scope.path}:before_cache"].invoke + rescue => e + exception = e + puts "Failed task: #{e.class} #{e.message}".red + end + if ENV['SKIP_CLEANUP'] + puts 'Skipping cleanup, disposable environments are great'.yellow + else + puts 'Cleaning up' + Rake::Task["#{flavor.scope.path}:cleanup"].invoke + end + raise exception if exception + end + end +end diff --git a/oracle/ci/resources/DropTest.sql b/oracle/ci/resources/DropTest.sql new file mode 100644 index 0000000000000..50efa16bf84af --- /dev/null +++ b/oracle/ci/resources/DropTest.sql @@ -0,0 +1,33 @@ +/*----------------------------------------------------------------------------- + * Copyright 2017, Oracle and/or its affiliates. All rights reserved. + *---------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * DropTest.sql + * Drops database objects used for testing. + * + * Run this like: + * sqlplus / as sysdba @DropTest + * + * Note that the script TestEnv.sql should be modified if you would like to + * use something other than the default schemas and passwords. + *---------------------------------------------------------------------------*/ + +whenever sqlerror exit failure + +-- setup environment +@@TestEnv.sql + +begin + + for r in + ( select username + from dba_users + where username in (upper('&main_user'), upper('&proxy_user')) + ) loop + execute immediate 'drop user ' || r.username || ' cascade'; + end loop; + +end; +/ + diff --git a/oracle/ci/resources/SetupTest.sql b/oracle/ci/resources/SetupTest.sql new file mode 100644 index 0000000000000..5f1e4577f88ed --- /dev/null +++ b/oracle/ci/resources/SetupTest.sql @@ -0,0 +1,885 @@ +/*----------------------------------------------------------------------------- + * Copyright 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * + * Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. + * + * Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, + * Canada. All rights reserved. + *---------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * SetupTest.sql + * Creates users and populates their schemas with the tables and packages + * necessary for the cx_Oracle test suite. + * + * Run this like: + * sqlplus / as sysdba @SetupTest + * + * Note that the script TestEnv.sql should be modified if you would like to use + * something other than the default configuration. + *---------------------------------------------------------------------------*/ + +whenever sqlerror exit failure + +-- drop existing users, if present +@@DropTest.sql + +alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'; +alter session set nls_numeric_characters='.,'; + +create user &main_user identified by &main_password +quota unlimited on users +default tablespace users; + +create user &proxy_user identified by &proxy_password; +alter user &proxy_user grant connect through &main_user; + +grant create session to &proxy_user; + +grant + create session, + create table, + create procedure, + create type, + select any dictionary, + change notification +to &main_user; + +grant execute on dbms_aqadm to &main_user; + +-- create types +create type &main_user..udt_SubObject as object ( + SubNumberValue number, + SubStringValue varchar2(60) +); +/ + +create type &main_user..udt_ObjectArray as +varray(10) of &main_user..udt_SubObject; +/ + +create type &main_user..udt_Object as object ( + NumberValue number, + StringValue varchar2(60), + FixedCharValue char(10), + NStringValue nvarchar2(60), + NFixedCharValue nchar(10), + IntValue integer, + SmallIntValue smallint, + FloatValue float, + BinaryFloatValue binary_float, + BinaryDoubleValue binary_double, + DateValue date, + TimestampValue timestamp, + TimestampTZValue timestamp with time zone, + TimestampLTZValue timestamp with local time zone, + CLOBValue clob, + NCLOBValue nclob, + BLOBValue blob, + SubObjectValue &main_user..udt_SubObject, + SubObjectArray &main_user..udt_ObjectArray +); +/ + +create type &main_user..udt_Array as varray(10) of number; +/ + +create or replace type &main_user..udt_Building as object ( + BuildingId number(9), + NumFloors number(3), + Description varchar2(60), + DateBuilt date +); +/ + +-- create tables +create table &main_user..TestNumbers ( + IntCol number(9) not null, + NumberCol number(9, 2) not null, + FloatCol float not null, + UnconstrainedCol number not null, + NullableCol number(38) +); + +create table &main_user..TestStrings ( + IntCol number(9) not null, + StringCol varchar2(20) not null, + RawCol raw(30) not null, + FixedCharCol char(40) not null, + NullableCol varchar2(50) +); + +create table &main_user..TestUnicodes ( + IntCol number(9) not null, + UnicodeCol nvarchar2(20) not null, + FixedUnicodeCol nchar(40) not null, + NullableCol nvarchar2(50) +); + +create table &main_user..TestDates ( + IntCol number(9) not null, + DateCol date not null, + NullableCol date +); + +create table &main_user..TestCLOBs ( + IntCol number(9) not null, + CLOBCol clob not null +); + +create table &main_user..TestNCLOBs ( + IntCol number(9) not null, + NCLOBCol nclob not null +); + +create table &main_user..TestBLOBs ( + IntCol number(9) not null, + BLOBCol blob not null +); + +create table &main_user..TestXML ( + IntCol number(9) not null, + XMLCol xmltype not null +); + +create table &main_user..TestLongs ( + IntCol number(9) not null, + LongCol long not null +); + +create table &main_user..TestLongRaws ( + IntCol number(9) not null, + LongRawCol long raw not null +); + +create table &main_user..TestTempTable ( + IntCol number(9) not null, + StringCol varchar2(400), + constraint TestTempTable_pk primary key (IntCol) +); + +create table &main_user..TestArrayDML ( + IntCol number(9) not null, + StringCol varchar2(100), + IntCol2 number(3), + constraint TestArrayDML_pk primary key (IntCol) +); + +create table &main_user..TestObjects ( + IntCol number(9) not null, + ObjectCol &main_user..udt_Object, + ArrayCol &main_user..udt_Array +); + +create table &main_user..TestTimestamps ( + IntCol number(9) not null, + TimestampCol timestamp not null, + NullableCol timestamp +); + +create table &main_user..TestIntervals ( + IntCol number(9) not null, + IntervalCol interval day to second not null, + NullableCol interval day to second +); + +create table &main_user..TestUniversalRowids ( + IntCol number(9) not null, + StringCol varchar2(250) not null, + DateCol date not null, + constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol) +) organization index; + +create table &main_user..TestBuildings ( + BuildingId number(9) not null, + BuildingObj &main_user..udt_Building not null +); + +-- populate tables +begin + for i in 1..10 loop + insert into &main_user..TestNumbers + values (i, i + i * 0.25, i + i * .75, i * i * i + i *.5, + decode(mod(i, 2), 0, null, power(143, i))); + end loop; +end; +/ + +declare + + t_RawValue raw(30); + + function ConvertHexDigit(a_Value number) return varchar2 is + begin + if a_Value between 0 and 9 then + return to_char(a_Value); + end if; + return chr(ascii('A') + a_Value - 10); + end; + + function ConvertToHex(a_Value varchar2) return varchar2 is + t_HexValue varchar2(60); + t_Digit number; + begin + for i in 1..length(a_Value) loop + t_Digit := ascii(substr(a_Value, i, 1)); + t_HexValue := t_HexValue || + ConvertHexDigit(trunc(t_Digit / 16)) || + ConvertHexDigit(mod(t_Digit, 16)); + end loop; + return t_HexValue; + end; + +begin + for i in 1..10 loop + t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i))); + insert into &main_user..TestStrings + values (i, 'String ' || to_char(i), t_RawValue, + 'Fixed Char ' || to_char(i), + decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i))); + end loop; +end; +/ + +begin + for i in 1..10 loop + insert into &main_user..TestUnicodes + values (i, 'Unicode ' || unistr('\3042') || ' ' || to_char(i), + 'Fixed Unicode ' || to_char(i), + decode(mod(i, 2), 0, null, unistr('Nullable ') || to_char(i))); + end loop; +end; +/ + +begin + for i in 1..10 loop + insert into &main_user..TestDates + values (i, to_date(20021209, 'YYYYMMDD') + i + i * .1, + decode(mod(i, 2), 0, null, + to_date(20021209, 'YYYYMMDD') + i + i + i * .15)); + end loop; +end; +/ + +begin + for i in 1..100 loop + insert into &main_user..TestXML + values (i, '' || + dbms_random.string('x', 1024) || ''); + end loop; +end; +/ + +begin + for i in 1..10 loop + insert into &main_user..TestTimestamps + values (i, to_timestamp('20021209', 'YYYYMMDD') + + to_dsinterval(to_char(i) || ' 00:00:' || to_char(i * 2) || + '.' || to_char(i * 50)), + decode(mod(i, 2), 0, to_timestamp(null, 'YYYYMMDD'), + to_timestamp('20021209', 'YYYYMMDD') + + to_dsinterval(to_char(i + 1) || ' 00:00:' || + to_char(i * 3) || '.' || to_char(i * 125)))); + end loop; +end; +/ + +begin + for i in 1..10 loop + insert into &main_user..TestIntervals + values (i, to_dsinterval(to_char(i) || ' ' || to_char(i) || ':' || + to_char(i * 2) || ':' || to_char(i * 3)), + decode(mod(i, 2), 0, to_dsinterval(null), + to_dsinterval(to_char(i + 5) || ' ' || to_char(i + 2) || ':' || + to_char(i * 2 + 5) || ':' || to_char(i * 3 + 5)))); + end loop; +end; +/ + +insert into &main_user..TestObjects values (1, + &main_user..udt_Object(1, 'First row', 'First', 'N First Row', 'N First', + 2, 5, 12.5, 25.25, 50.125, to_date(20070306, 'YYYYMMDD'), + to_timestamp('20080912 16:40:00', 'YYYYMMDD HH24:MI:SS'), + to_timestamp_tz('20091013 17:50:00 00:00', + 'YYYYMMDD HH24:MI:SS TZH:TZM'), + to_timestamp_tz('20101114 18:55:00 00:00', + 'YYYYMMDD HH24:MI:SS TZH:TZM'), + 'Short CLOB value', 'Short NCLOB Value', + utl_raw.cast_to_raw('Short BLOB value'), + &main_user..udt_SubObject(11, 'Sub object 1'), + &main_user..udt_ObjectArray( + &main_user..udt_SubObject(5, 'first element'), + &main_user..udt_SubObject(6, 'second element'))), + &main_user..udt_Array(5, 10, null, 20)); + +insert into &main_user..TestObjects values (2, null, + &main_user..udt_Array(3, null, 9, 12, 15)); + +insert into &main_user..TestObjects values (3, + &main_user..udt_Object(3, 'Third row', 'Third', 'N Third Row', 'N Third', + 4, 10, 43.25, 86.5, 192.125, to_date(20070621, 'YYYYMMDD'), + to_timestamp('20071213 07:30:45', 'YYYYMMDD HH24:MI:SS'), + to_timestamp_tz('20170621 23:18:45 00:00', + 'YYYYMMDD HH24:MI:SS TZH:TZM'), + to_timestamp_tz('20170721 08:27:13 00:00', + 'YYYYMMDD HH24:MI:SS TZH:TZM'), + 'Another short CLOB value', 'Another short NCLOB Value', + utl_raw.cast_to_raw('Yet another short BLOB value'), + &main_user..udt_SubObject(13, 'Sub object 3'), + &main_user..udt_ObjectArray( + &main_user..udt_SubObject(10, 'element #1'), + &main_user..udt_SubObject(20, 'element #2'), + &main_user..udt_SubObject(30, 'element #3'), + &main_user..udt_SubObject(40, 'element #4'))), null); + +commit; + +-- create procedures for testing callproc() +create procedure &main_user..proc_Test ( + a_InValue varchar2, + a_InOutValue in out number, + a_OutValue out number +) as +begin + a_InOutValue := a_InOutValue * length(a_InValue); + a_OutValue := length(a_InValue); +end; +/ + +create procedure &main_user..proc_TestNoArgs as +begin + null; +end; +/ + +-- create functions for testing callfunc() +create function &main_user..func_Test ( + a_String varchar2, + a_ExtraAmount number +) return number as +begin + return length(a_String) + a_ExtraAmount; +end; +/ + +create function &main_user..func_TestNoArgs +return number as +begin + return 712; +end; +/ + +-- create packages +create or replace package &main_user..pkg_TestStringArrays as + + type udt_StringList is table of varchar2(100) index by binary_integer; + + function TestInArrays ( + a_StartingLength number, + a_Array udt_StringList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_StringList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_StringList + ); + + procedure TestIndexBy ( + a_Array out nocopy udt_StringList + ); + +end; +/ + +create or replace package body &main_user..pkg_TestStringArrays as + + function TestInArrays ( + a_StartingLength number, + a_Array udt_StringList + ) return number is + t_Length number; + begin + t_Length := a_StartingLength; + for i in 1..a_Array.count loop + t_Length := t_Length + length(a_Array(i)); + end loop; + return t_Length; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_StringList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := 'Converted element # ' || + to_char(i) || ' originally had length ' || + to_char(length(a_Array(i))); + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_StringList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := 'Test out element # ' || to_char(i); + end loop; + end; + + procedure TestIndexBy ( + a_Array out nocopy udt_StringList + ) is + begin + a_Array(-1048576) := 'First element'; + a_Array(-576) := 'Second element'; + a_Array(284) := 'Third element'; + a_Array(8388608) := 'Fourth element'; + end; + +end; +/ + +create or replace package &main_user..pkg_TestUnicodeArrays as + + type udt_UnicodeList is table of nvarchar2(100) index by binary_integer; + + function TestInArrays ( + a_StartingLength number, + a_Array udt_UnicodeList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_UnicodeList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_UnicodeList + ); + +end; +/ + +create or replace package body &main_user..pkg_TestUnicodeArrays as + + function TestInArrays ( + a_StartingLength number, + a_Array udt_UnicodeList + ) return number is + t_Length number; + begin + t_Length := a_StartingLength; + for i in 1..a_Array.count loop + t_Length := t_Length + length(a_Array(i)); + end loop; + return t_Length; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_UnicodeList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := unistr('Converted element ' || unistr('\3042') || + ' # ') || to_char(i) || ' originally had length ' || + to_char(length(a_Array(i))); + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_UnicodeList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := unistr('Test out element ') || unistr('\3042') || + ' # ' || to_char(i); + end loop; + end; + +end; +/ + +create or replace package &main_user..pkg_TestNumberArrays as + + type udt_NumberList is table of number index by binary_integer; + + function TestInArrays ( + a_StartingValue number, + a_Array udt_NumberList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_NumberList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_NumberList + ); + +end; +/ + +create or replace package body &main_user..pkg_TestNumberArrays as + + function TestInArrays ( + a_StartingValue number, + a_Array udt_NumberList + ) return number is + t_Value number; + begin + t_Value := a_StartingValue; + for i in 1..a_Array.count loop + t_Value := t_Value + a_Array(i); + end loop; + return t_Value; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_NumberList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := a_Array(i) * 10; + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_NumberList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := i * 100; + end loop; + end; + +end; +/ + +create or replace package &main_user..pkg_TestDateArrays as + + type udt_DateList is table of date index by binary_integer; + + function TestInArrays ( + a_StartingValue number, + a_BaseDate date, + a_Array udt_DateList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_DateList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_DateList + ); + +end; +/ + +create or replace package body &main_user..pkg_TestDateArrays as + + function TestInArrays ( + a_StartingValue number, + a_BaseDate date, + a_Array udt_DateList + ) return number is + t_Value number; + begin + t_Value := a_StartingValue; + for i in 1..a_Array.count loop + t_Value := t_Value + a_Array(i) - a_BaseDate; + end loop; + return t_Value; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_DateList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := a_Array(i) + 7; + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_DateList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := to_date(20021212, 'YYYYMMDD') + i * 1.2; + end loop; + end; + +end; +/ + +create or replace package &main_user..pkg_TestOutCursors as + + type udt_RefCursor is ref cursor; + + procedure TestOutCursor ( + a_MaxIntValue number, + a_Cursor out udt_RefCursor + ); + +end; +/ + +create or replace package body &main_user..pkg_TestOutCursors as + + procedure TestOutCursor ( + a_MaxIntValue number, + a_Cursor out udt_RefCursor + ) is + begin + open a_Cursor for + select + IntCol, + StringCol + from TestStrings + where IntCol <= a_MaxIntValue + order by IntCol; + end; + +end; +/ + +create or replace package &main_user..pkg_TestBooleans as + + type udt_BooleanList is table of boolean index by binary_integer; + + function GetStringRep ( + a_Value boolean + ) return varchar2; + + function IsLessThan10 ( + a_Value number + ) return boolean; + + function TestInArrays ( + a_Value udt_BooleanList + ) return number; + + procedure TestOutArrays ( + a_NumElements number, + a_Value out nocopy udt_BooleanList + ); + +end; +/ + +create or replace package body &main_user..pkg_TestBooleans as + + function GetStringRep ( + a_Value boolean + ) return varchar2 is + begin + if a_Value is null then + return 'NULL'; + elsif a_Value then + return 'TRUE'; + end if; + return 'FALSE'; + end; + + function IsLessThan10 ( + a_Value number + ) return boolean is + begin + return a_Value < 10; + end; + + function TestInArrays ( + a_Value udt_BooleanList + ) return number is + t_Result pls_integer; + begin + t_Result := 0; + for i in 1..a_Value.count loop + if a_Value(i) then + t_Result := t_Result + 1; + end if; + end loop; + return t_Result; + end; + + procedure TestOutArrays ( + a_NumElements number, + a_Value out nocopy udt_BooleanList + ) is + begin + for i in 1..a_NumElements loop + a_Value(i) := (mod(i, 2) = 1); + end loop; + end; + +end; +/ + +create or replace package &main_user..pkg_TestBindObject as + + function GetStringRep ( + a_Object udt_Object + ) return varchar2; + +end; +/ + +create or replace package body &main_user..pkg_TestBindObject as + + function GetStringRep ( + a_Object udt_SubObject + ) return varchar2 is + begin + if a_Object is null then + return 'null'; + end if; + return 'udt_SubObject(' || + nvl(to_char(a_Object.SubNumberValue), 'null') || ', ' || + case when a_Object.SubStringValue is null then 'null' + else '''' || a_Object.SubStringValue || '''' end || ')'; + end; + + function GetStringRep ( + a_Array udt_ObjectArray + ) return varchar2 is + t_StringRep varchar2(4000); + begin + if a_Array is null then + return 'null'; + end if; + t_StringRep := 'udt_ObjectArray('; + for i in 1..a_Array.count loop + if i > 1 then + t_StringRep := t_StringRep || ', '; + end if; + t_StringRep := t_StringRep || GetStringRep(a_Array(i)); + end loop; + return t_StringRep || ')'; + end; + + function GetStringRep ( + a_Object udt_Object + ) return varchar2 is + begin + if a_Object is null then + return 'null'; + end if; + return 'udt_Object(' || + nvl(to_char(a_Object.NumberValue), 'null') || ', ' || + case when a_Object.StringValue is null then 'null' + else '''' || a_Object.StringValue || '''' end || ', ' || + case when a_Object.FixedCharValue is null then 'null' + else '''' || a_Object.FixedCharValue || '''' end || ', ' || + case when a_Object.DateValue is null then 'null' + else 'to_date(''' || + to_char(a_Object.DateValue, 'YYYY-MM-DD') || + ''', ''YYYY-MM-DD'')' end || ', ' || + case when a_Object.TimestampValue is null then 'null' + else 'to_timestamp(''' || to_char(a_Object.TimestampValue, + 'YYYY-MM-DD HH24:MI:SS') || + ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' || + GetStringRep(a_Object.SubObjectValue) || ', ' || + GetStringRep(a_Object.SubObjectArray) || ')'; + end; + +end; +/ + +create or replace package &main_user..pkg_TestRecords as + + type udt_Record is record ( + NumberValue number, + StringValue varchar2(30), + DateValue date, + TimestampValue timestamp, + BooleanValue boolean + ); + + type udt_RecordArray is table of udt_Record index by binary_integer; + + function GetStringRep ( + a_Value udt_Record + ) return varchar2; + + procedure TestOut ( + a_Value out nocopy udt_Record + ); + + function TestInArrays ( + a_Value udt_RecordArray + ) return varchar2; + +end; +/ + +create or replace package body &main_user..pkg_TestRecords as + + function GetStringRep ( + a_Value udt_Record + ) return varchar2 is + begin + return 'udt_Record(' || + nvl(to_char(a_Value.NumberValue), 'null') || ', ' || + case when a_Value.StringValue is null then 'null' + else '''' || a_Value.StringValue || '''' end || ', ' || + case when a_Value.DateValue is null then 'null' + else 'to_date(''' || + to_char(a_Value.DateValue, 'YYYY-MM-DD') || + ''', ''YYYY-MM-DD'')' end || ', ' || + case when a_Value.TimestampValue is null then 'null' + else 'to_timestamp(''' || to_char(a_Value.TimestampValue, + 'YYYY-MM-DD HH24:MI:SS') || + ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' || + case when a_Value.BooleanValue is null then 'null' + when a_Value.BooleanValue then 'true' + else 'false' end || ')'; + end; + + procedure TestOut ( + a_Value out nocopy udt_Record + ) is + begin + a_Value.NumberValue := 25; + a_Value.StringValue := 'String in record'; + a_Value.DateValue := to_date(20160216, 'YYYYMMDD'); + a_Value.TimestampValue := to_timestamp('20160216 18:23:55', + 'YYYYMMDD HH24:MI:SS'); + a_Value.BooleanValue := true; + end; + + function TestInArrays ( + a_Value udt_RecordArray + ) return varchar2 is + t_Result varchar2(4000); + begin + for i in 0..a_Value.count - 1 loop + if t_Result is not null then + t_Result := t_Result || '; '; + end if; + t_Result := t_Result || GetStringRep(a_Value(i)); + end loop; + return t_Result; + end; + +end; +/ + diff --git a/oracle/ci/resources/TestEnv.sql b/oracle/ci/resources/TestEnv.sql new file mode 100644 index 0000000000000..1ce00f3c0c205 --- /dev/null +++ b/oracle/ci/resources/TestEnv.sql @@ -0,0 +1,29 @@ +/*----------------------------------------------------------------------------- + * Copyright 2017, Oracle and/or its affiliates. All rights reserved. + *---------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * TestEnv.sql + * Sets up configuration for the SetupTest.sql and DropTest.sql scripts. + * Change the values below if you would like to use something other than the + * default values. Note that the environment variables noted below will also + * need to be set, or the Python script TestEnv.py will need to be changed if + * non-default values are used. + *---------------------------------------------------------------------------*/ + +set echo off termout on feedback off verify off + +define main_user = "cx_Oracle" -- $CX_ORACLE_TEST_MAIN_USER +define main_password = "welcome" -- $CX_ORACLE_TEST_MAIN_PASSWORD +define proxy_user = "cx_Oracle_proxy" -- $CX_ORACLE_TEST_PROXY_USER +define proxy_password = "welcome" -- $CX_ORACLE_TEST_PROXY_PASSWORD + +prompt ************************************************************************ +prompt CONFIGURATION +prompt ************************************************************************ +prompt Main Schema: &main_user +prompt Proxy Schema: &proxy_user +prompt + +set echo on verify on feedback on + diff --git a/oracle/ci/resources/get_instantclient.py b/oracle/ci/resources/get_instantclient.py new file mode 100755 index 0000000000000..62e826d0b6044 --- /dev/null +++ b/oracle/ci/resources/get_instantclient.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +import pexpect +import os +import sys +import getopt +import platform + + +TARGET = 'instantclient' + +def usage(): + print 'usage: {} -a yes|no [-u user | -p pass | -d destination]'.format(sys.argv[0]) + print '\t -a | --agree= : user to oracle SSO' + print '\t -u | --user= : user to oracle SSO' + print '\t -p | --pass= : pass to oracle SSO' + print '\t -d | --dest= : destination to download instantclient' + print '\n\n\t You may also specify user ane passwd with ORACLE_USER and ORACLE pass env vars.' + +def executable_in_path(cmd): + for directory in os.environ.get("PATH", "").split(os.pathsep): + cmd_candidate = os.path.join(directory, cmd) + if os.path.exists(cmd_candidate): + return True + + return False + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], "ha:u:p:d:v", + ["help", "agree=", "user=", "pass=", "dest="]) + except getopt.GetoptError as err: + # print help information and exit: + print str(err) + usage() + sys.exit(2) + + username = None + password = None + agree = False + destination = None + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-a", "--agree"): + agree = True if a.strip().lower() == "yes" else False + elif o in ("-u", "--user"): + username = a + elif o in ("-p", "--pass"): + password = a + elif o in ("-d", "--dest"): + destination = a + else: + assert False, "unhandled option" + + if not agree: + print "You must agree to the oracle instantclient terms and conditions to continue." + usage() + sys.exit(2) + + if not username: + username = os.environ.get('ORACLE_USER') + if not password: + password = os.environ.get('ORACLE_PASS') + if not destination: + destination = os.environ.get('ORACLE_DIR') if 'ORACLE_DIR' in os.environ else os.getcwd() + + if not username or not password: + print 'Credential env vars not set - quitting' + usage() + sys.exit(2) + + if not executable_in_path('npm'): + print 'npm required to continue. Please install npm or otherwise download instantclient manually.' + sys.exit(-1) + + if os.path.isdir(os.path.join(destination, TARGET)): + print 'instantclient already installed or conflicting directory present - quitting' + sys.exit(0) + + + npm = pexpect.spawnu('npm install --prefix {} instantclient'.format(destination)) + npm.logfile = sys.stdout + + npm.expect(u'Press \(Y\) to Install, anything else to Cancel\? ', timeout=120) + npm.sendline('Y') + npm.expect(u'Press \(Y\) to Accept the License Agreement, anything else to Cancel\? ') + npm.sendline('Y') + npm.logfile = None + + npm.expect(u'Please enter your username: ') + npm.sendline(username) + npm.expect(u'Please enter your password: ') + npm.sendline(password) + npm.expect(u'Directory .* created.', timeout=120) + sys.stdout.flush() + npm.logfile = sys.stdout + if 'linux' in platform.system().lower(): + npm.expect(u'instantclient@', timeout=120) + + try: + npm.expect(pexpect.EOF, timeout=120) + except Exception: + pass + finally: + sys.stdout.flush() + + print 'InstantClient installation complete.' + + +if __name__ == "__main__": + main() diff --git a/oracle/conf.yaml.example b/oracle/conf.yaml.example new file mode 100644 index 0000000000000..7647aca60fe2d --- /dev/null +++ b/oracle/conf.yaml.example @@ -0,0 +1,30 @@ +init_config: + +instances: + # The Oracle check requires access to the `cx_Oracle` Python + # module. Due to restrictions on installation, this requires + # the following steps in order to be included with the agent + # + # 1. Download the relevant Oracle Instant Client: + # http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html + # Both the basic client and the client sdk will be required + # Example dir: ~/oracle + # 2. Decompress this library in a given directory available to + # all users on the given machine (i.e. /opt/oracle) + # mkdir -p /opt/oracle/ && cd /opt/oracle/ + # unzip ~/oracle/instantclient-basic-linux.x64-12.1.0.2.0.zip + # unzip ~/oracle/instantclient-sdk-linux.x64-12.1.0.2.0.zip + # 3. Set required env variables for cx_Oracle installation + # export LD_RUN_PATH=/opt/oracle/instantclient_12_1 + # export ORACLE_HOME=/opt/oracle/instantclient_12_1 + # 4. Install instant client in agent's virtual environment + # /opt/datadog-agent/embedded/bin/pip install cx_Oracle + # 5. Following cx_Oracle installation, ensure LD_LIBRARY_PATH points + # to this directory when starting/restarting the agent + # export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_1 + # + - server: 127.0.0.1 + user: my_username + password: my_password + tags: + - my_tag diff --git a/oracle/manifest.json b/oracle/manifest.json new file mode 100644 index 0000000000000..3e9bb13ca2942 --- /dev/null +++ b/oracle/manifest.json @@ -0,0 +1,12 @@ +{ + "maintainer": "help@datadoghq.com", + "manifest_version": "0.1.0", + "max_agent_version": "6.0.0", + "min_agent_version": "5.6.3", + "name": "oracle", + "short_description": "oracle description.", + "support": "contrib", + "supported_os": ["linux","mac_os"], + "version": "1.0.0", + "guid": "6c4ddc46-2763-4c56-8b71-c838b7f82d7b" +} diff --git a/oracle/metadata.csv b/oracle/metadata.csv new file mode 100644 index 0000000000000..f946d0407b16f --- /dev/null +++ b/oracle/metadata.csv @@ -0,0 +1,27 @@ +metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name +oracle.buffer_cachehit_ratio,gauge,,fraction,,Ratio of buffer cache hits,1,oracle,buff cache hit ratio +oracle.cursor_cachehit_ratio,gauge,,fraction,,Ratio of cursor cache hits,1,oracle,cursor cache hit ratio +oracle.library_cachehit_ratio,gauge,,fraction,,Ratio of library cache hits,1,oracle,lib cache hit ratio +oracle.shared_pool_free,gauge,,percent,,shared pool free memory %,0,oracle,shared pool free +oracle.physical_reads,gauge,,reads,sec,physical reads per sec,0,oracle,phys reads +oracle.physical_writes,gauge,,writes,sec,physical writes per sec,0,oracle,phys reads +oracle.enqueue_timeouts,gauge,,timeouts,sec,enqueue timeouts per sec,0,oracle,enqueue timeouts +oracle.gc_cr_receive_time,gauge,,blocks,sec,GC CR receive time,0,oracle,gc cr receive time +oracle.cache_blocks_corrupt,gauge,,blocks,,corrupt cache blocks,0,oracle,corrupt cache blocks +oracle.cache_blocks_lost,gauge,,blocks,,lost cache blocks,0,oracle,lost cache blocks +oracle.logons,gauge,,attempts,,number of logon attempts,0,oracle,logons +oracle.active_sessions,gauge,,sessions,,number of active sessions,0,oracle,active sessions +oracle.long_table_scans,gauge,,scans,sec,number of long table scans per sec,0,oracle,long tbl scans +oracle.service_response_time,gauge,,secs,,service response time,0,oracle,svc resp time +oracle.user_rollbacks,gauge,,operations,,number of user rollbacks,0,oracle,user rollbacks +oracle.sorts_per_user_call,gauge,,sorts,attempt,sorts per user call,0,oracle,sorts per user call +oracle.rows_per_sort,gauge,,rows,operation,rows per sort,0,oracle,rows per sort +oracle.disk_sorts,gauge,,operations,sec,disk sorts per second,0,oracle,disk sorts +oracle.memory_sorts_ratio,gauge,,fraction,,memory sorts ratio,0,oracle,memory sort ratio +oracle.database_wait_time_ratio,gauge,,fraction,,memory sorts per second,0,oracle,db wait-time ratio +oracle.session_limit_usage,gauge,,percent,,session limit usage,0,oracle,session lim usage % +oracle.session_count,gauge,,session,,session count,0,oracle,session count +oracle.temp_space_used,gauge,,bytes,,temp space used,0,oracle,temp space used +oracle.tablespace.used,gauge,,bytes,,tablespace used,0,oracle,tablespace used +oracle.tablespace.size,gauge,,bytes,,tablespace size,0,oracle,tablespace size +oracle.tablespace.in_use,gauge,,fraction,,tablespace in-use,0,oracle,tablespace in-use diff --git a/oracle/requirements.txt b/oracle/requirements.txt new file mode 100644 index 0000000000000..d0cdc939e237e --- /dev/null +++ b/oracle/requirements.txt @@ -0,0 +1,2 @@ +# integration pip requirements +cx_Oracle==6.0.1 diff --git a/oracle/test_oracle.py b/oracle/test_oracle.py new file mode 100644 index 0000000000000..f06c378f71373 --- /dev/null +++ b/oracle/test_oracle.py @@ -0,0 +1,108 @@ +# (C) Datadog, Inc. 2010-2017 +# All rights reserved +# Licensed under Simplified BSD License (see LICENSE) + +# stdlib +import logging +import time + +# 3p +from nose.plugins.attrib import attr +import cx_Oracle + +# project +from tests.checks.common import AgentCheckTest + +logging.basicConfig() + +""" +Using the "system" user as permission granting not available +for default "system" user + +Set up Oracle instant client: +http://jasonstitt.com/cx_oracle_on_os_x + +Set: +export ORACLE_HOME=/opt/oracle/instantclient_12_1/ +export DYLD_LIBRARY_PATH="$ORACLE_HOME:$DYLD_LIBRARY_PATH" +""" + +CONFIG = { + 'init_config': {}, + 'instances': [{ + 'server': 'localhost:1521', + 'user': 'system', + 'password': 'oracle', + 'service_name': 'xe', + }] +} + +SERVICE_CHECK_NAME = 'oracle.can_connect' +METRICS = [ + 'oracle.tablespace.used', + 'oracle.tablespace.size', + 'oracle.tablespace.in_use', + 'oracle.buffer_cachehit_ratio', + 'oracle.cursor_cachehit_ratio', + 'oracle.library_cachehit_ratio', + 'oracle.shared_pool_free', + 'oracle.physical_reads', + 'oracle.physical_writes', + 'oracle.enqueue_timeouts', + 'oracle.gc_cr_receive_time', + 'oracle.cache_blocks_corrupt', + 'oracle.cache_blocks_lost', + 'oracle.logons', + 'oracle.active_sessions', + 'oracle.long_table_scans', + 'oracle.service_response_time', + 'oracle.user_rollbacks', + 'oracle.sorts_per_user_call', + 'oracle.rows_per_sort', + 'oracle.disk_sorts', + 'oracle.memory_sorts_ratio', + 'oracle.database_wait_time_ratio', + 'oracle.enqueue_timeouts', + 'oracle.session_limit_usage', + 'oracle.session_count', + 'oracle.temp_space_used', +] + +@attr(requires='oracle') +class TestOracle(AgentCheckTest): + """Basic Test for oracle integration.""" + CHECK_NAME = 'oracle' + + def setUp(self): + conn_string = 'cx_Oracle/welcome@//localhost:1521/xe' + connection = cx_Oracle.connect(conn_string) + + # mess around a bit to pupulate metrics + cursor = connection.cursor() + cursor.execute("select 'X' from dual") + + # truncate + cursor.execute("truncate table TestTempTable") + + # insert + rows = [{u"value": n} for n in range(250)] + cursor.arraysize = 100 + statement = "insert into TestTempTable (IntCol) values (:value)" + cursor.executemany(statement, rows) + connection.commit() + + # select + cursor.execute("select count(*) from TestTempTable") + _, = cursor.fetchone() + + # wait to populate + time.sleep(90) + + def testOracle(self): + self.run_check_twice(CONFIG) + + for m in METRICS: + self.assertMetric(m, at_least=1) + + self.assertServiceCheck(SERVICE_CHECK_NAME) + self.coverage_report()