Initial commit with BSL

This commit is contained in:
Andy Pavlo 2019-08-23 11:47:19 -04:00
commit 3e564ce922
286 changed files with 177642 additions and 0 deletions

6
.dockerignore Normal file
View File

@ -0,0 +1,6 @@
*.swp
*~
*.pyc
*.md
.travis.yml
LICENSE

104
.gitignore vendored Normal file
View File

@ -0,0 +1,104 @@
# Byte-compiled / optimized / DLL files #
#########################################
__pycache__/
*.py[cod]
*$py.class
*.com
*.class
*.dll
*.exe
*.o
*.so
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Distribution / packaging #
############################
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# PyInstaller #
###############
*.manifest
*.spec
# Installer logs #
##################
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports #
################################
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Env #
#######
.python-version
.env
venv/
ENV/
# Eclipse #
###########
.project
.pydevproject
.settings
.classpath
# Intellij and PyCharm #
.idea
*.iml
*.iws
out/
# vim #
*~
*.swp
*.swo
# Text editor configs #
#######################
.vimrc

58
.travis.yml Normal file
View File

@ -0,0 +1,58 @@
dist: bionic
sudo: required
services:
- docker
env:
global:
- DOCKER_COMPOSE_VERSION=1.24.1
matrix:
- DOCKER_OS=ubuntu-18.04
- DOCKER_OS=centos-7
before_install:
# Install latest versions of docker and docker-compose
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- sudo apt-get update
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- docker --version
- docker-compose --version
before_script:
# Set correct dockerfile
- cd $TRAVIS_BUILD_DIR/docker
- sed -i "s|Dockerfile\.base-.*|Dockerfile\.base-$DOCKER_OS|" docker-compose.yml
- sed -i "s|Dockerfile\.base-.*|Dockerfile\.base-$DOCKER_OS|" docker-compose.test.yml
- sudo service mysql stop || true
script:
- cd $TRAVIS_BUILD_DIR/docker
# Build master images and run the webserver
- docker-compose build
- docker-compose up -d
- docker logs ottertune
- docker-compose rm -f -s -v
# Build test images and run tests
- ci_env=`bash <(curl -s https://codecov.io/env)`
- docker-compose -f docker-compose.test.yml build
- docker-compose -f docker-compose.test.yml up -d
- docker-compose -f docker-compose.test.yml run --workdir="/app/client/controller" --rm test gradle build
- docker-compose -f docker-compose.test.yml run --workdir="/app/server" --rm $ci_env test bash -c "coverage run --omit=\"*/tests/*\" -m unittest discover -s analysis/tests -v && (codecov -F analysis || (sleep 5 && codecov -F analysis) || (sleep 5 && codecov -F analysis))"
- docker-compose -f docker-compose.test.yml run --workdir="/app/server/website" --rm $ci_env test bash -c "./wait-for-it.sh && coverage run --source=website manage.py test --noinput -v 2 && (codecov -F website || (sleep 5 && codecov -F website) || (sleep 5 && codecov -F website))"
# Only run source validation once (on ubuntu 18.04)
- if [ $DOCKER_OS == ubuntu-18.04 ]; then
docker-compose -f docker-compose.test.yml run --workdir="/app" --rm test python3 script/validators/source_validator.py;
fi
after_script:
# Cleanup docker containers, images, and volumes
- docker-compose rm -f -s -v
- docker system prune -a -f
- docker volume prune -f

100
LICENSE Normal file
View File

@ -0,0 +1,100 @@
Business Source License 1.1
Parameters
Licensor: Carnegie Mellon University
Licensed Work: OtterTune
The Licensed Work is (c) 2019 Carnegie Mellon University
Additional Use Grant: You may make use of the Licensed Work, provided that
you may not use the Licensed Work for a Database
Service.
A “Database Service” is a commercial offering that
allows third parties (other than your employees and
contractors) to access the functionality of the
Licensed Work by creating tables whose schemas are
controlled by such third parties.
Change Date: 2024-01-18
Change License: Apache License, Version 2.0
Notice
The Business Source License (this document, or the “License”) is not an Open
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
“Business Source License” is a trademark of MariaDB Corporation Ab.
-----------------------------------------------------------------------------
Business Source License 1.1
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
MariaDB hereby grants you permission to use this Licenses text to license
your works, and to refer to it using the trademark “Business Source License”,
as long as you comply with the Covenants of Licensor below.
Covenants of Licensor
In consideration of the right to use this Licenses text and the “Business
Source License” name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version,
or a license that is compatible with GPL Version 2.0 or a later version,
where “compatible” means that software provided under the Change License can
be included in a program with software provided under GPL Version 2.0 or a
later version. Licensor may specify additional Change Licenses without
limitation.
2. To either: (a) specify an additional grant of rights to use that does not
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text “None”.
3. To specify a Change Date.
4. Not to modify this License in any other way.

24
README.md Normal file
View File

@ -0,0 +1,24 @@
# OtterTune
[![Build Status](https://travis-ci.org/cmu-db/ottertune.svg?branch=master)](https://travis-ci.org/cmu-db/ottertune)
[![codecov.io](https://codecov.io/github/cmu-db/ottertune/coverage.svg?branch=master)](https://codecov.io/github/cmu-db/ottertune)
OtterTune is a new tool developed by students and researchers in the [Carnegie Mellon Database Group](http://db.cs.cmu.edu/projects/autotune/) that can automatically find good settings for a database management system's configuration knobs. The goal is to make it easier for anyone to deploy a DBMS without any expertise in database administration. To tune new DBMS deployments, OtterTune reuses training data gathered from previous tuning sessions. Because OtterTune does not need to generate an initial dataset for training its ML models, tuning time is drastically reduced.
For more information, see our [paper](http://db.cs.cmu.edu/papers/2017/p1009-van-aken.pdf).
```
@inproceedings{vanaken17,
author = {Van Aken, Dana and Pavlo, Andrew and Gordon, Geoffrey J. and Zhang, Bohan},
title = {Automatic Database Management System Tuning Through Large-scale Machine Learning},
booktitle = {Proceedings of the 2017 ACM International Conference on Management of Data},
series = {SIGMOD '17},
year = {2017},
pages = {1009--1024},
numpages = {16},
}
```
## Contributors
See the [people page](https://github.com/cmu-db/ottertune/graphs/contributors) for the full list of contributors.

28
client/controller/.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# Mac OS X hidden file
.DS_Store
# workspace configuration files
.settings/
.metadata/
# Gradle
.gradle/
/gradlew.bat
/gradlew
/settings.gradle
# Intellij
.idea
/dbcollector.iml
# generated files
bin/
build/
out/
output/
# lib
lib/
# log file
*.log

View File

@ -0,0 +1,12 @@
## OtterTune Controller
The controller is responsible for collecting database metrics and knobs information during an experiment.</br>
#### Usage:
To build the project, run `gradle build`.</br>
To run the controller, you need to provide a configuration file and provide command line arguments (command line arguments are optional). Then run `gradle run`.
* Command line arguments:
* time (flag : `-t`) </br>
The duration of the experiment in `seconds`. The default time is set to 300 seconds.
* configuration file path (flag : `-c`) </br>
The path of the input configuration file (required). Sample config files are under the directory `config`.

View File

@ -0,0 +1,90 @@
plugins {
id "de.undercouch.download" version "3.3.0"
id "com.github.spotbugs" version "2.0.0"
}
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'application'
apply plugin: 'project-report'
mainClassName = "com.controller.Main"
test.testLogging { exceptionFormat "full"; events "failed", "passed", "skipped" }
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
runtime fileTree(dir: 'lib', include: '*.jar')
compile group: 'net.sourceforge.collections', name: 'collections-generic', version: '4.01'
compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
compile group: 'log4j', name: 'log4j', version: '1.2.17'
compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.3'
// https://mvnrepository.com/artifact/com.github.fge/json-schema-validator
compile group: 'com.github.fge', name: 'json-schema-validator', version: '2.2.6'
// https://mvnrepository.com/artifact/com.github.fge/jackson-coreutils
compile group: 'com.github.fge', name: 'jackson-coreutils', version: '1.8'
// https://mvnrepository.com/artifact/com.github.fge/json-schema-core
compile group: 'com.github.fge', name: 'json-schema-core', version: '1.2.5'
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.0'
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.8.0'
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.0'
compile(group: "com.github.java-json-tools", name: "json-schema-validator", version: "2.2.8");
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
// https://mvnrepository.com/artifact/commons-cli/commons-cli
compile group: 'commons-cli', name: 'commons-cli', version: '1.2'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.6'
// https://mvnrepository.com/artifact/org.postgresql/postgresql
compile group: 'org.postgresql', name: 'postgresql', version: '9.4-1201-jdbc41'
// This lib has to be manually downloaded from Oracle
dependencies {compile files('lib/ojdbc8.jar')}
}
run {
if (project.hasProperty("appArgs")) {
args(appArgs.split(' '))
}
// Below is another way to add args but this causes gradle to
// always invoke the run application task
// args = ["-c","$config","-t","$time","-d","$dir"]
}
spotbugs {
toolVersion = '4.0.0-beta3'
ignoreFailures = true
}
// Note (07-29-2019): the HTML report for spotbugs is currently broken
tasks.withType(com.github.spotbugs.SpotBugsTask) {
reports {
xml.enabled true
html.enabled false
}
}
import de.undercouch.gradle.tasks.download.Download
task downloadJars(type: Download) {
src ([
'https://github.com/google/google-java-format/releases/download/google-java-format-1.5/google-java-format-1.5-all-deps.jar',
'https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.8/checkstyle-8.8-all.jar'
])
dest libsDir
overwrite false
}
build.finalizedBy(downloadJars)

View File

@ -0,0 +1,9 @@
{
"database_type" : "mysql",
"database_url" : "jdbc:mysql://localhost:3306/mysqldb",
"username" : "MY_DATABASE_USERNAME",
"password" : "MY_DATABASE_PASSWORD",
"upload_code" : "DEPRECATED",
"upload_url" : "DEPRECATED",
"workload_name" : "workload_name"
}

View File

@ -0,0 +1,9 @@
{
"database_type" : "oracle",
"database_url" : "jdbc:oracle:thin:@localhost:1521:orcldb",
"username" : "sys as sysdba",
"password" : "oracle",
"upload_code" : "DEPRECATED",
"upload_url" : "DEPRECATED",
"workload_name" : "tpcc"
}

View File

@ -0,0 +1,9 @@
{
"database_type" : "postgres",
"database_url" : "jdbc:postgresql://localhost:5432/postgres",
"username" : "MY_DATABASE_USERNAME",
"password" : "MY_DATABASE_PASSWORD",
"upload_code" : "DEPRECATED",
"upload_url" : "DEPRECATED",
"workload_name" : "workload_name"
}

View File

@ -0,0 +1,9 @@
{
"database_type" : "saphana",
"database_url" : "jdbc:sap://localhost:39015",
"username" : "MY_DATABASE_USERNAME",
"password" : "MY_DATABASE_PASSWORD",
"upload_code" : "DEPRECATED",
"upload_url" : "DEPRECATED",
"workload_name" : "workload_name"
}

View File

@ -0,0 +1,6 @@
#Thu Nov 30 15:45:05 EST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip

View File

@ -0,0 +1,28 @@
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, A1, FILE
log4j.rootLogger.layout=org.apache.log4j.PatternLayout
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{ABSOLUTE} (%F:%L) %-5p - %m%n
# Redirect log messages to a log file, support file rolling.
# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
# Set the name of the file
log4j.appender.FILE.file=controller.log
# Set the immediate flush to true (default)
log4j.appender.FILE.immediateFlush=true
# Set the threshold to debug mode
log4j.appender.FILE.Threshold=debug
# Set the append to false, overwrite
log4j.appender.FILE.append=true
# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{ABSOLUTE} (%F:%L) %-5p - %m%n

View File

@ -0,0 +1,517 @@
{
"global": {"global": {
"auto_generate_certs": "ON",
"auto_increment_increment": "1",
"auto_increment_offset": "1",
"autocommit": "ON",
"automatic_sp_privileges": "ON",
"avoid_temporal_upgrade": "OFF",
"back_log": "80",
"basedir": "/usr/local/Cellar/mysql/5.7.20/",
"big_tables": "OFF",
"bind_address": "127.0.0.1",
"binlog_cache_size": "32768",
"binlog_checksum": "CRC32",
"binlog_direct_non_transactional_updates": "OFF",
"binlog_error_action": "ABORT_SERVER",
"binlog_format": "ROW",
"binlog_group_commit_sync_delay": "0",
"binlog_group_commit_sync_no_delay_count": "0",
"binlog_gtid_simple_recovery": "ON",
"binlog_max_flush_queue_time": "0",
"binlog_order_commits": "ON",
"binlog_row_image": "FULL",
"binlog_rows_query_log_events": "OFF",
"binlog_stmt_cache_size": "32768",
"block_encryption_mode": "aes-128-ecb",
"bulk_insert_buffer_size": "8388608",
"character_set_client": "utf8",
"character_set_connection": "utf8",
"character_set_database": "utf8",
"character_set_filesystem": "binary",
"character_set_results": "",
"character_set_server": "utf8",
"character_set_system": "utf8",
"character_sets_dir": "/usr/local/Cellar/mysql/5.7.20/share/mysql/charsets/",
"check_proxy_users": "OFF",
"collation_connection": "utf8_general_ci",
"collation_database": "utf8_general_ci",
"collation_server": "utf8_general_ci",
"completion_type": "NO_CHAIN",
"concurrent_insert": "AUTO",
"connect_timeout": "10",
"core_file": "OFF",
"datadir": "/usr/local/var/mysql/",
"date_format": "%Y-%m-%d",
"datetime_format": "%Y-%m-%d %H:%i:%s",
"default_authentication_plugin": "mysql_native_password",
"default_password_lifetime": "0",
"default_storage_engine": "InnoDB",
"default_tmp_storage_engine": "InnoDB",
"default_week_format": "0",
"delay_key_write": "ON",
"delayed_insert_limit": "100",
"delayed_insert_timeout": "300",
"delayed_queue_size": "1000",
"disabled_storage_engines": "",
"disconnect_on_expired_password": "ON",
"div_precision_increment": "4",
"end_markers_in_json": "OFF",
"enforce_gtid_consistency": "OFF",
"eq_range_index_dive_limit": "200",
"error_count": "0",
"event_scheduler": "OFF",
"expire_logs_days": "0",
"explicit_defaults_for_timestamp": "OFF",
"external_user": "",
"flush": "OFF",
"flush_time": "0",
"foreign_key_checks": "ON",
"ft_boolean_syntax": "+ -><()~*:\"\"&|",
"ft_max_word_len": "84",
"ft_min_word_len": "4",
"ft_query_expansion_limit": "20",
"ft_stopword_file": "(built-in)",
"general_log": "OFF",
"general_log_file": "/usr/local/var/mysql/JiangShuli.log",
"group_concat_max_len": "1024",
"gtid_executed_compression_period": "1000",
"gtid_mode": "OFF",
"gtid_next": "AUTOMATIC",
"gtid_owned": "",
"gtid_purged": "",
"have_compress": "YES",
"have_crypt": "YES",
"have_dynamic_loading": "YES",
"have_geometry": "YES",
"have_openssl": "YES",
"have_profiling": "YES",
"have_query_cache": "YES",
"have_rtree_keys": "YES",
"have_ssl": "YES",
"have_statement_timeout": "YES",
"have_symlink": "YES",
"host_cache_size": "279",
"hostname": "JiangShuli.local",
"identity": "0",
"ignore_builtin_innodb": "OFF",
"ignore_db_dirs": "",
"init_connect": "",
"init_file": "",
"init_slave": "",
"innodb_adaptive_flushing": "ON",
"innodb_adaptive_flushing_lwm": "10",
"innodb_adaptive_hash_index": "ON",
"innodb_adaptive_hash_index_parts": "8",
"innodb_adaptive_max_sleep_delay": "150000",
"innodb_api_bk_commit_interval": "5",
"innodb_api_disable_rowlock": "OFF",
"innodb_api_enable_binlog": "OFF",
"innodb_api_enable_mdl": "OFF",
"innodb_api_trx_level": "0",
"innodb_autoextend_increment": "64",
"innodb_autoinc_lock_mode": "1",
"innodb_buffer_pool_chunk_size": "134217728",
"innodb_buffer_pool_dump_at_shutdown": "ON",
"innodb_buffer_pool_dump_now": "OFF",
"innodb_buffer_pool_dump_pct": "25",
"innodb_buffer_pool_filename": "ib_buffer_pool",
"innodb_buffer_pool_instances": "1",
"innodb_buffer_pool_load_abort": "OFF",
"innodb_buffer_pool_load_at_startup": "ON",
"innodb_buffer_pool_load_now": "OFF",
"innodb_buffer_pool_size": "134217728",
"innodb_change_buffer_max_size": "25",
"innodb_change_buffering": "all",
"innodb_checksum_algorithm": "crc32",
"innodb_checksums": "ON",
"innodb_cmp_per_index_enabled": "OFF",
"innodb_commit_concurrency": "0",
"innodb_compression_failure_threshold_pct": "5",
"innodb_compression_level": "6",
"innodb_compression_pad_pct_max": "50",
"innodb_concurrency_tickets": "5000",
"innodb_data_file_path": "ibdata1:12M:autoextend",
"innodb_data_home_dir": "",
"innodb_deadlock_detect": "ON",
"innodb_default_row_format": "dynamic",
"innodb_disable_sort_file_cache": "OFF",
"innodb_doublewrite": "ON",
"innodb_fast_shutdown": "1",
"innodb_file_format": "Barracuda",
"innodb_file_format_check": "ON",
"innodb_file_format_max": "Barracuda",
"innodb_file_per_table": "ON",
"innodb_fill_factor": "100",
"innodb_flush_log_at_timeout": "1",
"innodb_flush_log_at_trx_commit": "1",
"innodb_flush_method": "",
"innodb_flush_neighbors": "1",
"innodb_flush_sync": "ON",
"innodb_flushing_avg_loops": "30",
"innodb_force_load_corrupted": "OFF",
"innodb_force_recovery": "0",
"innodb_ft_aux_table": "",
"innodb_ft_cache_size": "8000000",
"innodb_ft_enable_diag_print": "OFF",
"innodb_ft_enable_stopword": "ON",
"innodb_ft_max_token_size": "84",
"innodb_ft_min_token_size": "3",
"innodb_ft_num_word_optimize": "2000",
"innodb_ft_result_cache_limit": "2000000000",
"innodb_ft_server_stopword_table": "",
"innodb_ft_sort_pll_degree": "2",
"innodb_ft_total_cache_size": "640000000",
"innodb_ft_user_stopword_table": "",
"innodb_io_capacity": "200",
"innodb_io_capacity_max": "2000",
"innodb_large_prefix": "ON",
"innodb_lock_wait_timeout": "50",
"innodb_locks_unsafe_for_binlog": "OFF",
"innodb_log_buffer_size": "16777216",
"innodb_log_checksums": "ON",
"innodb_log_compressed_pages": "ON",
"innodb_log_file_size": "50331648",
"innodb_log_files_in_group": "2",
"innodb_log_group_home_dir": "./",
"innodb_log_write_ahead_size": "8192",
"innodb_lru_scan_depth": "1024",
"innodb_max_dirty_pages_pct": "75.000000",
"innodb_max_dirty_pages_pct_lwm": "0.000000",
"innodb_max_purge_lag": "0",
"innodb_max_purge_lag_delay": "0",
"innodb_max_undo_log_size": "1073741824",
"innodb_monitor_disable": "",
"innodb_monitor_enable": "",
"innodb_monitor_reset": "",
"innodb_monitor_reset_all": "",
"innodb_old_blocks_pct": "37",
"innodb_old_blocks_time": "1000",
"innodb_online_alter_log_max_size": "134217728",
"innodb_open_files": "2000",
"innodb_optimize_fulltext_only": "OFF",
"innodb_page_cleaners": "1",
"innodb_page_size": "16384",
"innodb_print_all_deadlocks": "OFF",
"innodb_purge_batch_size": "300",
"innodb_purge_rseg_truncate_frequency": "128",
"innodb_purge_threads": "4",
"innodb_random_read_ahead": "OFF",
"innodb_read_ahead_threshold": "56",
"innodb_read_io_threads": "4",
"innodb_read_only": "OFF",
"innodb_replication_delay": "0",
"innodb_rollback_on_timeout": "OFF",
"innodb_rollback_segments": "128",
"innodb_sort_buffer_size": "1048576",
"innodb_spin_wait_delay": "6",
"innodb_stats_auto_recalc": "ON",
"innodb_stats_include_delete_marked": "OFF",
"innodb_stats_method": "nulls_equal",
"innodb_stats_on_metadata": "OFF",
"innodb_stats_persistent": "ON",
"innodb_stats_persistent_sample_pages": "20",
"innodb_stats_sample_pages": "8",
"innodb_stats_transient_sample_pages": "8",
"innodb_status_output": "OFF",
"innodb_status_output_locks": "OFF",
"innodb_strict_mode": "ON",
"innodb_support_xa": "ON",
"innodb_sync_array_size": "1",
"innodb_sync_spin_loops": "30",
"innodb_table_locks": "ON",
"innodb_temp_data_file_path": "ibtmp1:12M:autoextend",
"innodb_thread_concurrency": "0",
"innodb_thread_sleep_delay": "10000",
"innodb_tmpdir": "",
"innodb_undo_directory": "./",
"innodb_undo_log_truncate": "OFF",
"innodb_undo_logs": "128",
"innodb_undo_tablespaces": "0",
"innodb_use_native_aio": "OFF",
"innodb_version": "5.7.20",
"innodb_write_io_threads": "4",
"insert_id": "0",
"interactive_timeout": "28800",
"internal_tmp_disk_storage_engine": "InnoDB",
"join_buffer_size": "262144",
"keep_files_on_create": "OFF",
"key_buffer_size": "8388608",
"key_cache_age_threshold": "300",
"key_cache_block_size": "1024",
"key_cache_division_limit": "100",
"large_files_support": "ON",
"large_page_size": "0",
"large_pages": "OFF",
"last_insert_id": "0",
"lc_messages": "en_US",
"lc_messages_dir": "/usr/local/Cellar/mysql/5.7.20/share/mysql/",
"lc_time_names": "en_US",
"license": "GPL",
"local_infile": "ON",
"lock_wait_timeout": "31536000",
"locked_in_memory": "OFF",
"log_bin": "OFF",
"log_bin_basename": "",
"log_bin_index": "",
"log_bin_trust_function_creators": "OFF",
"log_bin_use_v1_row_events": "OFF",
"log_builtin_as_identified_by_password": "OFF",
"log_error": "./JiangShuli.local.err",
"log_error_verbosity": "3",
"log_output": "FILE",
"log_queries_not_using_indexes": "OFF",
"log_slave_updates": "OFF",
"log_slow_admin_statements": "OFF",
"log_slow_slave_statements": "OFF",
"log_statements_unsafe_for_binlog": "ON",
"log_syslog": "OFF",
"log_syslog_facility": "daemon",
"log_syslog_include_pid": "ON",
"log_syslog_tag": "",
"log_throttle_queries_not_using_indexes": "0",
"log_timestamps": "UTC",
"log_warnings": "2",
"long_query_time": "10.000000",
"low_priority_updates": "OFF",
"lower_case_file_system": "ON",
"lower_case_table_names": "2",
"master_info_repository": "FILE",
"master_verify_checksum": "OFF",
"max_allowed_packet": "4194304",
"max_binlog_cache_size": "18446744073709547520",
"max_binlog_size": "1073741824",
"max_binlog_stmt_cache_size": "18446744073709547520",
"max_connect_errors": "100",
"max_connections": "151",
"max_delayed_threads": "20",
"max_digest_length": "1024",
"max_error_count": "64",
"max_execution_time": "0",
"max_heap_table_size": "16777216",
"max_insert_delayed_threads": "20",
"max_join_size": "18446744073709551615",
"max_length_for_sort_data": "1024",
"max_points_in_geometry": "65536",
"max_prepared_stmt_count": "16382",
"max_relay_log_size": "0",
"max_seeks_for_key": "18446744073709551615",
"max_sort_length": "1024",
"max_sp_recursion_depth": "0",
"max_tmp_tables": "32",
"max_user_connections": "0",
"max_write_lock_count": "18446744073709551615",
"metadata_locks_cache_size": "1024",
"metadata_locks_hash_instances": "8",
"min_examined_row_limit": "0",
"multi_range_count": "256",
"myisam_data_pointer_size": "6",
"myisam_max_sort_file_size": "9223372036853727232",
"myisam_mmap_size": "18446744073709551615",
"myisam_recover_options": "OFF",
"myisam_repair_threads": "1",
"myisam_sort_buffer_size": "8388608",
"myisam_stats_method": "nulls_unequal",
"myisam_use_mmap": "OFF",
"mysql_native_password_proxy_users": "OFF",
"net_buffer_length": "16384",
"net_read_timeout": "30",
"net_retry_count": "10",
"net_write_timeout": "60",
"new": "OFF",
"ngram_token_size": "2",
"offline_mode": "OFF",
"old": "OFF",
"old_alter_table": "OFF",
"old_passwords": "0",
"open_files_limit": "5000",
"optimizer_prune_level": "1",
"optimizer_search_depth": "62",
"optimizer_switch": "index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on",
"optimizer_trace": "enabled=off,one_line=off",
"optimizer_trace_features": "greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on",
"optimizer_trace_limit": "1",
"optimizer_trace_max_mem_size": "16384",
"optimizer_trace_offset": "-1",
"parser_max_mem_size": "18446744073709551615",
"performance_schema": "ON",
"performance_schema_accounts_size": "-1",
"performance_schema_digests_size": "10000",
"performance_schema_events_stages_history_long_size": "10000",
"performance_schema_events_stages_history_size": "10",
"performance_schema_events_statements_history_long_size": "10000",
"performance_schema_events_statements_history_size": "10",
"performance_schema_events_transactions_history_long_size": "10000",
"performance_schema_events_transactions_history_size": "10",
"performance_schema_events_waits_history_long_size": "10000",
"performance_schema_events_waits_history_size": "10",
"performance_schema_hosts_size": "-1",
"performance_schema_max_cond_classes": "80",
"performance_schema_max_cond_instances": "-1",
"performance_schema_max_digest_length": "1024",
"performance_schema_max_file_classes": "80",
"performance_schema_max_file_handles": "32768",
"performance_schema_max_file_instances": "-1",
"performance_schema_max_index_stat": "-1",
"performance_schema_max_memory_classes": "320",
"performance_schema_max_metadata_locks": "-1",
"performance_schema_max_mutex_classes": "210",
"performance_schema_max_mutex_instances": "-1",
"performance_schema_max_prepared_statements_instances": "-1",
"performance_schema_max_program_instances": "-1",
"performance_schema_max_rwlock_classes": "40",
"performance_schema_max_rwlock_instances": "-1",
"performance_schema_max_socket_classes": "10",
"performance_schema_max_socket_instances": "-1",
"performance_schema_max_sql_text_length": "1024",
"performance_schema_max_stage_classes": "150",
"performance_schema_max_statement_classes": "193",
"performance_schema_max_statement_stack": "10",
"performance_schema_max_table_handles": "-1",
"performance_schema_max_table_instances": "-1",
"performance_schema_max_table_lock_stat": "-1",
"performance_schema_max_thread_classes": "50",
"performance_schema_max_thread_instances": "-1",
"performance_schema_session_connect_attrs_size": "512",
"performance_schema_setup_actors_size": "-1",
"performance_schema_setup_objects_size": "-1",
"performance_schema_users_size": "-1",
"pid_file": "/usr/local/var/mysql/JiangShuli.local.pid",
"plugin_dir": "/usr/local/Cellar/mysql/5.7.20/lib/plugin/",
"port": "3306",
"preload_buffer_size": "32768",
"profiling": "OFF",
"profiling_history_size": "15",
"protocol_version": "10",
"proxy_user": "",
"pseudo_slave_mode": "OFF",
"pseudo_thread_id": "3",
"query_alloc_block_size": "8192",
"query_cache_limit": "1048576",
"query_cache_min_res_unit": "4096",
"query_cache_size": "1048576",
"query_cache_type": "OFF",
"query_cache_wlock_invalidate": "OFF",
"query_prealloc_size": "8192",
"rand_seed1": "0",
"rand_seed2": "0",
"range_alloc_block_size": "4096",
"range_optimizer_max_mem_size": "8388608",
"rbr_exec_mode": "STRICT",
"read_buffer_size": "131072",
"read_only": "OFF",
"read_rnd_buffer_size": "262144",
"relay_log": "",
"relay_log_basename": "/usr/local/var/mysql/JiangShuli-relay-bin",
"relay_log_index": "/usr/local/var/mysql/JiangShuli-relay-bin.index",
"relay_log_info_file": "relay-log.info",
"relay_log_info_repository": "FILE",
"relay_log_purge": "ON",
"relay_log_recovery": "OFF",
"relay_log_space_limit": "0",
"report_host": "",
"report_password": "",
"report_port": "3306",
"report_user": "",
"require_secure_transport": "OFF",
"rpl_stop_slave_timeout": "31536000",
"secure_auth": "ON",
"secure_file_priv": "NULL",
"server_id": "0",
"server_id_bits": "32",
"server_uuid": "89286c98-d4ae-11e7-bb02-defe5cd105a2",
"session_track_gtids": "OFF",
"session_track_schema": "ON",
"session_track_state_change": "OFF",
"session_track_system_variables": "time_zone,autocommit,character_set_client,character_set_results,character_set_connection",
"session_track_transaction_info": "OFF",
"sha256_password_auto_generate_rsa_keys": "ON",
"sha256_password_private_key_path": "private_key.pem",
"sha256_password_proxy_users": "OFF",
"sha256_password_public_key_path": "public_key.pem",
"show_compatibility_56": "OFF",
"show_old_temporals": "OFF",
"skip_external_locking": "ON",
"skip_name_resolve": "OFF",
"skip_networking": "OFF",
"skip_show_database": "OFF",
"slave_allow_batching": "OFF",
"slave_checkpoint_group": "512",
"slave_checkpoint_period": "300",
"slave_compressed_protocol": "OFF",
"slave_exec_mode": "STRICT",
"slave_load_tmpdir": "/var/folders/hd/lw_b86v52pdfg323g_jn76k80000gn/T/",
"slave_max_allowed_packet": "1073741824",
"slave_net_timeout": "60",
"slave_parallel_type": "DATABASE",
"slave_parallel_workers": "0",
"slave_pending_jobs_size_max": "16777216",
"slave_preserve_commit_order": "OFF",
"slave_rows_search_algorithms": "TABLE_SCAN,INDEX_SCAN",
"slave_skip_errors": "OFF",
"slave_sql_verify_checksum": "ON",
"slave_transaction_retries": "10",
"slave_type_conversions": "",
"slow_launch_time": "2",
"slow_query_log": "OFF",
"slow_query_log_file": "/usr/local/var/mysql/JiangShuli-slow.log",
"socket": "/tmp/mysql.sock",
"sort_buffer_size": "262144",
"sql_auto_is_null": "OFF",
"sql_big_selects": "ON",
"sql_buffer_result": "OFF",
"sql_log_bin": "ON",
"sql_log_off": "OFF",
"sql_mode": "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
"sql_notes": "ON",
"sql_quote_show_create": "ON",
"sql_safe_updates": "OFF",
"sql_select_limit": "18446744073709551615",
"sql_slave_skip_counter": "0",
"sql_warnings": "OFF",
"ssl_ca": "ca.pem",
"ssl_capath": "",
"ssl_cert": "server-cert.pem",
"ssl_cipher": "",
"ssl_crl": "",
"ssl_crlpath": "",
"ssl_key": "server-key.pem",
"stored_program_cache": "256",
"super_read_only": "OFF",
"sync_binlog": "1",
"sync_frm": "ON",
"sync_master_info": "10000",
"sync_relay_log": "10000",
"sync_relay_log_info": "10000",
"system_time_zone": "EST",
"table_definition_cache": "1400",
"table_open_cache": "2000",
"table_open_cache_instances": "16",
"thread_cache_size": "9",
"thread_handling": "one-thread-per-connection",
"thread_stack": "262144",
"time_format": "%H:%i:%s",
"time_zone": "SYSTEM",
"timestamp": "1512532880.980241",
"tls_version": "TLSv1,TLSv1.1,TLSv1.2",
"tmp_table_size": "16777216",
"tmpdir": "/var/folders/hd/lw_b86v52pdfg323g_jn76k80000gn/T/",
"transaction_alloc_block_size": "8192",
"transaction_allow_batching": "OFF",
"transaction_isolation": "REPEATABLE-READ",
"transaction_prealloc_size": "4096",
"transaction_read_only": "OFF",
"transaction_write_set_extraction": "OFF",
"tx_isolation": "REPEATABLE-READ",
"tx_read_only": "OFF",
"unique_checks": "ON",
"updatable_views_with_limit": "YES",
"version": "5.7.20",
"version_comment": "Homebrew",
"version_compile_machine": "x86_64",
"version_compile_os": "osx10.12",
"wait_timeout": "28800",
"warning_count": "0"
}},
"local": null
}

View File

@ -0,0 +1,362 @@
{
"global": {"global": {
"aborted_clients": "0",
"aborted_connects": "0",
"binlog_cache_disk_use": "0",
"binlog_cache_use": "0",
"binlog_stmt_cache_disk_use": "0",
"binlog_stmt_cache_use": "0",
"bytes_received": "1227",
"bytes_sent": "29713",
"com_admin_commands": "0",
"com_alter_db": "0",
"com_alter_db_upgrade": "0",
"com_alter_event": "0",
"com_alter_function": "0",
"com_alter_instance": "0",
"com_alter_procedure": "0",
"com_alter_server": "0",
"com_alter_table": "0",
"com_alter_tablespace": "0",
"com_alter_user": "0",
"com_analyze": "0",
"com_assign_to_keycache": "0",
"com_begin": "0",
"com_binlog": "0",
"com_call_procedure": "0",
"com_change_db": "0",
"com_change_master": "0",
"com_change_repl_filter": "0",
"com_check": "0",
"com_checksum": "0",
"com_commit": "0",
"com_create_db": "0",
"com_create_event": "0",
"com_create_function": "0",
"com_create_index": "0",
"com_create_procedure": "0",
"com_create_server": "0",
"com_create_table": "0",
"com_create_trigger": "0",
"com_create_udf": "0",
"com_create_user": "0",
"com_create_view": "0",
"com_dealloc_sql": "0",
"com_delete": "0",
"com_delete_multi": "0",
"com_do": "0",
"com_drop_db": "0",
"com_drop_event": "0",
"com_drop_function": "0",
"com_drop_index": "0",
"com_drop_procedure": "0",
"com_drop_server": "0",
"com_drop_table": "0",
"com_drop_trigger": "0",
"com_drop_user": "0",
"com_drop_view": "0",
"com_empty_query": "0",
"com_execute_sql": "0",
"com_explain_other": "0",
"com_flush": "0",
"com_get_diagnostics": "0",
"com_grant": "0",
"com_group_replication_start": "0",
"com_group_replication_stop": "0",
"com_ha_close": "0",
"com_ha_open": "0",
"com_ha_read": "0",
"com_help": "0",
"com_insert": "0",
"com_insert_select": "0",
"com_install_plugin": "0",
"com_kill": "0",
"com_load": "0",
"com_lock_tables": "0",
"com_optimize": "0",
"com_preload_keys": "0",
"com_prepare_sql": "0",
"com_purge": "0",
"com_purge_before_date": "0",
"com_release_savepoint": "0",
"com_rename_table": "0",
"com_rename_user": "0",
"com_repair": "0",
"com_replace": "0",
"com_replace_select": "0",
"com_reset": "0",
"com_resignal": "0",
"com_revoke": "0",
"com_revoke_all": "0",
"com_rollback": "0",
"com_rollback_to_savepoint": "0",
"com_savepoint": "0",
"com_select": "2",
"com_set_option": "2",
"com_show_binlog_events": "0",
"com_show_binlogs": "0",
"com_show_charsets": "1",
"com_show_collations": "1",
"com_show_create_db": "0",
"com_show_create_event": "0",
"com_show_create_func": "0",
"com_show_create_proc": "0",
"com_show_create_table": "0",
"com_show_create_trigger": "0",
"com_show_create_user": "0",
"com_show_databases": "0",
"com_show_engine_logs": "0",
"com_show_engine_mutex": "0",
"com_show_engine_status": "0",
"com_show_errors": "0",
"com_show_events": "0",
"com_show_fields": "0",
"com_show_function_code": "0",
"com_show_function_status": "0",
"com_show_grants": "0",
"com_show_keys": "0",
"com_show_master_status": "0",
"com_show_open_tables": "0",
"com_show_plugins": "0",
"com_show_privileges": "0",
"com_show_procedure_code": "0",
"com_show_procedure_status": "0",
"com_show_processlist": "0",
"com_show_profile": "0",
"com_show_profiles": "0",
"com_show_relaylog_events": "0",
"com_show_slave_hosts": "0",
"com_show_slave_status": "0",
"com_show_status": "1",
"com_show_storage_engines": "0",
"com_show_table_status": "0",
"com_show_tables": "0",
"com_show_triggers": "0",
"com_show_variables": "2",
"com_show_warnings": "0",
"com_shutdown": "0",
"com_signal": "0",
"com_slave_start": "0",
"com_slave_stop": "0",
"com_stmt_close": "0",
"com_stmt_execute": "0",
"com_stmt_fetch": "0",
"com_stmt_prepare": "0",
"com_stmt_reprepare": "0",
"com_stmt_reset": "0",
"com_stmt_send_long_data": "0",
"com_truncate": "0",
"com_uninstall_plugin": "0",
"com_unlock_tables": "0",
"com_update": "0",
"com_update_multi": "0",
"com_xa_commit": "0",
"com_xa_end": "0",
"com_xa_prepare": "0",
"com_xa_recover": "0",
"com_xa_rollback": "0",
"com_xa_start": "0",
"compression": "OFF",
"connection_errors_accept": "0",
"connection_errors_internal": "0",
"connection_errors_max_connections": "0",
"connection_errors_peer_address": "0",
"connection_errors_select": "0",
"connection_errors_tcpwrap": "0",
"connections": "4",
"created_tmp_disk_tables": "0",
"created_tmp_files": "5",
"created_tmp_tables": "4",
"delayed_errors": "0",
"delayed_insert_threads": "0",
"delayed_writes": "0",
"flush_commands": "1",
"handler_commit": "0",
"handler_delete": "0",
"handler_discover": "0",
"handler_external_lock": "4",
"handler_mrr_init": "0",
"handler_prepare": "0",
"handler_read_first": "0",
"handler_read_key": "0",
"handler_read_last": "0",
"handler_read_next": "0",
"handler_read_prev": "0",
"handler_read_rnd": "0",
"handler_read_rnd_next": "2317",
"handler_rollback": "0",
"handler_savepoint": "0",
"handler_savepoint_rollback": "0",
"handler_update": "0",
"handler_write": "1287",
"innodb_available_undo_logs": "128",
"innodb_buffer_pool_bytes_data": "11288576",
"innodb_buffer_pool_bytes_dirty": "0",
"innodb_buffer_pool_dump_status": "Dumping of buffer pool not started",
"innodb_buffer_pool_load_status": "Buffer pool(s) load completed at 171205 23:01:14",
"innodb_buffer_pool_pages_data": "689",
"innodb_buffer_pool_pages_dirty": "0",
"innodb_buffer_pool_pages_flushed": "37",
"innodb_buffer_pool_pages_free": "7502",
"innodb_buffer_pool_pages_misc": "0",
"innodb_buffer_pool_pages_total": "8191",
"innodb_buffer_pool_read_ahead": "0",
"innodb_buffer_pool_read_ahead_evicted": "0",
"innodb_buffer_pool_read_ahead_rnd": "0",
"innodb_buffer_pool_read_requests": "1833",
"innodb_buffer_pool_reads": "655",
"innodb_buffer_pool_resize_status": "",
"innodb_buffer_pool_wait_free": "0",
"innodb_buffer_pool_write_requests": "515",
"innodb_data_fsyncs": "7",
"innodb_data_pending_fsyncs": "0",
"innodb_data_pending_reads": "0",
"innodb_data_pending_writes": "0",
"innodb_data_read": "10801664",
"innodb_data_reads": "683",
"innodb_data_writes": "54",
"innodb_data_written": "641024",
"innodb_dblwr_pages_written": "2",
"innodb_dblwr_writes": "1",
"innodb_log_waits": "0",
"innodb_log_write_requests": "0",
"innodb_log_writes": "2",
"innodb_num_open_files": "20",
"innodb_os_log_fsyncs": "4",
"innodb_os_log_pending_fsyncs": "0",
"innodb_os_log_pending_writes": "0",
"innodb_os_log_written": "1024",
"innodb_page_size": "16384",
"innodb_pages_created": "35",
"innodb_pages_read": "654",
"innodb_pages_written": "37",
"innodb_row_lock_current_waits": "0",
"innodb_row_lock_time": "0",
"innodb_row_lock_time_avg": "0",
"innodb_row_lock_time_max": "0",
"innodb_row_lock_waits": "0",
"innodb_rows_deleted": "0",
"innodb_rows_inserted": "0",
"innodb_rows_read": "8",
"innodb_rows_updated": "0",
"innodb_truncated_status_writes": "0",
"key_blocks_not_flushed": "0",
"key_blocks_unused": "6695",
"key_blocks_used": "3",
"key_read_requests": "6",
"key_reads": "3",
"key_write_requests": "0",
"key_writes": "0",
"last_query_cost": "0.000000",
"last_query_partial_plans": "2",
"locked_connects": "0",
"max_execution_time_exceeded": "0",
"max_execution_time_set": "0",
"max_execution_time_set_failed": "0",
"max_used_connections": "1",
"max_used_connections_time": "2017-12-05 23:01:20",
"not_flushed_delayed_rows": "0",
"ongoing_anonymous_transaction_count": "0",
"open_files": "14",
"open_streams": "0",
"open_table_definitions": "228",
"open_tables": "100",
"opened_files": "357",
"opened_table_definitions": "0",
"opened_tables": "1",
"performance_schema_accounts_lost": "0",
"performance_schema_cond_classes_lost": "0",
"performance_schema_cond_instances_lost": "0",
"performance_schema_digest_lost": "0",
"performance_schema_file_classes_lost": "0",
"performance_schema_file_handles_lost": "0",
"performance_schema_file_instances_lost": "0",
"performance_schema_hosts_lost": "0",
"performance_schema_index_stat_lost": "0",
"performance_schema_locker_lost": "0",
"performance_schema_memory_classes_lost": "0",
"performance_schema_metadata_lock_lost": "0",
"performance_schema_mutex_classes_lost": "0",
"performance_schema_mutex_instances_lost": "0",
"performance_schema_nested_statement_lost": "0",
"performance_schema_prepared_statements_lost": "0",
"performance_schema_program_lost": "0",
"performance_schema_rwlock_classes_lost": "0",
"performance_schema_rwlock_instances_lost": "0",
"performance_schema_session_connect_attrs_lost": "0",
"performance_schema_socket_classes_lost": "0",
"performance_schema_socket_instances_lost": "0",
"performance_schema_stage_classes_lost": "0",
"performance_schema_statement_classes_lost": "0",
"performance_schema_table_handles_lost": "0",
"performance_schema_table_instances_lost": "0",
"performance_schema_table_lock_stat_lost": "0",
"performance_schema_thread_classes_lost": "0",
"performance_schema_thread_instances_lost": "0",
"performance_schema_users_lost": "0",
"prepared_stmt_count": "0",
"qcache_free_blocks": "1",
"qcache_free_memory": "1031832",
"qcache_hits": "0",
"qcache_inserts": "0",
"qcache_lowmem_prunes": "0",
"qcache_not_cached": "2",
"qcache_queries_in_cache": "0",
"qcache_total_blocks": "1",
"queries": "11",
"questions": "9",
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApBHbP3hArKih5/R7ffR9\n8ZUpK1VCGmD/7PCjOMQiChcb+KYr7PdgokSw/D95Ua84u36bvRHiB+7a6uGSYHZv\nOMB/h+zP0pSdmucCF5CNRuFgA6D0SHP7lql8O1t4x0dBYGkI0bkUgCxZeeRZshLm\nG6rGMXh79j3WlZP2Ny87TbCcgpbvW0tYxM9/pc1em/8y3+hDd3V0tVSBk36TEuJd\nJRr8Of09H0paSNjPdnvaZYdpXyhZUD0frhLSfnCj52OqFcypeelArR/ntEQffqif\n7CQvVm06mD5a5BlflgIVvnCLSH5pTBcn5tnPACFwbxBMYeRzfmRu+TFqFMBaxEhl\nVQIDAQAB\n-----END PUBLIC KEY-----\n",
"select_full_join": "0",
"select_full_range_join": "0",
"select_range": "0",
"select_range_check": "0",
"select_scan": "6",
"slave_open_temp_tables": "0",
"slow_launch_threads": "0",
"slow_queries": "0",
"sort_merge_passes": "0",
"sort_range": "0",
"sort_rows": "0",
"sort_scan": "0",
"ssl_accept_renegotiates": "0",
"ssl_accepts": "0",
"ssl_callback_cache_hits": "0",
"ssl_cipher": "",
"ssl_cipher_list": "",
"ssl_client_connects": "0",
"ssl_connect_renegotiates": "0",
"ssl_ctx_verify_depth": "18446744073709551615",
"ssl_ctx_verify_mode": "5",
"ssl_default_timeout": "0",
"ssl_finished_accepts": "0",
"ssl_finished_connects": "0",
"ssl_server_not_after": "Nov 27 02:39:35 2027 GMT",
"ssl_server_not_before": "Nov 29 02:39:35 2017 GMT",
"ssl_session_cache_hits": "0",
"ssl_session_cache_misses": "0",
"ssl_session_cache_mode": "SERVER",
"ssl_session_cache_overflows": "0",
"ssl_session_cache_size": "128",
"ssl_session_cache_timeouts": "0",
"ssl_sessions_reused": "0",
"ssl_used_session_cache_entries": "0",
"ssl_verify_depth": "0",
"ssl_verify_mode": "0",
"ssl_version": "",
"table_locks_immediate": "101",
"table_locks_waited": "0",
"table_open_cache_hits": "1",
"table_open_cache_misses": "1",
"table_open_cache_overflows": "0",
"tc_log_max_pages_used": "0",
"tc_log_page_size": "0",
"tc_log_page_waits": "0",
"threads_cached": "0",
"threads_connected": "1",
"threads_created": "1",
"threads_running": "1",
"uptime": "8",
"uptime_since_flush_status": "8"
}},
"local": null
}

View File

@ -0,0 +1,362 @@
{
"global": {"global": {
"aborted_clients": "0",
"aborted_connects": "0",
"binlog_cache_disk_use": "0",
"binlog_cache_use": "0",
"binlog_stmt_cache_disk_use": "0",
"binlog_stmt_cache_use": "0",
"bytes_received": "1227",
"bytes_sent": "29713",
"com_admin_commands": "0",
"com_alter_db": "0",
"com_alter_db_upgrade": "0",
"com_alter_event": "0",
"com_alter_function": "0",
"com_alter_instance": "0",
"com_alter_procedure": "0",
"com_alter_server": "0",
"com_alter_table": "0",
"com_alter_tablespace": "0",
"com_alter_user": "0",
"com_analyze": "0",
"com_assign_to_keycache": "0",
"com_begin": "0",
"com_binlog": "0",
"com_call_procedure": "0",
"com_change_db": "0",
"com_change_master": "0",
"com_change_repl_filter": "0",
"com_check": "0",
"com_checksum": "0",
"com_commit": "0",
"com_create_db": "0",
"com_create_event": "0",
"com_create_function": "0",
"com_create_index": "0",
"com_create_procedure": "0",
"com_create_server": "0",
"com_create_table": "0",
"com_create_trigger": "0",
"com_create_udf": "0",
"com_create_user": "0",
"com_create_view": "0",
"com_dealloc_sql": "0",
"com_delete": "0",
"com_delete_multi": "0",
"com_do": "0",
"com_drop_db": "0",
"com_drop_event": "0",
"com_drop_function": "0",
"com_drop_index": "0",
"com_drop_procedure": "0",
"com_drop_server": "0",
"com_drop_table": "0",
"com_drop_trigger": "0",
"com_drop_user": "0",
"com_drop_view": "0",
"com_empty_query": "0",
"com_execute_sql": "0",
"com_explain_other": "0",
"com_flush": "0",
"com_get_diagnostics": "0",
"com_grant": "0",
"com_group_replication_start": "0",
"com_group_replication_stop": "0",
"com_ha_close": "0",
"com_ha_open": "0",
"com_ha_read": "0",
"com_help": "0",
"com_insert": "0",
"com_insert_select": "0",
"com_install_plugin": "0",
"com_kill": "0",
"com_load": "0",
"com_lock_tables": "0",
"com_optimize": "0",
"com_preload_keys": "0",
"com_prepare_sql": "0",
"com_purge": "0",
"com_purge_before_date": "0",
"com_release_savepoint": "0",
"com_rename_table": "0",
"com_rename_user": "0",
"com_repair": "0",
"com_replace": "0",
"com_replace_select": "0",
"com_reset": "0",
"com_resignal": "0",
"com_revoke": "0",
"com_revoke_all": "0",
"com_rollback": "0",
"com_rollback_to_savepoint": "0",
"com_savepoint": "0",
"com_select": "2",
"com_set_option": "2",
"com_show_binlog_events": "0",
"com_show_binlogs": "0",
"com_show_charsets": "1",
"com_show_collations": "1",
"com_show_create_db": "0",
"com_show_create_event": "0",
"com_show_create_func": "0",
"com_show_create_proc": "0",
"com_show_create_table": "0",
"com_show_create_trigger": "0",
"com_show_create_user": "0",
"com_show_databases": "0",
"com_show_engine_logs": "0",
"com_show_engine_mutex": "0",
"com_show_engine_status": "0",
"com_show_errors": "0",
"com_show_events": "0",
"com_show_fields": "0",
"com_show_function_code": "0",
"com_show_function_status": "0",
"com_show_grants": "0",
"com_show_keys": "0",
"com_show_master_status": "0",
"com_show_open_tables": "0",
"com_show_plugins": "0",
"com_show_privileges": "0",
"com_show_procedure_code": "0",
"com_show_procedure_status": "0",
"com_show_processlist": "0",
"com_show_profile": "0",
"com_show_profiles": "0",
"com_show_relaylog_events": "0",
"com_show_slave_hosts": "0",
"com_show_slave_status": "0",
"com_show_status": "1",
"com_show_storage_engines": "0",
"com_show_table_status": "0",
"com_show_tables": "0",
"com_show_triggers": "0",
"com_show_variables": "2",
"com_show_warnings": "0",
"com_shutdown": "0",
"com_signal": "0",
"com_slave_start": "0",
"com_slave_stop": "0",
"com_stmt_close": "0",
"com_stmt_execute": "0",
"com_stmt_fetch": "0",
"com_stmt_prepare": "0",
"com_stmt_reprepare": "0",
"com_stmt_reset": "0",
"com_stmt_send_long_data": "0",
"com_truncate": "0",
"com_uninstall_plugin": "0",
"com_unlock_tables": "0",
"com_update": "0",
"com_update_multi": "0",
"com_xa_commit": "0",
"com_xa_end": "0",
"com_xa_prepare": "0",
"com_xa_recover": "0",
"com_xa_rollback": "0",
"com_xa_start": "0",
"compression": "OFF",
"connection_errors_accept": "0",
"connection_errors_internal": "0",
"connection_errors_max_connections": "0",
"connection_errors_peer_address": "0",
"connection_errors_select": "0",
"connection_errors_tcpwrap": "0",
"connections": "4",
"created_tmp_disk_tables": "0",
"created_tmp_files": "5",
"created_tmp_tables": "4",
"delayed_errors": "0",
"delayed_insert_threads": "0",
"delayed_writes": "0",
"flush_commands": "1",
"handler_commit": "0",
"handler_delete": "0",
"handler_discover": "0",
"handler_external_lock": "4",
"handler_mrr_init": "0",
"handler_prepare": "0",
"handler_read_first": "0",
"handler_read_key": "0",
"handler_read_last": "0",
"handler_read_next": "0",
"handler_read_prev": "0",
"handler_read_rnd": "0",
"handler_read_rnd_next": "2317",
"handler_rollback": "0",
"handler_savepoint": "0",
"handler_savepoint_rollback": "0",
"handler_update": "0",
"handler_write": "1287",
"innodb_available_undo_logs": "128",
"innodb_buffer_pool_bytes_data": "11288576",
"innodb_buffer_pool_bytes_dirty": "0",
"innodb_buffer_pool_dump_status": "Dumping of buffer pool not started",
"innodb_buffer_pool_load_status": "Buffer pool(s) load completed at 171205 23:01:14",
"innodb_buffer_pool_pages_data": "689",
"innodb_buffer_pool_pages_dirty": "0",
"innodb_buffer_pool_pages_flushed": "37",
"innodb_buffer_pool_pages_free": "7502",
"innodb_buffer_pool_pages_misc": "0",
"innodb_buffer_pool_pages_total": "8191",
"innodb_buffer_pool_read_ahead": "0",
"innodb_buffer_pool_read_ahead_evicted": "0",
"innodb_buffer_pool_read_ahead_rnd": "0",
"innodb_buffer_pool_read_requests": "1833",
"innodb_buffer_pool_reads": "655",
"innodb_buffer_pool_resize_status": "",
"innodb_buffer_pool_wait_free": "0",
"innodb_buffer_pool_write_requests": "515",
"innodb_data_fsyncs": "7",
"innodb_data_pending_fsyncs": "0",
"innodb_data_pending_reads": "0",
"innodb_data_pending_writes": "0",
"innodb_data_read": "10801664",
"innodb_data_reads": "683",
"innodb_data_writes": "54",
"innodb_data_written": "641024",
"innodb_dblwr_pages_written": "2",
"innodb_dblwr_writes": "1",
"innodb_log_waits": "0",
"innodb_log_write_requests": "0",
"innodb_log_writes": "2",
"innodb_num_open_files": "20",
"innodb_os_log_fsyncs": "4",
"innodb_os_log_pending_fsyncs": "0",
"innodb_os_log_pending_writes": "0",
"innodb_os_log_written": "1024",
"innodb_page_size": "16384",
"innodb_pages_created": "35",
"innodb_pages_read": "654",
"innodb_pages_written": "37",
"innodb_row_lock_current_waits": "0",
"innodb_row_lock_time": "0",
"innodb_row_lock_time_avg": "0",
"innodb_row_lock_time_max": "0",
"innodb_row_lock_waits": "0",
"innodb_rows_deleted": "0",
"innodb_rows_inserted": "0",
"innodb_rows_read": "8",
"innodb_rows_updated": "0",
"innodb_truncated_status_writes": "0",
"key_blocks_not_flushed": "0",
"key_blocks_unused": "6695",
"key_blocks_used": "3",
"key_read_requests": "6",
"key_reads": "3",
"key_write_requests": "0",
"key_writes": "0",
"last_query_cost": "0.000000",
"last_query_partial_plans": "2",
"locked_connects": "0",
"max_execution_time_exceeded": "0",
"max_execution_time_set": "0",
"max_execution_time_set_failed": "0",
"max_used_connections": "1",
"max_used_connections_time": "2017-12-05 23:01:20",
"not_flushed_delayed_rows": "0",
"ongoing_anonymous_transaction_count": "0",
"open_files": "14",
"open_streams": "0",
"open_table_definitions": "228",
"open_tables": "100",
"opened_files": "357",
"opened_table_definitions": "0",
"opened_tables": "1",
"performance_schema_accounts_lost": "0",
"performance_schema_cond_classes_lost": "0",
"performance_schema_cond_instances_lost": "0",
"performance_schema_digest_lost": "0",
"performance_schema_file_classes_lost": "0",
"performance_schema_file_handles_lost": "0",
"performance_schema_file_instances_lost": "0",
"performance_schema_hosts_lost": "0",
"performance_schema_index_stat_lost": "0",
"performance_schema_locker_lost": "0",
"performance_schema_memory_classes_lost": "0",
"performance_schema_metadata_lock_lost": "0",
"performance_schema_mutex_classes_lost": "0",
"performance_schema_mutex_instances_lost": "0",
"performance_schema_nested_statement_lost": "0",
"performance_schema_prepared_statements_lost": "0",
"performance_schema_program_lost": "0",
"performance_schema_rwlock_classes_lost": "0",
"performance_schema_rwlock_instances_lost": "0",
"performance_schema_session_connect_attrs_lost": "0",
"performance_schema_socket_classes_lost": "0",
"performance_schema_socket_instances_lost": "0",
"performance_schema_stage_classes_lost": "0",
"performance_schema_statement_classes_lost": "0",
"performance_schema_table_handles_lost": "0",
"performance_schema_table_instances_lost": "0",
"performance_schema_table_lock_stat_lost": "0",
"performance_schema_thread_classes_lost": "0",
"performance_schema_thread_instances_lost": "0",
"performance_schema_users_lost": "0",
"prepared_stmt_count": "0",
"qcache_free_blocks": "1",
"qcache_free_memory": "1031832",
"qcache_hits": "0",
"qcache_inserts": "0",
"qcache_lowmem_prunes": "0",
"qcache_not_cached": "2",
"qcache_queries_in_cache": "0",
"qcache_total_blocks": "1",
"queries": "11",
"questions": "9",
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApBHbP3hArKih5/R7ffR9\n8ZUpK1VCGmD/7PCjOMQiChcb+KYr7PdgokSw/D95Ua84u36bvRHiB+7a6uGSYHZv\nOMB/h+zP0pSdmucCF5CNRuFgA6D0SHP7lql8O1t4x0dBYGkI0bkUgCxZeeRZshLm\nG6rGMXh79j3WlZP2Ny87TbCcgpbvW0tYxM9/pc1em/8y3+hDd3V0tVSBk36TEuJd\nJRr8Of09H0paSNjPdnvaZYdpXyhZUD0frhLSfnCj52OqFcypeelArR/ntEQffqif\n7CQvVm06mD5a5BlflgIVvnCLSH5pTBcn5tnPACFwbxBMYeRzfmRu+TFqFMBaxEhl\nVQIDAQAB\n-----END PUBLIC KEY-----\n",
"select_full_join": "0",
"select_full_range_join": "0",
"select_range": "0",
"select_range_check": "0",
"select_scan": "6",
"slave_open_temp_tables": "0",
"slow_launch_threads": "0",
"slow_queries": "0",
"sort_merge_passes": "0",
"sort_range": "0",
"sort_rows": "0",
"sort_scan": "0",
"ssl_accept_renegotiates": "0",
"ssl_accepts": "0",
"ssl_callback_cache_hits": "0",
"ssl_cipher": "",
"ssl_cipher_list": "",
"ssl_client_connects": "0",
"ssl_connect_renegotiates": "0",
"ssl_ctx_verify_depth": "18446744073709551615",
"ssl_ctx_verify_mode": "5",
"ssl_default_timeout": "0",
"ssl_finished_accepts": "0",
"ssl_finished_connects": "0",
"ssl_server_not_after": "Nov 27 02:39:35 2027 GMT",
"ssl_server_not_before": "Nov 29 02:39:35 2017 GMT",
"ssl_session_cache_hits": "0",
"ssl_session_cache_misses": "0",
"ssl_session_cache_mode": "SERVER",
"ssl_session_cache_overflows": "0",
"ssl_session_cache_size": "128",
"ssl_session_cache_timeouts": "0",
"ssl_sessions_reused": "0",
"ssl_used_session_cache_entries": "0",
"ssl_verify_depth": "0",
"ssl_verify_mode": "0",
"ssl_version": "",
"table_locks_immediate": "101",
"table_locks_waited": "0",
"table_open_cache_hits": "1",
"table_open_cache_misses": "1",
"table_open_cache_overflows": "0",
"tc_log_max_pages_used": "0",
"tc_log_page_size": "0",
"tc_log_page_waits": "0",
"threads_cached": "0",
"threads_connected": "1",
"threads_created": "1",
"threads_running": "1",
"uptime": "8",
"uptime_since_flush_status": "8"
}},
"local": null
}

View File

@ -0,0 +1,8 @@
{
"start_time": 1535653369274,
"end_time": 1535653559607,
"observation_time": 190,
"database_type": "mysql",
"database_version": "5.7.20",
"workload_name": "workload_name"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
{
"start_time": 1563264802685,
"end_time": 1563265002918,
"observation_time": 200,
"database_type": "oracle",
"database_version": "19.0.0.0.0",
"workload_name": "tpcc"
}

View File

@ -0,0 +1,274 @@
{
"global": {"global": {
"DateStyle": "ISO, MDY",
"IntervalStyle": "postgres",
"TimeZone": "America/New_York",
"allow_system_table_mods": "off",
"application_name": "",
"archive_command": "(disabled)",
"archive_mode": "off",
"archive_timeout": "0",
"array_nulls": "on",
"authentication_timeout": "1min",
"autovacuum": "on",
"autovacuum_analyze_scale_factor": "0.1",
"autovacuum_analyze_threshold": "50",
"autovacuum_freeze_max_age": "200000000",
"autovacuum_max_workers": "3",
"autovacuum_multixact_freeze_max_age": "400000000",
"autovacuum_naptime": "1min",
"autovacuum_vacuum_cost_delay": "20ms",
"autovacuum_vacuum_cost_limit": "-1",
"autovacuum_vacuum_scale_factor": "0.2",
"autovacuum_vacuum_threshold": "50",
"autovacuum_work_mem": "-1",
"backend_flush_after": "0",
"backslash_quote": "safe_encoding",
"bgwriter_delay": "200ms",
"bgwriter_flush_after": "0",
"bgwriter_lru_maxpages": "100",
"bgwriter_lru_multiplier": "2",
"block_size": "8192",
"bonjour": "off",
"bonjour_name": "",
"bytea_output": "hex",
"check_function_bodies": "on",
"checkpoint_completion_target": "0.5",
"checkpoint_flush_after": "0",
"checkpoint_timeout": "5min",
"checkpoint_warning": "30s",
"client_encoding": "UTF8",
"client_min_messages": "notice",
"cluster_name": "",
"commit_delay": "0",
"commit_siblings": "5",
"config_file": "/Users/MacadamiaKitten/Desktop/psql_db/postgresql.conf",
"constraint_exclusion": "partition",
"cpu_index_tuple_cost": "0.005",
"cpu_operator_cost": "0.0025",
"cpu_tuple_cost": "0.01",
"cursor_tuple_fraction": "0.1",
"data_checksums": "off",
"data_directory": "/Users/MacadamiaKitten/Desktop/psql_db",
"db_user_namespace": "off",
"deadlock_timeout": "1s",
"debug_assertions": "off",
"debug_pretty_print": "on",
"debug_print_parse": "off",
"debug_print_plan": "off",
"debug_print_rewritten": "off",
"default_statistics_target": "100",
"default_tablespace": "",
"default_text_search_config": "pg_catalog.english",
"default_transaction_deferrable": "off",
"default_transaction_isolation": "read committed",
"default_transaction_read_only": "off",
"default_with_oids": "off",
"dynamic_library_path": "$libdir",
"dynamic_shared_memory_type": "posix",
"effective_cache_size": "4GB",
"effective_io_concurrency": "0",
"enable_bitmapscan": "on",
"enable_gathermerge": "on",
"enable_hashagg": "on",
"enable_hashjoin": "on",
"enable_indexonlyscan": "on",
"enable_indexscan": "on",
"enable_material": "on",
"enable_mergejoin": "on",
"enable_nestloop": "on",
"enable_seqscan": "on",
"enable_sort": "on",
"enable_tidscan": "on",
"escape_string_warning": "on",
"event_source": "PostgreSQL",
"exit_on_error": "off",
"external_pid_file": "",
"extra_float_digits": "3",
"force_parallel_mode": "off",
"from_collapse_limit": "8",
"fsync": "on",
"full_page_writes": "on",
"geqo": "on",
"geqo_effort": "5",
"geqo_generations": "0",
"geqo_pool_size": "0",
"geqo_seed": "0",
"geqo_selection_bias": "2",
"geqo_threshold": "12",
"gin_fuzzy_search_limit": "0",
"gin_pending_list_limit": "4MB",
"hba_file": "/Users/MacadamiaKitten/Desktop/psql_db/pg_hba.conf",
"hot_standby": "on",
"hot_standby_feedback": "off",
"huge_pages": "try",
"ident_file": "/Users/MacadamiaKitten/Desktop/psql_db/pg_ident.conf",
"idle_in_transaction_session_timeout": "0",
"ignore_checksum_failure": "off",
"ignore_system_indexes": "off",
"integer_datetimes": "on",
"join_collapse_limit": "8",
"krb_caseins_users": "off",
"krb_server_keyfile": "FILE:/usr/local/etc/postgresql/krb5.keytab",
"lc_collate": "en_US.UTF-8",
"lc_ctype": "en_US.UTF-8",
"lc_messages": "en_US.UTF-8",
"lc_monetary": "en_US.UTF-8",
"lc_numeric": "en_US.UTF-8",
"lc_time": "en_US.UTF-8",
"listen_addresses": "localhost",
"lo_compat_privileges": "off",
"local_preload_libraries": "",
"lock_timeout": "0",
"log_autovacuum_min_duration": "-1",
"log_checkpoints": "off",
"log_connections": "off",
"log_destination": "stderr",
"log_directory": "log",
"log_disconnections": "off",
"log_duration": "off",
"log_error_verbosity": "default",
"log_executor_stats": "off",
"log_file_mode": "0600",
"log_filename": "postgresql-%Y-%m-%d_%H%M%S.log",
"log_hostname": "off",
"log_line_prefix": "%m [%p] ",
"log_lock_waits": "off",
"log_min_duration_statement": "-1",
"log_min_error_statement": "error",
"log_min_messages": "warning",
"log_parser_stats": "off",
"log_planner_stats": "off",
"log_replication_commands": "off",
"log_rotation_age": "1d",
"log_rotation_size": "10MB",
"log_statement": "none",
"log_statement_stats": "off",
"log_temp_files": "-1",
"log_timezone": "US/Eastern",
"log_truncate_on_rotation": "off",
"logging_collector": "off",
"maintenance_work_mem": "64MB",
"max_connections": "100",
"max_files_per_process": "1000",
"max_function_args": "100",
"max_identifier_length": "63",
"max_index_keys": "32",
"max_locks_per_transaction": "64",
"max_logical_replication_workers": "4",
"max_parallel_workers": "8",
"max_parallel_workers_per_gather": "2",
"max_pred_locks_per_page": "2",
"max_pred_locks_per_relation": "-2",
"max_pred_locks_per_transaction": "64",
"max_prepared_transactions": "0",
"max_replication_slots": "10",
"max_stack_depth": "2MB",
"max_standby_archive_delay": "30s",
"max_standby_streaming_delay": "30s",
"max_sync_workers_per_subscription": "2",
"max_wal_senders": "10",
"max_wal_size": "1GB",
"max_worker_processes": "8",
"min_parallel_index_scan_size": "512kB",
"min_parallel_table_scan_size": "8MB",
"min_wal_size": "80MB",
"old_snapshot_threshold": "-1",
"operator_precedence_warning": "off",
"parallel_setup_cost": "1000",
"parallel_tuple_cost": "0.1",
"password_encryption": "md5",
"port": "5432",
"post_auth_delay": "0",
"pre_auth_delay": "0",
"quote_all_identifiers": "off",
"random_page_cost": "4",
"replacement_sort_tuples": "150000",
"restart_after_crash": "on",
"row_security": "on",
"search_path": "\"$user\", public",
"segment_size": "1GB",
"seq_page_cost": "1",
"server_encoding": "UTF8",
"server_version": "10.1",
"server_version_num": "100001",
"session_preload_libraries": "",
"session_replication_role": "origin",
"shared_buffers": "128MB",
"shared_preload_libraries": "",
"ssl": "off",
"ssl_ca_file": "",
"ssl_cert_file": "server.crt",
"ssl_ciphers": "HIGH:MEDIUM:+3DES:!aNULL",
"ssl_crl_file": "",
"ssl_dh_params_file": "",
"ssl_ecdh_curve": "prime256v1",
"ssl_key_file": "server.key",
"ssl_prefer_server_ciphers": "on",
"standard_conforming_strings": "on",
"statement_timeout": "0",
"stats_temp_directory": "pg_stat_tmp",
"superuser_reserved_connections": "3",
"synchronize_seqscans": "on",
"synchronous_commit": "on",
"synchronous_standby_names": "",
"syslog_facility": "local0",
"syslog_ident": "postgres",
"syslog_sequence_numbers": "on",
"syslog_split_messages": "on",
"tcp_keepalives_count": "8",
"tcp_keepalives_idle": "7200",
"tcp_keepalives_interval": "75",
"temp_buffers": "8MB",
"temp_file_limit": "-1",
"temp_tablespaces": "",
"timezone_abbreviations": "Default",
"trace_notify": "off",
"trace_recovery_messages": "log",
"trace_sort": "off",
"track_activities": "on",
"track_activity_query_size": "1024",
"track_commit_timestamp": "off",
"track_counts": "on",
"track_functions": "none",
"track_io_timing": "off",
"transaction_deferrable": "off",
"transaction_isolation": "read committed",
"transaction_read_only": "off",
"transform_null_equals": "off",
"unix_socket_directories": "/tmp",
"unix_socket_group": "",
"unix_socket_permissions": "0777",
"update_process_title": "on",
"vacuum_cost_delay": "0",
"vacuum_cost_limit": "200",
"vacuum_cost_page_dirty": "20",
"vacuum_cost_page_hit": "1",
"vacuum_cost_page_miss": "10",
"vacuum_defer_cleanup_age": "0",
"vacuum_freeze_min_age": "50000000",
"vacuum_freeze_table_age": "150000000",
"vacuum_multixact_freeze_min_age": "5000000",
"vacuum_multixact_freeze_table_age": "150000000",
"wal_block_size": "8192",
"wal_buffers": "4MB",
"wal_compression": "off",
"wal_consistency_checking": "",
"wal_keep_segments": "0",
"wal_level": "replica",
"wal_log_hints": "off",
"wal_receiver_status_interval": "10s",
"wal_receiver_timeout": "1min",
"wal_retrieve_retry_interval": "5s",
"wal_segment_size": "16MB",
"wal_sender_timeout": "1min",
"wal_sync_method": "open_datasync",
"wal_writer_delay": "200ms",
"wal_writer_flush_after": "1MB",
"work_mem": "4MB",
"xmlbinary": "base64",
"xmloption": "content",
"zero_damaged_pages": "off"
}},
"local": null
}

View File

@ -0,0 +1,582 @@
{
"global": {
"pg_stat_archiver": {
"archived_count": "0",
"failed_count": "0",
"stats_reset": "2017-11-10 10:59:47.397075-05"
},
"pg_stat_bgwriter": {
"buffers_alloc": "87670",
"buffers_backend": "81032",
"buffers_backend_fsync": "0",
"buffers_checkpoint": "33250",
"buffers_clean": "49590",
"checkpoint_sync_time": "19",
"checkpoint_write_time": "597851",
"checkpoints_req": "2",
"checkpoints_timed": "1277",
"maxwritten_clean": "325",
"stats_reset": "2017-11-10 10:59:47.397075-05"
}
},
"local": {
"table": {
"pg_stat_user_tables": {
"history": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"last_autoanalyze": "2017-11-20 15:59:02.567618-05",
"n_dead_tup": "0",
"n_live_tup": "60854",
"n_mod_since_analyze": "854",
"n_tup_del": "0",
"n_tup_hot_upd": "0",
"n_tup_ins": "60854",
"n_tup_upd": "0",
"relid": "16536",
"relname": "history",
"schemaname": "public",
"seq_scan": "2",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"warehouse": {
"analyze_count": "0",
"autoanalyze_count": "2",
"autovacuum_count": "2",
"idx_scan": "202634",
"idx_tup_fetch": "202634",
"last_autoanalyze": "2017-11-20 19:23:34.236294-05",
"last_autovacuum": "2017-11-20 19:23:34.235793-05",
"n_dead_tup": "0",
"n_live_tup": "2",
"n_mod_since_analyze": "0",
"n_tup_del": "0",
"n_tup_hot_upd": "854",
"n_tup_ins": "2",
"n_tup_upd": "854",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"stock": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "644561",
"idx_tup_fetch": "644561",
"last_autoanalyze": "2017-11-20 15:59:01.368483-05",
"n_dead_tup": "4364",
"n_live_tup": "200000",
"n_mod_since_analyze": "8901",
"n_tup_del": "0",
"n_tup_hot_upd": "5305",
"n_tup_ins": "200000",
"n_tup_upd": "8901",
"relid": "16523",
"relname": "stock",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"customer": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "125261",
"idx_tup_fetch": "85299628",
"last_autoanalyze": "2017-11-20 15:59:18.824212-05",
"n_dead_tup": "1510",
"n_live_tup": "60000",
"n_mod_since_analyze": "1594",
"n_tup_del": "0",
"n_tup_hot_upd": "262",
"n_tup_ins": "60000",
"n_tup_upd": "1594",
"relid": "16540",
"relname": "customer",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"order_line": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "1655",
"idx_tup_fetch": "33762",
"last_autoanalyze": "2017-11-20 16:00:11.017507-05",
"n_dead_tup": "2550",
"n_live_tup": "608373",
"n_mod_since_analyze": "16230",
"n_tup_del": "0",
"n_tup_hot_upd": "5393",
"n_tup_ins": "608373",
"n_tup_upd": "7329",
"relid": "16513",
"relname": "order_line",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"oorder": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "627652",
"idx_tup_fetch": "627652",
"last_autoanalyze": "2017-11-20 15:59:54.690984-05",
"n_dead_tup": "117",
"n_live_tup": "60889",
"n_mod_since_analyze": "1629",
"n_tup_del": "0",
"n_tup_hot_upd": "662",
"n_tup_ins": "60900",
"n_tup_upd": "740",
"relid": "16528",
"relname": "oorder",
"schemaname": "public",
"seq_scan": "4",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"new_order": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "1481",
"idx_tup_fetch": "1480",
"last_autoanalyze": "2017-11-20 16:00:11.217111-05",
"n_dead_tup": "751",
"n_live_tup": "16964",
"n_mod_since_analyze": "1629",
"n_tup_del": "740",
"n_tup_hot_upd": "0",
"n_tup_ins": "17715",
"n_tup_upd": "0",
"relid": "16518",
"relname": "new_order",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"district": {
"analyze_count": "0",
"autoanalyze_count": "2",
"autovacuum_count": "0",
"idx_scan": "122234",
"idx_tup_fetch": "122234",
"last_autoanalyze": "2017-11-20 19:23:34.201509-05",
"n_dead_tup": "33",
"n_live_tup": "20",
"n_mod_since_analyze": "0",
"n_tup_del": "0",
"n_tup_hot_upd": "1754",
"n_tup_ins": "20",
"n_tup_upd": "1754",
"relid": "16549",
"relname": "district",
"schemaname": "public",
"seq_scan": "2221",
"seq_tup_read": "41522",
"vacuum_count": "0"
},
"item": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "209020",
"idx_tup_fetch": "209009",
"last_autoanalyze": "2017-11-20 15:59:26.613728-05",
"n_dead_tup": "0",
"n_live_tup": "102000",
"n_mod_since_analyze": "2000",
"n_tup_del": "0",
"n_tup_hot_upd": "0",
"n_tup_ins": "100000",
"n_tup_upd": "0",
"relid": "16554",
"relname": "item",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
}
},
"pg_statio_user_tables": {
"history": {
"heap_blks_hit": "184380",
"heap_blks_read": "746",
"relid": "16536",
"relname": "history",
"schemaname": "public"
},
"order_line": {
"heap_blks_hit": "1869417",
"heap_blks_read": "12419",
"idx_blks_hit": "1788651",
"idx_blks_read": "3708",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"warehouse": {
"heap_blks_hit": "404486",
"heap_blks_read": "80",
"idx_blks_hit": "202643",
"idx_blks_read": "6",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
},
"new_order": {
"heap_blks_hit": "37856",
"heap_blks_read": "192",
"idx_blks_hit": "38225",
"idx_blks_read": "134",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"heap_blks_hit": "1920817",
"heap_blks_read": "11757",
"idx_blks_hit": "2447522",
"idx_blks_read": "1530",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"heap_blks_hit": "1378399",
"heap_blks_read": "928",
"idx_blks_hit": "3979052",
"idx_blks_read": "1881",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"district": {
"heap_blks_hit": "249754",
"heap_blks_read": "3",
"idx_blks_hit": "122259",
"idx_blks_read": "5",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"heap_blks_hit": "509702",
"heap_blks_read": "4542",
"idx_blks_hit": "617914",
"idx_blks_read": "877",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"customer": {
"heap_blks_hit": "70136669",
"heap_blks_read": "13826",
"idx_blks_hit": "1411491",
"idx_blks_read": "2716",
"relid": "16540",
"relname": "customer",
"schemaname": "public",
"tidx_blks_hit": "0",
"tidx_blks_read": "0",
"toast_blks_hit": "0",
"toast_blks_read": "0"
}
}
},
"database": {
"pg_stat_database": {
"postgres": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "115229324",
"blks_read": "104188",
"conflicts": "0",
"datid": "12558",
"datname": "postgres",
"deadlocks": "0",
"numbackends": "1",
"stats_reset": "2017-11-10 11:14:57.116228-05",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "1818",
"tup_fetched": "103355344",
"tup_inserted": "2210752",
"tup_returned": "110741743",
"tup_updated": "32675",
"xact_commit": "19082",
"xact_rollback": "17"
},
"tpcc": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "16384",
"datname": "tpcc",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
},
"template1": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "1",
"datname": "template1",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
},
"template0": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "12557",
"datname": "template0",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
}
},
"pg_stat_database_conflicts": {
"postgres": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "12558",
"datname": "postgres"
},
"tpcc": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "16384",
"datname": "tpcc"
},
"template1": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "1",
"datname": "template1"
},
"template0": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "12557",
"datname": "template0"
}
}
},
"indexes": {
"pg_stat_user_indexes": {
"order_line": {
"idx_scan": "1655",
"idx_tup_fetch": "33762",
"idx_tup_read": "35698",
"indexrelid": "16516",
"indexrelname": "order_line_pkey",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"new_order": {
"idx_scan": "1481",
"idx_tup_fetch": "1480",
"idx_tup_read": "2200",
"indexrelid": "16521",
"indexrelname": "new_order_pkey",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"idx_scan": "644561",
"idx_tup_fetch": "644561",
"idx_tup_read": "647319",
"indexrelid": "16526",
"indexrelname": "stock_pkey",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"idx_scan": "616371",
"idx_tup_fetch": "616371",
"idx_tup_read": "616371",
"indexrelid": "16565",
"indexrelname": "idx_order",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"customer": {
"idx_scan": "82442",
"idx_tup_fetch": "85256809",
"idx_tup_read": "85256841",
"indexrelid": "16564",
"indexrelname": "idx_customer_name",
"relid": "16540",
"relname": "customer",
"schemaname": "public"
},
"district": {
"idx_scan": "122234",
"idx_tup_fetch": "122234",
"idx_tup_read": "122234",
"indexrelid": "16552",
"indexrelname": "district_pkey",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"idx_scan": "209020",
"idx_tup_fetch": "209009",
"idx_tup_read": "209009",
"indexrelid": "16557",
"indexrelname": "item_pkey",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"warehouse": {
"idx_scan": "202634",
"idx_tup_fetch": "201331",
"idx_tup_read": "202634",
"indexrelid": "16562",
"indexrelname": "warehouse_pkey",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
}
},
"pg_statio_user_indexes": {
"order_line": {
"idx_blks_hit": "1788651",
"idx_blks_read": "3708",
"indexrelid": "16516",
"indexrelname": "order_line_pkey",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"new_order": {
"idx_blks_hit": "38225",
"idx_blks_read": "134",
"indexrelid": "16521",
"indexrelname": "new_order_pkey",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"idx_blks_hit": "2447522",
"idx_blks_read": "1530",
"indexrelid": "16526",
"indexrelname": "stock_pkey",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"idx_blks_hit": "3689479",
"idx_blks_read": "733",
"indexrelid": "16565",
"indexrelname": "idx_order",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"customer": {
"idx_blks_hit": "1151523",
"idx_blks_read": "1589",
"indexrelid": "16564",
"indexrelname": "idx_customer_name",
"relid": "16540",
"relname": "customer",
"schemaname": "public"
},
"district": {
"idx_blks_hit": "122259",
"idx_blks_read": "5",
"indexrelid": "16552",
"indexrelname": "district_pkey",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"idx_blks_hit": "617914",
"idx_blks_read": "877",
"indexrelid": "16557",
"indexrelname": "item_pkey",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"warehouse": {
"idx_blks_hit": "202643",
"idx_blks_read": "6",
"indexrelid": "16562",
"indexrelname": "warehouse_pkey",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
}
}
}
}
}

View File

@ -0,0 +1,582 @@
{
"global": {
"pg_stat_archiver": {
"archived_count": "0",
"failed_count": "0",
"stats_reset": "2017-11-10 10:59:47.397075-05"
},
"pg_stat_bgwriter": {
"buffers_alloc": "87670",
"buffers_backend": "81032",
"buffers_backend_fsync": "0",
"buffers_checkpoint": "33250",
"buffers_clean": "49590",
"checkpoint_sync_time": "19",
"checkpoint_write_time": "597851",
"checkpoints_req": "2",
"checkpoints_timed": "1277",
"maxwritten_clean": "325",
"stats_reset": "2017-11-10 10:59:47.397075-05"
}
},
"local": {
"table": {
"pg_stat_user_tables": {
"history": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"last_autoanalyze": "2017-11-20 15:59:02.567618-05",
"n_dead_tup": "0",
"n_live_tup": "60854",
"n_mod_since_analyze": "854",
"n_tup_del": "0",
"n_tup_hot_upd": "0",
"n_tup_ins": "60854",
"n_tup_upd": "0",
"relid": "16536",
"relname": "history",
"schemaname": "public",
"seq_scan": "2",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"warehouse": {
"analyze_count": "0",
"autoanalyze_count": "2",
"autovacuum_count": "2",
"idx_scan": "202634",
"idx_tup_fetch": "202634",
"last_autoanalyze": "2017-11-20 19:23:34.236294-05",
"last_autovacuum": "2017-11-20 19:23:34.235793-05",
"n_dead_tup": "0",
"n_live_tup": "2",
"n_mod_since_analyze": "0",
"n_tup_del": "0",
"n_tup_hot_upd": "854",
"n_tup_ins": "2",
"n_tup_upd": "854",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"stock": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "644561",
"idx_tup_fetch": "644561",
"last_autoanalyze": "2017-11-20 15:59:01.368483-05",
"n_dead_tup": "4364",
"n_live_tup": "200000",
"n_mod_since_analyze": "8901",
"n_tup_del": "0",
"n_tup_hot_upd": "5305",
"n_tup_ins": "200000",
"n_tup_upd": "8901",
"relid": "16523",
"relname": "stock",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"customer": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "125261",
"idx_tup_fetch": "85299628",
"last_autoanalyze": "2017-11-20 15:59:18.824212-05",
"n_dead_tup": "1510",
"n_live_tup": "60000",
"n_mod_since_analyze": "1594",
"n_tup_del": "0",
"n_tup_hot_upd": "262",
"n_tup_ins": "60000",
"n_tup_upd": "1594",
"relid": "16540",
"relname": "customer",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"order_line": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "1655",
"idx_tup_fetch": "33762",
"last_autoanalyze": "2017-11-20 16:00:11.017507-05",
"n_dead_tup": "2550",
"n_live_tup": "608373",
"n_mod_since_analyze": "16230",
"n_tup_del": "0",
"n_tup_hot_upd": "5393",
"n_tup_ins": "608373",
"n_tup_upd": "7329",
"relid": "16513",
"relname": "order_line",
"schemaname": "public",
"seq_scan": "3",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"oorder": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "627652",
"idx_tup_fetch": "627652",
"last_autoanalyze": "2017-11-20 15:59:54.690984-05",
"n_dead_tup": "117",
"n_live_tup": "60889",
"n_mod_since_analyze": "1629",
"n_tup_del": "0",
"n_tup_hot_upd": "662",
"n_tup_ins": "60900",
"n_tup_upd": "740",
"relid": "16528",
"relname": "oorder",
"schemaname": "public",
"seq_scan": "4",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"new_order": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "1481",
"idx_tup_fetch": "1480",
"last_autoanalyze": "2017-11-20 16:00:11.217111-05",
"n_dead_tup": "751",
"n_live_tup": "16964",
"n_mod_since_analyze": "1629",
"n_tup_del": "740",
"n_tup_hot_upd": "0",
"n_tup_ins": "17715",
"n_tup_upd": "0",
"relid": "16518",
"relname": "new_order",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
},
"district": {
"analyze_count": "0",
"autoanalyze_count": "2",
"autovacuum_count": "0",
"idx_scan": "122234",
"idx_tup_fetch": "122234",
"last_autoanalyze": "2017-11-20 19:23:34.201509-05",
"n_dead_tup": "33",
"n_live_tup": "20",
"n_mod_since_analyze": "0",
"n_tup_del": "0",
"n_tup_hot_upd": "1754",
"n_tup_ins": "20",
"n_tup_upd": "1754",
"relid": "16549",
"relname": "district",
"schemaname": "public",
"seq_scan": "2221",
"seq_tup_read": "41522",
"vacuum_count": "0"
},
"item": {
"analyze_count": "0",
"autoanalyze_count": "1",
"autovacuum_count": "0",
"idx_scan": "209020",
"idx_tup_fetch": "209009",
"last_autoanalyze": "2017-11-20 15:59:26.613728-05",
"n_dead_tup": "0",
"n_live_tup": "102000",
"n_mod_since_analyze": "2000",
"n_tup_del": "0",
"n_tup_hot_upd": "0",
"n_tup_ins": "100000",
"n_tup_upd": "0",
"relid": "16554",
"relname": "item",
"schemaname": "public",
"seq_scan": "1",
"seq_tup_read": "0",
"vacuum_count": "0"
}
},
"pg_statio_user_tables": {
"history": {
"heap_blks_hit": "184380",
"heap_blks_read": "746",
"relid": "16536",
"relname": "history",
"schemaname": "public"
},
"order_line": {
"heap_blks_hit": "1869417",
"heap_blks_read": "12419",
"idx_blks_hit": "1788651",
"idx_blks_read": "3708",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"warehouse": {
"heap_blks_hit": "404486",
"heap_blks_read": "80",
"idx_blks_hit": "202643",
"idx_blks_read": "6",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
},
"new_order": {
"heap_blks_hit": "37856",
"heap_blks_read": "192",
"idx_blks_hit": "38225",
"idx_blks_read": "134",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"heap_blks_hit": "1920817",
"heap_blks_read": "11757",
"idx_blks_hit": "2447522",
"idx_blks_read": "1530",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"heap_blks_hit": "1378399",
"heap_blks_read": "928",
"idx_blks_hit": "3979052",
"idx_blks_read": "1881",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"district": {
"heap_blks_hit": "249754",
"heap_blks_read": "3",
"idx_blks_hit": "122259",
"idx_blks_read": "5",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"heap_blks_hit": "509702",
"heap_blks_read": "4542",
"idx_blks_hit": "617914",
"idx_blks_read": "877",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"customer": {
"heap_blks_hit": "70136669",
"heap_blks_read": "13826",
"idx_blks_hit": "1411491",
"idx_blks_read": "2716",
"relid": "16540",
"relname": "customer",
"schemaname": "public",
"tidx_blks_hit": "0",
"tidx_blks_read": "0",
"toast_blks_hit": "0",
"toast_blks_read": "0"
}
}
},
"database": {
"pg_stat_database": {
"postgres": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "115229324",
"blks_read": "104188",
"conflicts": "0",
"datid": "12558",
"datname": "postgres",
"deadlocks": "0",
"numbackends": "1",
"stats_reset": "2017-11-10 11:14:57.116228-05",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "1818",
"tup_fetched": "103355344",
"tup_inserted": "2210752",
"tup_returned": "110741743",
"tup_updated": "32675",
"xact_commit": "19082",
"xact_rollback": "17"
},
"tpcc": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "16384",
"datname": "tpcc",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
},
"template1": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "1",
"datname": "template1",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
},
"template0": {
"blk_read_time": "0",
"blk_write_time": "0",
"blks_hit": "0",
"blks_read": "0",
"conflicts": "0",
"datid": "12557",
"datname": "template0",
"deadlocks": "0",
"numbackends": "0",
"temp_bytes": "0",
"temp_files": "0",
"tup_deleted": "0",
"tup_fetched": "0",
"tup_inserted": "0",
"tup_returned": "0",
"tup_updated": "0",
"xact_commit": "0",
"xact_rollback": "0"
}
},
"pg_stat_database_conflicts": {
"postgres": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "12558",
"datname": "postgres"
},
"tpcc": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "16384",
"datname": "tpcc"
},
"template1": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "1",
"datname": "template1"
},
"template0": {
"confl_bufferpin": "0",
"confl_deadlock": "0",
"confl_lock": "0",
"confl_snapshot": "0",
"confl_tablespace": "0",
"datid": "12557",
"datname": "template0"
}
}
},
"indexes": {
"pg_stat_user_indexes": {
"order_line": {
"idx_scan": "1655",
"idx_tup_fetch": "33762",
"idx_tup_read": "35698",
"indexrelid": "16516",
"indexrelname": "order_line_pkey",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"new_order": {
"idx_scan": "1481",
"idx_tup_fetch": "1480",
"idx_tup_read": "2200",
"indexrelid": "16521",
"indexrelname": "new_order_pkey",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"idx_scan": "644561",
"idx_tup_fetch": "644561",
"idx_tup_read": "647319",
"indexrelid": "16526",
"indexrelname": "stock_pkey",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"idx_scan": "616371",
"idx_tup_fetch": "616371",
"idx_tup_read": "616371",
"indexrelid": "16565",
"indexrelname": "idx_order",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"customer": {
"idx_scan": "82442",
"idx_tup_fetch": "85256809",
"idx_tup_read": "85256841",
"indexrelid": "16564",
"indexrelname": "idx_customer_name",
"relid": "16540",
"relname": "customer",
"schemaname": "public"
},
"district": {
"idx_scan": "122234",
"idx_tup_fetch": "122234",
"idx_tup_read": "122234",
"indexrelid": "16552",
"indexrelname": "district_pkey",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"idx_scan": "209020",
"idx_tup_fetch": "209009",
"idx_tup_read": "209009",
"indexrelid": "16557",
"indexrelname": "item_pkey",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"warehouse": {
"idx_scan": "202634",
"idx_tup_fetch": "201331",
"idx_tup_read": "202634",
"indexrelid": "16562",
"indexrelname": "warehouse_pkey",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
}
},
"pg_statio_user_indexes": {
"order_line": {
"idx_blks_hit": "1788651",
"idx_blks_read": "3708",
"indexrelid": "16516",
"indexrelname": "order_line_pkey",
"relid": "16513",
"relname": "order_line",
"schemaname": "public"
},
"new_order": {
"idx_blks_hit": "38225",
"idx_blks_read": "134",
"indexrelid": "16521",
"indexrelname": "new_order_pkey",
"relid": "16518",
"relname": "new_order",
"schemaname": "public"
},
"stock": {
"idx_blks_hit": "2447522",
"idx_blks_read": "1530",
"indexrelid": "16526",
"indexrelname": "stock_pkey",
"relid": "16523",
"relname": "stock",
"schemaname": "public"
},
"oorder": {
"idx_blks_hit": "3689479",
"idx_blks_read": "733",
"indexrelid": "16565",
"indexrelname": "idx_order",
"relid": "16528",
"relname": "oorder",
"schemaname": "public"
},
"customer": {
"idx_blks_hit": "1151523",
"idx_blks_read": "1589",
"indexrelid": "16564",
"indexrelname": "idx_customer_name",
"relid": "16540",
"relname": "customer",
"schemaname": "public"
},
"district": {
"idx_blks_hit": "122259",
"idx_blks_read": "5",
"indexrelid": "16552",
"indexrelname": "district_pkey",
"relid": "16549",
"relname": "district",
"schemaname": "public"
},
"item": {
"idx_blks_hit": "617914",
"idx_blks_read": "877",
"indexrelid": "16557",
"indexrelname": "item_pkey",
"relid": "16554",
"relname": "item",
"schemaname": "public"
},
"warehouse": {
"idx_blks_hit": "202643",
"idx_blks_read": "6",
"indexrelid": "16562",
"indexrelname": "warehouse_pkey",
"relid": "16559",
"relname": "warehouse",
"schemaname": "public"
}
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"start_time": 1535653369274,
"end_time": 1535653559607,
"observation_time": 190,
"database_type": "postgres",
"database_version": "9.3",
"workload_name": "workload_name"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
{
"start_time": 1535653369274,
"end_time": 1535653559607,
"observation_time": 190,
"database_type": "saphana",
"database_version": "2.00.023.00.1513691289",
"workload_name": "workload_name"
}

View File

@ -0,0 +1,105 @@
/*
* OtterTune - ControllerConfiguration.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller;
/** Controller Configuration. */
public class ControllerConfiguration {
private DatabaseType dbType;
private String dbName;
private String dbUsername;
private String dbPassword;
private String dbURL;
private String uploadCode;
private String uploadURL;
private String workloadName;
public ControllerConfiguration() {}
public ControllerConfiguration(
String dbName,
String dbUsername,
String dbPassword,
String dbURL,
String uploadCode,
String uploadURL,
String workloadName) {
this.dbType = DatabaseType.get(dbName);
this.dbName = dbName;
this.dbUsername = dbUsername;
this.dbPassword = dbPassword;
this.dbURL = dbURL;
this.uploadCode = uploadCode;
this.uploadURL = uploadURL;
this.workloadName = workloadName;
}
/* Mutators */
public void setDBType(DatabaseType dbType) {
this.dbType = dbType;
}
public void setDBName(String dbName) {
this.dbName = dbName;
}
public void setDBUsername(String dbUsername) {
this.dbUsername = dbUsername;
}
public void setPassword(String dbPassword) {
this.dbPassword = dbPassword;
}
public void setDBURL(String dbURL) {
this.dbURL = dbURL;
}
public void setUploadCode(String uploadCode) {
this.uploadCode = uploadCode;
}
public void setUploadURL(String uploadURL) {
this.uploadURL = uploadURL;
}
public void setWorkloadName(String workloadName) {
this.workloadName = workloadName;
}
/* Getters */
public DatabaseType getDBType() {
return this.dbType;
}
public String getDBName() {
return this.dbName;
}
public String getDBUsername() {
return this.dbUsername;
}
public String getDBPassword() {
return this.dbPassword;
}
public String getDBURL() {
return this.dbURL;
}
public String getUploadCode() {
return this.uploadCode;
}
public String getUploadURL() {
return this.uploadURL;
}
public String getWorkloadName() {
return this.workloadName;
}
}

View File

@ -0,0 +1,296 @@
/*
* OtterTune - Main.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller;
import com.controller.collectors.DBCollector;
import com.controller.collectors.MySQLCollector;
import com.controller.collectors.OracleCollector;
import com.controller.collectors.PostgresCollector;
import com.controller.collectors.SAPHanaCollector;
import com.controller.types.JSONSchemaType;
import com.controller.util.FileUtil;
import com.controller.util.JSONUtil;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import sun.misc.Signal;
/**
* Controller main.
*
* @author Shuli
*/
public class Main {
static final Logger LOG = Logger.getLogger(Main.class);
// Default output directory name
private static final String DEFAULT_DIRECTORY = "output";
// Default observation period time (5 minutes)
private static final int DEFAULT_TIME_SECONDS = -1;
// Path to JSON schema directory
private static final String SCHEMA_PATH = "src/main/java/com/controller/json_validation_schema";
private static final int TO_MILLISECONDS = 1000;
private static boolean keepRunning = true;
private static boolean firstCollecting = false;
public static void main(String[] args) {
// Initialize log4j
PropertyConfigurator.configure("log4j.properties");
// Initialize keepRunning
keepRunning = true;
// Initialize firstCollecting
firstCollecting = false;
// Create the command line parser
CommandLineParser parser = new PosixParser();
Options options = new Options();
options.addOption("c", "config", true, "[required] Controller configuration file");
options.addOption("t", "time", true, "The observation time in seconds, default is 300s");
options.addOption(
"d", "directory", true, "Base directory for the result files, default is 'output'");
options.addOption("h", "help", true, "Print this help");
String configFilePath = null;
// Parse the command line arguments
CommandLine argsLine;
try {
argsLine = parser.parse(options, args);
} catch (ParseException e) {
LOG.error("Unable to Parse command line arguments");
printUsage(options);
return;
}
if (argsLine.hasOption("h")) {
printUsage(options);
return;
} else if (argsLine.hasOption("c") == false) {
LOG.error("Missing configuration file");
printUsage(options);
return;
}
int time = DEFAULT_TIME_SECONDS;
if (argsLine.hasOption("t")) {
time = Integer.parseInt(argsLine.getOptionValue("t"));
}
LOG.info("Experiment time is set to: " + time);
String outputDirectory = DEFAULT_DIRECTORY;
if (argsLine.hasOption("d")) {
outputDirectory = argsLine.getOptionValue("d");
}
LOG.info("Experiment output directory is set to: " + outputDirectory);
FileUtil.makeDirIfNotExists(outputDirectory);
// Parse controller configuration file
String configPath = argsLine.getOptionValue("c");
File configFile = new File(configPath);
// Check config format
if (!JSONSchemaType.isValidJson(JSONSchemaType.CONFIG, configFile)) {
LOG.error("Invalid configuration JSON format");
return;
}
// Load configuration file
ControllerConfiguration config = null;
try {
JSONObject input = new JSONObject(FileUtil.readFile(configFile));
config =
new ControllerConfiguration(
input.getString("database_type"),
input.getString("username"),
input.getString("password"),
input.getString("database_url"),
input.getString("upload_code"),
input.getString("upload_url"),
input.getString("workload_name"));
} catch (JSONException e) {
e.printStackTrace();
}
DBCollector collector = getCollector(config);
try {
// add a signal handler
Signal.handle(new Signal("INT"), signal -> firstCollecting = true);
File f = new File("pid.txt");
// get pid of this process and write the pid to a file before recording the start time
if (time < 0) {
String vmName = ManagementFactory.getRuntimeMXBean().getName();
int p = vmName.indexOf("@");
int pid = Integer.valueOf(vmName.substring(0, p));
try {
f.createNewFile();
PrintWriter pidWriter = new PrintWriter(f);
pidWriter.println(pid);
pidWriter.flush();
pidWriter.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
LOG.info("Output the process pid to pid.txt");
while (!firstCollecting) {
Thread.sleep(1);
}
// first collection (before queries)
LOG.info("First collection of metrics before experiment");
String metricsBefore = collector.collectMetrics();
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, metricsBefore)) {
LOG.error("Invalid output JSON format (metrics_before)");
return;
}
PrintWriter metricsWriter =
new PrintWriter(FileUtil.joinPath(outputDirectory, "metrics_before.json"), "UTF-8");
metricsWriter.println(metricsBefore);
metricsWriter.close();
String knobs = collector.collectParameters();
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, knobs)) {
LOG.error("Invalid output JSON format (knobs)");
return;
}
PrintWriter knobsWriter =
new PrintWriter(FileUtil.joinPath(outputDirectory, "knobs.json"), "UTF-8");
knobsWriter.println(knobs);
knobsWriter.close();
// add a signal handler
Signal.handle(new Signal("INT"), signal -> keepRunning = false);
// record start time
long startTime = System.currentTimeMillis();
LOG.info("Starting the experiment ...");
// go to sleep
if (time >= 0) {
Thread.sleep(time * TO_MILLISECONDS);
} else {
while (keepRunning) {
Thread.sleep(1);
}
f.delete();
}
long endTime = System.currentTimeMillis();
long observationTime = time >= 0 ? time : (endTime - startTime) / TO_MILLISECONDS;
LOG.info("Done running the experiment");
// summary json obj
JSONObject summary = null;
try {
summary = new JSONObject();
summary.put("start_time", startTime);
summary.put("end_time", endTime);
summary.put("observation_time", observationTime);
summary.put("database_type", config.getDBName());
summary.put("database_version", collector.collectVersion());
summary.put("workload_name", config.getWorkloadName());
} catch (JSONException e) {
e.printStackTrace();
}
if (!JSONSchemaType.isValidJson(JSONSchemaType.SUMMARY, summary.toString())) {
LOG.error("Invalid summary JSON format");
return;
}
// write summary JSONObject into a JSON file
PrintWriter summaryout =
new PrintWriter(FileUtil.joinPath(outputDirectory, "summary.json"), "UTF-8");
summaryout.println(JSONUtil.format(summary.toString()));
summaryout.close();
// second collection (after workload execution)
LOG.info("Second collection of metrics after experiment");
collector = getCollector(config);
String metricsAfter = collector.collectMetrics();
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, metricsAfter)) {
LOG.error("Invalid output JSON format (metrics_after)");
return;
}
PrintWriter metricsWriterFinal =
new PrintWriter(FileUtil.joinPath(outputDirectory, "metrics_after.json"), "UTF-8");
metricsWriterFinal.println(metricsAfter);
metricsWriterFinal.close();
} catch (FileNotFoundException | UnsupportedEncodingException | InterruptedException e) {
LOG.error("Failed to produce output files");
e.printStackTrace();
}
if (config.getUploadURL() != null && !config.getUploadURL().equals("")) {
Map<String, String> outfiles = new HashMap<>();
outfiles.put("knobs", FileUtil.joinPath(outputDirectory, "knobs.json"));
outfiles.put("metrics_before", FileUtil.joinPath(outputDirectory, "metrics_before.json"));
outfiles.put("metrics_after", FileUtil.joinPath(outputDirectory, "metrics_after.json"));
outfiles.put("summary", FileUtil.joinPath(outputDirectory, "summary.json"));
try {
ResultUploader.upload(config.getUploadURL(), config.getUploadCode(), outfiles);
} catch (IOException ioe) {
LOG.warn("Failed to upload results from the controller");
}
} else {
LOG.warn("Empty upload URL. Skipping upload...");
}
}
private static void printUsage(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("controller", options);
}
private static DBCollector getCollector(ControllerConfiguration config) {
DBCollector collector = null;
switch (config.getDBType()) {
case POSTGRES:
collector =
new PostgresCollector(
config.getDBURL(), config.getDBUsername(), config.getDBPassword());
break;
case MYSQL:
collector =
new MySQLCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
break;
case SAPHANA:
collector =
new SAPHanaCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
break;
case ORACLE:
collector =
new OracleCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
break;
default:
LOG.error("Invalid database type");
throw new RuntimeException("Invalid database type");
}
return collector;
}
}

View File

@ -0,0 +1,62 @@
/*
* OtterTune - ResultUploader.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* Uploading the result.
*
* @author Shuli
*/
public class ResultUploader {
public static void upload(String uploadURL, String uploadCode,
Map<String, String> files) throws IOException {
try {
List<String> filesToSendNames = new ArrayList<>();
List<File> filesToSend = new ArrayList<>();
for (String fileName : files.keySet()) {
String path = files.get(fileName);
filesToSendNames.add(fileName);
File f = new File(path);
filesToSend.add(f);
}
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(uploadURL);
MultipartEntityBuilder mb =
MultipartEntityBuilder.create().addTextBody("upload_code", uploadCode);
for (int i = 0; i < filesToSendNames.size(); i++) {
mb.addPart(filesToSendNames.get(i), new FileBody(filesToSend.get(i)));
}
HttpEntity reqEntity = mb.build();
httppost.setEntity(reqEntity);
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity resEntity = response.getEntity();
EntityUtils.consume(resEntity);
} finally {
response.close();
}
} catch (IOException e) {
throw new IOException();
}
}
}

View File

@ -0,0 +1,51 @@
/*
* OtterTune - DBCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.util.JSONUtil;
import java.util.Map;
import java.util.TreeMap;
import org.apache.log4j.Logger;
public class DBCollector implements DBParameterCollector {
private static final Logger LOG = Logger.getLogger(DBCollector.class);
protected static final String JSON_GLOBAL_KEY = "global";
protected static final String JSON_LOCAL_KEY = "local";
protected final Map<String, String> dbParameters = new TreeMap<String, String>();
protected final Map<String, String> dbMetrics = new TreeMap<String, String>();
protected final StringBuilder version = new StringBuilder();
@Override
public boolean hasParameters() {
return (dbParameters.isEmpty() == false);
}
@Override
public boolean hasMetrics() {
return (dbMetrics.isEmpty() == false);
}
@Override
public String collectParameters() {
return JSONUtil.format(JSONUtil.toJSONString(dbParameters));
}
@Override
public String collectMetrics() {
return JSONUtil.format(JSONUtil.toJSONString(dbMetrics));
}
@Override
public String collectVersion() {
return version.toString();
}
}

View File

@ -0,0 +1,19 @@
/*
* OtterTune - DBParameterCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
public interface DBParameterCollector {
boolean hasParameters();
boolean hasMetrics();
String collectParameters();
String collectMetrics();
String collectVersion();
}

View File

@ -0,0 +1,105 @@
/*
* OtterTune - MySQLCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.util.JSONUtil;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONStringer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import org.apache.log4j.Logger;
/** */
public class MySQLCollector extends DBCollector {
private static final Logger LOG = Logger.getLogger(MySQLCollector.class);
private static final String VERSION_SQL = "SELECT @@GLOBAL.version;";
private static final String PARAMETERS_SQL = "SHOW VARIABLES;";
private static final String METRICS_SQL = "SHOW STATUS";
public MySQLCollector(String oriDBUrl, String username, String password) {
try {
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
Statement s = conn.createStatement();
// Collect DBMS version
ResultSet out = s.executeQuery(VERSION_SQL);
if (out.next()) {
this.version.append(out.getString(1));
}
// Collect DBMS parameters
out = s.executeQuery(PARAMETERS_SQL);
while (out.next()) {
dbParameters.put(out.getString(1).toLowerCase(), out.getString(2));
}
// Collect DBMS internal metrics
out = s.executeQuery(METRICS_SQL);
while (out.next()) {
dbMetrics.put(out.getString(1).toLowerCase(), out.getString(2));
}
conn.close();
} catch (SQLException e) {
LOG.error("Error while collecting DB parameters: " + e.getMessage());
e.printStackTrace();
}
}
@Override
public String collectParameters() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobLocal = new JSONObject();
JSONObject job = new JSONObject();
for (String k : dbParameters.keySet()) {
job.put(k, dbParameters.get(k));
}
// "global is a fake view_name (a placeholder)"
jobLocal.put("global", job);
stringer.value(jobLocal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
@Override
public String collectMetrics() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobGlobal = new JSONObject();
JSONObject job = new JSONObject();
for (Map.Entry<String, String> entry : dbMetrics.entrySet()) {
job.put(entry.getKey(), entry.getValue());
}
// "global" is a a placeholder
jobGlobal.put("global", job);
stringer.value(jobGlobal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException e) {
e.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
}

View File

@ -0,0 +1,108 @@
/*
* OtterTune - OracleCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.util.JSONUtil;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONStringer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import org.apache.log4j.Logger;
/** */
public class OracleCollector extends DBCollector {
private static final Logger LOG = Logger.getLogger(MySQLCollector.class);
private static final String VERSION_SQL = "select VERSION from product_component_version";
private static final String PARAMETERS_SQL = "select name, value from v$parameter";
private static final String PARAMETERS_SQL_WITH_HIDDEN =
"select x.ksppinm name, y.ksppstvl value from sys.x$ksppi x, sys.x$ksppcv y where"
+ " x.inst_id = userenv('Instance') and y.inst_id = userenv('Instance') and x.indx = y.indx";
private static final String METRICS_SQL = "select name, value from v$sysstat";
public OracleCollector(String oriDBUrl, String username, String password) {
try {
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
Statement statement = conn.createStatement();
// Collect DBMS version
ResultSet out = statement.executeQuery(VERSION_SQL);
if (out.next()) {
this.version.append(out.getString(1));
}
// Collect DBMS parameters
out = statement.executeQuery(PARAMETERS_SQL_WITH_HIDDEN);
while (out.next()) {
dbParameters.put(out.getString(1).toLowerCase(), out.getString(2));
}
// Collect DBMS internal metrics
out = statement.executeQuery(METRICS_SQL);
while (out.next()) {
dbMetrics.put(out.getString(1).toLowerCase(), out.getString(2));
}
conn.close();
} catch (SQLException e) {
LOG.error("Error while collecting DB parameters: " + e.getMessage());
e.printStackTrace();
}
}
@Override
public String collectParameters() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobLocal = new JSONObject();
JSONObject job = new JSONObject();
for (Map.Entry<String, String> entry : dbParameters.entrySet()) {
job.put(entry.getKey(), entry.getValue());
}
// "global is a fake view_name (a placeholder)"
jobLocal.put("global", job);
stringer.value(jobLocal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
@Override
public String collectMetrics() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobGlobal = new JSONObject();
JSONObject job = new JSONObject();
for (Map.Entry<String, String> entry : dbMetrics.entrySet()) {
job.put(entry.getKey(), entry.getValue());
}
// "global" is a placeholder
jobGlobal.put("global", job);
stringer.value(jobGlobal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException e) {
e.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
}

View File

@ -0,0 +1,237 @@
/*
* OtterTune - PostgresCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.util.JSONUtil;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONStringer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.log4j.Logger;
public class PostgresCollector extends DBCollector {
private static final Logger LOG = Logger.getLogger(PostgresCollector.class);
private static final String VERSION_SQL = "SELECT version();";
private static final String PARAMETERS_SQL = "SHOW ALL;";
private boolean oldVersion = false;
private static final String[] PG_STAT_VIEWS = {
"pg_stat_archiver", "pg_stat_bgwriter", "pg_stat_database",
"pg_stat_database_conflicts", "pg_stat_user_tables", "pg_statio_user_tables",
"pg_stat_user_indexes", "pg_statio_user_indexes"
};
private static final String[] PG_STAT_VIEWS_OLD_VERSION = {
"pg_stat_bgwriter", "pg_stat_database",
"pg_stat_database_conflicts", "pg_stat_user_tables", "pg_statio_user_tables",
"pg_stat_user_indexes", "pg_statio_user_indexes"
};
private static final String[] PG_STAT_VIEWS_LOCAL_DATABASE = {
"pg_stat_database", "pg_stat_database_conflicts"
};
private static final String PG_STAT_VIEWS_LOCAL_DATABASE_KEY = "datname";
private static final String[] PG_STAT_VIEWS_LOCAL_TABLE = {
"pg_stat_user_tables", "pg_statio_user_tables"
};
private static final String PG_STAT_VIEWS_LOCAL_TABLE_KEY = "relname";
private static final String[] PG_STAT_VIEWS_LOCAL_INDEXES = {
"pg_stat_user_indexes", "pg_statio_user_indexes"
};
private static final String PG_STAT_VIEWS_LOCAL_INDEXES_KEY = "relname";
private final Map<String, List<Map<String, String>>> pgMetrics;
public PostgresCollector(String oriDBUrl, String username, String password) {
pgMetrics = new HashMap<>();
try {
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
Statement s = conn.createStatement();
// Collect DBMS version
ResultSet out = s.executeQuery(VERSION_SQL);
if (out.next()) {
String[] outStr = out.getString(1).split(" ");
String[] verStr = outStr[1].split("\\.");
this.version.append(verStr[0]);
this.version.append(".");
this.version.append(verStr[1]);
}
// Collect DBMS parameters
out = s.executeQuery(PARAMETERS_SQL);
while (out.next()) {
dbParameters.put(out.getString("name"), out.getString("setting"));
}
// Collect DBMS internal metrics
String[] pgStatViews = PG_STAT_VIEWS;
if (Float.parseFloat(this.version.toString()) < 9.4) {
this.oldVersion = true;
pgStatViews = PG_STAT_VIEWS_OLD_VERSION;
}
for (String viewName : pgStatViews) {
out = s.executeQuery("SELECT * FROM " + viewName);
pgMetrics.put(viewName, getMetrics(out));
}
conn.close();
} catch (SQLException e) {
e.printStackTrace();
LOG.error("Error while collecting DB parameters: " + e.getMessage());
}
}
@Override
public boolean hasMetrics() {
return (pgMetrics.isEmpty() == false);
}
private JSONObject genMapJSONObj(Map<String, String> mapin) {
JSONObject res = new JSONObject();
try {
for (String key : mapin.keySet()) {
res.put(key, mapin.get(key));
}
} catch (JSONException je) {
LOG.error(je);
}
return res;
}
private JSONObject genLocalJSONObj(String viewName, String jsonKeyName) {
JSONObject thisViewObj = new JSONObject();
List<Map<String, String>> thisViewList = pgMetrics.get(viewName);
try {
for (Map<String, String> dbmap : thisViewList) {
String jsonkey = dbmap.get(jsonKeyName);
thisViewObj.put(jsonkey, genMapJSONObj(dbmap));
}
} catch (JSONException je) {
LOG.error(je);
}
return thisViewObj;
}
@Override
public String collectMetrics() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
// create global objects for two views: "pg_stat_archiver" and "pg_stat_bgwriter"
JSONObject jobGlobal = new JSONObject();
// "pg_stat_archiver" (only one instance in the list) >= version 9.4
if (!this.oldVersion) {
Map<String, String> archiverList = pgMetrics.get("pg_stat_archiver").get(0);
jobGlobal.put("pg_stat_archiver", genMapJSONObj(archiverList));
}
// "pg_stat_bgwriter" (only one instance in the list)
Map<String, String> bgwriterList = pgMetrics.get("pg_stat_bgwriter").get(0);
jobGlobal.put("pg_stat_bgwriter", genMapJSONObj(bgwriterList));
// add global json object
stringer.value(jobGlobal);
stringer.key(JSON_LOCAL_KEY);
// create local objects for the rest of the views
JSONObject jobLocal = new JSONObject();
// "table"
JSONObject jobTable = new JSONObject();
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_TABLE.length; i++) {
String viewName = PG_STAT_VIEWS_LOCAL_TABLE[i];
String jsonKeyName = PG_STAT_VIEWS_LOCAL_TABLE_KEY;
jobTable.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
jobLocal.put("table", jobTable);
// "database"
JSONObject jobDatabase = new JSONObject();
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_DATABASE.length; i++) {
String viewName = PG_STAT_VIEWS_LOCAL_DATABASE[i];
String jsonKeyName = PG_STAT_VIEWS_LOCAL_DATABASE_KEY;
jobDatabase.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
jobLocal.put("database", jobDatabase);
// "indexes"
JSONObject jobIndexes = new JSONObject();
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_INDEXES.length; i++) {
String viewName = PG_STAT_VIEWS_LOCAL_INDEXES[i];
String jsonKeyName = PG_STAT_VIEWS_LOCAL_INDEXES_KEY;
jobIndexes.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
jobLocal.put("indexes", jobIndexes);
// add local json object
stringer.value(jobLocal);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
private static List<Map<String, String>> getMetrics(ResultSet out) throws SQLException {
ResultSetMetaData metadata = out.getMetaData();
int numColumns = metadata.getColumnCount();
String[] columnNames = new String[numColumns];
for (int i = 0; i < numColumns; ++i) {
columnNames[i] = metadata.getColumnName(i + 1).toLowerCase();
}
List<Map<String, String>> metrics = new ArrayList<Map<String, String>>();
while (out.next()) {
Map<String, String> metricMap = new TreeMap<String, String>();
for (int i = 0; i < numColumns; ++i) {
metricMap.put(columnNames[i], out.getString(i + 1));
}
metrics.add(metricMap);
}
return metrics;
}
@Override
public String collectParameters() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobLocal = new JSONObject();
JSONObject job = new JSONObject();
for (String k : dbParameters.keySet()) {
job.put(k, dbParameters.get(k));
}
// "global is a fake view_name (a placeholder)"
jobLocal.put("global", job);
stringer.value(jobLocal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
}

View File

@ -0,0 +1,231 @@
/*
* OtterTune - SAPHanaCollector.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.util.JSONUtil;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONStringer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.log4j.Logger;
public class SAPHanaCollector extends DBCollector {
private static final Logger LOG = Logger.getLogger(SAPHanaCollector.class);
private static final String VERSION_SQL = "SELECT VERSION from M_DATABASE";
private static final String PARAMETERS_SQL = "Select * from M_INIFILE_CONTENTS";
private static final String[] SAP_SYS_VIEW = {
"m_host_agent_metrics",
"m_caches",
"m_disk_usage",
"m_garbage_collection_statistics",
"m_host_resource_utilization",
"m_data_volumes"
};
private static final String[] SAP_SYS_LOCAL_VIEW = {"m_table_statistics", "m_rs_indexes"};
private static final String[] SAP_SYS_VIEW_GLOBAL = {
"m_host_agent_metrics",
"m_caches",
"m_disk_usage",
"m_garbage_collection_statistics",
"m_host_resource_utilization",
"m_data_volumes"
};
private static final String[] SAP_SYS_VIEW_GLOBAL_KEY = {
"instance_id", "cache_id", "usage_type", "store_type", "host", "volume_id"
};
private static final String[] SAP_SYS_VIEW_LOCAL_TABLE = {"m_table_statistics"};
private static final String[] SAP_SYS_VIEW_LOCAL_TABLE_KEY = {"table_name"};
private static final String[] SAP_SYS_VIEW_LOCAL_INDEXES = {"m_rs_indexes"};
private static final String[] SAP_SYS_VIEW_LOCAL_INDEXES_KEY = {"index_name"};
private final Map<String, List<Map<String, String>>> pgMetrics;
public SAPHanaCollector(String oriDBUrl, String username, String password) {
pgMetrics = new HashMap<>();
try {
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
Statement s = conn.createStatement();
// Collect DBMS version
ResultSet out = s.executeQuery(VERSION_SQL);
if (out.next()) {
this.version.append(out.getString(1));
}
// Collect DBMS parameters
out = s.executeQuery(PARAMETERS_SQL);
while (out.next()) {
dbParameters.put(
"("
+ out.getString("FILE_NAME")
+ ","
+ out.getString("LAYER_NAME")
+ ","
+ out.getString("TENANT_NAME")
+ ","
+ out.getString("HOST")
+ ","
+ out.getString("SECTION")
+ ","
+ out.getString("KEY")
+ ")",
out.getString("VALUE"));
}
// Collect DBMS internal metrics
for (String viewName : SAP_SYS_VIEW) {
out = s.executeQuery("SELECT * FROM " + viewName);
pgMetrics.put(viewName, getMetrics(out));
}
for (String viewName : SAP_SYS_LOCAL_VIEW) {
out = s.executeQuery("SELECT * FROM " + viewName + " where schema_name = 'SYSTEM' ");
pgMetrics.put(viewName, getMetrics(out));
}
conn.close();
} catch (SQLException e) {
e.printStackTrace();
LOG.error("Error while collecting DB parameters: " + e.getMessage());
}
}
@Override
public boolean hasMetrics() {
return (pgMetrics.isEmpty() == false);
}
private JSONObject genMapJSONObj(Map<String, String> mapin) {
JSONObject res = new JSONObject();
try {
for (String key : mapin.keySet()) {
res.put(key, mapin.get(key));
}
} catch (JSONException je) {
LOG.error(je);
}
return res;
}
private JSONObject genLocalJSONObj(String viewName, String jsonKeyName) {
JSONObject thisViewObj = new JSONObject();
List<Map<String, String>> thisViewList = pgMetrics.get(viewName);
try {
for (Map<String, String> dbmap : thisViewList) {
String jsonkey = dbmap.get(jsonKeyName);
thisViewObj.put(jsonkey, genMapJSONObj(dbmap));
}
} catch (JSONException je) {
LOG.error(je);
}
return thisViewObj;
}
@Override
public String collectMetrics() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobGlobal = new JSONObject();
JSONObject jobMetric = new JSONObject();
for (int i = 0; i < SAP_SYS_VIEW_GLOBAL.length; i++) {
String viewName = SAP_SYS_VIEW_GLOBAL[i];
String jsonKeyName = SAP_SYS_VIEW_GLOBAL_KEY[i];
jobGlobal.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
// add global json object
stringer.value(jobGlobal);
stringer.key(JSON_LOCAL_KEY);
// create local objects for the rest of the views
JSONObject jobLocal = new JSONObject();
// "table"
JSONObject jobTable = new JSONObject();
for (int i = 0; i < SAP_SYS_VIEW_LOCAL_TABLE.length; i++) {
String viewName = SAP_SYS_VIEW_LOCAL_TABLE[i];
String jsonKeyName = SAP_SYS_VIEW_LOCAL_TABLE_KEY[i];
jobTable.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
jobLocal.put("table", jobTable);
// "indexes"
JSONObject jobIndexes = new JSONObject();
for (int i = 0; i < SAP_SYS_VIEW_LOCAL_INDEXES.length; i++) {
String viewName = SAP_SYS_VIEW_LOCAL_INDEXES[i];
String jsonKeyName = SAP_SYS_VIEW_LOCAL_INDEXES_KEY[i];
jobIndexes.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
}
jobLocal.put("indexes", jobIndexes);
// add local json object
stringer.value(jobLocal);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
private static List<Map<String, String>> getMetrics(ResultSet out) throws SQLException {
ResultSetMetaData metadata = out.getMetaData();
int numColumns = metadata.getColumnCount();
String[] columnNames = new String[numColumns];
for (int i = 0; i < numColumns; ++i) {
columnNames[i] = metadata.getColumnName(i + 1).toLowerCase();
}
List<Map<String, String>> metrics = new ArrayList<Map<String, String>>();
while (out.next()) {
Map<String, String> metricMap = new TreeMap<String, String>();
for (int i = 0; i < numColumns; ++i) {
metricMap.put(columnNames[i], out.getString(i + 1));
}
metrics.add(metricMap);
}
return metrics;
}
@Override
public String collectParameters() {
JSONStringer stringer = new JSONStringer();
try {
stringer.object();
stringer.key(JSON_GLOBAL_KEY);
JSONObject jobLocal = new JSONObject();
JSONObject job = new JSONObject();
for (String k : dbParameters.keySet()) {
job.put(k, dbParameters.get(k));
}
// "global is a fake view_name (a placeholder)"
jobLocal.put("global", job);
stringer.value(jobLocal);
stringer.key(JSON_LOCAL_KEY);
stringer.value(null);
stringer.endObject();
} catch (JSONException jsonexn) {
jsonexn.printStackTrace();
}
return JSONUtil.format(stringer.toString());
}
}

View File

@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "summary of collector output",
"description": "config files: user input",
"type": "object",
"properties": {
"database_type": {
"type" : "string"
},
"database_url": {
"type" : "string"
},
"username" :{
"type" : "string"
},
"password": {
"type" : "string"
},
"upload_code": {
"type" : "string"
},
"upload_url": {
"type" : "string"
},
"workload_name": {
"type" : "string"
}
},
"required": [
"database_type",
"database_url",
"username",
"password",
"upload_code",
"upload_url",
"workload_name"
]
}

View File

@ -0,0 +1,47 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "collector output",
"description": "Collected metrics or knobs info",
"type": "object",
"properties": {
"global" : {
"type": ["object", "null"],
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "object",
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"local" : {
"type": ["object", "null"],
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "object",
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "object",
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "object",
"patternProperties" : {
"^[A-Za-z0-9 -_]" : {
"type" : "string"
}
}
}
}
}
}
}
}
}
},
"required": ["global", "local"]
}

View File

@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "summary of collector output",
"description": "output summary",
"type": "object",
"properties": {
"database_type": {
"type" : "string"
},
"start_time": {
"type" : "number"
},
"observation_time" :{
"type" : "number"
},
"end_time": {
"type" : "number"
},
"database_version": {
"type" : "string"
}
},
"required": [
"database_type",
"start_time",
"observation_time",
"end_time",
"database_version"
]
}

View File

@ -0,0 +1,66 @@
/*
* OtterTune - DatabaseType.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/** Database Type. */
public enum DatabaseType {
/** Parameters: (1) JDBC Driver String */
MYSQL("com.mysql.jdbc.Driver"),
MYROCKS("com.mysql.jdbc.Driver"),
POSTGRES("org.postgresql.Driver"),
SAPHANA("com.sap.db.jdbc.Driver"),
ORACLE("oracle.jdbc.driver.OracleDriver");
private DatabaseType(String driver) {
this.driver = driver;
}
/**
* This is the suggested driver string to use in the configuration xml This corresponds to the
* <B>'driver'</b> attribute.
*/
private final String driver;
// ---------------------------------------------------------------
// ACCESSORS
// ----------------------------------------------------------------
/**
* Returns the suggested driver string to use for the given database type
*
* @return
*/
public String getSuggestedDriver() {
return (this.driver);
}
// ----------------------------------------------------------------
// STATIC METHODS + MEMBERS
// ----------------------------------------------------------------
protected static final Map<Integer, DatabaseType> idx_lookup =
new HashMap<Integer, DatabaseType>();
protected static final Map<String, DatabaseType> name_lookup =
new HashMap<String, DatabaseType>();
static {
for (DatabaseType vt : EnumSet.allOf(DatabaseType.class)) {
DatabaseType.idx_lookup.put(vt.ordinal(), vt);
DatabaseType.name_lookup.put(vt.name().toUpperCase(), vt);
}
}
public static DatabaseType get(String name) {
DatabaseType ret = DatabaseType.name_lookup.get(name.toUpperCase());
return (ret);
}
}

View File

@ -0,0 +1,63 @@
/*
* OtterTune - JSONSchemaType.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.types;
import com.controller.util.FileUtil;
import com.controller.util.ValidationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.main.JsonSchema;
import java.io.File;
import java.io.IOException;
public enum JSONSchemaType {
/** Parameters: (1) schema filename */
OUTPUT("schema.json"),
CONFIG("config_schema.json"),
SUMMARY("summary_schema.json");
// Path to JSON schema directory
private static final String SCHEMA_PATH = "src/main/java/com/controller/json_validation_schema";
private final JsonSchema schema;
private JSONSchemaType(String fileName) {
JsonSchema newSchema = null;
String configPath = FileUtil.joinPath(SCHEMA_PATH, fileName);
try {
newSchema = ValidationUtils.getSchemaNode(new File(configPath));
} catch (IOException | ProcessingException e) {
e.printStackTrace();
}
this.schema = newSchema;
}
public JsonSchema getSchema() {
return this.schema;
}
public static boolean isValidJson(JSONSchemaType schemaType, String jsonString) {
try {
JsonNode jsonNode = ValidationUtils.getJsonNode(jsonString);
return ValidationUtils.isJsonValid(schemaType.getSchema(), jsonNode);
} catch (IOException | ProcessingException e) {
e.printStackTrace();
}
return false;
}
public static boolean isValidJson(JSONSchemaType schemaType, File jsonFile) {
try {
JsonNode jsonNode = ValidationUtils.getJsonNode(jsonFile);
return ValidationUtils.isJsonValid(schemaType.getSchema(), jsonNode);
} catch (IOException | ProcessingException e) {
e.printStackTrace();
}
return false;
}
}

View File

@ -0,0 +1,297 @@
/*
* OtterTune - ClassUtil.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.log4j.Logger;
/** @author pavlo */
public abstract class ClassUtil {
private static final Logger LOG = Logger.getLogger(ClassUtil.class);
private static final Class<?>[] EMPTY_ARRAY = new Class[] {};
private static final Map<Class<?>, List<Class<?>>> CACHE_getSuperClasses =
new HashMap<Class<?>, List<Class<?>>>();
private static final Map<Class<?>, Set<Class<?>>> CACHE_getInterfaceClasses =
new HashMap<Class<?>, Set<Class<?>>>();
/**
* Check if the given object is an array (primitve or native).
* http://www.java2s.com/Code/Java/Reflection/Checkifthegivenobjectisanarrayprimitveornative.htm
*
* @param obj Object to test.
* @return True of the object is an array.
*/
public static boolean isArray(final Object obj) {
return (obj != null ? obj.getClass().isArray() : false);
}
public static boolean[] isArray(final Object[] objs) {
boolean[] isArray = new boolean[objs.length];
for (int i = 0; i < objs.length; i++) {
isArray[i] = ClassUtil.isArray(objs[i]);
} // FOR
return (isArray);
}
/**
* Convert a Enum array to a Field array This assumes that the name of each Enum element
* corresponds to a data member in the clas
*
* @param <E>
* @param clazz
* @param members
* @return
* @throws NoSuchFieldException
*/
public static <E extends Enum<?>> Field[] getFieldsFromMembersEnum(Class<?> clazz, E[] members)
throws NoSuchFieldException {
Field[] fields = new Field[members.length];
for (int i = 0; i < members.length; i++) {
fields[i] = clazz.getDeclaredField(members[i].name().toLowerCase());
} // FOR
return (fields);
}
/**
* Get the generic types for the given field
*
* @param field
* @return
*/
public static List<Class<?>> getGenericTypes(Field field) {
ArrayList<Class<?>> genericClasses = new ArrayList<Class<?>>();
Type gtype = field.getGenericType();
if (gtype instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) gtype;
getGenericTypesImpl(ptype, genericClasses);
}
return (genericClasses);
}
private static void getGenericTypesImpl(ParameterizedType ptype, List<Class<?>> classes) {
// list the actual type arguments
for (Type t : ptype.getActualTypeArguments()) {
if (t instanceof Class) {
// System.err.println("C: " + t);
classes.add((Class<?>) t);
} else if (t instanceof ParameterizedType) {
ParameterizedType next = (ParameterizedType) t;
// System.err.println("PT: " + next);
classes.add((Class<?>) next.getRawType());
getGenericTypesImpl(next, classes);
}
} // FOR
return;
}
/**
* Return an ordered list of all the sub-classes for a given class Useful when dealing with
* generics
*
* @param elementClass
* @return
*/
public static List<Class<?>> getSuperClasses(Class<?> elementClass) {
List<Class<?>> ret = ClassUtil.CACHE_getSuperClasses.get(elementClass);
if (ret == null) {
ret = new ArrayList<Class<?>>();
while (elementClass != null) {
ret.add(elementClass);
elementClass = elementClass.getSuperclass();
} // WHILE
ret = Collections.unmodifiableList(ret);
ClassUtil.CACHE_getSuperClasses.put(elementClass, ret);
}
return (ret);
}
/**
* Get a set of all of the interfaces that the element_class implements
*
* @param elementClass
* @return
*/
@SuppressWarnings("unchecked")
public static Collection<Class<?>> getInterfaces(Class<?> elementClass) {
Set<Class<?>> ret = ClassUtil.CACHE_getInterfaceClasses.get(elementClass);
if (ret == null) {
// ret = new HashSet<Class<?>>();
// Queue<Class<?>> queue = new LinkedList<Class<?>>();
// queue.add(element_class);
// while (!queue.isEmpty()) {
// Class<?> current = queue.poll();
// for (Class<?> i : current.getInterfaces()) {
// ret.add(i);
// queue.add(i);
// } // FOR
// } // WHILE
ret = new HashSet<Class<?>>(ClassUtils.getAllInterfaces(elementClass));
if (elementClass.isInterface()) {
ret.add(elementClass);
}
ret = Collections.unmodifiableSet(ret);
ClassUtil.CACHE_getInterfaceClasses.put(elementClass, ret);
}
return (ret);
}
@SuppressWarnings("unchecked")
public static <T> T newInstance(String className, Object[] params, Class<?>[] classes) {
return ((T) ClassUtil.newInstance(ClassUtil.getClass(className), params, classes));
}
public static <T> T newInstance(Class<T> targetClass, Object[] params, Class<?>[] classes) {
// Class<?> const_params[] = new Class<?>[params.length];
// for (int i = 0; i < params.length; i++) {
// const_params[i] = params[i].getClass();
// System.err.println("[" + i + "] " + params[i] + " " + params[i].getClass());
// } // FOR
Constructor<T> constructor = ClassUtil.getConstructor(targetClass, classes);
T ret = null;
try {
ret = constructor.newInstance(params);
} catch (Exception ex) {
throw new RuntimeException(
"Failed to create new instance of " + targetClass.getSimpleName(), ex);
}
return (ret);
}
/**
* Create an object for the given class and initialize it from conf
*
* @param theClass class of which an object is created
* @param expected the expected parent class or interface
* @return a new object
*/
public static <T> T newInstance(Class<?> theClass, Class<T> expected) {
T result;
try {
if (!expected.isAssignableFrom(theClass)) {
throw new Exception(
"Specified class "
+ theClass.getName()
+ ""
+ "does not extend/implement "
+ expected.getName());
}
Class<? extends T> clazz = (Class<? extends T>) theClass;
Constructor<? extends T> meth = clazz.getDeclaredConstructor(EMPTY_ARRAY);
meth.setAccessible(true);
result = meth.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
public static <T> T newInstance(String className, Class<T> expected)
throws ClassNotFoundException {
return newInstance(getClass(className), expected);
}
/**
* @param <T>
* @param targetClass
* @param params
* @return
*/
@SuppressWarnings("unchecked")
public static <T> Constructor<T> getConstructor(Class<T> targetClass, Class<?>... params) {
NoSuchMethodException error = null;
try {
return (targetClass.getConstructor(params));
} catch (NoSuchMethodException ex) {
// The first time we get this it can be ignored
// We'll try to be nice and find a match for them
error = ex;
}
assert (error != null);
if (LOG.isDebugEnabled()) {
LOG.debug("TARGET CLASS: " + targetClass);
LOG.debug("TARGET PARAMS: " + Arrays.toString(params));
}
List<Class<?>>[] paramSuper = (List<Class<?>>[]) new List[params.length];
for (int i = 0; i < params.length; i++) {
paramSuper[i] = ClassUtil.getSuperClasses(params[i]);
if (LOG.isDebugEnabled()) {
LOG.debug(" SUPER[" + params[i].getSimpleName() + "] => " + paramSuper[i]);
}
} // FOR
for (Constructor<?> c : targetClass.getConstructors()) {
Class<?>[] ctypes = c.getParameterTypes();
if (LOG.isDebugEnabled()) {
LOG.debug("CANDIDATE: " + c);
LOG.debug("CANDIDATE PARAMS: " + Arrays.toString(ctypes));
}
if (params.length != ctypes.length) {
continue;
}
for (int i = 0; i < params.length; i++) {
List<Class<?>> csuper = ClassUtil.getSuperClasses(ctypes[i]);
if (LOG.isDebugEnabled()) {
LOG.debug(" SUPER[" + ctypes[i].getSimpleName() + "] => " + csuper);
}
if (CollectionUtils.intersection(paramSuper[i], csuper).isEmpty() == false) {
return ((Constructor<T>) c);
}
} // FOR (param)
} // FOR (constructors)
throw new RuntimeException(
"Failed to retrieve constructor for " + targetClass.getSimpleName(), error);
}
/**
* @param className
* @return
*/
public static Class<?> getClass(String className) {
Class<?> targetClass = null;
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
targetClass = (Class<?>) loader.loadClass(className);
} catch (Exception ex) {
throw new RuntimeException("Failed to retrieve class for " + className, ex);
}
return (targetClass);
}
/**
* Returns true if asserts are enabled. This assumes that we're always using the default system
* ClassLoader
*/
public static boolean isAssertsEnabled() {
boolean ret = false;
try {
assert (false);
} catch (AssertionError ex) {
ret = true;
}
return (ret);
}
}

View File

@ -0,0 +1,491 @@
/*
* OtterTune - CollectionUtil.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.commons.lang.NotImplementedException;
/** @author pavlo */
public abstract class CollectionUtil {
private static final Random RANDOM = new Random();
/**
* Put all of the elements in items into the given array This assumes that the array has been
* pre-allocated
*
* @param <T>
* @param items
* @param array
*/
public static <T> void toArray(
Collection<T> items, Object[] array, boolean convertToPrimitive) {
assert (items.size() == array.length);
int i = 0;
for (T t : items) {
if (convertToPrimitive) {
if (t instanceof Long) {
array[i] = ((Long) t).longValue();
} else if (t instanceof Integer) {
array[i] = ((Integer) t).intValue();
} else if (t instanceof Double) {
array[i] = ((Double) t).doubleValue();
} else if (t instanceof Boolean) {
array[i] = ((Boolean) t).booleanValue();
}
} else {
array[i] = t;
}
}
return;
}
public static int[] toIntArray(Collection<Integer> items) {
int[] ret = new int[items.size()];
int idx = 0;
for (Integer i : items) {
assert (i != null);
ret[idx++] = i.intValue();
} // FOR
return (ret);
}
/**
* Put all the values of an Iterator into a List
*
* @param <T>
* @param it
* @return
*/
public static <T> List<T> list(Iterator<T> it) {
List<T> list = new ArrayList<T>();
CollectionUtil.addAll(list, it);
return (list);
}
/**
* Put all of the values of an Enumeration into a new List
*
* @param <T>
* @param e
* @return
*/
public static <T> List<T> list(Enumeration<T> e) {
return (list(iterable(e)));
}
/**
* Put all of the values of an Iterable into a new List
*
* @param <T>
* @param it
* @return
*/
public static <T> List<T> list(Iterable<T> it) {
return (list(it.iterator()));
}
/**
* Put all the values of an Iterator into a Set
*
* @param <T>
* @param it
* @return
*/
public static <T> Set<T> set(Iterator<T> it) {
Set<T> set = new HashSet<T>();
CollectionUtil.addAll(set, it);
return (set);
}
/**
* Put all of the values of an Iterable into a new Set
*
* @param <T>
* @param it
* @return
*/
public static <T> Set<T> set(Iterable<T> it) {
return (set(it.iterator()));
}
/**
* Returns a list containing the string representations of the elements in the collection
*
* @param <T>
* @param data
* @return
*/
public static <T> List<String> toStringList(Collection<T> data) {
List<String> ret = new ArrayList<String>();
for (T t : data) {
ret.add(t.toString());
}
return (ret);
}
/**
* Returns a set containing the string representations of the elements in the collection
*
* @param <T>
* @param data
* @return
*/
public static <T> Set<String> toStringSet(Collection<T> data) {
Set<String> ret = new HashSet<String>();
for (T t : data) {
ret.add(t.toString());
}
return (ret);
}
/**
* Return a random value from the given Collection
*
* @param <T>
* @param items
*/
public static <T> T random(Collection<T> items) {
return (CollectionUtil.random(items, RANDOM));
}
/**
* Return a random value from the given Collection
*
* @param <T>
* @param items
* @param rand
* @return
*/
public static <T> T random(Collection<T> items, Random rand) {
int idx = rand.nextInt(items.size());
return (CollectionUtil.get(items, idx));
}
/**
* Return a random value from the given Iterable
*
* @param <T>
* @param it
* @return
*/
public static <T> T random(Iterable<T> it) {
return (CollectionUtil.random(it, RANDOM));
}
/**
* Return a random value from the given Iterable
*
* @param <T>
* @param it
* @param rand
* @return
*/
public static <T> T random(Iterable<T> it, Random rand) {
List<T> list = new ArrayList<T>();
for (T t : it) {
list.add(t);
} // FOR
return (CollectionUtil.random(list, rand));
}
public static <E extends Enum<?>> Set<E> getAllExcluding(E[] elements, E... excluding) {
Set<E> excludeSet = new HashSet<E>();
for (E e : excluding) {
excludeSet.add(e);
}
Set<E> elementsSet = new HashSet<E>();
for (int i = 0; i < elements.length; i++) {
if (!excludeSet.contains(elements[i])) {
elementsSet.add(elements[i]);
}
} // FOR
return (elementsSet);
// Crappy java....
// Object new_elements[] = new Object[elements_set.size()];
// elements_set.toArray(new_elements);
// return ((E[])new_elements);
}
/**
* Add all the items in the array to a Collection
*
* @param <T>
* @param data
* @param items
*/
public static <T> Collection<T> addAll(Collection<T> data, T... items) {
for (T i : (T[]) items) {
data.add(i);
}
return (data);
}
/**
* Add all the items in the Enumeration into a Collection
*
* @param <T>
* @param data
* @param items
*/
public static <T> Collection<T> addAll(Collection<T> data, Enumeration<T> items) {
while (items.hasMoreElements()) {
data.add(items.nextElement());
} // WHILE
return (data);
}
/**
* Add all of the items from the Iterable into the given collection
*
* @param <T>
* @param data
* @param items
*/
public static <T> Collection<T> addAll(Collection<T> data, Iterable<T> items) {
return (CollectionUtil.addAll(data, items.iterator()));
}
/**
* Add all of the items from the Iterator into the given collection
*
* @param <T>
* @param data
* @param items
*/
public static <T> Collection<T> addAll(Collection<T> data, Iterator<T> items) {
while (items.hasNext()) {
data.add(items.next());
} // WHILE
return (data);
}
/**
* @param <T>
* @param <U>
* @param map
* @return
*/
public static <T, U extends Comparable<U>> T getGreatest(Map<T, U> map) {
T maxKey = null;
U maxValue = null;
for (Entry<T, U> e : map.entrySet()) {
T key = e.getKey();
U value = e.getValue();
if (maxValue == null || value.compareTo(maxValue) > 0) {
maxValue = value;
maxKey = key;
}
} // FOR
return (maxKey);
}
/**
* Return the first item in a Iterable
*
* @param <T>
* @param items
* @return
*/
public static <T> T first(Iterable<T> items) {
return (CollectionUtil.get(items, 0));
}
/**
* Return the first item in a Iterator
*
* @param <T>
* @param items
* @return
*/
public static <T> T first(Iterator<T> items) {
return (items.hasNext() ? items.next() : null);
}
/**
* Returns the first item in an Enumeration
*
* @param <T>
* @param items
* @return
*/
public static <T> T first(Enumeration<T> items) {
return (items.hasMoreElements() ? items.nextElement() : null);
}
/**
* Return the ith element of a set. Super lame
*
* @param <T>
* @param items
* @param idx
* @return
*/
public static <T> T get(Iterable<T> items, int idx) {
if (items instanceof AbstractList<?>) {
return ((AbstractList<T>) items).get(idx);
} else if (items instanceof ListOrderedSet<?>) {
return ((ListOrderedSet<T>) items).get(idx);
}
int ctr = 0;
for (T t : items) {
if (ctr++ == idx) {
return (t);
}
}
return (null);
}
/**
* Return the last item in an Iterable
*
* @param <T>
* @param items
* @return
*/
public static <T> T last(Iterable<T> items) {
T last = null;
if (items instanceof AbstractList<?>) {
AbstractList<T> list = (AbstractList<T>) items;
last = (list.isEmpty() ? null : list.get(list.size() - 1));
} else {
for (T t : items) {
last = t;
}
}
return (last);
}
/**
* Return the last item in an array
*
* @param <T>
* @param items
* @return
*/
public static <T> T last(T... items) {
if (items != null && items.length > 0) {
return (items[items.length - 1]);
}
return (null);
}
/**
* @param <K>
* @param <V>
* @param map
* @return
*/
public static <K extends Comparable<?>, V> List<V> getSortedList(
SortedMap<K, Collection<V>> map) {
List<V> ret = new ArrayList<V>();
for (Collection<V> col : map.values()) {
ret.addAll(col);
} // FOR
return (ret);
}
/**
* Wrap an Iterable around an Iterator
*
* @param <T>
* @param it
* @return
*/
public static <T> Iterable<T> iterable(final Iterator<T> it) {
return (new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return (it);
}
});
}
public static <T> Iterable<T> iterable(final T[] values) {
return (new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int idx = 0;
@Override
public boolean hasNext() {
return (this.idx < values.length);
}
@Override
public T next() {
if (this.idx == values.length) {
throw new NoSuchElementException();
}
return values[this.idx++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
});
}
/**
* Wrap an Iterable around an Enumeration
*
* @param <T>
* @param e
* @return
*/
public static <T> Iterable<T> iterable(final Enumeration<T> e) {
return (new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return (e.hasMoreElements());
}
@Override
public T next() {
return (e.nextElement());
}
@Override
public void remove() {
throw new NotImplementedException();
}
};
}
});
}
public static <T> T pop(Collection<T> items) {
T t = CollectionUtil.first(items);
if (t != null) {
boolean ret = items.remove(t);
assert (ret);
}
return (t);
}
}

View File

@ -0,0 +1,383 @@
/*
* OtterTune - FileUtil.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import org.apache.log4j.Logger;
/** @author pavlo */
public abstract class FileUtil {
private static final Logger LOG = Logger.getLogger(FileUtil.class);
private static final Pattern EXT_SPLIT = Pattern.compile("\\.");
/**
* Join path components
*
* @param args
* @return
*/
public static String joinPath(String... args) {
StringBuilder result = new StringBuilder();
boolean first = true;
for (String a : args) {
if (a != null && a.length() > 0) {
if (!first) {
result.append("/");
}
result.append(a);
first = false;
}
}
return result.toString();
}
/**
* Given a basename for a file, find the next possible filename if this file already exists. For
* example, if the file test.res already exists, create a file called, test.1.res
*
* @param basename
* @return
*/
public static String getNextFilename(String basename) {
if (!exists(basename)) {
return basename;
}
File f = new File(basename);
if (f != null && f.isFile()) {
String[] parts = EXT_SPLIT.split(basename);
// Check how many files already exist
int counter = 1;
String nextName = parts[0] + "." + counter + "." + parts[1];
while (exists(nextName)) {
++counter;
nextName = parts[0] + "." + counter + "." + parts[1];
}
return nextName;
}
// Should we throw instead??
return null;
}
public static boolean exists(String path) {
return (new File(path).exists());
}
public static String realpath(String path) {
File f = new File(path);
String ret = null;
try {
ret = f.getCanonicalPath();
} catch (Exception ex) {
LOG.warn(ex);
}
return (ret);
}
public static String basename(String path) {
return (new File(path)).getName();
}
public static String getExtension(File f) {
if (f != null && f.isFile()) {
String[] parts = EXT_SPLIT.split(f.getName());
if (parts.length > 1) {
return (parts[parts.length - 1]);
}
}
return (null);
}
/**
* Create any directory in the list paths if it doesn't exist
*
* @param paths
*/
public static void makeDirIfNotExists(String... paths) {
for (String p : paths) {
if (p == null) {
continue;
}
File f = new File(p);
if (f.exists() == false) {
f.mkdirs();
}
} // FOR
}
/**
* Return a File handle to a temporary file location
*
* @param ext the suffix of the filename
* @param deleteOnExit whether to delete this file after the JVM exits
* @return
*/
public static File getTempFile(String ext, boolean deleteOnExit) {
return getTempFile(null, ext, deleteOnExit);
}
public static File getTempFile(String ext) {
return (FileUtil.getTempFile(null, ext, false));
}
public static File getTempFile(String prefix, String suffix, boolean deleteOnExit) {
File tempFile;
if (suffix != null && suffix.startsWith(".") == false) {
suffix = "." + suffix;
}
if (prefix == null) {
prefix = "hstore";
}
try {
tempFile = File.createTempFile(prefix, suffix);
if (deleteOnExit) {
tempFile.deleteOnExit();
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return (tempFile);
}
/**
* Unsafely create a temporary directory Yes I said that this was unsafe. I don't care...
*
* @return
*/
public static File getTempDirectory() {
final File temp = FileUtil.getTempFile(null);
if (!(temp.delete())) {
throw new RuntimeException("Could not delete temp file: " + temp.getAbsolutePath());
} else if (!(temp.mkdir())) {
throw new RuntimeException("Could not create temp directory: " + temp.getAbsolutePath());
}
return (temp);
}
public static File writeStringToFile(String filePath, String content) throws IOException {
return (FileUtil.writeStringToFile(new File(filePath), content));
}
public static File writeStringToFile(File file, String content) throws IOException {
FileWriter writer = new FileWriter(file);
writer.write(content);
writer.flush();
writer.close();
return (file);
}
/**
* Write the given string to a temporary file Will not delete the file after the JVM exits
*
* @param content
* @return
*/
public static File writeStringToTempFile(String content) {
return (writeStringToTempFile(content, "tmp", false));
}
/**
* Write the given string to a temporary file with the given extension as the suffix Will not
* delete the file after the JVM exits
*
* @param content
* @param ext
* @return
*/
public static File writeStringToTempFile(String content, String ext) {
return (writeStringToTempFile(content, ext, false));
}
/**
* Write the given string to a temporary file with the given extension as the suffix If
* deleteOnExit is true, then the file will be removed when the JVM exits
*
* @param content
* @param ext
* @param deleteOnExit
* @return
*/
public static File writeStringToTempFile(String content, String ext, boolean deleteOnExit) {
File tempFile = FileUtil.getTempFile(ext, deleteOnExit);
try {
FileUtil.writeStringToFile(tempFile, content);
} catch (Exception e) {
throw new RuntimeException(e);
}
return tempFile;
}
public static String readFile(File path) {
LOG.debug(path);
return (readFile(path.getAbsolutePath()));
}
public static String readFile(String path) {
StringBuilder buffer = new StringBuilder();
try {
BufferedReader in = FileUtil.getReader(path);
while (in.ready()) {
buffer.append(in.readLine()).append("\n");
} // WHILE
in.close();
} catch (IOException ex) {
throw new RuntimeException("Failed to read file contents from '" + path + "'", ex);
}
return (buffer.toString());
}
/**
* Creates a BufferedReader for the given input path Can handle both gzip and plain text files
*
* @param path
* @return
* @throws IOException
*/
public static BufferedReader getReader(String path) throws IOException {
return (FileUtil.getReader(new File(path)));
}
/**
* Creates a BufferedReader for the given input path Can handle both gzip and plain text files
*
* @param file
* @return
* @throws IOException
*/
public static BufferedReader getReader(File file) throws IOException {
if (!file.exists()) {
throw new IOException("The file '" + file + "' does not exist");
}
BufferedReader in = null;
if (file.getPath().endsWith(".gz")) {
FileInputStream fin = new FileInputStream(file);
GZIPInputStream gzis = new GZIPInputStream(fin);
in = new BufferedReader(new InputStreamReader(gzis));
LOG.debug("Reading in the zipped contents of '" + file.getName() + "'");
} else {
in = new BufferedReader(new FileReader(file));
LOG.debug("Reading in the contents of '" + file.getName() + "'");
}
return (in);
}
public static byte[] readBytesFromFile(String path) throws IOException {
File file = new File(path);
FileInputStream in = new FileInputStream(file);
// Create the byte array to hold the data
long length = file.length();
byte[] bytes = new byte[(int) length];
LOG.debug("Reading in the contents of '" + file.getAbsolutePath() + "'");
// Read in the bytes
int offset = 0;
int numRead = 0;
while ((offset < bytes.length)
&& ((numRead = in.read(bytes, offset, bytes.length - offset)) >= 0)) {
offset += numRead;
} // WHILE
if (offset < bytes.length) {
throw new IOException("Failed to read the entire contents of '" + file.getName() + "'");
}
in.close();
return (bytes);
}
/**
* Find the path to a directory below our current location in the source tree Throws a
* RuntimeException if we go beyond our repository checkout
*
* @param dirName
* @return
* @throws IOException
*/
public static File findDirectory(String dirName) throws IOException {
return (FileUtil.find(dirName, new File(".").getCanonicalFile(), true).getCanonicalFile());
}
/**
* Find the path to a directory below our current location in the source tree Throws a
* RuntimeException if we go beyond our repository checkout
*
* @param dirName
* @return
* @throws IOException
*/
public static File findFile(String fileName) throws IOException {
return (FileUtil.find(fileName, new File(".").getCanonicalFile(), false).getCanonicalFile());
}
private static final File find(String name, File current, boolean isdir) throws IOException {
LOG.debug("Find Current Location = " + current);
boolean hasSvn = false;
for (File file : current.listFiles()) {
if (file.getCanonicalPath().endsWith(File.separator + name) && file.isDirectory() == isdir) {
return (file);
// Make sure that we don't go to far down...
} else if (file.getCanonicalPath().endsWith(File.separator + ".svn")) {
hasSvn = true;
}
} // FOR
// If we didn't see an .svn directory, then we went too far down
if (!hasSvn) {
throw new RuntimeException(
"Unable to find directory '" + name + "' [last_dir=" + current.getAbsolutePath() + "]");
}
File next = new File(current.getCanonicalPath() + File.separator + "");
return (FileUtil.find(name, next, isdir));
}
/**
* Returns a list of all the files in a directory whose name starts with the provided prefix
*
* @param dir
* @param filePrefix
* @return
* @throws IOException
*/
public static List<File> getFilesInDirectory(final File dir, final String filePrefix)
throws IOException {
assert (dir.isDirectory()) : "Invalid search directory path: " + dir;
FilenameFilter filter =
new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return (name.startsWith(filePrefix));
}
};
return (Arrays.asList(dir.listFiles(filter)));
}
public static String getAbsPath(String dirPath) {
String current = null;
try {
current = new java.io.File(dirPath).getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
return current;
}
}

View File

@ -0,0 +1,23 @@
/*
* OtterTune - JSONSerializable.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONString;
import com.controller.util.json.JSONStringer;
import java.io.IOException;
public interface JSONSerializable extends JSONString {
public void save(String outputPath) throws IOException;
public void load(String inputPath) throws IOException;
public void toJSON(JSONStringer stringer) throws JSONException;
public void fromJSON(JSONObject jsonObject) throws JSONException;
}

View File

@ -0,0 +1,800 @@
/*
* OtterTune - JSONUtil.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import com.controller.util.json.JSONArray;
import com.controller.util.json.JSONException;
import com.controller.util.json.JSONObject;
import com.controller.util.json.JSONStringer;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import org.apache.log4j.Logger;
/** @author pavlo */
public abstract class JSONUtil {
private static final Logger LOG = Logger.getLogger(JSONUtil.class.getName());
private static final String JSON_CLASS_SUFFIX = "_class";
private static final Map<Class<?>, Field[]> SERIALIZABLE_FIELDS =
new HashMap<Class<?>, Field[]>();
/**
* @param clazz
* @return
*/
public static Field[] getSerializableFields(Class<?> clazz, String... fieldsToExclude) {
Field[] ret = SERIALIZABLE_FIELDS.get(clazz);
if (ret == null) {
Collection<String> exclude = CollectionUtil.addAll(new HashSet<String>(), fieldsToExclude);
synchronized (SERIALIZABLE_FIELDS) {
ret = SERIALIZABLE_FIELDS.get(clazz);
if (ret == null) {
List<Field> fields = new ArrayList<Field>();
for (Field f : clazz.getFields()) {
int modifiers = f.getModifiers();
if (Modifier.isTransient(modifiers) == false
&& Modifier.isPublic(modifiers) == true
&& Modifier.isStatic(modifiers) == false
&& exclude.contains(f.getName()) == false) {
fields.add(f);
}
} // FOR
ret = fields.toArray(new Field[0]);
SERIALIZABLE_FIELDS.put(clazz, ret);
}
} // SYNCH
}
return (ret);
}
/**
* JSON Pretty Print
*
* @param json
* @return
* @throws JSONException
*/
public static String format(String json) {
try {
return (JSONUtil.format(new JSONObject(json)));
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* JSON Pretty Print
*
* @param <T>
* @param object
* @return
*/
public static <T extends JSONSerializable> String format(T object) {
JSONStringer stringer = new JSONStringer();
try {
if (object instanceof JSONObject) {
return ((JSONObject) object).toString(2);
}
stringer.object();
object.toJSON(stringer);
stringer.endObject();
} catch (JSONException ex) {
throw new RuntimeException(ex);
}
return (JSONUtil.format(stringer.toString()));
}
public static String format(JSONObject o) {
try {
return o.toString(1);
} catch (JSONException ex) {
throw new RuntimeException(ex);
}
}
/**
* @param <T>
* @param object
* @return
*/
public static String toJSONString(Object object) {
JSONStringer stringer = new JSONStringer();
try {
if (object instanceof JSONSerializable) {
stringer.object();
((JSONSerializable) object).toJSON(stringer);
stringer.endObject();
} else if (object != null) {
Class<?> clazz = object.getClass();
// stringer.key(clazz.getSimpleName());
JSONUtil.writeFieldValue(stringer, clazz, object);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
return (stringer.toString());
}
public static <T extends JSONSerializable> T fromJSONString(T t, String json) {
try {
JSONObject jsonObject = new JSONObject(json);
t.fromJSON(jsonObject);
} catch (JSONException ex) {
throw new RuntimeException("Failed to deserialize object " + t, ex);
}
return (t);
}
/**
* Write the contents of a JSONSerializable object out to a file on the local disk
*
* @param <T>
* @param object
* @param outputPath
* @throws IOException
*/
public static <T extends JSONSerializable> void save(T object, String outputPath)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(
"Writing out contents of "
+ object.getClass().getSimpleName()
+ " to '"
+ outputPath
+ "'");
}
File f = new File(outputPath);
try {
FileUtil.makeDirIfNotExists(f.getParent());
String json = object.toJSONString();
FileUtil.writeStringToFile(f, format(json));
} catch (Exception ex) {
LOG.error(
"Failed to serialize the " + object.getClass().getSimpleName() + " file '" + f + "'", ex);
throw new IOException(ex);
}
}
/**
* Load in a JSONSerialable stored in a file
*
* @param <T>
* @param object
* @param inputPath
* @throws Exception
*/
public static <T extends JSONSerializable> void load(T object, String inputPath)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(
"Loading in serialized "
+ object.getClass().getSimpleName()
+ " from '"
+ inputPath
+ "'");
}
String contents = FileUtil.readFile(inputPath);
if (contents.isEmpty()) {
throw new IOException(
"The " + object.getClass().getSimpleName() + " file '" + inputPath + "' is empty");
}
try {
object.fromJSON(new JSONObject(contents));
} catch (Exception ex) {
if (LOG.isDebugEnabled()) {
LOG.error(
"Failed to deserialize the "
+ object.getClass().getSimpleName()
+ " from file '"
+ inputPath
+ "'",
ex);
}
throw new IOException(ex);
}
if (LOG.isDebugEnabled()) {
LOG.debug("The loading of the " + object.getClass().getSimpleName() + " is complete");
}
}
/**
* For a given Enum, write out the contents of the corresponding field to the JSONObject We assume
* that the given object has matching fields that correspond to the Enum members, except that
* their names are lower case.
*
* @param <E>
* @param <T>
* @param stringer
* @param object
* @param baseClass
* @param members
* @throws JSONException
*/
public static <E extends Enum<?>, T> void fieldsToJSON(
JSONStringer stringer, T object, Class<? extends T> baseClass, E[] members)
throws JSONException {
try {
fieldsToJSON(
stringer, object, baseClass, ClassUtil.getFieldsFromMembersEnum(baseClass, members));
} catch (NoSuchFieldException ex) {
throw new JSONException(ex);
}
}
/**
* For a given list of Fields, write out the contents of the corresponding field to the JSONObject
* The each of the JSONObject's elements will be the upper case version of the Field's name
*
* @param <T>
* @param stringer
* @param object
* @param baseClass
* @param fields
* @throws JSONException
*/
public static <T> void fieldsToJSON(
JSONStringer stringer, T object, Class<? extends T> baseClass, Field[] fields)
throws JSONException {
if (LOG.isDebugEnabled()) {
LOG.debug("Serializing out " + fields.length + " elements for " + baseClass.getSimpleName());
}
for (Field f : fields) {
String jsonKey = f.getName().toUpperCase();
stringer.key(jsonKey);
try {
Class<?> fclass = f.getType();
Object fvalue = f.get(object);
// Null
if (fvalue == null) {
writeFieldValue(stringer, fclass, fvalue);
// Maps
} else if (fvalue instanceof Map) {
writeFieldValue(stringer, fclass, fvalue);
// Everything else
} else {
writeFieldValue(stringer, fclass, fvalue);
}
} catch (Exception ex) {
throw new JSONException(ex);
}
} // FOR
}
/**
* @param stringer
* @param fieldClass
* @param fieldValue
* @throws JSONException
*/
public static void writeFieldValue(
JSONStringer stringer, Class<?> fieldClass, Object fieldValue) throws JSONException {
// Null
if (fieldValue == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("writeNullFieldValue(" + fieldClass + ", " + fieldValue + ")");
}
stringer.value(null);
// Collections
} else if (ClassUtil.getInterfaces(fieldClass).contains(Collection.class)) {
if (LOG.isDebugEnabled()) {
LOG.debug("writeCollectionFieldValue(" + fieldClass + ", " + fieldValue + ")");
}
stringer.array();
for (Object value : (Collection<?>) fieldValue) {
if (value == null) {
stringer.value(null);
} else {
writeFieldValue(stringer, value.getClass(), value);
}
} // FOR
stringer.endArray();
// Maps
} else if (fieldValue instanceof Map) {
if (LOG.isDebugEnabled()) {
LOG.debug("writeMapFieldValue(" + fieldClass + ", " + fieldValue + ")");
}
stringer.object();
for (Entry<?, ?> e : ((Map<?, ?>) fieldValue).entrySet()) {
// We can handle null keys
String keyValue = null;
if (e.getKey() != null) {
// deserialize it on the other side
Class<?> keyClass = e.getKey().getClass();
keyValue = makePrimitiveValue(keyClass, e.getKey()).toString();
}
stringer.key(keyValue);
// We can also handle null values. Where is your god now???
if (e.getValue() == null) {
stringer.value(null);
} else {
writeFieldValue(stringer, e.getValue().getClass(), e.getValue());
}
} // FOR
stringer.endObject();
// Primitive
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("writePrimitiveFieldValue(" + fieldClass + ", " + fieldValue + ")");
}
stringer.value(makePrimitiveValue(fieldClass, fieldValue));
}
return;
}
/**
* Read data from the given JSONObject and populate the given Map
*
* @param jsonObject
* @param map
* @param innerClasses
* @throws Exception
*/
@SuppressWarnings("unchecked")
protected static void readMapField(
final JSONObject jsonObject, final Map map, final Stack<Class> innerClasses)
throws Exception {
Class<?> keyClass = innerClasses.pop();
Class<?> valClass = innerClasses.pop();
Collection<Class<?>> valInterfaces = ClassUtil.getInterfaces(valClass);
assert (jsonObject != null);
for (String jsonKey : CollectionUtil.iterable(jsonObject.keys())) {
final Stack<Class> nextInnerClasses = new Stack<Class>();
nextInnerClasses.addAll(innerClasses);
assert (nextInnerClasses.equals(innerClasses));
// KEY
Object key = JSONUtil.getPrimitiveValue(jsonKey, keyClass);
// VALUE
Object object = null;
if (jsonObject.isNull(jsonKey)) {
// Nothing...
} else if (valInterfaces.contains(List.class)) {
object = new ArrayList();
readCollectionField(
jsonObject.getJSONArray(jsonKey), (Collection) object, nextInnerClasses);
} else if (valInterfaces.contains(Set.class)) {
object = new HashSet();
readCollectionField(
jsonObject.getJSONArray(jsonKey), (Collection) object, nextInnerClasses);
} else if (valInterfaces.contains(Map.class)) {
object = new HashMap();
readMapField(jsonObject.getJSONObject(jsonKey), (Map) object, nextInnerClasses);
} else {
String jsonString = jsonObject.getString(jsonKey);
try {
object = JSONUtil.getPrimitiveValue(jsonString, valClass);
} catch (Exception ex) {
System.err.println("val_interfaces: " + valInterfaces);
LOG.error(
"Failed to deserialize value '"
+ jsonString
+ "' from inner map key '"
+ jsonKey
+ "'");
throw ex;
}
}
map.put(key, object);
}
}
/**
* Read data from the given JSONArray and populate the given Collection
*
* @param jsonArray
* @param collection
* @param innerClasses
* @throws Exception
*/
@SuppressWarnings("unchecked")
protected static void readCollectionField(
final JSONArray jsonArray, final Collection collection, final Stack<Class> innerClasses)
throws Exception {
// We need to figure out what the inner type of the collection is
// If it's a Collection or a Map, then we need to instantiate it before
// we can call readFieldValue() again for it.
Class innerClass = innerClasses.pop();
Collection<Class<?>> innerInterfaces = ClassUtil.getInterfaces(innerClass);
for (int i = 0, cnt = jsonArray.length(); i < cnt; i++) {
final Stack<Class> nextInnerClasses = new Stack<Class>();
nextInnerClasses.addAll(innerClasses);
assert (nextInnerClasses.equals(innerClasses));
Object value = null;
// Null
if (jsonArray.isNull(i)) {
value = null;
// Lists
} else if (innerInterfaces.contains(List.class)) {
value = new ArrayList();
readCollectionField(jsonArray.getJSONArray(i), (Collection) value, nextInnerClasses);
// Sets
} else if (innerInterfaces.contains(Set.class)) {
value = new HashSet();
readCollectionField(jsonArray.getJSONArray(i), (Collection) value, nextInnerClasses);
// Maps
} else if (innerInterfaces.contains(Map.class)) {
value = new HashMap();
readMapField(jsonArray.getJSONObject(i), (Map) value, nextInnerClasses);
// Values
} else {
String jsonString = jsonArray.getString(i);
value = JSONUtil.getPrimitiveValue(jsonString, innerClass);
}
collection.add(value);
} // FOR
return;
}
/**
* @param jsonObject
* @param jsonKey
* @param fieldHandle
* @param object
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static void readFieldValue(
final JSONObject jsonObject, final String jsonKey, Field fieldHandle, Object object)
throws Exception {
assert (jsonObject.has(jsonKey)) : "No entry exists for '" + jsonKey + "'";
Class<?> fieldClass = fieldHandle.getType();
Object fieldObject = fieldHandle.get(object);
// String field_name = field_handle.getName();
// Null
if (jsonObject.isNull(jsonKey)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Field " + jsonKey + " is null");
}
fieldHandle.set(object, null);
// Collections
} else if (ClassUtil.getInterfaces(fieldClass).contains(Collection.class)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Field " + jsonKey + " is a collection");
}
assert (fieldObject != null);
Stack<Class> innerClasses = new Stack<Class>();
innerClasses.addAll(ClassUtil.getGenericTypes(fieldHandle));
Collections.reverse(innerClasses);
JSONArray jsonInner = jsonObject.getJSONArray(jsonKey);
if (jsonInner == null) {
throw new JSONException("No array exists for '" + jsonKey + "'");
}
readCollectionField(jsonInner, (Collection) fieldObject, innerClasses);
// Maps
} else if (fieldObject instanceof Map) {
if (LOG.isDebugEnabled()) {
LOG.debug("Field " + jsonKey + " is a map");
}
assert (fieldObject != null);
Stack<Class> innerClasses = new Stack<Class>();
innerClasses.addAll(ClassUtil.getGenericTypes(fieldHandle));
Collections.reverse(innerClasses);
JSONObject jsonInner = jsonObject.getJSONObject(jsonKey);
if (jsonInner == null) {
throw new JSONException("No object exists for '" + jsonKey + "'");
}
readMapField(jsonInner, (Map) fieldObject, innerClasses);
// Everything else...
} else {
Class explicitFieldClass = JSONUtil.getClassForField(jsonObject, jsonKey);
if (explicitFieldClass != null) {
fieldClass = explicitFieldClass;
if (LOG.isDebugEnabled()) {
LOG.debug(
"Found explict field class " + fieldClass.getSimpleName() + " for " + jsonKey);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Field " + jsonKey + " is primitive type " + fieldClass.getSimpleName());
}
Object value = JSONUtil.getPrimitiveValue(jsonObject.getString(jsonKey), fieldClass);
fieldHandle.set(object, value);
if (LOG.isDebugEnabled()) {
LOG.debug("Set field " + jsonKey + " to '" + value + "'");
}
}
}
/**
* For the given enum, load in the values from the JSON object into the current object This will
* throw errors if a field is missing
*
* @param <E>
* @param jsonObject
* @param members
* @throws JSONException
*/
public static <E extends Enum<?>, T> void fieldsFromJSON(
JSONObject jsonObject, T object, Class<? extends T> baseClass, E... members)
throws JSONException {
JSONUtil.fieldsFromJSON(jsonObject, object, baseClass, false, members);
}
/**
* For the given enum, load in the values from the JSON object into the current object If
* ignore_missing is false, then JSONUtil will not throw an error if a field is missing
*
* @param <E>
* @param <T>
* @param jsonObject
* @param object
* @param baseClass
* @param ignoreMissing
* @param members
* @throws JSONException
*/
public static <E extends Enum<?>, T> void fieldsFromJSON(
JSONObject jsonObject,
T object,
Class<? extends T> baseClass,
boolean ignoreMissing,
E... members)
throws JSONException {
try {
fieldsFromJSON(
jsonObject,
object,
baseClass,
ignoreMissing,
ClassUtil.getFieldsFromMembersEnum(baseClass, members));
} catch (NoSuchFieldException ex) {
throw new JSONException(ex);
}
}
/**
* For the given list of Fields, load in the values from the JSON object into the current object
* If ignore_missing is false, then JSONUtil will not throw an error if a field is missing
*
* @param <E>
* @param <T>
* @param jsonObject
* @param object
* @param baseClass
* @param ignoreMissing
* @param fields
* @throws JSONException
*/
public static <E extends Enum<?>, T> void fieldsFromJSON(
JSONObject jsonObject,
T object,
Class<? extends T> baseClass,
boolean ignoreMissing,
Field... fields)
throws JSONException {
for (Field fieldHandle : fields) {
String jsonKey = fieldHandle.getName().toUpperCase();
if (LOG.isDebugEnabled()) {
LOG.debug("Retreiving value for field '" + jsonKey + "'");
}
if (!jsonObject.has(jsonKey)) {
String msg =
"JSONObject for "
+ baseClass.getSimpleName()
+ " does not have key '"
+ jsonKey
+ "': "
+ CollectionUtil.list(jsonObject.keys());
if (ignoreMissing) {
if (LOG.isDebugEnabled()) {
LOG.warn(msg);
}
continue;
} else {
throw new JSONException(msg);
}
}
try {
readFieldValue(jsonObject, jsonKey, fieldHandle, object);
} catch (Exception ex) {
// System.err.println(field_class + ": " + ClassUtil.getSuperClasses(field_class));
LOG.error(
"Unable to deserialize field '" + jsonKey + "' from " + baseClass.getSimpleName(),
ex);
throw new JSONException(ex);
}
} // FOR
}
/**
* Return the class of a field if it was stored in the JSONObject along with the value If there is
* no class information, then this will return null
*
* @param jsonObject
* @param jsonKey
* @return
* @throws JSONException
*/
private static Class<?> getClassForField(JSONObject jsonObject, String jsonKey)
throws JSONException {
Class<?> fieldClass = null;
// Check whether we also stored the class
if (jsonObject.has(jsonKey + JSON_CLASS_SUFFIX)) {
try {
fieldClass = ClassUtil.getClass(jsonObject.getString(jsonKey + JSON_CLASS_SUFFIX));
} catch (Exception ex) {
LOG.error("Failed to include class for field '" + jsonKey + "'", ex);
throw new JSONException(ex);
}
}
return (fieldClass);
}
/**
* Return the proper serialization string for the given value
*
* @param fieldClass
* @param fieldValue
* @return
*/
private static Object makePrimitiveValue(Class<?> fieldClass, Object fieldValue) {
Object value = null;
// Class
if (fieldClass.equals(Class.class)) {
value = ((Class<?>) fieldValue).getName();
// JSONSerializable
} else if (ClassUtil.getInterfaces(fieldClass).contains(JSONSerializable.class)) {
// Just return the value back. The JSON library will take care of it
// System.err.println(field_class + ": " + field_value);
value = fieldValue;
// Everything else
} else {
value = fieldValue; // .toString();
}
return (value);
}
/**
* For the given JSON string, figure out what kind of object it is and return it
*
* @param jsonValue
* @param fieldClass
* @return
* @throws Exception
*/
public static Object getPrimitiveValue(String jsonValue, Class<?> fieldClass) throws Exception {
Object value = null;
// Class
if (fieldClass.equals(Class.class)) {
value = ClassUtil.getClass(jsonValue);
if (value == null) {
throw new JSONException("Failed to get class from '" + jsonValue + "'");
}
// Enum
} else if (fieldClass.isEnum()) {
for (Object o : fieldClass.getEnumConstants()) {
Enum<?> e = (Enum<?>) o;
if (jsonValue.equals(e.name())) {
return (e);
}
} // FOR
throw new JSONException(
"Invalid enum value '"
+ jsonValue
+ "': "
+ Arrays.toString(fieldClass.getEnumConstants()));
// JSONSerializable
} else if (ClassUtil.getInterfaces(fieldClass).contains(JSONSerializable.class)) {
value = ClassUtil.newInstance(fieldClass, null, null);
((JSONSerializable) value).fromJSON(new JSONObject(jsonValue));
// Boolean
} else if (fieldClass.equals(Boolean.class) || fieldClass.equals(boolean.class)) {
// We have to use field_class.equals() because the value may be null
value = Boolean.parseBoolean(jsonValue);
// Short
} else if (fieldClass.equals(Short.class) || fieldClass.equals(short.class)) {
value = Short.parseShort(jsonValue);
// Integer
} else if (fieldClass.equals(Integer.class) || fieldClass.equals(int.class)) {
value = Integer.parseInt(jsonValue);
// Long
} else if (fieldClass.equals(Long.class) || fieldClass.equals(long.class)) {
value = Long.parseLong(jsonValue);
// Float
} else if (fieldClass.equals(Float.class) || fieldClass.equals(float.class)) {
value = Float.parseFloat(jsonValue);
// Double
} else if (fieldClass.equals(Double.class) || fieldClass.equals(double.class)) {
value = Double.parseDouble(jsonValue);
// String
} else if (fieldClass.equals(String.class)) {
value = jsonValue.toString();
}
return (value);
}
public static Class<?> getPrimitiveType(String jsonValue) {
Object value = null;
// CHECKSTYLE:OFF
// Class
try {
value = ClassUtil.getClass(jsonValue);
if (value != null) return (Class.class);
} catch (Throwable ex) {
} // IGNORE
// Short
try {
value = Short.parseShort(jsonValue);
return (Short.class);
} catch (NumberFormatException ex) {
} // IGNORE
// Integer
try {
value = Integer.parseInt(jsonValue);
return (Integer.class);
} catch (NumberFormatException ex) {
} // IGNORE
// Long
try {
value = Long.parseLong(jsonValue);
return (Long.class);
} catch (NumberFormatException ex) {
} // IGNORE
// Float
try {
value = Float.parseFloat(jsonValue);
return (Float.class);
} catch (NumberFormatException ex) {
} // IGNORE
// Double
try {
value = Double.parseDouble(jsonValue);
return (Double.class);
} catch (NumberFormatException ex) {
} // IGNORE
// CHECKSTYLE:ON
// Boolean
if (jsonValue.equalsIgnoreCase("true") || jsonValue.equalsIgnoreCase("false")) {
return (Boolean.class);
}
// Default: String
return (String.class);
}
}

View File

@ -0,0 +1,138 @@
/*
* OtterTune - ValidationUtils.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ValidationUtils {
public static final String JSON_V4_SCHEMA_IDENTIFIER = "http://json-schema.org/draft-04/schema#";
public static final String JSON_SCHEMA_IDENTIFIER_ELEMENT = "$schema";
public static JsonNode getJsonNode(String jsonText) throws IOException {
return JsonLoader.fromString(jsonText);
} // getJsonNode(text) ends
public static JsonNode getJsonNode(File jsonFile) throws IOException {
return JsonLoader.fromFile(jsonFile);
} // getJsonNode(File) ends
public static JsonNode getJsonNode(URL url) throws IOException {
return JsonLoader.fromURL(url);
} // getJsonNode(URL) ends
public static JsonNode getJsonNodeFromResource(String resource) throws IOException {
return JsonLoader.fromResource(resource);
} // getJsonNode(Resource) ends
public static JsonSchema getSchemaNode(String schemaText)
throws IOException, ProcessingException {
final JsonNode schemaNode = getJsonNode(schemaText);
return getSchemaNodeHelper(schemaNode);
} // getSchemaNode(text) ends
public static JsonSchema getSchemaNode(File schemaFile) throws IOException, ProcessingException {
final JsonNode schemaNode = getJsonNode(schemaFile);
return getSchemaNodeHelper(schemaNode);
} // getSchemaNode(File) ends
public static JsonSchema getSchemaNode(URL schemaFile) throws IOException, ProcessingException {
final JsonNode schemaNode = getJsonNode(schemaFile);
return getSchemaNodeHelper(schemaNode);
} // getSchemaNode(URL) ends
public static JsonSchema getSchemaNodeFromResource(String resource)
throws IOException, ProcessingException {
final JsonNode schemaNode = getJsonNodeFromResource(resource);
return getSchemaNodeHelper(schemaNode);
} // getSchemaNode() ends
public static void validateJson(String schemaText, String jsonText)
throws IOException, ProcessingException {
final JsonSchema schemaNode = getSchemaNode(schemaText);
final JsonNode jsonNode = getJsonNode(jsonText);
validateJson(schemaNode, jsonNode);
} // validateJson(text) ends
public static void validateJson(File schemaFile, File jsonFile)
throws IOException, ProcessingException {
final JsonSchema schemaNode = getSchemaNode(schemaFile);
final JsonNode jsonNode = getJsonNode(jsonFile);
validateJson(schemaNode, jsonNode);
} // validateJson(File) ends
public static void validateJson(JsonSchema jsonSchemaNode, JsonNode jsonNode)
throws ProcessingException {
ProcessingReport report = jsonSchemaNode.validate(jsonNode);
if (!report.isSuccess()) {
for (ProcessingMessage processingMessage : report) {
throw new ProcessingException(processingMessage);
}
}
} // validateJson(Node) ends
public static void validateJson(URL schemaDocument, URL jsonDocument)
throws IOException, ProcessingException {
final JsonSchema schemaNode = getSchemaNode(schemaDocument);
final JsonNode jsonNode = getJsonNode(jsonDocument);
validateJson(schemaNode, jsonNode);
} // validateJson(URL) ends
public static boolean isJsonValid(JsonSchema jsonSchemaNode, JsonNode jsonNode)
throws ProcessingException {
ProcessingReport report = jsonSchemaNode.validate(jsonNode);
return report.isSuccess();
} // validateJson(Node) ends
public static boolean isJsonValid(String schemaText, String jsonText)
throws ProcessingException, IOException {
final JsonSchema schemaNode = getSchemaNode(schemaText);
final JsonNode jsonNode = getJsonNode(jsonText);
return isJsonValid(schemaNode, jsonNode);
} // validateJson(Node) ends
public static boolean isJsonValid(File schemaFile, File jsonFile)
throws ProcessingException, IOException {
final JsonSchema schemaNode = getSchemaNode(schemaFile);
final JsonNode jsonNode = getJsonNode(jsonFile);
return isJsonValid(schemaNode, jsonNode);
} // validateJson(Node) ends
public static boolean isJsonValid(URL schemaURL, URL jsonURL)
throws ProcessingException, IOException {
final JsonSchema schemaNode = getSchemaNode(schemaURL);
final JsonNode jsonNode = getJsonNode(jsonURL);
return isJsonValid(schemaNode, jsonNode);
} // validateJson(Node) ends
public static void validateJsonResource(String schemaResource, String jsonResource)
throws IOException, ProcessingException {
final JsonSchema schemaNode = getSchemaNode(schemaResource);
final JsonNode jsonNode = getJsonNodeFromResource(jsonResource);
validateJson(schemaNode, jsonNode);
} // validateJsonResource() ends
private static JsonSchema getSchemaNodeHelper(JsonNode jsonNode) throws ProcessingException {
final JsonNode schemaIdentifier = jsonNode.get(JSON_SCHEMA_IDENTIFIER_ELEMENT);
if (null == schemaIdentifier) {
((ObjectNode) jsonNode).put(JSON_SCHEMA_IDENTIFIER_ELEMENT, JSON_V4_SCHEMA_IDENTIFIER);
}
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
return factory.getJsonSchema(jsonNode);
} // getSchemaNodeHelper() ends
}

View File

@ -0,0 +1,862 @@
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.controller.util.json;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* A JSONArray is an ordered sequence of values. Its external text form is a string wrapped in
* square brackets with commas separating the values. The internal form is an object having <code>
* get</code> and <code>opt</code> methods for accessing the values by index, and <code>put</code>
* methods for adding or replacing values. The values can be any of these types: <code>Boolean
* </code>, <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>, <code>String
* </code>, or the <code>JSONObject.NULL object</code>.
*
* <p>The constructor can convert a JSON text into a Java object. The <code>toString</code> method
* converts to JSON text.
*
* <p>A <code>get</code> method returns a value if one can be found, and throws an exception if one
* cannot be found. An <code>opt</code> method returns a default value instead of throwing an
* exception, and so is useful for obtaining optional values.
*
* <p>The generic <code>get()</code> and <code>opt()</code> methods return an object which you can
* cast or query for type. There are also typed <code>get</code> and <code>opt</code> methods that
* do type checking and type coersion for you.
*
* <p>The texts produced by the <code>toString</code> methods strictly conform to JSON syntax rules.
* The constructors are more forgiving in the texts they will accept:
*
* <ul>
* <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just before the closing
* bracket.
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
* &nbsp;<small>(comma)</small> elision.
* <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single quote)</small>.
* <li>Strings do not need to be quoted at all if they do not begin with a quote or single quote,
* and if they do not contain leading or trailing spaces, and if they do not contain any of
* these characters: <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
* and if they are not the reserved words <code>true</code>, <code>false</code>, or <code>null
* </code>.
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as well as by <code>,
* </code> <small>(comma)</small>.
* <li>Numbers may have the <code>0-</code> <small>(octal)</small> or <code>0x-</code>
* <small>(hex)</small> prefix.
* <li>Comments written in the slashshlash, slashstar, and hash conventions will be ignored.
* </ul>
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONArray {
/** The arrayList where the JSONArray's properties are kept. */
private ArrayList<Object> myArrayList;
/** Construct an empty JSONArray. */
public JSONArray() {
this.myArrayList = new ArrayList<Object>();
}
/**
* Construct a JSONArray from a JSONTokener.
*
* @param x A JSONTokener
* @throws JSONException If there is a syntax error.
*/
public JSONArray(JSONTokener x) throws JSONException {
this();
char c = x.nextClean();
char q;
if (c == '[') {
q = ']';
} else if (c == '(') {
q = ')';
} else {
throw x.syntaxError("A JSONArray text must start with '['");
}
if (x.nextClean() == ']') {
return;
}
x.back();
for (; ; ) {
if (x.nextClean() == ',') {
x.back();
this.myArrayList.add(null);
} else {
x.back();
this.myArrayList.add(x.nextValue());
}
c = x.nextClean();
switch (c) {
case ';':
case ',':
if (x.nextClean() == ']') {
return;
}
x.back();
break;
case ']':
case ')':
if (q != c) {
throw x.syntaxError("Expected a '" + new Character(q) + "'");
}
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
}
}
/**
* Construct a JSONArray from a source JSON text.
*
* @param source A string that begins with <code>[</code>&nbsp;<small>(left bracket)</small> and
* ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
* @throws JSONException If there is a syntax error.
*/
public JSONArray(String source) throws JSONException {
this(new JSONTokener(source));
}
/**
* Construct a JSONArray from a Collection.
*
* @param collection A Collection.
*/
public JSONArray(Collection<?> collection) {
this.myArrayList =
(collection == null) ? new ArrayList<Object>() : new ArrayList<Object>(collection);
}
/** Construct a JSONArray from a collection of beans. The collection should have Java Beans. */
public JSONArray(Collection<?> collection, boolean includeSuperClass) {
this.myArrayList = new ArrayList<Object>();
if (collection != null) {
for (Iterator<?> iter = collection.iterator(); iter.hasNext(); ) {
this.myArrayList.add(new JSONObject(iter.next(), includeSuperClass));
}
}
}
/**
* Construct a JSONArray from an array
*
* @throws JSONException If not an array.
*/
public JSONArray(Object array) throws JSONException {
this();
if (array.getClass().isArray()) {
int length = Array.getLength(array);
for (int i = 0; i < length; i += 1) {
this.put(Array.get(array, i));
}
} else {
throw new JSONException("JSONArray initial value should be a string or collection or array.");
}
}
/**
* Construct a JSONArray from an array with a bean. The array should have Java Beans.
*
* @throws JSONException If not an array.
*/
public JSONArray(Object array, boolean includeSuperClass) throws JSONException {
this();
if (array.getClass().isArray()) {
int length = Array.getLength(array);
for (int i = 0; i < length; i += 1) {
this.put(new JSONObject(Array.get(array, i), includeSuperClass));
}
} else {
throw new JSONException("JSONArray initial value should be a string or collection or array.");
}
}
/**
* Get the object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value.
* @throws JSONException If there is no value for the index.
*/
public Object get(int index) throws JSONException {
Object o = opt(index);
if (o == null) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
return o;
}
/**
* Get the boolean value associated with an index. The string values "true" and "false" are
* converted to boolean.
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
* @throws JSONException If there is no value for the index or if the value is not convertable to
* boolean.
*/
public boolean getBoolean(int index) throws JSONException {
Object o = get(index);
if (o.equals(Boolean.FALSE)
|| (o instanceof String && ((String) o).equalsIgnoreCase("false"))) {
return false;
} else if (o.equals(Boolean.TRUE)
|| (o instanceof String && ((String) o).equalsIgnoreCase("true"))) {
return true;
}
throw new JSONException("JSONArray[" + index + "] is not a Boolean.");
}
/**
* Get the double value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
*/
public double getDouble(int index) throws JSONException {
Object o = get(index);
try {
return o instanceof Number
? ((Number) o).doubleValue()
: Double.valueOf((String) o).doubleValue();
} catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the int value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
* if the value cannot be converted to a number.
*/
public int getInt(int index) throws JSONException {
Object o = get(index);
return o instanceof Number ? ((Number) o).intValue() : (int) getDouble(index);
}
/**
* Get the JSONArray associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONArray value.
* @throws JSONException If there is no value for the index. or if the value is not a JSONArray
*/
public JSONArray getJSONArray(int index) throws JSONException {
Object o = get(index);
if (o instanceof JSONArray) {
return (JSONArray) o;
}
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
}
/**
* Get the JSONObject associated with an index.
*
* @param index subscript
* @return A JSONObject value.
* @throws JSONException If there is no value for the index or if the value is not a JSONObject
*/
public JSONObject getJSONObject(int index) throws JSONException {
Object o = get(index);
if (o instanceof JSONObject) {
return (JSONObject) o;
}
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
}
/**
* Get the long value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
*/
public long getLong(int index) throws JSONException {
Object o = get(index);
return o instanceof Number ? ((Number) o).longValue() : (long) getDouble(index);
}
/**
* Get the string associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A string value.
* @throws JSONException If there is no value for the index.
*/
public String getString(int index) throws JSONException {
return get(index).toString();
}
/**
* Determine if the value is null.
*
* @param index The index must be between 0 and length() - 1.
* @return true if the value at the index is null, or if there is no value.
*/
public boolean isNull(int index) {
return JSONObject.NULL.equals(opt(index));
}
/**
* Make a string from the contents of this JSONArray. The <code>separator</code> string is
* inserted between each element. Warning: This method assumes that the data structure is
* acyclical.
*
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join(String separator) throws JSONException {
int len = length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(separator);
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
}
return sb.toString();
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int length() {
return this.myArrayList.size();
}
/**
* Get the optional object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value, or null if there is no object at that index.
*/
public Object opt(int index) {
return (index < 0 || index >= length()) ? null : this.myArrayList.get(index);
}
/**
* Get the optional boolean value associated with an index. It returns false if there is no value
* at that index, or if the value is not Boolean.TRUE or the String "true".
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
*/
public boolean optBoolean(int index) {
return optBoolean(index, false);
}
/**
* Get the optional boolean value associated with an index. It returns the defaultValue if there
* is no value at that index or if it is not a Boolean or the String "true" or "false" (case
* insensitive).
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue A boolean default.
* @return The truth.
*/
public boolean optBoolean(int index, boolean defaultValue) {
try {
return getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional double value associated with an index. NaN is returned if there is no value
* for the index, or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public double optDouble(int index) {
return optDouble(index, Double.NaN);
}
/**
* Get the optional double value associated with an index. The defaultValue is returned if there
* is no value for the index, or if the value is not a number and cannot be converted to a number.
*
* @param index subscript
* @param defaultValue The default value.
* @return The value.
*/
public double optDouble(int index, double defaultValue) {
try {
return getDouble(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional int value associated with an index. Zero is returned if there is no value for
* the index, or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public int optInt(int index) {
return optInt(index, 0);
}
/**
* Get the optional int value associated with an index. The defaultValue is returned if there is
* no value for the index, or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public int optInt(int index, int defaultValue) {
try {
return getInt(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional JSONArray associated with an index.
*
* @param index subscript
* @return A JSONArray value, or null if the index has no value, or if the value is not a
* JSONArray.
*/
public JSONArray optJSONArray(int index) {
Object o = opt(index);
return o instanceof JSONArray ? (JSONArray) o : null;
}
/**
* Get the optional JSONObject associated with an index. Null is returned if the key is not found,
* or null if the index has no value, or if the value is not a JSONObject.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index) {
Object o = opt(index);
return o instanceof JSONObject ? (JSONObject) o : null;
}
/**
* Get the optional long value associated with an index. Zero is returned if there is no value for
* the index, or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public long optLong(int index) {
return optLong(index, 0);
}
/**
* Get the optional long value associated with an index. The defaultValue is returned if there is
* no value for the index, or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public long optLong(int index, long defaultValue) {
try {
return getLong(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an empty string if there is
* no value at that index. If the value is not a string and is not null, then it is coverted to a
* string.
*
* @param index The index must be between 0 and length() - 1.
* @return A String value.
*/
public String optString(int index) {
return optString(index, "");
}
/**
* Get the optional string associated with an index. The defaultValue is returned if the key is
* not found.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return A String value.
*/
public String optString(int index, String defaultValue) {
Object o = opt(index);
return o != null ? o.toString() : defaultValue;
}
/**
* Append a boolean value. This increases the array's length by one.
*
* @param value A boolean value.
* @return this.
*/
public JSONArray put(boolean value) {
put(value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
* Collection.
*
* @param value A Collection value.
* @return this.
*/
public JSONArray put(Collection<?> value) {
put(new JSONArray(value));
return this;
}
/**
* Append a double value. This increases the array's length by one.
*
* @param value A double value.
* @throws JSONException if the value is not finite.
* @return this.
*/
public JSONArray put(double value) throws JSONException {
Double d = new Double(value);
JSONObject.testValidity(d);
put(d);
return this;
}
/**
* Append an int value. This increases the array's length by one.
*
* @param value An int value.
* @return this.
*/
public JSONArray put(int value) {
put(new Integer(value));
return this;
}
/**
* Append an long value. This increases the array's length by one.
*
* @param value A long value.
* @return this.
*/
public JSONArray put(long value) {
put(new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which is produced from a
* Map.
*
* @param value A Map value.
* @return this.
*/
public JSONArray put(Map<?, ?> value) {
put(new JSONObject(value));
return this;
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double, Integer, JSONArray,
* JSONObject, Long, or String, or the JSONObject.NULL object.
* @return this.
*/
public JSONArray put(Object value) {
this.myArrayList.add(value);
return this;
}
/**
* Put or replace a boolean value in the JSONArray. If the index is greater than the length of the
* JSONArray, then null elements will be added as necessary to pad it out.
*
* @param index The subscript.
* @param value A boolean value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, boolean value) throws JSONException {
put(index, value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
* Collection.
*
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is not finite.
*/
public JSONArray put(int index, Collection<?> value) throws JSONException {
put(index, new JSONArray(value));
return this;
}
/**
* Put or replace a double value. If the index is greater than the length of the JSONArray, then
* null elements will be added as necessary to pad it out.
*
* @param index The subscript.
* @param value A double value.
* @return this.
* @throws JSONException If the index is negative or if the value is not finite.
*/
public JSONArray put(int index, double value) throws JSONException {
put(index, new Double(value));
return this;
}
/**
* Put or replace an int value. If the index is greater than the length of the JSONArray, then
* null elements will be added as necessary to pad it out.
*
* @param index The subscript.
* @param value An int value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, int value) throws JSONException {
put(index, new Integer(value));
return this;
}
/**
* Put or replace a long value. If the index is greater than the length of the JSONArray, then
* null elements will be added as necessary to pad it out.
*
* @param index The subscript.
* @param value A long value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, long value) throws JSONException {
put(index, new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which is produced from a
* Map.
*
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an invalid number.
*/
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
put(index, new JSONObject(value));
return this;
}
/**
* Put or replace an object value in the JSONArray. If the index is greater than the length of the
* JSONArray, then null elements will be added as necessary to pad it out.
*
* @param index The subscript.
* @param value The value to put into the array. The value should be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
* @return this.
* @throws JSONException If the index is negative or if the the value is an invalid number.
*/
public JSONArray put(int index, Object value) throws JSONException {
JSONObject.testValidity(value);
if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
if (index < length()) {
this.myArrayList.set(index, value);
} else {
while (index != length()) {
put(JSONObject.NULL);
}
put(value);
}
return this;
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values of this JSONArray.
*
* @param names A JSONArray containing a list of key strings. These will be paired with the
* values.
* @return A JSONObject, or null if there are no names or if this JSONArray has no values.
* @throws JSONException If any of the names are null.
*/
public JSONObject toJSONObject(JSONArray names) throws JSONException {
if (names == null || names.length() == 0 || length() == 0) {
return null;
}
JSONObject jo = new JSONObject();
for (int i = 0; i < names.length(); i += 1) {
jo.put(names.getString(i), this.opt(i));
}
return jo;
}
/**
* Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it
* is not possible to produce a syntactically correct JSON text then null will be returned
* instead. This could occur if the array contains an invalid number.
*
* <p>Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable representation of the array.
*/
public String toString() {
try {
return '[' + join(",") + ']';
} catch (Exception e) {
return null;
}
}
/**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
* structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of indentation.
* @return a printable, displayable, transmittable representation of the object, beginning with
* <code>[</code>&nbsp;<small>(left bracket)</small> and ending with <code>]</code>
* &nbsp;<small>(right bracket)</small>.
* @throws JSONException
*/
public String toString(int indentFactor) throws JSONException {
return toString(indentFactor, 0);
}
/**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
* structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of indentation.
* @param indent The indention of the top level.
* @return a printable, displayable, transmittable representation of the array.
* @throws JSONException
*/
String toString(int indentFactor, int indent) throws JSONException {
int len = length();
if (len == 0) {
return "[]";
}
int i;
StringBuffer sb = new StringBuffer("[");
if (len == 1) {
sb.append(JSONObject.valueToString(this.myArrayList.get(0), indentFactor, indent));
} else {
int newindent = indent + indentFactor;
// sb.append('\n');
boolean intType = false;
for (i = 0; i < len; i += 1) {
if (this.myArrayList.get(i).getClass() != Integer.class) {
if (i == 0) {
sb.append('\n');
}
if (i > 0) {
sb.append(",\n");
}
for (int j = 0; j < newindent; j += 1) {
sb.append(' ');
}
} else {
intType = true;
if (i > 0) sb.append(", ");
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i), indentFactor, newindent));
}
if (intType == false) {
sb.append('\n');
for (i = 0; i < indent; i += 1) {
sb.append(' ');
}
}
}
sb.append(']');
return sb.toString();
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For compactness, no whitespace is
* added.
*
* <p>Warning: This method assumes that the data structure is acyclical.
*
* @return The writer.
* @throws JSONException
*/
public Writer write(Writer writer) throws JSONException {
try {
boolean b = false;
int len = length();
writer.write('[');
for (int i = 0; i < len; i += 1) {
if (b) {
writer.write(',');
}
Object v = this.myArrayList.get(i);
if (v instanceof JSONObject) {
((JSONObject) v).write(writer);
} else if (v instanceof JSONArray) {
((JSONArray) v).write(writer);
} else {
writer.write(JSONObject.valueToString(v));
}
b = true;
}
writer.write(']');
return writer;
} catch (IOException e) {
throw new JSONException(e);
}
}
}

View File

@ -0,0 +1,30 @@
package com.controller.util.json;
/**
* The JSONException is thrown by the JSON.org classes then things are amiss.
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONException extends Exception {
private static final long serialVersionUID = 1L;
private Throwable cause;
/**
* Constructs a JSONException with an explanatory message.
*
* @param message Detail about the reason for the exception.
*/
public JSONException(String message) {
super(message);
}
public JSONException(Throwable t) {
super(t.getMessage());
this.cause = t;
}
public Throwable getCause() {
return this.cause;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
package com.controller.util.json;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code> method so that a class
* can change the behavior of <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
* and <code>JSONWriter.value(</code>Object<code>)</code>. The <code>toJSONString</code> method will
* be used instead of the default behavior of using the Object's <code>toString()</code> method and
* quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own JSON serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}

View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2006 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.controller.util.json;
import java.io.StringWriter;
/**
* JSONStringer provides a quick and convenient way of producing JSON text. The texts produced
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
* transmission or storage. Each instance of JSONStringer can produce one JSON text.
*
* <p>A JSONStringer instance provides a <code>value</code> method for appending values to the text,
* and a <code>key</code> method for adding keys before values in objects. There are <code>array
* </code> and <code>endArray</code> methods that make and bound array values, and <code>object
* </code> and <code>endObject</code> methods which make and bound object values. All of these
* methods return the JSONWriter instance, permitting cascade style. For example,
*
* <pre>
* myString = new JSONStringer()
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject()
* .toString();</pre>
*
* which produces the string
*
* <pre>
* {"JSON":"Hello, World!"}</pre>
*
* <p>The first method called must be <code>array</code> or <code>object</code>. There are no
* methods for adding commas or colons. JSONStringer adds them for you. Objects and arrays can be
* nested up to 20 levels deep.
*
* <p>This can sometimes be easier than using a JSONObject to build a string.
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONStringer extends JSONWriter {
/** Make a fresh JSONStringer. It can be used to build one JSON text. */
public JSONStringer() {
super(new StringWriter());
}
/**
* Return the JSON text. This method is used to obtain the product of the JSONStringer instance.
* It will return <code>null</code> if there was a problem in the construction of the JSON text
* (such as the calls to <code>array</code> were not properly balanced with calls to <code>
* endArray</code>).
*
* @return The JSON text.
*/
public String toString() {
return this.mode == 'd' ? this.writer.toString() : null;
}
}

View File

@ -0,0 +1,403 @@
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.controller.util.json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* A JSONTokener takes a source string and extracts characters and tokens from it. It is used by the
* JSONObject and JSONArray constructors to parse JSON source strings.
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONTokener {
private int index;
private Reader reader;
private char lastChar;
private boolean useLastChar;
/**
* Construct a JSONTokener from a string.
*
* @param reader A reader.
*/
public JSONTokener(Reader reader) {
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
this.useLastChar = false;
this.index = 0;
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
}
/**
* Back up one character. This provides a sort of lookahead capability, so that you can test for a
* digit or letter before attempting to parse the next number or identifier.
*/
public void back() throws JSONException {
if (useLastChar || index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
index -= 1;
useLastChar = true;
}
/**
* Get the hex value of a character (base16).
*
* @param c A character between '0' and '9' or between 'A' and 'F' or between 'a' and 'f'.
* @return An int between 0 and 15, or -1 if c was not a hex digit.
*/
public static int dehexchar(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - ('A' - 10);
}
if (c >= 'a' && c <= 'f') {
return c - ('a' - 10);
}
return -1;
}
/**
* Determine if the source string still contains characters that next() can consume.
*
* @return true if not yet at the end of the source.
*/
public boolean more() throws JSONException {
char nextChar = next();
if (nextChar == 0) {
return false;
}
back();
return true;
}
/**
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
*/
public char next() throws JSONException {
if (this.useLastChar) {
this.useLastChar = false;
if (this.lastChar != 0) {
this.index += 1;
}
return this.lastChar;
}
int c;
try {
c = this.reader.read();
} catch (IOException exc) {
throw new JSONException(exc);
}
if (c <= 0) { // End of stream
this.lastChar = 0;
return 0;
}
this.index += 1;
this.lastChar = (char) c;
return this.lastChar;
}
/**
* Consume the next character, and check that it matches a specified character.
*
* @param c The character to match.
* @return The character.
* @throws JSONException if the character does not match.
*/
public char next(char c) throws JSONException {
char n = next();
if (n != c) {
throw syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
}
return n;
}
/**
* Get the next n characters.
*
* @param n The number of characters to take.
* @return A string of n characters.
* @throws JSONException Substring bounds error if there are not n characters remaining in the
* source string.
*/
public String next(int n) throws JSONException {
if (n == 0) {
return "";
}
char[] buffer = new char[n];
int pos = 0;
if (this.useLastChar) {
this.useLastChar = false;
buffer[0] = this.lastChar;
pos = 1;
}
try {
int len;
while ((pos < n) && ((len = reader.read(buffer, pos, n - pos)) != -1)) {
pos += len;
}
} catch (IOException exc) {
throw new JSONException(exc);
}
this.index += pos;
if (pos < n) {
throw syntaxError("Substring bounds error");
}
this.lastChar = buffer[n - 1];
return new String(buffer);
}
/**
* Get the next char in the string, skipping whitespace.
*
* @throws JSONException
* @return A character, or 0 if there are no more characters.
*/
public char nextClean() throws JSONException {
for (; ; ) {
char c = next();
if (c == 0 || c > ' ') {
return c;
}
}
}
/**
* Return the characters up to the next close quote character. Backslash processing is done. The
* formal JSON format does not allow strings in single quotes, but an implementation is allowed to
* accept them.
*
* @param quote The quoting character, either <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (; ; ) {
c = next();
switch (c) {
case 0:
case '\n':
case '\r':
throw syntaxError("Unterminated string");
case '\\':
c = next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
sb.append((char) Integer.parseInt(next(4), 16));
break;
case 'x':
sb.append((char) Integer.parseInt(next(2), 16));
break;
default:
sb.append(c);
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
/**
* Get the text up but not including the specified character or the end of line, whichever comes
* first.
*
* @param d A delimiter character.
* @return A string.
*/
public String nextTo(char d) throws JSONException {
StringBuffer sb = new StringBuffer();
for (; ; ) {
char c = next();
if (c == d || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the text up but not including one of the specified delimiter characters or the end of line,
* whichever comes first.
*
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (; ; ) {
c = next();
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the next value. The value can be a Boolean, Double, Integer, JSONArray, JSONObject, Long,
* or String, or the JSONObject.NULL object.
*
* @throws JSONException If syntax error.
* @return An object.
*/
public Object nextValue() throws JSONException {
char c = nextClean();
String s;
switch (c) {
case '"':
case '\'':
return nextString(c);
case '{':
back();
return new JSONObject(this);
case '[':
case '(':
back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuffer sb = new StringBuffer();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = next();
}
back();
s = sb.toString().trim();
if (s.equals("")) {
throw syntaxError("Missing value");
}
return JSONObject.stringToValue(s);
}
/**
* Skip characters until the next character is the requested character. If the requested character
* is not found, no characters are skipped.
*
* @param to A character to skip to.
* @return The requested character, or zero if the requested character is not found.
*/
public char skipTo(char to) throws JSONException {
char c;
try {
int startIndex = this.index;
reader.mark(Integer.MAX_VALUE);
do {
c = next();
if (c == 0) {
reader.reset();
this.index = startIndex;
return c;
}
} while (c != to);
} catch (IOException exc) {
throw new JSONException(exc);
}
back();
return c;
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message) {
return new JSONException(message + toString());
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at character [this.index]"
*/
public String toString() {
return " at character " + index;
}
}

View File

@ -0,0 +1,309 @@
/*
Copyright (c) 2006 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.controller.util.json;
import java.io.IOException;
import java.io.Writer;
/**
* JSONWriter provides a quick and convenient way of producing JSON text. The texts produced
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
* transmission or storage. Each instance of JSONWriter can produce one JSON text.
*
* <p>A JSONWriter instance provides a <code>value</code> method for appending values to the text,
* and a <code>key</code> method for adding keys before values in objects. There are <code>array
* </code> and <code>endArray</code> methods that make and bound array values, and <code>object
* </code> and <code>endObject</code> methods which make and bound object values. All of these
* methods return the JSONWriter instance, permitting a cascade style. For example,
*
* <pre>
* new JSONWriter(myWriter)
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject();</pre>
*
* which writes
*
* <pre>
* {"JSON":"Hello, World!"}</pre>
*
* <p>The first method called must be <code>array</code> or <code>object</code>. There are no
* methods for adding commas or colons. JSONWriter adds them for you. Objects and arrays can be
* nested up to 20 levels deep.
*
* <p>This can sometimes be easier than using a JSONObject to build a string.
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONWriter {
private static final int maxdepth = 20;
/** The comma flag determines if a comma should be output before the next value. */
private boolean comma;
/** The current mode. Values: 'a' (array), 'd' (done), 'i' (initial), 'k' (key), 'o' (object). */
protected char mode;
/** The object/array stack. */
private JSONObject stack[];
/** The stack top index. A value of 0 indicates that the stack is empty. */
private int top;
/** The writer that will receive the output. */
protected Writer writer;
/** Make a fresh JSONWriter. It can be used to build one JSON text. */
public JSONWriter(Writer w) {
this.comma = false;
this.mode = 'i';
this.stack = new JSONObject[maxdepth];
this.top = 0;
this.writer = w;
}
/**
* Append a value.
*
* @param s A string value.
* @return this
* @throws JSONException If the value is out of sequence.
*/
private JSONWriter append(String s) throws JSONException {
if (s == null) {
throw new JSONException("Null pointer");
}
if (this.mode == 'o' || this.mode == 'a') {
try {
if (this.comma && this.mode == 'a') {
this.writer.write(',');
}
this.writer.write(s);
} catch (IOException e) {
throw new JSONException(e);
}
if (this.mode == 'o') {
this.mode = 'k';
}
this.comma = true;
return this;
}
throw new JSONException("Value out of sequence.");
}
/**
* Begin appending a new array. All values until the balancing <code>endArray</code> will be
* appended to this array. The <code>endArray</code> method must be called to mark the array's
* end.
*
* @return this
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
* place (for example as a key or after the end of the outermost array or object).
*/
public JSONWriter array() throws JSONException {
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
this.push(null);
this.append("[");
this.comma = false;
return this;
}
throw new JSONException("Misplaced array.");
}
/**
* End something.
*
* @param m Mode
* @param c Closing character
* @return this
* @throws JSONException If unbalanced.
*/
private JSONWriter end(char m, char c) throws JSONException {
if (this.mode != m) {
throw new JSONException(m == 'o' ? "Misplaced endObject." : "Misplaced endArray.");
}
this.pop(m);
try {
this.writer.write(c);
} catch (IOException e) {
throw new JSONException(e);
}
this.comma = true;
return this;
}
/**
* End an array. This method most be called to balance calls to <code>array</code>.
*
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endArray() throws JSONException {
return this.end('a', ']');
}
/**
* End an object. This method most be called to balance calls to <code>object</code>.
*
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endObject() throws JSONException {
return this.end('k', '}');
}
/**
* Append a key. The key will be associated with the next value. In an object, every value must be
* preceded by a key.
*
* @param s A key string.
* @return this
* @throws JSONException If the key is out of place. For example, keys do not belong in arrays or
* if the key is null.
*/
public JSONWriter key(String s) throws JSONException {
if (s == null) {
throw new JSONException("Null key.");
}
if (this.mode == 'k') {
try {
if (this.comma) {
this.writer.write(',');
}
stack[top - 1].putOnce(s, Boolean.TRUE);
this.writer.write(JSONObject.quote(s));
this.writer.write(':');
this.comma = false;
this.mode = 'o';
return this;
} catch (IOException e) {
throw new JSONException(e);
}
}
throw new JSONException("Misplaced key.");
}
/**
* Begin appending a new object. All keys and values until the balancing <code>endObject</code>
* will be appended to this object. The <code>endObject</code> method must be called to mark the
* object's end.
*
* @return this
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
* place (for example as a key or after the end of the outermost array or object).
*/
public JSONWriter object() throws JSONException {
if (this.mode == 'i') {
this.mode = 'o';
}
if (this.mode == 'o' || this.mode == 'a') {
this.append("{");
this.push(new JSONObject());
this.comma = false;
return this;
}
throw new JSONException("Misplaced object.");
}
/**
* Pop an array or object scope.
*
* @param c The scope to close.
* @throws JSONException If nesting is wrong.
*/
private void pop(char c) throws JSONException {
if (this.top <= 0) {
throw new JSONException("Nesting error.");
}
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
if (m != c) {
throw new JSONException("Nesting error.");
}
this.top -= 1;
this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
}
/**
* Push an array or object scope.
*
* @param c The scope to open.
* @throws JSONException If nesting is too deep.
*/
private void push(JSONObject jo) throws JSONException {
if (this.top >= maxdepth) {
throw new JSONException("Nesting too deep.");
}
this.stack[this.top] = jo;
this.mode = jo == null ? 'a' : 'k';
this.top += 1;
}
/**
* Append either the value <code>true</code> or the value <code>false</code>.
*
* @param b A boolean.
* @return this
* @throws JSONException
*/
public JSONWriter value(boolean b) throws JSONException {
return this.append(b ? "true" : "false");
}
/**
* Append a double value.
*
* @param d A double.
* @return this
* @throws JSONException If the number is not finite.
*/
public JSONWriter value(double d) throws JSONException {
return this.value(new Double(d));
}
/**
* Append a long value.
*
* @param l A long.
* @return this
* @throws JSONException
*/
public JSONWriter value(long l) throws JSONException {
return this.append(Long.toString(l));
}
/**
* Append an object value.
*
* @param o The object to append. It can be null, or a Boolean, Number, String, JSONObject, or
* JSONArray, or an object with a toJSONString() method.
* @return this
* @throws JSONException If the value is out of sequence.
*/
public JSONWriter value(Object o) throws JSONException {
return this.append(JSONObject.valueToString(o));
}
}

View File

@ -0,0 +1,440 @@
package com.controller.util.json;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* Test class. This file is not formally a member of the com.controller.util.json library. It is
* just a casual test tool.
*/
public class Test {
/**
* Entry point.
*
* @param args
*/
public static void main(String args[]) {
Iterator<?> it;
JSONArray a;
JSONObject j;
JSONStringer jj;
String s;
/**
* Obj is a typical class that implements JSONString. It also provides some beanie methods that
* can be used to construct a JSONObject. It also demonstrates constructing a JSONObject with an
* array of names.
*/
class Obj implements JSONString {
public String aString;
public double aNumber;
public boolean aBoolean;
public Obj(String string, double n, boolean b) {
this.aString = string;
this.aNumber = n;
this.aBoolean = b;
}
public double getNumber() {
return this.aNumber;
}
public String getString() {
return this.aString;
}
public boolean isBoolean() {
return this.aBoolean;
}
public String getBENT() {
return "All uppercase key";
}
public String getX() {
return "x";
}
public String toJSONString() {
return "{"
+ JSONObject.quote(this.aString)
+ ":"
+ JSONObject.doubleToString(this.aNumber)
+ "}";
}
public String toString() {
return this.getString()
+ " "
+ this.getNumber()
+ " "
+ this.isBoolean()
+ "."
+ this.getBENT()
+ " "
+ this.getX();
}
}
Obj obj = new Obj("A beany object", 42, true);
try {
s =
"{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }";
j = new JSONObject(s);
System.out.println(j.toString(2));
jj = new JSONStringer();
s =
jj.object()
.key("single")
.value("MARIE HAA'S")
.key("Johnny")
.value("MARIE HAA\\'S")
.key("foo")
.value("bar")
.key("baz")
.array()
.object()
.key("quux")
.value("Thanks, Josh!")
.endObject()
.endArray()
.key("obj keys")
.value(JSONObject.getNames(obj))
.endObject()
.toString();
System.out.println(s);
System.out.println(
new JSONStringer()
.object()
.key("a")
.array()
.array()
.array()
.value("b")
.endArray()
.endArray()
.endArray()
.endObject()
.toString());
jj = new JSONStringer();
jj.array();
jj.value(1);
jj.array();
jj.value(null);
jj.array();
jj.object();
jj.key("empty-array").array().endArray();
jj.key("answer").value(42);
jj.key("null").value(null);
jj.key("false").value(false);
jj.key("true").value(true);
jj.key("big").value(123456789e+88);
jj.key("small").value(123456789e-88);
jj.key("empty-object").object().endObject();
jj.key("long");
jj.value(9223372036854775807L);
jj.endObject();
jj.value("two");
jj.endArray();
jj.value(true);
jj.endArray();
jj.value(98.6);
jj.value(-100.0);
jj.object();
jj.endObject();
jj.object();
jj.key("one");
jj.value(1.00);
jj.endObject();
jj.value(obj);
jj.endArray();
System.out.println(jj.toString());
System.out.println(new JSONArray(jj.toString()).toString(4));
int ar[] = {1, 2, 3};
JSONArray ja = new JSONArray(ar);
System.out.println(ja.toString());
String sa[] = {"aString", "aNumber", "aBoolean"};
j = new JSONObject(obj, sa);
j.put("Testing JSONString interface", obj);
System.out.println(j.toString(4));
j =
new JSONObject(
"{slashes: '///', closetag: '</script>', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}");
System.out.println(j.toString(2));
System.out.println("");
j =
new JSONObject(
"{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, 1.00000000000000001,"
+ " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], "
+ " to : null, op : 'Good',"
+ "ten:10} postfix comment");
j.put("String", "98.6");
j.put("JSONObject", new JSONObject());
j.put("JSONArray", new JSONArray());
j.put("int", 57);
j.put("double", 123456789012345678901234567890.);
j.put("true", true);
j.put("false", false);
j.put("null", JSONObject.NULL);
j.put("bool", "true");
j.put("zero", -0.0);
j.put("\\u2028", "\u2028");
j.put("\\u2029", "\u2029");
a = j.getJSONArray("foo");
a.put(666);
a.put(2001.99);
a.put("so \"fine\".");
a.put("so <fine>.");
a.put(true);
a.put(false);
a.put(new JSONArray());
a.put(new JSONObject());
j.put("keys", JSONObject.getNames(j));
System.out.println(j.toString(4));
System.out.println("String: " + j.getDouble("String"));
System.out.println(" bool: " + j.getBoolean("bool"));
System.out.println(" to: " + j.getString("to"));
System.out.println(" true: " + j.getString("true"));
System.out.println(" foo: " + j.getJSONArray("foo"));
System.out.println(" op: " + j.getString("op"));
System.out.println(" ten: " + j.getInt("ten"));
System.out.println(" oops: " + j.optBoolean("oops"));
j =
new JSONObject(
"{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}");
System.out.println(j.toString(2));
System.out.println("isNull: " + j.isNull("nix"));
System.out.println(" has: " + j.has("nix"));
System.out.println("");
j =
new JSONObject(
"{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}");
System.out.println(j.toString(2));
System.out.println("");
j =
new JSONObject(
"{script: 'It is not allowed in HTML to send a close script tag in a string<script>because it confuses browsers</script>so we insert a backslash before the /'}");
System.out.println(j.toString());
System.out.println("");
JSONTokener jt =
new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}");
j = new JSONObject(jt);
System.out.println(j.toString());
System.out.println("pre: " + j.optInt("pre"));
int i = jt.skipTo('{');
System.out.println(i);
j = new JSONObject(jt);
System.out.println(j.toString());
System.out.println("");
a = new JSONArray(" [\"<escape>\", next is an implied null , , ok,] ");
System.out.println(a.toString());
System.out.println("");
System.out.println("");
j =
new JSONObject(
"{ fun => with non-standard forms ; forgiving => This package can be used to parse formats that are similar to but not stricting conforming to JSON; why=To make it easier to migrate existing data to JSON,one = [[1.00]]; uno=[[{1=>1}]];'+':+6e66 ;pluses=+++;empty = '' , 'double':0.666,true: TRUE, false: FALSE, null=NULL;[true] = [[!,@;*]]; string=> o. k. ; \r oct=0666; hex=0x666; dec=666; o=0999; noh=0x0x}");
System.out.println(j.toString(4));
System.out.println("");
if (j.getBoolean("true") && !j.getBoolean("false")) {
System.out.println("It's all good");
}
System.out.println("");
j = new JSONObject(j, new String[] {"dec", "oct", "hex", "missing"});
System.out.println(j.toString(4));
System.out.println("");
System.out.println(new JSONStringer().array().value(a).value(j).endArray());
j =
new JSONObject(
"{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}");
System.out.println(j.toString(4));
System.out.println("\ngetInt");
System.out.println("int " + j.getInt("int"));
System.out.println("long " + j.getInt("long"));
System.out.println("longer " + j.getInt("longer"));
System.out.println("double " + j.getInt("double"));
System.out.println("string " + j.getInt("string"));
System.out.println("\ngetLong");
System.out.println("int " + j.getLong("int"));
System.out.println("long " + j.getLong("long"));
System.out.println("longer " + j.getLong("longer"));
System.out.println("double " + j.getLong("double"));
System.out.println("string " + j.getLong("string"));
System.out.println("\ngetDouble");
System.out.println("int " + j.getDouble("int"));
System.out.println("long " + j.getDouble("long"));
System.out.println("longer " + j.getDouble("longer"));
System.out.println("double " + j.getDouble("double"));
System.out.println("string " + j.getDouble("string"));
j.put("good sized", 9223372036854775807L);
System.out.println(j.toString(4));
a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]");
System.out.println(a.toString(4));
System.out.println("\nKeys: ");
it = j.keys();
while (it.hasNext()) {
s = (String) it.next();
System.out.println(s + ": " + j.getString(s));
}
System.out.println("\naccumulate: ");
j = new JSONObject();
j.accumulate("stooge", "Curly");
j.accumulate("stooge", "Larry");
j.accumulate("stooge", "Moe");
a = j.getJSONArray("stooge");
a.put(5, "Shemp");
System.out.println(j.toString(4));
System.out.println("\nwrite:");
System.out.println(j.write(new StringWriter()));
Collection<?> c = null;
Map<?, ?> m = null;
j = new JSONObject(m);
a = new JSONArray(c);
j.append("stooge", "Joe DeRita");
j.append("stooge", "Shemp");
j.accumulate("stooges", "Curly");
j.accumulate("stooges", "Larry");
j.accumulate("stooges", "Moe");
j.accumulate("stoogearray", j.get("stooges"));
j.put("map", m);
j.put("collection", c);
j.put("array", a);
a.put(m);
a.put(c);
System.out.println(j.toString(4));
s =
"{plist=Apple; AnimalSmells = { pig = piggish; lamb = lambish; worm = wormy; }; AnimalSounds = { pig = oink; lamb = baa; worm = baa; Lisa = \"Why is the worm talking like a lamb?\" } ; AnimalColors = { pig = pink; lamb = black; worm = pink; } } ";
j = new JSONObject(s);
System.out.println(j.toString(4));
s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")";
a = new JSONArray(s);
System.out.println(a.toString());
System.out.println("\nTesting Exceptions: ");
System.out.print("Exception: ");
try {
a = new JSONArray();
a.put(Double.NEGATIVE_INFINITY);
a.put(Double.NaN);
System.out.println(a.toString());
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(j.getDouble("stooge"));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(j.getDouble("howard"));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(j.put(null, "howard"));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(a.getDouble(0));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(a.get(-1));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
System.out.println(a.put(Double.NaN));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
ja = new JSONArray(new Object());
System.out.println(ja.toString());
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
s = "[)";
a = new JSONArray(s);
System.out.println(a.toString());
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
s = "{\"koda\": true, \"koda\": true}";
j = new JSONObject(s);
System.out.println(j.toString(4));
} catch (Exception e) {
System.out.println(e);
}
System.out.print("Exception: ");
try {
jj = new JSONStringer();
s =
jj.object()
.key("bosanda")
.value("MARIE HAA'S")
.key("bosanda")
.value("MARIE HAA\\'S")
.endObject()
.toString();
System.out.println(j.toString(4));
} catch (Exception e) {
System.out.println(e);
}
} catch (Exception e) {
System.out.println(e.toString());
}
}
}

View File

@ -0,0 +1,50 @@
/*
* OtterTune - AbstractJSONValidationTestCase.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.types.JSONSchemaType;
import com.controller.util.FileUtil;
import java.io.File;
import junit.framework.TestCase;
public abstract class AbstractJSONValidationTestCase extends TestCase {
private static final String SAMPLE_OUTPUT_PATH = "sample_output";
private static final String SAMPLE_CONFIG_PATH = "config";
protected String dbName;
protected void setUp(String dbName) throws Exception {
super.setUp();
this.dbName = dbName;
}
public void testJsonKnobs() {
String jsonKnobsPath = FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "knobs.json");
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonKnobsPath)));
}
public void testJsonMetrics() {
String jsonMetricsBeforePath =
FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "metrics_before.json");
String jsonMetricsAfterPath =
FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "metrics_after.json");
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonMetricsBeforePath)));
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonMetricsAfterPath)));
}
public void testJsonSummary() {
String jsonSummaryPath = FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "summary.json");
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.SUMMARY, new File(jsonSummaryPath)));
}
public void testJsonConfig() {
String jsonConfigPath =
FileUtil.joinPath(SAMPLE_CONFIG_PATH, "sample_" + this.dbName + "_config.json");
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.CONFIG, new File(jsonConfigPath)));
}
}

View File

@ -0,0 +1,42 @@
/*
* OtterTune - TestInvalidJSON.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
import com.controller.types.JSONSchemaType;
import junit.framework.TestCase;
public class TestInvalidJSON extends TestCase {
// Wrong number of levels for "global"
private static final String BAD_JSON_TEXT_1 =
"{"
+ " \"global\" : {"
+ " \"global\" : {"
+ " \"auto_generate_certs\": {"
+ " \"auto_pram\" : \"NO\""
+ " }"
+ " }"
+ " },"
+ " \"local\" : {"
+ " }"
+ "}";
// Lacking "local"
private static final String BAD_JSON_TEXT_2 =
"{"
+ " \"global\" : {"
+ " \"global1\" : {"
+ " \"auto_generate_certs\": \"ON\""
+ " }"
+ " }"
+ "}";
public void testBadJSONOutput() {
assertFalse(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, BAD_JSON_TEXT_1));
assertFalse(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, BAD_JSON_TEXT_2));
}
}

View File

@ -0,0 +1,15 @@
/*
* OtterTune - TestMySQLJSON.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
public class TestMySQLJSON extends AbstractJSONValidationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp("mysql");
}
}

View File

@ -0,0 +1,15 @@
/*
* OtterTune - TestOracleJSON.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
public class TestOracleJSON extends AbstractJSONValidationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp("oracle");
}
}

View File

@ -0,0 +1,15 @@
/*
* OtterTune - TestPostgresJSON.java
*
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
*/
package com.controller.collectors;
public class TestPostgresJSON extends AbstractJSONValidationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp("postgres");
}
}

View File

@ -0,0 +1,69 @@
#
# OtterTune - ConfParser.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Mar 23, 2018
@author: Jacky, bohan, Dongsheng
'''
import sys
import json
from collections import OrderedDict
def change_postgres_conf(recommendation, postgresqlconf):
lines = postgresqlconf.readlines()
settings_idx = lines.index("# Add settings for extensions here\n")
postgresqlconf.seek(0)
postgresqlconf.truncate(0)
lines = lines[0:(settings_idx + 1)]
for line in lines:
postgresqlconf.write(line)
for (knob_name, knob_value) in list(recommendation.items()):
postgresqlconf.write(str(knob_name) + " = " + str(knob_value) + "\n")
def change_oracle_conf(recommendation, oracle_conf):
lines = oracle_conf.readlines()
signal = "# configurations recommended by ottertune:\n"
if signal not in lines:
oracle_conf.write('\n' + signal)
oracle_conf.flush()
oracle_conf.seek(0)
lines = oracle_conf.readlines()
settings_idx = lines.index(signal)
oracle_conf.seek(0)
oracle_conf.truncate(0)
lines = lines[0:(settings_idx + 1)]
for line in lines:
oracle_conf.write(line)
for (knob_name, knob_value) in list(recommendation.items()):
oracle_conf.write(str(knob_name) + " = " + str(knob_value).strip('B') + "\n")
def main():
if len(sys.argv) != 4:
raise Exception("Usage: python [DB type] ConfParser.py [Next Config] [Current Config]")
database_type = sys.argv[1]
next_config_name = sys.argv[2]
cur_config_name = sys.argv[3]
with open(next_config_name, 'r') as next_config, open(cur_config_name, 'r+') as cur_config:
config = json.load(next_config, encoding="UTF-8", object_pairs_hook=OrderedDict)
recommendation = config['recommendation']
if database_type == 'postgres':
change_postgres_conf(recommendation, cur_config)
elif database_type == 'oracle':
change_oracle_conf(recommendation, cur_config)
else:
raise Exception("Database Type {} Not Implemented !".format(database_type))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,43 @@
#
# OtterTune - LatencyUDF.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
import sys
import json
from collections import OrderedDict
def main():
if (len(sys.argv) != 2):
raise Exception("Usage: python udf.py [Output Directory]")
with open(sys.argv[1] + "/summary.json", "r") as f:
conf = json.load(f,
encoding="UTF-8",
object_pairs_hook=OrderedDict)
start_time = conf['start_time']
end_time = conf['end_time']
with open(sys.argv[1] + "/metrics_before.json", "r") as f:
conf_before = json.load(f,
encoding="UTF-8",
object_pairs_hook=OrderedDict)
conf_before['global']['udf'] = OrderedDict([("latency", "0")])
with open(sys.argv[1] + "/metrics_after.json", "r") as f:
conf_after = json.load(f,
encoding="UTF-8",
object_pairs_hook=OrderedDict)
conf_after['global']['udf'] = OrderedDict([("latency", str(end_time - start_time))])
with open(sys.argv[1] + "/metrics_before.json", "w") as f:
f.write(json.dumps(conf_before, indent=4))
with open(sys.argv[1] + "/metrics_after.json", "w") as f:
f.write(json.dumps(conf_after, indent=4))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,21 @@
{
"database_type" : "postgres",
"database_name" : "tpcc",
"database_disk": "/dev/xvda1",
"database_conf": "/etc/postgresql/9.6/main/postgresql.conf",
"database_save_path": "/home/ubuntu/ottertune",
"username" : "bohan",
"password" : "bohan",
"oltpbench_home": "/home/ubuntu/oltpbench",
"oltpbench_config": "/home/ubuntu/oltpbench/config/tpcc_config_postgres.xml",
"oltpbench_workload": "tpcc",
"oltpbench_log" : "/home/ubuntu/ottertune/client/driver/oltp.log",
"controller_config": "/home/ubuntu/ottertune/client/controller/config/sample_postgres_config.json",
"controller_log" : "/home/ubuntu/ottertune/client/driver/controller.log",
"save_path": "/home/ubuntu/results",
"upload_url" : "http://127.0.0.1:8000",
"upload_code" : "I5I10PXK3PK27FM86YYS",
"lhs_knob_path" : "/home/ubuntu/ottertune/client/driver/knobs/postgres-96.json",
"lhs_save_path" : "/home/ubuntu/ottertune/client/driver/configs",
"oracle_awr_enabled": false
}

421
client/driver/fabfile.py vendored Normal file
View File

@ -0,0 +1,421 @@
#
# OtterTune - fabfile.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Mar 23, 2018
@author: bohan
'''
import sys
import json
import logging
import time
import os.path
import re
import glob
from multiprocessing import Process
from fabric.api import (env, local, task, lcd)
from fabric.state import output as fabric_output
LOG = logging.getLogger()
LOG.setLevel(logging.DEBUG)
Formatter = logging.Formatter( # pylint: disable=invalid-name
"%(asctime)s [%(levelname)s] %(message)s")
# print the log
ConsoleHandler = logging.StreamHandler(sys.stdout) # pylint: disable=invalid-name
ConsoleHandler.setFormatter(Formatter)
LOG.addHandler(ConsoleHandler)
# Fabric environment settings
env.hosts = ['localhost']
fabric_output.update({
'running': True,
'stdout': True,
})
# intervals of restoring the databse
RELOAD_INTERVAL = 10
# maximum disk usage
MAX_DISK_USAGE = 90
with open('driver_config.json', 'r') as f:
CONF = json.load(f)
@task
def check_disk_usage():
partition = CONF['database_disk']
disk_use = 0
cmd = "df -h {}".format(partition)
out = local(cmd, capture=True).splitlines()[1]
m = re.search('\d+(?=%)', out) # pylint: disable=anomalous-backslash-in-string
if m:
disk_use = int(m.group(0))
LOG.info("Current Disk Usage: %s%s", disk_use, '%')
return disk_use
@task
def check_memory_usage():
cmd = 'free -m -h'
local(cmd)
@task
def restart_database():
if CONF['database_type'] == 'postgres':
cmd = 'sudo service postgresql restart'
elif CONF['database_type'] == 'oracle':
cmd = 'sh oracleScripts/shutdownOracle.sh && sh oracleScripts/startupOracle.sh'
else:
raise Exception("Database Type {} Not Implemented !".format(CONF['database_type']))
local(cmd)
@task
def drop_database():
if CONF['database_type'] == 'postgres':
cmd = "PGPASSWORD={} dropdb -e --if-exists {} -U {}".\
format(CONF['password'], CONF['database_name'], CONF['username'])
else:
raise Exception("Database Type {} Not Implemented !".format(CONF['database_type']))
local(cmd)
@task
def create_database():
if CONF['database_type'] == 'postgres':
cmd = "PGPASSWORD={} createdb -e {} -U {}".\
format(CONF['password'], CONF['database_name'], CONF['username'])
else:
raise Exception("Database Type {} Not Implemented !".format(CONF['database_type']))
local(cmd)
@task
def change_conf():
next_conf = 'next_config'
cmd = "sudo python3 ConfParser.py {} {} {}".\
format(CONF['database_type'], next_conf, CONF['database_conf'])
local(cmd)
@task
def load_oltpbench():
cmd = "./oltpbenchmark -b {} -c {} --create=true --load=true".\
format(CONF['oltpbench_workload'], CONF['oltpbench_config'])
with lcd(CONF['oltpbench_home']): # pylint: disable=not-context-manager
local(cmd)
@task
def run_oltpbench():
cmd = "./oltpbenchmark -b {} -c {} --execute=true -s 5 -o outputfile".\
format(CONF['oltpbench_workload'], CONF['oltpbench_config'])
with lcd(CONF['oltpbench_home']): # pylint: disable=not-context-manager
local(cmd)
@task
def run_oltpbench_bg():
cmd = "./oltpbenchmark -b {} -c {} --execute=true -s 5 -o outputfile > {} 2>&1 &".\
format(CONF['oltpbench_workload'], CONF['oltpbench_config'], CONF['oltpbench_log'])
with lcd(CONF['oltpbench_home']): # pylint: disable=not-context-manager
local(cmd)
@task
def run_controller():
cmd = 'sudo gradle run -PappArgs="-c {} -d output/" --no-daemon > {}'.\
format(CONF['controller_config'], CONF['controller_log'])
with lcd("../controller"): # pylint: disable=not-context-manager
local(cmd)
@task
def signal_controller():
pid = int(open('../controller/pid.txt').read())
cmd = 'sudo kill -2 {}'.format(pid)
with lcd("../controller"): # pylint: disable=not-context-manager
local(cmd)
@task
def save_dbms_result():
t = int(time.time())
files = ['knobs.json', 'metrics_after.json', 'metrics_before.json', 'summary.json']
for f_ in files:
f_prefix = f_.split('.')[0]
cmd = 'cp ../controller/output/{} {}/{}__{}.json'.\
format(f_, CONF['save_path'], t, f_prefix)
local(cmd)
@task
def free_cache():
cmd = 'sync; sudo bash -c "echo 1 > /proc/sys/vm/drop_caches"'
local(cmd)
@task
def upload_result():
cmd = 'python3 ../../server/website/script/upload/upload.py \
../controller/output/ {} {}/new_result/'.format(CONF['upload_code'],
CONF['upload_url'])
local(cmd)
@task
def get_result():
cmd = 'python3 ../../script/query_and_get.py {} {} 5'.\
format(CONF['upload_url'], CONF['upload_code'])
local(cmd)
@task
def add_udf():
cmd = 'sudo python3 ./LatencyUDF.py ../controller/output/'
local(cmd)
@task
def upload_batch():
cmd = 'python3 ./upload_batch.py {} {} {}/new_result/'.format(CONF['save_path'],
CONF['upload_code'],
CONF['upload_url'])
local(cmd)
@task
def dump_database():
db_file_path = '{}/{}.dump'.format(CONF['database_save_path'], CONF['database_name'])
if os.path.exists(db_file_path):
LOG.info('%s already exists ! ', db_file_path)
return False
else:
LOG.info('Dump database %s to %s', CONF['database_name'], db_file_path)
# You must create a directory named dpdata through sqlplus in your Oracle database
if CONF['database_type'] == 'oracle':
cmd = 'expdp {}/{}@{} schemas={} dumpfile={}.dump DIRECTORY=dpdata'.format(
'c##tpcc', 'oracle', 'orcldb', 'c##tpcc', 'orcldb')
elif CONF['database_type'] == 'postgres':
cmd = 'PGPASSWORD={} pg_dump -U {} -F c -d {} > {}'.format(CONF['password'],
CONF['username'],
CONF['database_name'],
db_file_path)
else:
raise Exception("Database Type {} Not Implemented !".format(CONF['database_type']))
local(cmd)
return True
@task
def restore_database():
if CONF['database_type'] == 'oracle':
# You must create a directory named dpdata through sqlplus in your Oracle database
# The following script assumes such directory exists.
# You may want to modify the username, password, and dump file name in the script
cmd = 'sh oracleScripts/restoreOracle.sh'
elif CONF['database_type'] == 'postgres':
db_file_path = '{}/{}.dump'.format(CONF['database_save_path'], CONF['database_name'])
drop_database()
create_database()
cmd = 'PGPASSWORD={} pg_restore -U {} -n public -j 8 -F c -d {} {}'.\
format(CONF['password'], CONF['username'], CONF['database_name'], db_file_path)
else:
raise Exception("Database Type {} Not Implemented !".format(CONF['database_type']))
LOG.info('Start restoring database')
local(cmd)
LOG.info('Finish restoring database')
def _ready_to_start_oltpbench():
return (os.path.exists(CONF['controller_log']) and
'Output the process pid to'
in open(CONF['controller_log']).read())
def _ready_to_start_controller():
return (os.path.exists(CONF['oltpbench_log']) and
'Warmup complete, starting measurements'
in open(CONF['oltpbench_log']).read())
def _ready_to_shut_down_controller():
pid_file_path = '../controller/pid.txt'
return (os.path.exists(pid_file_path) and os.path.exists(CONF['oltpbench_log']) and
'Output throughput samples into file' in open(CONF['oltpbench_log']).read())
def clean_logs():
# remove oltpbench log
cmd = 'rm -f {}'.format(CONF['oltpbench_log'])
local(cmd)
# remove controller log
cmd = 'rm -f {}'.format(CONF['controller_log'])
local(cmd)
@task
def lhs_samples(count=10):
cmd = 'python3 lhs.py {} {} {}'.format(count, CONF['lhs_knob_path'], CONF['lhs_save_path'])
local(cmd)
@task
def loop():
# free cache
free_cache()
# remove oltpbench log and controller log
clean_logs()
# restart database
restart_database()
# check disk usage
if check_disk_usage() > MAX_DISK_USAGE:
LOG.WARN('Exceeds max disk usage %s', MAX_DISK_USAGE)
# run controller from another process
p = Process(target=run_controller, args=())
p.start()
LOG.info('Run the controller')
# run oltpbench as a background job
while not _ready_to_start_oltpbench():
pass
run_oltpbench_bg()
LOG.info('Run OLTP-Bench')
# the controller starts the first collection
while not _ready_to_start_controller():
pass
signal_controller()
LOG.info('Start the first collection')
# stop the experiment
while not _ready_to_shut_down_controller():
pass
signal_controller()
LOG.info('Start the second collection, shut down the controller')
p.join()
# add user defined target objective
# add_udf()
# save result
save_dbms_result()
# upload result
upload_result()
# get result
get_result()
# change config
change_conf()
@task
def run_lhs():
datadir = CONF['lhs_save_path']
samples = glob.glob(os.path.join(datadir, 'config_*'))
# dump database if it's not done before.
dump = dump_database()
for i, sample in enumerate(samples):
# reload database periodically
if RELOAD_INTERVAL > 0:
if i % RELOAD_INTERVAL == 0:
if i == 0 and dump is False:
restore_database()
elif i > 0:
restore_database()
# free cache
free_cache()
LOG.info('\n\n Start %s-th sample %s \n\n', i, sample)
# check memory usage
# check_memory_usage()
# check disk usage
if check_disk_usage() > MAX_DISK_USAGE:
LOG.WARN('Exceeds max disk usage %s', MAX_DISK_USAGE)
# copy lhs-sampled config to the to-be-used config
cmd = 'cp {} next_config'.format(sample)
local(cmd)
# remove oltpbench log and controller log
clean_logs()
# change config
change_conf()
# restart database
restart_database()
if CONF.get('oracle_awr_enabled', False):
# create snapshot for oracle AWR report
if CONF['database_type'] == 'oracle':
local('sh snapshotOracle.sh')
# run controller from another process
p = Process(target=run_controller, args=())
p.start()
# run oltpbench as a background job
while not _ready_to_start_oltpbench():
pass
run_oltpbench_bg()
LOG.info('Run OLTP-Bench')
while not _ready_to_start_controller():
pass
signal_controller()
LOG.info('Start the first collection')
while not _ready_to_shut_down_controller():
pass
# stop the experiment
signal_controller()
LOG.info('Start the second collection, shut down the controller')
p.join()
# save result
save_dbms_result()
# upload result
upload_result()
if CONF.get('oracle_awr_enabled', False):
# create oracle AWR report for performance analysis
if CONF['database_type'] == 'oracle':
local('sh oracleScripts/snapshotOracle.sh && sh oracleScripts/awrOracle.sh')
@task
def run_loops(max_iter=1):
# dump database if it's not done before.
dump = dump_database()
for i in range(int(max_iter)):
if RELOAD_INTERVAL > 0:
if i % RELOAD_INTERVAL == 0:
if i == 0 and dump is False:
restore_database()
elif i > 0:
restore_database()
LOG.info('The %s-th Loop Starts / Total Loops %s', i + 1, max_iter)
loop()
LOG.info('The %s-th Loop Ends / Total Loops %s', i + 1, max_iter)

View File

@ -0,0 +1,83 @@
[
{
"name": "SHARED_POOL_SIZE",
"tuning_range": {
"minval": "500MB",
"maxval": "2500MB"
},
"default": "1500MB",
"type": "bytes"
},
{
"name": "DB_CACHE_SIZE",
"tuning_range": {
"minval": "10GB",
"maxval": "24GB"
},
"default": "14GB",
"type": "bytes"
},
{
"name": "LOG_BUFFER",
"tuning_range": {
"minval": "10MB",
"maxval": "1000MB"
},
"default": "20MB",
"type": "bytes"
},
{
"name": "LARGE_POOL_SIZE",
"tuning_range": {
"minval": "10MB",
"maxval": "1000MB"
},
"default": "100MB",
"type": "bytes"
},
{
"name": "STREAMS_POOL_SIZE",
"tuning_range": {
"minval": "10MB",
"maxval": "1000MB"
},
"default": "100MB",
"type": "bytes"
},
{
"name": "bitmap_merge_area_size",
"tuning_range": {
"minval": "1000000",
"maxval": "20000000"
},
"default": "1MB",
"type": "integer"
},
{
"name": "create_bitmap_area_size",
"tuning_range": {
"minval": "1000000",
"maxval": "100000000"
},
"default": "8MB",
"type": "integer"
},
{
"name": "hash_area_size",
"tuning_range": {
"minval": "65536",
"maxval": "1000000"
},
"default": "65536",
"type": "integer"
},
{
"name": "sort_area_size",
"tuning_range": {
"minval": "128000",
"maxval": "2000000"
},
"default": "128000",
"type": "integer"
}
]

View File

@ -0,0 +1,110 @@
[
{
"name": "effective_cache_size",
"tuning_range": {
"minval": "4GB",
"maxval": "16GB"
},
"default": "4GB",
"type": "bytes"
},
{
"name": "shared_buffers",
"tuning_range": {
"minval": "128MB",
"maxval": "12GB"
},
"default": "128MB",
"type": "bytes"
},
{
"name": "max_parallel_workers_per_gather",
"tuning_range": {
"minval": 0,
"maxval": 8
},
"default": 0,
"type": "integer"
},
{
"name": "default_statistics_target",
"tuning_range": {
"minval": 100,
"maxval": 2048
},
"default": 100,
"type": "integer"
},
{
"name": "bgwriter_lru_maxpages",
"tuning_range": {
"minval": 0,
"maxval": 1000
},
"default": 10,
"type": "integer"
},
{
"name": "checkpoint_timeout",
"tuning_range": {
"minval": "10ms",
"maxval": "1min"
},
"default": "200ms",
"type": "time"
},
{
"name": "random_page_cost",
"tuning_range": {
"minval": 1,
"maxval": 10
},
"default": 4.0,
"type": "float"
},
{
"name": "checkpoint_completion_target",
"tuning_range": {
"minval": 0.1,
"maxval": 0.9
},
"default": 0.5,
"type": "float"
},
{
"name": "checkpoint_timeout",
"tuning_range": {
"minval": "1min",
"maxval": "30min"
},
"default": "5min",
"type": "time"
},
{
"name": "max_wal_size",
"tuning_range": {
"minval": "256MB",
"maxval": "16GB"
},
"default": "1GB",
"type": "bytes"
},
{
"name": "temp_buffers",
"tuning_range": {
"minval": "8MB",
"maxval": "1GB"
},
"default": "8MB",
"type": "bytes"
},
{
"name": "work_mem",
"tuning_range": {
"minval": "4MB",
"maxval": "1GB"
},
"default": "4MB",
"type": "bytes"
}
]

126
client/driver/lhs.py Normal file
View File

@ -0,0 +1,126 @@
#
# OtterTune - lhs.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
import sys
import json
import os
import numpy as np
from pyDOE import lhs
from scipy.stats import uniform
from hurry.filesize import size
BYTES_SYSTEM = [
(1024 ** 5, 'PB'),
(1024 ** 4, 'TB'),
(1024 ** 3, 'GB'),
(1024 ** 2, 'MB'),
(1024 ** 1, 'kB'),
(1024 ** 0, 'B'),
]
TIME_SYSTEM = [
(1000 * 60 * 60 * 24, 'd'),
(1000 * 60 * 60, 'h'),
(1000 * 60, 'min'),
(1000, 's'),
(1, 'ms'),
]
def get_raw_size(value, system):
for factor, suffix in system:
if value.endswith(suffix):
if len(value) == len(suffix):
amount = 1
else:
try:
amount = int(value[:-len(suffix)])
except ValueError:
continue
return amount * factor
return None
def get_knob_raw(value, knob_type):
if knob_type == 'integer':
return int(value)
elif knob_type == 'float':
return float(value)
elif knob_type == 'bytes':
return get_raw_size(value, BYTES_SYSTEM)
elif knob_type == 'time':
return get_raw_size(value, TIME_SYSTEM)
else:
raise Exception('Knob Type does not support')
def get_knob_readable(value, knob_type):
if knob_type == 'integer':
return int(round(value))
elif knob_type == 'float':
return float(value)
elif knob_type == 'bytes':
value = int(round(value))
return size(value, system=BYTES_SYSTEM)
elif knob_type == 'time':
value = int(round(value))
return size(value, system=TIME_SYSTEM)
else:
raise Exception('Knob Type does not support')
def get_knobs_readable(values, types):
result = []
for i, value in enumerate(values):
result.append(get_knob_readable(value, types[i]))
return result
def main(args):
if (len(sys.argv) != 4):
raise Exception("Usage: python3 lhs.py [Samples Count] [Knob Path] [Save Path]")
knob_path = args[2]
save_path = args[3]
with open(knob_path, "r") as f:
tuning_knobs = json.load(f)
names = []
maxvals = []
minvals = []
types = []
for knob in tuning_knobs:
names.append(knob['name'])
maxvals.append(get_knob_raw(knob['tuning_range']['maxval'], knob['type']))
minvals.append(get_knob_raw(knob['tuning_range']['minval'], knob['type']))
types.append(knob['type'])
nsamples = int(args[1])
nfeats = len(tuning_knobs)
samples = lhs(nfeats, samples=nsamples, criterion='maximin')
maxvals = np.array(maxvals)
minvals = np.array(minvals)
scales = maxvals - minvals
for fidx in range(nfeats):
samples[:, fidx] = uniform(loc=minvals[fidx], scale=scales[fidx]).ppf(samples[:, fidx])
samples_readable = []
for sample in samples:
samples_readable.append(get_knobs_readable(sample, types))
config = {'recommendation': {}}
for sidx in range(nsamples):
for fidx in range(nfeats):
config["recommendation"][names[fidx]] = samples_readable[sidx][fidx]
with open(os.path.join(save_path, 'config_' + str(sidx)), 'w+') as f:
f.write(json.dumps(config))
if __name__ == '__main__':
main(sys.argv)

1
client/driver/lhs.sh Executable file
View File

@ -0,0 +1 @@
sudo -b nohup fab run_lhs > lhs.log 2>&1

View File

@ -0,0 +1,9 @@
#!/bin/sh
su - oracle <<EON
oracle
sqlplus / as sysdba <<EOF
@/home/oracle/ottertune/client/driver/autoawr.sql;
quit
EOF
exit
EON

View File

@ -0,0 +1,23 @@
#!/bin/sh
su - oracle <<EON
oracle #system password
sqlplus / as sysdba <<EOF
drop user c##tpcc cascade;
# username
create user c##tpcc identified by oracle;
# username password
quit
EOF
impdp 'userid="/ as sysdba"' schemas=c##tpcc dumpfile=orcldb.dump DIRECTORY=dpdata
# username database_name db_directory
sqlplus / as sysdba <<EOF #restart the database
shutdown immediate
startup
quit
EOF
exit
EON

View File

@ -0,0 +1,11 @@
#!/bin/sh
su - oracle <<EON
oracle
sqlplus / as sysdba <<EOF
shutdown immediate
exit
EOF
exit
EON

View File

@ -0,0 +1,11 @@
#!/bin/sh
su - oracle <<EON
oracle
sqlplus / as sysdba <<EOF
exec dbms_workload_repository.create_snapshot;
quit
EOF
exit
EON

View File

@ -0,0 +1,11 @@
#!/bin/sh
su - oracle <<EON
oracle
sqlplus / as sysdba <<EOF
startup
quit
EOF
exit
EON

View File

@ -0,0 +1,63 @@
#
# OtterTune - upload_batch.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
import argparse
import glob
import logging
import os
import requests
# Logging
LOG = logging.getLogger(__name__)
LOG.addHandler(logging.StreamHandler())
LOG.setLevel(logging.INFO)
# Upload all files in the datadir to OtterTune's server side.
# You may want to upload your training data to the non-tuning session.
def upload_batch(datadir, upload_code, url):
samples = glob.glob(os.path.join(datadir, '*__summary.json'))
count = len(samples)
samples_prefix = []
LOG.info('Uploading %d samples in %s...', count, datadir)
for sample in samples:
prefix = sample.split('/')[-1].split('__')[0]
samples_prefix.append(prefix)
for i in range(count):
prefix = samples_prefix[i]
params = {
'summary': open(os.path.join(datadir, '{}__summary.json'.format(prefix)), 'rb'),
'knobs': open(os.path.join(datadir, '{}__knobs.json'.format(prefix)), 'rb'),
'metrics_before': open(os.path.join(datadir,
'{}__metrics_before.json'.format(prefix)), 'rb'),
'metrics_after': open(os.path.join(datadir,
'{}__metrics_after.json'.format(prefix)), 'rb'),
}
LOG.info('Upload %d-th sample %s__*.json', i + 1, prefix)
response = requests.post(url,
files=params,
data={'upload_code': upload_code})
LOG.info(response.content)
def main():
parser = argparse.ArgumentParser(description="Upload generated data to the website")
parser.add_argument('datadir', type=str, nargs=1,
help='Directory containing the generated data')
parser.add_argument('upload_code', type=str, nargs=1,
help='The website\'s upload code')
parser.add_argument('url', type=str, default='http://0.0.0.0:8000/new_result/',
nargs='?', help='The upload url: server_ip/new_result/')
args = parser.parse_args()
upload_batch(args.datadir[0], args.upload_code[0], args.url)
if __name__ == "__main__":
main()

1
docker/.dockerignore Normal file
View File

@ -0,0 +1 @@
*.swp

23
docker/Dockerfile Normal file
View File

@ -0,0 +1,23 @@
FROM ottertune-base
ENV DJANGO_SETTINGS_MODULE=website.settings
ENV C_FORCE_ROOT=true
RUN mkdir -p /app
COPY . /app
WORKDIR /app/server/website
COPY ./docker/credentials.py ./website/settings
COPY ./docker/start.sh .
COPY ./docker/createadmin.py .
COPY ./docker/wait-for-it.sh .
RUN chmod +x ./*.sh
RUN sed s/'@localhost'/'@rabbitmq'/g ./website/settings/common.py > tmp \
&& mv tmp ./website/settings/common.py
ENTRYPOINT ["./start.sh"]

View File

@ -0,0 +1,31 @@
FROM centos:7
ARG GRADLE_VERSION=gradle-5.5.1
ENV GRADLE_HOME=/opt/${GRADLE_VERSION}
ENV PATH=${GRADLE_HOME}/bin:${PATH}
COPY ./server/website/requirements.txt /
RUN yum update -y \
&& yum install -y mariadb mariadb-devel \
https://centos7.iuscommunity.org/ius-release.rpm \
&& yum install -y gcc git MySQL-python openldap-devel \
parallel python36u python36u-devel python36u-libs \
python36u-pip python36u-tkinter rabbitmq-server \
java-11-openjdk-devel wget which unzip curl \
&& yum -y autoremove \
&& yum clean metadata \
&& yum clean all \
&& ln -s `which python3.6` /usr/bin/python3 \
&& ln -s `which pip3.6` /usr/bin/pip3 \
&& wget https://services.gradle.org/distributions/${GRADLE_VERSION}-bin.zip \
&& unzip ${GRADLE_VERSION}-bin.zip -d /opt \
&& rm ${GRADLE_VERSION}-bin.zip \
&& python3 --version \
&& pip3 --version \
&& javac --version \
&& gradle --version \
&& pip3 install -r /requirements.txt \
&& rm /requirements.txt

View File

@ -0,0 +1,24 @@
FROM ubuntu:18.04
ARG GRADLE_VERSION=gradle-5.5.1
ENV DEBIAN_FRONTEND=noninteractive
ENV GRADLE_HOME=/opt/${GRADLE_VERSION}
ENV PATH=${GRADLE_HOME}/bin:${PATH}
COPY ./server/website/requirements.txt /
RUN apt-get update \
&& apt-get install -y python3.6 python3-pip python3-tk \
mysql-client libmysqlclient-dev python-mysqldb \
openjdk-11-jdk git unzip wget curl \
&& wget https://services.gradle.org/distributions/${GRADLE_VERSION}-bin.zip \
&& unzip ${GRADLE_VERSION}-bin.zip -d /opt \
&& rm ${GRADLE_VERSION}-bin.zip \
&& python3 --version \
&& pip3 --version \
&& javac --version \
&& gradle --version \
&& pip3 install -r /requirements.txt \
&& rm /requirements.txt

21
docker/Dockerfile.test Normal file
View File

@ -0,0 +1,21 @@
FROM ottertune-base
ENV DJANGO_SETTINGS_MODULE=website.settings
ENV C_FORCE_ROOT=true
RUN mkdir -p /app
COPY . /app
WORKDIR /app/server/website
RUN pip3 install codecov
COPY ./docker/credentials.py ./website/settings
COPY ./docker/wait-for-it.sh .
RUN chmod +x ./*.sh
RUN sed s/'@localhost'/'@rabbitmq'/g ./website/settings/common.py > tmp \
&& mv tmp ./website/settings/common.py

26
docker/createadmin.py Normal file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "website.settings")
django.setup()
from django.contrib.auth.models import User
username = os.environ.get('ADMIN_USER', 'admin')
password = os.environ.get('ADMIN_PASSWORD')
email = os.environ.get('ADMIN_EMAIL', 'admin@example.com')
if password:
if not User.objects.filter(username=username).exists():
print(f"Creating '{username}' user...")
User.objects.create_superuser(username=username,
password=password,
email=email)
print(f"'{username}' user created!")
else:
print(f"'{username}' user already exists! Setting '{username}' password")
u = User.objects.get(username=username)
u.set_password(password)
u.save()

24
docker/credentials.py Normal file
View File

@ -0,0 +1,24 @@
import secrets
from os import environ as env
db_user = env.get('MYSQL_USER')
db_pwd = env.get('MYSQL_PASSWORD')
db_host = env.get('MYSQL_HOST')
db_port = env.get('MYSQL_PORT', '3306')
debug = env.get('DEBUG')
SECRET_KEY = secrets.token_hex(16)
DATABASES = {
'default': {'ENGINE': 'django.db.backends.mysql',
'NAME': 'ottertune',
'USER': db_user,
'PASSWORD': db_pwd,
'HOST': db_host,
'PORT': db_port,
'OPTIONS': {'init_command': 'SET sql_mode=\'STRICT_TRANS_TABLES\',innodb_strict_mode=1',}
}
}
DEBUG = True
ADMINS = ()
MANAGERS = ADMINS
ALLOWED_HOSTS = []

View File

@ -0,0 +1,85 @@
version: "3"
services:
base:
build:
context: ../
dockerfile: ./docker/Dockerfile.base-ubuntu-18.04
image: ottertune-base
container_name: ottertune-base
labels:
NAME: "ottertune-base"
test:
build:
context: ../
dockerfile: ./docker/Dockerfile.test
image: ottertune-test
container_name: ottertune-test
expose:
- "8000"
ports:
- "8000:8000"
links:
- mysql
- rabbitmq
depends_on:
- mysql
- rabbitmq
environment:
DEBUG: 'True'
MYSQL_USER: 'root'
MYSQL_PASSWORD: 'ottertune'
MYSQL_HOST: 'mysql'
MAX_DB_CONN_ATTEMPTS: 15
labels:
NAME: "ottertune-test"
volumes:
- media_data:/app/server/website/media
networks:
- ottertune-net
mysql:
image: mysql:5.7
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 'ottertune'
MYSQL_PASSWORD: 'ottertune'
MYSQL_DATABASE: 'ottertune'
expose:
- "3306"
ports:
- "3306:3306"
labels:
NAME: "mysql"
volumes:
- mysql_data:/var/lib/mysql
networks:
- ottertune-net
rabbitmq:
image: "rabbitmq:3-management"
container_name: rabbitmq
restart: always
hostname: "rabbitmq"
environment:
RABBITMQ_DEFAULT_USER: "guest"
RABBITMQ_DEFAULT_PASS: "guest"
RABBITMQ_DEFAULT_VHOST: "/"
expose:
- "15672"
- "5672"
ports:
- "15672:15672"
- "5672:5672"
labels:
NAME: "rabbitmq"
networks:
- ottertune-net
volumes:
mysql_data:
media_data:
networks:
ottertune-net:
driver: bridge

88
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,88 @@
version: "3"
services:
base:
build:
context: ../
dockerfile: ./docker/Dockerfile.base-ubuntu-18.04
image: ottertune-base
container_name: ottertune-base
labels:
NAME: "ottertune-base"
web:
build:
context: ../
dockerfile: ./docker/Dockerfile
image: ottertune
container_name: ottertune
expose:
- "8000"
ports:
- "8000:8000"
links:
- mysql
- rabbitmq
depends_on:
- base
- mysql
- rabbitmq
environment:
DEBUG: 'True'
ADMIN_PASSWORD: 'changeme'
ADMIN_EMAIL: 'admin@example.com'
MYSQL_USER: 'root'
MYSQL_PASSWORD: 'ottertune'
MYSQL_HOST: 'mysql'
MAX_DB_CONN_ATTEMPTS: 15
labels:
NAME: "ottertune"
volumes:
- media_data:/app/server/website/media
networks:
- ottertune-net
mysql:
image: mysql:5.7
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 'ottertune'
MYSQL_PASSWORD: 'ottertune'
MYSQL_DATABASE: 'ottertune'
expose:
- "3306"
ports:
- "3306:3306"
labels:
NAME: "mysql"
volumes:
- mysql_data:/var/lib/mysql
networks:
- ottertune-net
rabbitmq:
image: "rabbitmq:3-management"
container_name: rabbitmq
restart: always
hostname: "rabbitmq"
environment:
RABBITMQ_DEFAULT_USER: "guest"
RABBITMQ_DEFAULT_PASS: "guest"
RABBITMQ_DEFAULT_VHOST: "/"
expose:
- "15672"
- "5672"
ports:
- "15672:15672"
- "5672:5672"
labels:
NAME: "rabbitmq"
networks:
- ottertune-net
volumes:
mysql_data:
media_data:
networks:
ottertune-net:
driver: bridge

14
docker/start.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Wait for MySQL connection
/bin/bash wait-for-it.sh
## Needs a connection to a DB so migrations go here
python3 manage.py makemigrations website
python3 manage.py migrate
python3 createadmin.py
python3 manage.py celery worker --loglevel=info --pool=threads &
python3 manage.py celerybeat --verbosity=2 --loglevel=info &
python3 manage.py runserver 0.0.0.0:8000

19
docker/wait-for-it.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
# wait until MySQL is really available
maxcounter=${MAX_DB_CONN_ATTEMPTS:-45}
echo "Trying to connect to mysql, max attempts="$maxcounter
counter=1
while ! mysql --host="$MYSQL_HOST" --protocol TCP -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "show databases;" > /dev/null 2>&1; do
sleep 1
counter=`expr $counter + 1`
if [ $counter -gt $maxcounter ]; then
>&2 echo "We have been waiting for MySQL too long already; failing."
exit 1
fi;
done
echo "-=------------------------------------------------------"
echo "-=------------------------------------------------------"
echo "Connected to MySQL!"
echo "-=------------------------------------------------------"
echo "-=------------------------------------------------------"

View File

@ -0,0 +1,238 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html.
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name = "Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="100"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly"/>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="2"/>
</module>
<!-- <module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="4"/>
</module> -->
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore">
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<!-- <module name="NonEmptyAtclauseDescription"/> -->
<!-- <module name="JavadocTagContinuationIndentation"/> -->
<!-- <module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module> -->
<!-- <module name="JavadocParagraph"/> -->
<!-- <module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module> -->
<!-- <module name="JavadocMethod">
<property name="scope" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
</module> -->
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<!-- <module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module> -->
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
<module name="SuppressionCommentFilter"/>
</module>
</module>

View File

@ -0,0 +1,2 @@
[pycodestyle]
ignore = E501

View File

@ -0,0 +1,378 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute. Adds the analysis directory to the system path
# variable which is necessary to avoid ImportErrors in our website code.
init-hook='import os, sys; cwd = os.getcwd(); analysis_path = os.path.join(cwd, 'server') if '/server/' not in cwd else cwd[0:cwd.index('/server/') + len('/server/') - 1]; sys.path.insert(0, analysis_path)'
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Use multiple processes to speed up Pylint.
jobs=1
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Allow optimization of some AST trees. This will activate a peephole AST
# optimizer, which will apply various small optimizations. For instance, it can
# be used to obtain the result of joining multiple strings with the addition
# operator. Joining a lot of strings can lead to a maximum recursion error in
# Pylint and this flag can prevent that. It has one side effect, the resulting
# AST will be different than the one from reality.
optimize-ast=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=HIGH,INFERENCE_FAILURE,UNDEFINED
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time. See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,missing-docstring,too-few-public-methods,too-many-arguments,too-many-locals,too-many-instance-attributes,too-many-statements,locally-disabled,superfluous-parens,too-many-branches,not-callable,too-many-nested-blocks,fixme,redefined-variable-type,no-member,locally-enabled,too-many-public-methods
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set). This supports can work
# with qualified names.
ignored-classes=
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_$|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=100
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[BASIC]
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,input
# Good variable names which should always be accepted, separated by a comma
good-names=ex,Run,_,mu,y
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Regular expression matching correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for function names
function-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct variable names
variable-rgx=(([a-z_][a-z0-9_]{0,30})|([XK][a-z0-9_]{0,30}))$
# Naming hint for variable names
variable-name-hint=[a-z_][a-z0-9_]{0,30}$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z_][a-z0-9_]{1,30})|([XK][a-z0-9_]{0,30}))$
# Naming hint for attribute names
attr-name-hint=[a-z_][a-z0-9_]{1,30}$
# Regular expression matching correct argument names
argument-rgx=(([a-z_][a-z0-9_]{0,30})|([XK][a-z0-9_]{0,30}))$
# Naming hint for argument names
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Naming hint for class names
class-name-hint=[A-Z_][a-zA-Z0-9]+$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Naming hint for method names
method-name-hint=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
[ELIF]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

View File

@ -0,0 +1,198 @@
#
# OtterTune - formatter.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
import argparse
import functools
import logging
import os
import re
import subprocess
import sys
import autopep8
EXIT_SUCCESS = 0
EXIT_FAILURE = -1
# ==============================================
# LOGGING CONFIGURATION
# ==============================================
LOG = logging.getLogger(__name__)
LOG_HANDLER = logging.StreamHandler()
LOG_FORMATTER = logging.Formatter(
fmt='%(asctime)s [%(funcName)s:%(lineno)03d] %(levelname)-5s: %(message)s',
datefmt='%H:%M:%S'
)
LOG_HANDLER.setFormatter(LOG_FORMATTER)
LOG.addHandler(LOG_HANDLER)
LOG.setLevel(logging.INFO)
# ==============================================
# CONFIGURATION
# ==============================================
# NOTE: the absolute path to ottertune directory is calculated from current
# directory structure: ottertune/server/website/scripts/validators/<this_file>
# OTTERTUNE_DIR needs to be redefined if the directory structure is changed.
CODE_SOURCE_DIR = os.path.abspath(os.path.dirname(__file__))
OTTERTUNE_DIR = os.path.abspath(functools.reduce(os.path.join,
[CODE_SOURCE_DIR,
os.path.pardir,
os.path.pardir]))
JAVA_JAR_PATH = os.path.join(
OTTERTUNE_DIR, 'controller/build/libs/google-java-format-1.5-all-deps.jar')
# ==============================================
# FILE HEADER FORMATS
# ==============================================
PYTHON_HEADER_FORMAT = (
"#\n"
"# OtterTune - {filename}\n"
"#\n"
"# Copyright (c) 2017-18, Carnegie Mellon University Database Group\n"
"#\n"
).format
# Regex for updating old headers
PYTHON_HEADER_REGEX = re.compile(r'#\n#.*\n#\n# Copyright.*\n#\n')
JAVA_HEADER_FORMAT = (
"/*\n"
" * OtterTune - {filename}\n"
" *\n"
" * Copyright (c) 2017-18, Carnegie Mellon University Database Group\n"
" */\n\n"
).format
JAVA_HEADER_REGEX = re.compile(r'/\*\n \*.*\n \*\n \* Copyright.*\n \*/\n\n')
# ==============================================
# UTILITY FUNCTION DEFINITIONS
# ==============================================
def format_file(file_path, update_header, format_code):
if file_path.endswith(".py"):
format_python_file(file_path, update_header, format_code)
elif file_path.endswith(".java"):
format_java_file(file_path, update_header, format_code)
def update_file_header(file_contents, file_name, header_format, header_regex):
new_header = header_format(filename=os.path.basename(file_name))
header_match = header_regex.search(file_contents)
if header_match:
# Replace the old header with the new one
old_header = header_match.group()
file_contents = file_contents.replace(old_header, new_header)
else:
# Add new header
file_contents = new_header + file_contents
return file_contents
def format_java_file(file_path, update_header, format_code):
if not file_path.endswith(".java"):
return
if update_header:
with open(file_path, 'r') as f:
file_contents = f.read()
file_contents = update_file_header(file_contents,
os.path.basename(file_path),
JAVA_HEADER_FORMAT,
JAVA_HEADER_REGEX)
with open(file_path, 'w') as f:
f.write(file_contents)
if format_code:
if not os.path.exists(JAVA_JAR_PATH):
controller_dir = os.path.join(OTTERTUNE_DIR, 'controller')
subprocess.check_output(["gradle", "downloadJars"], cwd=controller_dir)
subprocess.check_output(["java", "-jar", JAVA_JAR_PATH, "-r", file_path])
def format_python_file(file_path, update_header, format_code):
if not file_path.endswith(".py"):
return
with open(file_path, 'r') as f:
file_contents = f.read()
if update_header:
file_contents = update_file_header(file_contents,
os.path.basename(file_path),
PYTHON_HEADER_FORMAT,
PYTHON_HEADER_REGEX)
if format_code:
# Use the autopep8 module to format the source code. autopep8 uses
# pycodestyle to detect the style errors it should fix and thus it
# should fix all (or most) of them, however, it does not use pylint
# so it may not fix all of its reported errors.
options = {"max_line_length": 100}
file_contents = autopep8.fix_code(file_contents, options=options)
with open(file_path, 'w') as f:
f.write(file_contents)
# Format all the files in the dir passed as argument
def format_dir(dir_path, update_header, format_code):
for subdir, _, files in os.walk(dir_path): # pylint: disable=not-an-iterable
for file_path in files:
file_path = subdir + os.path.sep + file_path
format_file(file_path, update_header, format_code)
def main():
parser = argparse.ArgumentParser(description='Formats python source files in place')
parser.add_argument('--no-update-header', action='store_true',
help='Do not update the source file headers')
parser.add_argument('--no-format-code', action='store_true',
help='Do not format the source files use autopep8')
parser.add_argument('--staged-files', action='store_true',
help='Apply the selected action(s) to all staged files (git)')
parser.add_argument('paths', metavar='PATH', type=str, nargs='*',
help='Files or directories to (recursively) apply the actions to')
args = parser.parse_args()
if args.no_update_header and args.no_format_code:
LOG.info("No actions to perform (both --no-update-header and "
"--no-format-code given). Exiting...")
sys.exit(EXIT_FAILURE)
elif args.staged_files:
targets = [os.path.abspath(os.path.join(OTTERTUNE_DIR, f))
for f in subprocess.check_output(["git", "diff",
"--name-only", "HEAD",
"--cached",
"--diff-filter=d"]).split()]
if not targets:
LOG.error("No staged files or not calling from a repository. Exiting...")
sys.exit(EXIT_FAILURE)
elif not args.paths:
LOG.error("No files or directories given. Exiting...")
sys.exit(EXIT_FAILURE)
else:
targets = args.paths
for x in targets:
if os.path.isfile(x):
LOG.info("Scanning file: " + x)
format_file(x, not args.no_update_header, not args.no_format_code)
elif os.path.isdir(x):
LOG.info("Scanning directory: " + x)
format_dir(x, not args.no_update_header, not args.no_format_code)
else:
LOG.error("%s isn't a file or directory", x)
sys.exit(EXIT_FAILURE)
if __name__ == '__main__':
main()

79
script/git-hooks/pre-commit Executable file
View File

@ -0,0 +1,79 @@
#!/bin/sh
# Source validation pre-commit hook
#
# Adapted from the source validation pre-commit hook used in Peloton.
# (see https://github.com/cmu-db/peloton/blob/master/script/git-hooks/pre-commit)
#
# This script collects all modified files and runs it through our source code
# validation script. The validation script returns 0 on success and 1 on any
# failure. This script can also run the server and controller tests by
# uncommenting lines 26-28 and 31-33, respectively.
#
# To enable, symlink this file to '.git/hooks/pre-commit' like so:
# cd $OTTERTUNE_DIR/.git/hooks
# ln -s ../../script/git-hooks/pre-commit ./pre-commit
FILES=$(git diff --name-only HEAD --cached --diff-filter=d | grep '\.\(py\)$')
SERVER_TESTS_RESULT=0
CONTROLLER_TESTS_RESULT=0
VALIDATOR_RESULT=0
if [ -n "$FILES" ]; then
# Uncomment to run the server tests
# cd server/website && python manage.py test -v 2
# SERVER_TESTS_RESULT=$?
# cd ../..
# Uncomment to run the controller tests
# cd controller && gradle build -q
# CONTROLLER_TESTS_RESULT=$?
# cd ..
# Run source code validator
python script/validators/source_validator.py $FILES
VALIDATOR_RESULT=$?
if [ "$VALIDATOR_RESULT" -ne 0 ] || [ "$SERVER_TESTS_RESULT" -ne 0 ] || [ "$CONTROLLER_TESTS_RESULT" -ne 0 ]; then
echo " +------------------------------------------------------------+"
echo " | |"
echo " | OTTERTUNE PRE-COMMIT HOOK |"
echo " | |"
echo " +------------------------------------------------------------+"
echo ""
if [ "$SERVER_TESTS_RESULT" -ne 0 ]; then
echo " FAILED server tests!"
echo ""
fi
if [ "$CONTROLLER_TESTS_RESULT" -ne 0 ]; then
echo " FAILED controller tests!"
echo ""
fi
if [ "$VALIDATOR_RESULT" -ne 0 ]; then
echo " FAILED source validation!"
echo ""
echo " Use the formatting script to help format all changed files:"
echo " (ottertune/script/formatting/formatter.py)"
echo ""
echo " \"python formatter.py --staged-files\""
echo ""
fi
echo " To temporarily bypass the pre-commit hook, use:"
echo ""
echo " \"git commit --no-verify\""
echo
echo " Be aware that changed files have to be staged again!"
exit 1
fi
fi
exit 0

54
script/query_and_get.py Normal file
View File

@ -0,0 +1,54 @@
#
# OtterTune - query_and_get.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Feb 11, 2018
@author: taodai
'''
import sys
import time
import logging
import json
import urllib.request
# Logging
LOG = logging.getLogger(__name__)
LOG.addHandler(logging.StreamHandler())
LOG.setLevel(logging.INFO)
# take 3 arguments, save result to next_config in working directory
# base_url: for instance, https://0.0.0.0:8000/
# upload_code: upload code...
# query_interval: time (in second) between queries
def main():
base_url = sys.argv[1].strip('/')
upload_code = sys.argv[2]
query_interval = int(sys.argv[3])
request = base_url + '/query_and_get/' + upload_code
timer = 0
start = time.time()
while True:
response = urllib.request.urlopen(request).read().decode()
if 'Fail' in response:
LOG.info('Tuning failed\n')
break
elif response == 'null' or 'not ready' in response:
time.sleep(query_interval)
timer += query_interval
LOG.info('%s s\n', str(timer))
else:
next_conf_f = open('next_config', 'w')
next_conf_f.write(json.loads(response))
next_conf_f.close()
break
elapsed_time = time.time() - start
LOG.info('Elapsed time: %s\n', str(elapsed_time))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,437 @@
#!/usr/bin/env python
# encoding: utf-8
#
# OtterTune - source_validator.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
# ==============================================
# SOURCE VALIDATOR
# ==============================================
#
# Adapted from the source validator used by Peloton.
# (see https://github.com/cmu-db/peloton/blob/master/script/validators/source_validator.py)
import argparse
import logging
import imp
import os
import re
import subprocess
import sys
import json
import functools
from collections import namedtuple
from fabric.api import lcd, local, settings, quiet
EXIT_SUCCESS = 0
EXIT_FAILURE = -1
# ==============================================
# CONFIGURATION
# ==============================================
# Logging
LOG = logging.getLogger(__name__)
LOG.addHandler(logging.StreamHandler())
LOG.setLevel(logging.INFO)
# NOTE: the absolute path to ottertune directory is calculated from current
# directory structure: ottertune/server/website/scripts/validators/<this_file>
# OTTERTUNE_DIR needs to be redefined if the directory structure is changed.
CODE_SOURCE_DIR = os.path.abspath(os.path.dirname(__file__))
OTTERTUNE_DIR = os.path.abspath(functools.reduce(os.path.join,
[CODE_SOURCE_DIR,
os.path.pardir,
os.path.pardir]))
# Other directory paths used are relative to OTTERTUNE_DIR
DEFAULT_DIRS = [
OTTERTUNE_DIR
]
# Directories that should NOT be checked
EXCLUDE_DIRECTORIES = [
# Django-generated directories
os.path.join(OTTERTUNE_DIR, "server/website/website/migrations"),
# Source code files from json.org
os.path.join(OTTERTUNE_DIR, "client/controller/src/main/java/com/controller/util/json"),
# Django settings
os.path.join(OTTERTUNE_DIR, 'server/website/website/settings'),
# Docker files
os.path.join(OTTERTUNE_DIR, 'docker'),
]
# Files that should NOT be checked
EXCLUDE_FILES = [
# Django-generated files
os.path.join(OTTERTUNE_DIR, 'server/website/manage.py'),
# Docker files
os.path.join(OTTERTUNE_DIR, 'server/website/createadmin.py'),
]
CHECKSTYLE_JAR_PATH = os.path.join(OTTERTUNE_DIR,
"client/controller/build/libs/checkstyle-8.8-all.jar")
# Regex patterns
PYCODESTYLE_COMMENT_PATTERN = re.compile(r'#\s*pycodestyle:\s*disable\s*=\s*[\w\,\s]+$')
PYTHON_ILLEGAL_PATTERNS = [
(re.compile(r'^print[ (]'), "Do not use 'print'. Use the logging module instead.")
]
JAVA_ILLEGAL_PATTERNS = [
(re.compile(r'^System.out.println'), "Do not use println. Use the logging module instead.")
]
PYTHON_HEADER_PATTERN = re.compile(r'#\n#.*\n#\n# Copyright.*\n#\n')
JAVA_HEADER_PATTERN = re.compile(r'/\*\n \*.*\n \*\n \* Copyright.*\n \*/\n\n')
# Stdout format strings
SEPARATOR = 80 * '-'
OUTPUT_FMT = (
'' + SEPARATOR + '\n\n'
'\033[1m' # start bold text
'%s\n'
'FAILED: %s\n\n'
'\033[0m' # end bold text
'%s'
)
VALIDATOR_FMT = '{name}\n{u}\n{out}'.format
MSG_PREFIX_FMT = ' {filename}:{line:3d}: '.format
MSG_SUFFIX_FMT = ' ({symbol})'.format
# ==============================================
# UTILITY FUNCTION DEFINITIONS
# ==============================================
def format_message(filename, line, message, symbol=None):
out_prefix = MSG_PREFIX_FMT(filename=filename, line=line)
out_suffix = '' if symbol is None else MSG_SUFFIX_FMT(symbol=symbol)
# Crop the message details to make the output more readable
max_msg_len = 80 - len(out_prefix) - len(out_suffix)
if len(message) > max_msg_len:
message = message[:max_msg_len - 3] + '...'
output = (out_prefix + message + out_suffix).replace('\n', '')
return output + '\n'
def validate_validator(modules, config_path):
status = True
# Check if required modules are installed
for module in modules:
if module is not None:
try:
imp.find_module(module)
except ImportError:
LOG.error("Cannot find module %s", module)
status = False
# Check that the config file exists if assigned
if config_path is not None and not os.path.isfile(config_path):
LOG.error("Cannot find config file %s", config_path)
status = False
return status
# Validate the file passed as argument
def validate_file(file_path):
if file_path in EXCLUDE_FILES:
return True
if not file_path.endswith(".py") and not file_path.endswith(".java"):
return True
LOG.debug("Validating file: %s", file_path)
status = True
output = []
failed_validators = []
for validator in VALIDATORS:
val_status, val_output = validator.validate_fn(
file_path, validator.config_path)
if not val_status:
status = False
output.append(VALIDATOR_FMT(name=validator.name,
u='-' * len(validator.name),
out=val_output))
failed_validators.append(validator.name)
if not status:
LOG.info(OUTPUT_FMT, file_path, ', '.join(failed_validators), '\n'.join(output))
return status
# Validate all the files in the root_dir passed as argument
def validate_dir(root_dir):
if root_dir in EXCLUDE_DIRECTORIES:
return True
status = True
for root, dirs, files in os.walk(root_dir): # pylint: disable=not-an-iterable
# Remove excluded dirs from list
dirs[:] = [d for d in dirs if os.path.join(root, d) not in EXCLUDE_DIRECTORIES]
for file_path in files:
file_path = os.path.join(root, file_path)
if not validate_file(file_path):
status = False
return status
# ==============================================
# VALIDATOR FUNCTION DEFINITIONS
# ==============================================
def check_pylint(file_path, config_path=None):
if not file_path.endswith(".py"):
return True, None
options = [
'--output-format=json',
'--reports=yes',
]
if config_path is not None:
options.append('--rcfile=' + config_path)
with settings(warn_only=True), quiet():
res = local('pylint {} {}'.format(' '.join(options), file_path), capture=True)
if res.stdout == '':
assert res.return_code == 0, 'return_code={}, expected=0\n{}'.format(
res.return_code, res.stderr)
return True, None
output = []
errors = json.loads(res.stdout)
for entry in errors:
# Remove extra whitespace and hints
msg = entry['message'].replace('^', '').replace('|', '')
msg = re.sub(' +', ' ', msg)
msg = msg.strip()
output.append(format_message(os.path.basename(file_path), entry['line'],
msg, entry['symbol']))
output = ''.join(output)
return res.return_code == 0, output
def check_pycodestyle(file_path, config_path=None):
import pycodestyle
if not file_path.endswith(".py"):
return True, None
# A custom reporter class for pycodestyle that checks for disabled errors
# and formats the style report output.
class CustomReporter(pycodestyle.StandardReport):
def get_file_results(self):
# Iterates through the lines of code that generated lint errors and
# checks if the given error has been disabled for that line via an
# inline comment (e.g., # pycodestyle: disable=E201,E226). Those
# that have been disabled are not treated as errors.
self._deferred_print.sort()
results = []
prev_line_num = -1
prev_line_errs = []
for line_number, _, code, text, _ in self._deferred_print:
if prev_line_num == line_number:
err_codes = prev_line_errs
else:
line = self.lines[line_number - 1]
m = PYCODESTYLE_COMMENT_PATTERN.search(line)
if m and m.group(0):
err_codes = [ec.strip() for ec in m.group(0).split('=')[1].split(',')]
else:
err_codes = []
prev_line_num = line_number
prev_line_errs = err_codes
if code in err_codes:
# Error is disabled in source
continue
results.append(format_message(os.path.basename(file_path),
self.line_offset + line_number,
text, code))
return results, len(results) == 0
# END CustomReporter class
options = {} if config_path is None else {'config_file': config_path}
style = pycodestyle.StyleGuide(quiet=True, **options)
# Set the reporter option to our custom one
style.options.reporter = CustomReporter
style.init_report()
report = style.check_files([file_path])
results, status = report.get_file_results()
output = None if status else ''.join(results)
return status, output
def check_java_checkstyle(file_path, config_path=None):
if not file_path.endswith(".java"):
return True, None
if not os.path.exists(CHECKSTYLE_JAR_PATH):
with lcd(os.path.join(OTTERTUNE_DIR, "client/controller")): # pylint: disable=not-context-manager
local("gradle downloadJars")
options = '' if config_path is None else '-c ' + config_path
with quiet():
res = local("java -jar {} {} {}".format(CHECKSTYLE_JAR_PATH, options, file_path),
capture=True)
lines = res.stdout.split('\n')
assert len(lines) >= 2 and lines[0] == "Starting audit..." and lines[-1] == "Audit done."
if len(lines) == 2:
return True, None
output = []
for line in lines[1:-1]:
parts = line.strip().split(':')
line_number = int(parts[1])
text, code = parts[-1].rsplit('[', 1)
text = text.strip()
code = code[:-1]
output.append(format_message(os.path.basename(file_path), line_number, text, code))
output = ''.join(output)
return False, output
def check_illegal_patterns(file_path, config_path=None): # pylint: disable=unused-argument
if file_path.endswith(".py"):
illegal_patterns = PYTHON_ILLEGAL_PATTERNS
comment = "#"
elif file_path.endswith(".java"):
illegal_patterns = JAVA_ILLEGAL_PATTERNS
comment = "//"
else:
return True, None
line_num = 1
output = []
status = True
with open(file_path, 'r') as f:
for line in f:
line = line.strip()
for pattern_info in illegal_patterns:
if not line.startswith(comment) and pattern_info[0].search(line):
output.append(format_message(filename=os.path.basename(file_path),
line=line_num,
message=pattern_info[1]))
status = False
line_num += 1
output = None if status else ''.join(output)
return status, output
def check_header(file_path, config_file=None): # pylint: disable=unused-argument
if file_path.endswith(".py"):
header_pattern = PYTHON_HEADER_PATTERN
elif file_path.endswith(".java"):
header_pattern = JAVA_HEADER_PATTERN
else:
return True, None
status = True
output = None
with open(file_path, 'r') as f:
file_contents = f.read()
header_match = header_pattern.search(file_contents)
filename = os.path.basename(file_path)
if header_match:
if filename not in header_match.group(0):
status = False
output = format_message(filename=filename, line=2,
message="Incorrect filename in header")
else:
status = False
output = format_message(filename=filename, line=1,
message='Missing header')
return status, output
# ==============================================
# VALIDATORS
# ==============================================
# Struct for storing validator metadata
Validator = namedtuple('Validator', 'name validate_fn modules config_path')
VALIDATORS = [
# Runs pylint on python source
Validator('check_pylint', check_pylint, ['pylint'],
os.path.join(OTTERTUNE_DIR, "script/formatting/config/pylintrc")),
# Runs pycodestyle on python source
Validator('check_pycodestyle', check_pycodestyle, ['pycodestyle'],
os.path.join(OTTERTUNE_DIR, "script/formatting/config/pycodestyle")),
# Runs checkstyle on the java source
Validator("check_java_checkstyle", check_java_checkstyle, [],
os.path.join(OTTERTUNE_DIR, "script/formatting/config/google_checks.xml")),
# Checks that the python/java source files do not use illegal patterns
Validator('check_illegal_patterns', check_illegal_patterns, [], None),
# Checks that the python/java source files have headers
Validator('check_header', check_header, [], None)
]
# ==============================================
# MAIN FUNCTION
# ==============================================
def main():
parser = argparse.ArgumentParser(description="Validate OtterTune's source code")
parser.add_argument('paths', metavar='PATH', type=str, nargs='*',
help='Files or directories to (recursively) validate')
parser.add_argument('--staged-files', action='store_true',
help='Apply the selected action(s) to all staged files (git)')
args = parser.parse_args()
LOG.info('\nRunning source validators:\n%s\n',
'\n'.join(' ' + v.name for v in VALIDATORS))
for validator in VALIDATORS:
if not validate_validator(validator.modules, validator.config_path):
sys.exit(EXIT_FAILURE)
if args.staged_files:
targets = [os.path.abspath(os.path.join(OTTERTUNE_DIR, f))
for f in subprocess.check_output(["git", "diff", "--name-only", "HEAD",
"--cached", "--diff-filter=d"]).split()]
if not targets:
LOG.error("No staged files or not calling from a repository. Exiting...")
sys.exit(EXIT_FAILURE)
elif args.paths:
targets = args.paths
else:
targets = DEFAULT_DIRS
for target in targets:
target = os.path.abspath(target)
if os.path.isfile(target):
LOG.debug("Scanning file: %s\n", target)
status = validate_file(target)
elif os.path.isdir(target):
LOG.debug("Scanning directory: %s\n", target)
status = validate_dir(target)
else:
LOG.error("%s isn't a file or directory", target)
sys.exit(EXIT_FAILURE)
if not status:
LOG.info(SEPARATOR + '\n')
LOG.info("Validation NOT successful\n")
sys.exit(EXIT_FAILURE)
LOG.info("Validation successful\n")
sys.exit(EXIT_SUCCESS)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,5 @@
#
# OtterTune - __init__.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#

19
server/analysis/base.py Normal file
View File

@ -0,0 +1,19 @@
#
# OtterTune - base.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Oct 25, 2017
@author: dva
'''
from abc import ABCMeta, abstractmethod
class ModelBase(object, metaclass=ABCMeta):
@abstractmethod
def _reset(self):
pass

793
server/analysis/cluster.py Normal file
View File

@ -0,0 +1,793 @@
#
# OtterTune - cluster.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Jul 4, 2016
@author: dva
'''
from abc import ABCMeta, abstractproperty
from collections import OrderedDict
import os
import json
import copy
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans as SklearnKMeans
from celery.utils.log import get_task_logger
from .base import ModelBase
# Log debug messages
LOGGER = get_task_logger(__name__)
class KMeans(ModelBase):
"""
KMeans:
Fits an Sklearn KMeans model to X.
See also
--------
http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html
Attributes
----------
n_clusters_ : int
The number of clusters, K
cluster_inertia_ : float
Sum of squared distances of samples to their closest cluster center
cluster_labels_ : array, [n_clusters_]
Labels indicating the membership of each point
cluster_centers_ : array, [n_clusters, n_features]
Coordinates of cluster centers
sample_labels_ : array, [n_samples]
Labels for each of the samples in X
sample_distances_ : array, [n_samples]
The distance between each sample point and its cluster's center
Constants
---------
SAMPLE_CUTOFF_ : int
If n_samples > SAMPLE_CUTOFF_ then sample distances
are NOT recorded
"""
SAMPLE_CUTOFF_ = 1000
def __init__(self):
self.model_ = None
self.n_clusters_ = None
self.sample_labels_ = None
self.sample_distances_ = None
@property
def cluster_inertia_(self):
# Sum of squared distances of samples to their closest cluster center
return None if self.model_ is None else \
self.model_.inertia_
@property
def cluster_labels_(self):
# Cluster membership labels for each point
return None if self.model_ is None else \
copy.deepcopy(self.model_.labels_)
@property
def cluster_centers_(self):
# Coordinates of the cluster centers
return None if self.model_ is None else \
copy.deepcopy(self.model_.cluster_centers_)
def _reset(self):
"""Resets all attributes (erases the model)"""
self.model_ = None
self.n_clusters_ = None
self.sample_labels_ = None
self.sample_distances_ = None
def fit(self, X, K, sample_labels=None, estimator_params=None):
"""Fits a Sklearn KMeans model to X.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
K : int
The number of clusters.
sample_labels : array-like, shape (n_samples), optional
Labels for each of the samples in X.
estimator_params : dict, optional
The parameters to pass to the KMeans estimators.
Returns
-------
self
"""
self._reset()
# Note: previously set n_init=50
self.model_ = SklearnKMeans(K)
if estimator_params is not None:
assert isinstance(estimator_params, dict)
self.model_.set_params(**estimator_params)
# Compute Kmeans model
self.model_.fit(X)
if sample_labels is None:
sample_labels = ["sample_{}".format(i) for i in range(X.shape[0])]
assert len(sample_labels) == X.shape[0]
self.sample_labels_ = np.array(sample_labels)
self.n_clusters_ = K
# Record sample label/distance from its cluster center
self.sample_distances_ = OrderedDict()
for cluster_label in range(self.n_clusters_):
assert cluster_label not in self.sample_distances_
member_rows = X[self.cluster_labels_ == cluster_label, :]
member_labels = self.sample_labels_[self.cluster_labels_ == cluster_label]
centroid = np.expand_dims(self.cluster_centers_[cluster_label], axis=0)
# "All clusters must have at least 1 member!"
if member_rows.shape[0] == 0:
return None
# Calculate distance between each member row and the current cluster
dists = np.empty(member_rows.shape[0])
dist_labels = []
for j, (row, label) in enumerate(zip(member_rows, member_labels)):
dists[j] = cdist(np.expand_dims(row, axis=0), centroid, "euclidean").squeeze()
dist_labels.append(label)
# Sort the distances/labels in ascending order
sort_order = np.argsort(dists)
dists = dists[sort_order]
dist_labels = np.array(dist_labels)[sort_order]
self.sample_distances_[cluster_label] = {
"sample_labels": dist_labels,
"distances": dists,
}
return self
def get_closest_samples(self):
"""Returns a list of the labels of the samples that are located closest
to their cluster's center.
Returns
----------
closest_samples : list
A list of the sample labels that are located the closest to
their cluster's center.
"""
if self.sample_distances_ is None:
raise Exception("No model has been fit yet!")
return [samples['sample_labels'][0] for samples in list(self.sample_distances_.values())]
def get_memberships(self):
'''
Return the memberships in each cluster
'''
memberships = OrderedDict()
for cluster_label, samples in list(self.sample_distances_.items()):
memberships[cluster_label] = OrderedDict(
[(l, d) for l, d in zip(samples["sample_labels"], samples["distances"])])
return json.dumps(memberships, indent=4)
class KMeansClusters(ModelBase):
"""
KMeansClusters:
Fits a KMeans model to X for clusters in the range [min_cluster_, max_cluster_].
Attributes
----------
min_cluster_ : int
The minimum cluster size to fit a KMeans model to
max_cluster_ : int
The maximum cluster size to fit a KMeans model to
cluster_map_ : dict
A dictionary mapping the cluster size (K) to the KMeans
model fitted to X with K clusters
sample_labels_ : array, [n_samples]
Labels for each of the samples in X
"""
def __init__(self):
self.min_cluster_ = None
self.max_cluster_ = None
self.cluster_map_ = None
self.sample_labels_ = None
def _reset(self):
"""Resets all attributes (erases the model)"""
self.min_cluster_ = None
self.max_cluster_ = None
self.cluster_map_ = None
self.sample_labels_ = None
def fit(self, X, min_cluster, max_cluster, sample_labels=None, estimator_params=None):
"""Fits a KMeans model to X for each cluster in the range [min_cluster, max_cluster].
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
min_cluster : int
The minimum cluster size to fit a KMeans model to.
max_cluster : int
The maximum cluster size to fit a KMeans model to.
sample_labels : array-like, shape (n_samples), optional
Labels for each of the samples in X.
estimator_params : dict, optional
The parameters to pass to the KMeans estimators.
Returns
-------
self
"""
self._reset()
self.min_cluster_ = min_cluster
self.max_cluster_ = max_cluster
self.cluster_map_ = {}
if sample_labels is None:
sample_labels = ["sample_{}".format(i) for i in range(X.shape[1])]
self.sample_labels_ = sample_labels
for K in range(self.min_cluster_, self.max_cluster_ + 1):
tmp = KMeans().fit(X, K, self.sample_labels_, estimator_params)
if tmp is None: # Set maximum cluster
assert K > min_cluster, "min_cluster is too large for the model"
self.max_cluster_ = K - 1
break
else:
self.cluster_map_[K] = tmp
return self
def save(self, savedir):
"""Saves the KMeans model results
Parameters
----------
savedir : string
Path to the directory to save the results in.
"""
if self.cluster_map_ is None:
raise Exception("No models have been fitted yet!")
cluster_map = OrderedDict()
inertias = []
for K, model in sorted(self.cluster_map_.items()):
cluster_map[K] = {
"cluster_inertia": model.cluster_inertia_,
"cluster_labels": model.cluster_labels_,
"cluster_centers": model.cluster_centers_,
}
inertias.append(model.cluster_inertia_)
# Save sum of squares plot (elbow curve)
fig = plt.figure()
plt.plot(list(cluster_map.keys()), inertias, '--o')
plt.xlabel("Number of clusters (K)")
plt.ylabel("Within sum of squares W_k")
plt.title("Within Sum of Squares vs. Number of Clusters")
fig.canvas.set_window_title(os.path.basename(savedir))
savepath = os.path.join(savedir, "kmeans_sum_of_squares.pdf")
plt.savefig(savepath, bbox_inches="tight")
plt.close()
# save cluster memberships
for K in range(self.min_cluster_, self.max_cluster_ + 1):
savepath = os.path.join(savedir,
"memberships_{}-clusters.json".format(K))
members = self.cluster_map_[K].get_memberships()
with open(savepath, "w") as f:
f.write(members)
class KSelection(ModelBase, metaclass=ABCMeta):
"""KSelection:
Abstract class for techniques that approximate the optimal
number of clusters (K).
Attributes
----------
optimal_num_clusters_ : int
An estimation of the optimal number of clusters K for
a KMeans model fit to X
clusters_ : array, [n_clusters]
The sizes of the clusters
name_ : string
The name of this technique
"""
NAME_ = None
def __init__(self):
self.optimal_num_clusters_ = None
self.clusters_ = None
def _reset(self):
"""Resets all attributes (erases the model)"""
self.optimal_num_clusters_ = None
self.clusters_ = None
@abstractproperty
def name_(self):
pass
def save(self, savedir):
"""Saves the estimation of the optimal # of clusters.
Parameters
----------
savedir : string
Path to the directory to save the results in.
"""
if self.optimal_num_clusters_ is None:
raise Exception("Optimal number of clusters has not been computed!")
# Save the computed optimal number of clusters
savepath = os.path.join(savedir, self.name_ + "_optimal_num_clusters.txt")
with open(savepath, "w") as f:
f.write(str(self.optimal_num_clusters_))
class GapStatistic(KSelection):
"""GapStatistic:
Approximates the optimal number of clusters (K).
References
----------
https://web.stanford.edu/~hastie/Papers/gap.pdf
Attributes
----------
optimal_num_clusters_ : int
An estimation of the optimal number of clusters K for
a KMeans model fit to X
clusters_ : array, [n_clusters]
The sizes of the clusters
name_ : string
The name of this technique
log_wks_ : array, [n_clusters]
The within-dispersion measures of X (log)
log_wkbs_ : array, [n_clusters]
The within-dispersion measures of the generated
reference data sets
khats_ : array, [n_clusters]
The gap-statistic for each cluster
"""
NAME_ = "gap-statistic"
def __init__(self):
super(GapStatistic, self).__init__()
self.log_wks_ = None
self.log_wkbs_ = None
self.khats_ = None
@property
def name_(self):
return self.NAME_
def _reset(self):
"""Resets all attributes (erases the model)"""
super(GapStatistic, self)._reset()
self.log_wks_ = None
self.log_wkbs_ = None
self.khats_ = None
def fit(self, X, cluster_map, n_b=50):
"""Estimates the optimal number of clusters (K) for a
KMeans model trained on X.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
cluster_map_ : dict
A dictionary mapping each cluster size (K) to the KMeans
model fitted to X with K clusters
n_B : int
The number of reference data sets to generate
Returns
-------
self
"""
self._reset()
mins, maxs = GapStatistic.bounding_box(X)
n_clusters = len(cluster_map)
# Dispersion for real distribution
log_wks = np.zeros(n_clusters)
log_wkbs = np.zeros(n_clusters)
sk = np.zeros(n_clusters)
for indk, (K, model) in enumerate(sorted(cluster_map.items())):
# Computes Wk: the within-dispersion of each cluster size (k)
log_wks[indk] = np.log(model.cluster_inertia_ / (2.0 * K))
# Create B reference datasets
log_bwkbs = np.zeros(n_b)
for i in range(n_b):
Xb = np.empty_like(X)
for j in range(X.shape[1]):
Xb[:, j] = np.random.uniform(mins[j], maxs[j], size=X.shape[0])
Xb_model = KMeans().fit(Xb, K)
log_bwkbs[i] = np.log(Xb_model.cluster_inertia_ / (2.0 * K))
log_wkbs[indk] = sum(log_bwkbs) / n_b
sk[indk] = np.sqrt(sum((log_bwkbs - log_wkbs[indk]) ** 2) / n_b)
sk = sk * np.sqrt(1 + 1.0 / n_b)
khats = np.zeros(n_clusters)
gaps = log_wkbs - log_wks
gsks = gaps - sk
khats[1:] = gaps[0:-1] - gsks[1:]
self.clusters_ = np.array(sorted(cluster_map.keys()))
for i in range(1, n_clusters):
if gaps[i - 1] >= gsks[i]:
self.optimal_num_clusters_ = self.clusters_[i - 1]
break
if self.optimal_num_clusters_ is None:
LOGGER.info("GapStatistic NOT found the optimal k, \
use the last(maximum) k instead ")
self.optimal_num_clusters_ = self.clusters_[-1]
self.log_wks_ = log_wks
self.log_wkbs_ = log_wkbs
self.khats_ = khats
return self
@staticmethod
def bounding_box(X):
"""Computes the box that tightly bounds X
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
Returns
-------
The mins and maxs that make up the bounding box
"""
mins = np.min(X, axis=0)
maxs = np.max(X, axis=0)
return mins, maxs
@staticmethod
def Wk(X, mu, cluster_labels):
"""Computes the within-dispersion of each cluster size (k)
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
mu : array-like, shape (n_clusters, n_features)
Coordinates of cluster centers
cluster_labels: array-like, shape (n_samples)
Labels for each of the samples in X.
Returns
-------
The within-dispersion of each cluster (K)
"""
K = len(mu)
return sum([np.linalg.norm(mu[i] - x) ** 2 / (2.0 * K)
for i in range(K)
for x in X[cluster_labels == i]])
def save(self, savedir):
"""Saves the estimation results of the optimal # of clusters.
Parameters
----------
savedir : string
Path to the directory to save the results in.
"""
super(GapStatistic, self).save(savedir)
# Plot the calculated gap
gaps = self.log_wkbs_ - self.log_wks_
fig = plt.figure()
plt.plot(self.clusters_, gaps, '--o')
plt.title("Gap vs. Number of Clusters")
plt.xlabel("Number of clusters (K)")
plt.ylabel("gap_K")
fig.canvas.set_window_title(os.path.basename(savedir))
plt.savefig(os.path.join(savedir, self.name_ + ".pdf"), bbox_inches="tight")
plt.close()
# Plot the gap statistic
fig = plt.figure()
plt.bar(self.clusters_, self.khats_)
plt.title("Gap Statistic vs. Number of Clusters")
plt.xlabel("Number of clusters (K)")
plt.ylabel("gap(K)-(gap(K+1)-s(K+1))")
fig.canvas.set_window_title(os.path.basename(savedir))
plt.savefig(os.path.join(savedir, self.name_ + "_final.pdf"),
bbox_inches="tight")
plt.close()
class DetK(KSelection):
"""DetK:
Approximates the optimal number of clusters (K).
References
----------
https://www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf
Attributes
----------
optimal_num_clusters_ : int
An estimation of the optimal number of clusters K for
KMeans models fit to X
clusters_ : array, [n_clusters]
The sizes of the clusters
name_ : string
The name of this technique
fs_ : array, [n_clusters]
The computed evaluation functions F(K) for each cluster size K
"""
NAME_ = "det-k"
def __init__(self):
super(DetK, self).__init__()
self.fs_ = None
@property
def name_(self):
return DetK.NAME_
def _reset(self):
"""Resets all attributes (erases the model)"""
super(DetK, self)._reset()
self.fs_ = None
def fit(self, X, cluster_map):
"""Estimates the optimal number of clusters (K) for a
KMeans model trained on X.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
cluster_map_ : dict
A dictionary mapping each cluster size (K) to the KMeans
model fitted to X with K clusters
Returns
-------
self
"""
self._reset()
n_clusters = len(cluster_map)
nd = X.shape[1]
fs = np.empty(n_clusters)
sks = np.empty(n_clusters)
alpha = {}
# K from 1 to maximum_cluster_
for i, (K, model) \
in enumerate(sorted(cluster_map.items())):
# Compute alpha(K, nd) (i.e. alpha[K])
if K == 2:
alpha[K] = 1 - 3.0 / (4 * nd)
elif K > 2:
alpha[K] = alpha[K - 1] + (1 - alpha[K - 1]) / 6.0
sks[i] = model.cluster_inertia_
if K == 1:
fs[i] = 1
elif sks[i - 1] == 0:
fs[i] = 1
else:
fs[i] = sks[i] / (alpha[K] * sks[i - 1])
self.clusters_ = np.array(sorted(cluster_map.keys()))
self.optimal_num_clusters_ = self.clusters_[np.argmin(fs)]
self.fs_ = fs
return self
def save(self, savedir):
"""Saves the estimation results of the optimal # of clusters.
Parameters
----------
savedir : string
Path to the directory to save the results in.
"""
super(DetK, self).save(savedir)
# Plot the evaluation function
fig = plt.figure()
plt.plot(self.clusters_, self.fs_, '--o')
plt.xlabel("Number of clusters (K)")
plt.ylabel("Evaluation function (F_k)")
plt.title("Evaluation Function vs. Number of Clusters")
fig.canvas.set_window_title(os.path.basename(savedir))
savepath = os.path.join(savedir, self.name_ + "_eval_function.pdf")
plt.savefig(savepath, bbox_inches="tight")
plt.close()
class Silhouette(KSelection):
"""Det:
Approximates the optimal number of clusters (K).
References
----------
http://scikit-learn.org/stable/modules/generated/sklearn.metrics.silhouette_score.html
Attributes
----------
optimal_num_clusters_ : int
An estimation of the optimal number of clusters K for
KMeans models fit to X
clusters_ : array, [n_clusters]
The sizes of the clusters
name_ : string
The name of this technique
Score_ : array, [n_clusters]
The mean Silhouette Coefficient for each cluster size K
"""
# short for Silhouette score
NAME_ = "s-score"
def __init__(self):
super(Silhouette, self).__init__()
self.scores_ = None
@property
def name_(self):
return Silhouette.NAME_
def _reset(self):
"""Resets all attributes (erases the model)"""
super(Silhouette, self)._reset()
self.scores_ = None
def fit(self, X, cluster_map):
"""Estimates the optimal number of clusters (K) for a
KMeans model trained on X.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
cluster_map_ : dict
A dictionary mapping each cluster size (K) to the KMeans
model fitted to X with K clusters
Returns
-------
self
"""
self._reset()
n_clusters = len(cluster_map)
# scores = np.empty(n_clusters)
scores = np.zeros(n_clusters)
for i, (K, model) \
in enumerate(sorted(cluster_map.items())):
if K <= 1: # K >= 2
continue
scores[i] = silhouette_score(X, model.cluster_labels_)
self.clusters_ = np.array(sorted(cluster_map.keys()))
self.optimal_num_clusters_ = self.clusters_[np.argmax(scores)]
self.scores_ = scores
return self
def save(self, savedir):
"""Saves the estimation results of the optimal # of clusters.
Parameters
----------
savedir : string
Path to the directory to save the results in.
"""
super(Silhouette, self).save(savedir)
# Plot the evaluation function
fig = plt.figure()
plt.plot(self.clusters_, self.scores_, '--o')
plt.xlabel("Number of clusters (K)")
plt.ylabel("Silhouette scores")
plt.title("Silhouette Scores vs. Number of Clusters")
fig.canvas.set_window_title(os.path.basename(savedir))
savepath = os.path.join(savedir, self.name_ + "_eval_function.pdf")
plt.savefig(savepath, bbox_inches="tight")
plt.close()
def create_kselection_model(model_name):
"""Constructs the KSelection model object with the given name
Parameters
----------
model_name : string
Name of the KSelection model.
One of ['gap-statistic', 'det-k', 's-score']
Returns
-------
The constructed model object
"""
kselection_map = {
DetK.NAME_: DetK,
GapStatistic.NAME_: GapStatistic,
Silhouette.NAME_: Silhouette
}
if model_name not in kselection_map:
raise Exception("KSelection model {} not supported!".format(model_name))
else:
return kselection_map[model_name]()

View File

@ -0,0 +1,115 @@
#
# OtterTune - constraints.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Sep 8, 2016
@author: dvanaken
'''
import numpy as np
class ParamConstraintHelper(object):
def __init__(self, scaler, encoder=None, binary_vars=None,
init_flip_prob=0.3, flip_prob_decay=0.5):
if 'inverse_transform' not in dir(scaler):
raise Exception("Scaler object must provide function inverse_transform(X)")
if 'transform' not in dir(scaler):
raise Exception("Scaler object must provide function transform(X)")
self.scaler_ = scaler
if encoder is not None and len(encoder.n_values) > 0:
self.is_dummy_encoded_ = True
self.encoder_ = encoder.encoder
else:
self.is_dummy_encoded_ = False
self.binary_vars_ = binary_vars
self.init_flip_prob_ = init_flip_prob
self.flip_prob_decay_ = flip_prob_decay
def apply_constraints(self, sample, scaled=True, rescale=True):
conv_sample = self._handle_scaling(sample, scaled)
if self.is_dummy_encoded_:
# apply categorical (ie enum var, >=3 values) constraints
n_values = self.encoder_.n_values_
cat_start_indices = self.encoder_.feature_indices_
for i, nvals in enumerate(n_values):
start_idx = cat_start_indices[i]
cvals = conv_sample[start_idx: start_idx + nvals]
cvals = np.array(np.arange(nvals) == np.argmax(cvals), dtype=float)
assert np.sum(cvals) == 1
conv_sample[start_idx: start_idx + nvals] = cvals
# apply binary (0-1) constraints
if self.binary_vars_ is not None:
for i in self.binary_vars_:
# round to closest
if conv_sample[i] >= 0.5:
conv_sample[i] = 1
else:
conv_sample[i] = 0
conv_sample = self._handle_rescaling(conv_sample, rescale)
return conv_sample
def _handle_scaling(self, sample, scaled):
if scaled:
if sample.ndim == 1:
sample = sample.reshape(1, -1)
sample = self.scaler_.inverse_transform(sample).ravel()
else:
sample = np.array(sample)
return sample
def _handle_rescaling(self, sample, rescale):
if rescale:
if sample.ndim == 1:
sample = sample.reshape(1, -1)
return self.scaler_.transform(sample).ravel()
return sample
def randomize_categorical_features(self, sample, scaled=True, rescale=True):
# If there are no categorical features, this function is a no-op.
if not self.is_dummy_encoded_:
return sample
n_values = self.encoder_.n_values_
cat_start_indices = self.encoder_.feature_indices_
n_cat_feats = len(n_values)
conv_sample = self._handle_scaling(sample, scaled)
flips = np.zeros((n_cat_feats,), dtype=bool)
# Always flip at least one categorical feature
flips[0] = True
# Flip the rest with decreasing probability
p = self.init_flip_prob_
for i in range(1, n_cat_feats):
if np.random.rand() <= p:
flips[i] = True
p *= self.flip_prob_decay_
flip_shuffle_indices = np.random.choice(np.arange(n_cat_feats),
n_cat_feats,
replace=False)
flips = flips[flip_shuffle_indices]
for i, nvals in enumerate(n_values):
if flips[i]:
start_idx = cat_start_indices[i]
current_val = conv_sample[start_idx: start_idx + nvals]
assert np.all(np.logical_or(current_val == 0, current_val == 1)), \
"categorical {0}: value not 0/1: {1}".format(i, current_val)
choices = np.arange(nvals)[current_val != 1]
assert choices.size == nvals - 1
r = np.zeros(nvals)
r[np.random.choice(choices)] = 1
assert np.sum(r) == 1
conv_sample[start_idx: start_idx + nvals] = r
conv_sample = self._handle_rescaling(conv_sample, rescale)
return conv_sample

View File

@ -0,0 +1,111 @@
#
# OtterTune - factor_analysis.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
'''
Created on Jul 4, 2016
@author: dvanaken
'''
import numpy as np
from sklearn.decomposition import FactorAnalysis as SklearnFactorAnalysis
from .base import ModelBase
class FactorAnalysis(ModelBase):
"""FactorAnalysis (FA):
Fits an Sklearn FactorAnalysis model to X.
See also
--------
http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.FactorAnalysis.html
Attributes
----------
model_ : sklearn.decomposition.FactorAnalysis
The fitted FA model
components_ : array, [n_components, n_features]
Components (i.e., factors) with maximum variance
feature_labels_ : array, [n_features]
total_variance_ : float
The total amount of variance explained by the components
pvars_ : array, [n_components]
The percentage of the variance explained by each component
pvars_noise_ : array, [n_components]
The percentage of the variance explained by each component also
accounting for noise
"""
def __init__(self):
self.model_ = None
self.components_ = None
self.feature_labels_ = None
self.total_variance_ = None
self.pvars_ = None
self.pvars_noise_ = None
def _reset(self):
"""Resets all attributes (erases the model)"""
self.model_ = None
self.components_ = None
self.feature_labels_ = None
self.total_variance_ = None
self.pvars_ = None
self.pvars_noise_ = None
def fit(self, X, feature_labels=None, n_components=None, estimator_params=None):
"""Fits an Sklearn FA model to X.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
feature_labels : array-like, shape (n_features), optional
Labels for each of the features in X.
estimator_params : dict, optional
The parameters to pass to Sklearn's FA estimators.
Returns
-------
self
"""
self._reset()
if feature_labels is None:
feature_labels = ["feature_{}".format(i) for i in range(X.shape[1])]
self.feature_labels_ = feature_labels
if n_components is not None:
model = SklearnFactorAnalysis(n_components=n_components)
else:
model = SklearnFactorAnalysis()
self.model_ = model
if estimator_params is not None:
# Update Sklearn estimator params
assert isinstance(estimator_params, dict)
self.model_.set_params(**estimator_params)
self.model_.fit(X)
# Remove zero-valued components (n_components x n_features)
components_mask = np.sum(self.model_.components_ != 0.0, axis=1) > 0.0
self.components_ = self.model_.components_[components_mask]
# Compute the % variance explained (with/without noise)
c2 = np.sum(self.components_ ** 2, axis=1)
self.total_variance_ = np.sum(c2)
self.pvars_ = 100 * c2 / self.total_variance_
self.pvars_noise_ = 100 * c2 / (self.total_variance_ +
np.sum(self.model_.noise_variance_))
return self

Some files were not shown because too many files have changed in this diff Show More