From b120be1772f2416730ea1066782bf0813ca2dd1a Mon Sep 17 00:00:00 2001 From: Amrith Kumar Date: Sun, 9 Oct 2016 07:13:29 -0400 Subject: [PATCH] Merge trove-integration into trove This commit will merge into trove, the trove-integration tree as of commit 9f92ca853f8aa2f72921e54682c918941a8f0919. This is in preparation for making trove-integration go away. In addition, it supresses any consideration of the integration directory in the trove tox tests as it is understandably a small pile of pooh and in need of much cleanup. Change-Id: Ib7f2655c4c5ed86b5454708c04371ee55e37ec2d Partially-Implements-Blueprint: eliminate-trove-integration-and-redstack --- integration/.gitignore | 25 + integration/.gitreview | 4 + integration/README.md | 210 + integration/apidocs/pom.xml | 193 + .../apidocs/src/resources/cdb-devguide.xml | 1675 ++++++ .../src/resources/cdb-getting-started.xml | 1131 ++++ .../src/resources/cdb-mgmt-devguide.xml | 1267 +++++ .../src/resources/cdb-releasenotes.xml | 711 +++ .../src/resources/images/Choose_CS_Image.png | Bin 0 -> 85201 bytes .../resources/images/Choose_CS_Image_CCP.png | Bin 0 -> 24996 bytes .../src/resources/images/Choose_Image_CCP.png | Bin 0 -> 21253 bytes .../resources/images/Choose_Image_CCP.tiff | Bin 0 -> 26244 bytes .../images/Cloud_DB_Infographic-1.png | Bin 0 -> 66336 bytes .../images/Cloud_DB_Infographic-1.svg | 4533 +++++++++++++++++ .../src/resources/images/Create_CS.png | Bin 0 -> 31902 bytes .../src/resources/images/phpMyAdmin.png | Bin 0 -> 257577 bytes .../src/resources/samples/db-auth-20.json | 128 + .../src/resources/samples/db-auth-20.xml | 63 + .../src/resources/samples/db-auth.json | 72 + .../apidocs/src/resources/samples/db-auth.xml | 50 + .../samples/db-check-root-user-request.json | 8 + .../samples/db-check-root-user-request.xml | 8 + .../samples/db-check-root-user-response.json | 8 + .../samples/db-check-root-user-response.xml | 6 + .../db-create-database-instance-response.json | 41 + .../db-create-database-instance-response.xml | 24 + .../samples/db-create-databases-request.json | 19 + .../samples/db-create-databases-request.xml | 13 + .../samples/db-create-databases-response.json | 4 + .../samples/db-create-databases-response.xml | 4 + .../samples/db-create-instance-request.json | 37 + .../samples/db-create-instance-request.xml | 23 + .../samples/db-create-instance-response.json | 41 + .../samples/db-create-instance-response.xml | 19 + .../samples/db-create-users-request.json | 32 + .../samples/db-create-users-request.xml | 21 + .../samples/db-create-users-response.json | 4 + .../samples/db-create-users-response.xml | 4 + .../resources/samples/db-credentials-20.json | 18 + .../resources/samples/db-credentials-20.xml | 14 + .../src/resources/samples/db-credentials.json | 13 + .../src/resources/samples/db-credentials.xml | 11 + .../samples/db-delete-databases-request.json | 8 + .../samples/db-delete-databases-request.xml | 8 + .../samples/db-delete-databases-response.json | 4 + .../samples/db-delete-databases-response.xml | 4 + .../samples/db-delete-instance-request.json | 8 + .../samples/db-delete-instance-request.xml | 8 + .../samples/db-delete-instance-response.json | 4 + .../samples/db-delete-instance-response.xml | 4 + .../samples/db-delete-users-request.json | 8 + .../samples/db-delete-users-request.xml | 8 + .../samples/db-delete-users-response.json | 4 + .../samples/db-delete-users-response.xml | 4 + .../samples/db-enable-root-user-request.json | 8 + .../samples/db-enable-root-user-request.xml | 8 + .../samples/db-enable-root-user-response.json | 11 + .../samples/db-enable-root-user-response.xml | 7 + .../samples/db-faults-badRequest.json | 11 + .../samples/db-faults-badRequest.xml | 10 + .../samples/db-faults-instanceFault.json | 11 + .../samples/db-faults-instanceFault.xml | 10 + .../samples/db-faults-itemNotFound.json | 11 + .../samples/db-faults-itemNotFound.xml | 10 + .../samples/db-flavors-by-id-request.json | 8 + .../samples/db-flavors-by-id-request.xml | 8 + .../samples/db-flavors-by-id-response.json | 22 + .../samples/db-flavors-by-id-response.xml | 11 + .../resources/samples/db-flavors-request.json | 8 + .../resources/samples/db-flavors-request.xml | 8 + .../samples/db-flavors-response.json | 69 + .../resources/samples/db-flavors-response.xml | 32 + ...-gs-create-database-instance-response.json | 41 + ...b-gs-create-database-instance-response.xml | 22 + .../samples/db-guest-update-request.json | 10 + .../samples/db-guest-update-request.xml | 10 + .../samples/db-guest-update-response.json | 6 + .../samples/db-guest-update-response.xml | 6 + .../samples/db-instance-reboot-request.json | 10 + .../samples/db-instance-reboot-request.xml | 10 + .../samples/db-instance-reboot-response.json | 6 + .../samples/db-instance-reboot-response.xml | 6 + .../db-instance-resize-flavor-request.json | 12 + .../db-instance-resize-flavor-request.xml | 10 + .../db-instance-resize-flavor-response.json | 4 + .../db-instance-resize-flavor-response.xml | 4 + .../db-instance-resize-instance-request.json | 12 + .../db-instance-resize-instance-request.xml | 12 + .../db-instance-resize-instance-response.json | 6 + .../db-instance-resize-instance-response.xml | 6 + .../db-instance-resize-volume-request.json | 14 + .../db-instance-resize-volume-request.xml | 12 + .../db-instance-resize-volume-response.json | 4 + .../db-instance-resize-volume-response.xml | 4 + .../samples/db-instance-restart-request.json | 10 + .../samples/db-instance-restart-request.xml | 9 + .../samples/db-instance-restart-response.json | 4 + .../samples/db-instance-restart-response.xml | 4 + .../db-instance-status-detail-request.json | 8 + .../db-instance-status-detail-request.xml | 8 + .../db-instance-status-detail-response.json | 42 + .../db-instance-status-detail-response.xml | 20 + .../db-instance-update-guest-request.json | 11 + .../db-instance-update-guest-request.xml | 10 + .../db-instance-update-guest-response.json | 8 + .../db-instance-update-guest-response.xml | 8 + ...db-instances-index-pagination-request.json | 8 + .../db-instances-index-pagination-request.xml | 8 + ...b-instances-index-pagination-response.json | 45 + ...db-instances-index-pagination-response.xml | 23 + .../samples/db-instances-index-request.json | 8 + .../samples/db-instances-index-request.xml | 8 + .../samples/db-instances-index-response.json | 71 + .../samples/db-instances-index-response.xml | 34 + .../db-instances-paged-index-request.json | 8 + .../db-instances-paged-index-request.xml | 8 + .../db-instances-paged-index-response.json | 90 + .../db-instances-paged-index-response.xml | 41 + .../samples/db-list-databases-request.json | 8 + .../samples/db-list-databases-request.xml | 8 + .../samples/db-list-databases-response.json | 24 + .../samples/db-list-databases-response.xml | 12 + .../samples/db-list-users-request.json | 8 + .../samples/db-list-users-request.xml | 8 + .../samples/db-list-users-response.json | 28 + .../samples/db-list-users-response.xml | 24 + .../db-mgmt-get-account-details-request.json | 8 + .../db-mgmt-get-account-details-request.xml | 8 + .../db-mgmt-get-account-details-response.json | 24 + .../db-mgmt-get-account-details-response.xml | 12 + .../db-mgmt-get-host-detail-request.json | 8 + .../db-mgmt-get-host-detail-request.xml | 8 + .../db-mgmt-get-host-detail-response.json | 29 + .../db-mgmt-get-host-detail-response.xml | 12 + .../db-mgmt-get-instance-details-request.json | 8 + .../db-mgmt-get-instance-details-request.xml | 8 + ...db-mgmt-get-instance-details-response.json | 51 + .../db-mgmt-get-instance-details-response.xml | 20 + .../db-mgmt-get-root-details-request.json | 8 + .../db-mgmt-get-root-details-request.xml | 8 + .../db-mgmt-get-root-details-response.json | 12 + .../db-mgmt-get-root-details-response.xml | 7 + .../samples/db-mgmt-get-storage-request.json | 8 + .../samples/db-mgmt-get-storage-request.xml | 8 + .../samples/db-mgmt-get-storage-response.json | 23 + .../samples/db-mgmt-get-storage-response.xml | 12 + .../samples/db-mgmt-host-update-request.json | 12 + .../samples/db-mgmt-host-update-request.xml | 10 + .../samples/db-mgmt-host-update-response.json | 6 + .../samples/db-mgmt-host-update-response.xml | 6 + .../db-mgmt-instance-diagnostics-request.json | 8 + .../db-mgmt-instance-diagnostics-request.xml | 8 + ...db-mgmt-instance-diagnostics-response.json | 16 + .../db-mgmt-instance-diagnostics-response.xml | 7 + .../db-mgmt-instance-hwinfo-request.json | 8 + .../db-mgmt-instance-hwinfo-request.xml | 8 + .../db-mgmt-instance-hwinfo-response.json | 13 + .../db-mgmt-instance-hwinfo-response.xml | 15 + .../db-mgmt-instance-index-request.json | 8 + .../db-mgmt-instance-index-request.xml | 8 + .../db-mgmt-instance-index-response.json | 89 + .../db-mgmt-instance-index-response.xml | 34 + .../db-mgmt-list-accounts-request.json | 8 + .../samples/db-mgmt-list-accounts-request.xml | 8 + .../db-mgmt-list-accounts-response.json | 13 + .../db-mgmt-list-accounts-response.xml | 9 + .../samples/db-mgmt-list-hosts-request.json | 8 + .../samples/db-mgmt-list-hosts-request.xml | 8 + .../samples/db-mgmt-list-hosts-response.json | 13 + .../samples/db-mgmt-list-hosts-response.xml | 9 + .../resources/samples/db-request-types.json | 15 + .../resources/samples/db-response-types.xml | 24 + .../resources/samples/db-version-request.json | 8 + .../resources/samples/db-version-request.xml | 8 + .../samples/db-version-response.json | 18 + .../resources/samples/db-version-response.xml | 10 + .../samples/db-versions-request.json | 8 + .../resources/samples/db-versions-request.xml | 8 + .../samples/db-versions-response.json | 20 + .../samples/db-versions-response.xml | 12 + integration/scripts/conf.json.example | 12 + integration/scripts/conf/cassandra.conf | 6 + integration/scripts/conf/couchbase.conf | 6 + integration/scripts/conf/couchdb.conf | 6 + integration/scripts/conf/db2.conf | 6 + integration/scripts/conf/mariadb.conf | 6 + integration/scripts/conf/mongodb.conf | 6 + integration/scripts/conf/mysql.conf | 6 + integration/scripts/conf/percona.conf | 6 + integration/scripts/conf/postgresql.conf | 6 + integration/scripts/conf/pxc.conf | 6 + integration/scripts/conf/redis.conf | 6 + integration/scripts/conf/test_begin.conf | 101 + integration/scripts/conf/test_end.conf | 2 + integration/scripts/conf/vertica.conf | 6 + integration/scripts/create_vm | 84 + .../files/elements/apt-conf-dir/README.rst | 16 + .../extra-data.d/99-use-host-apt-confd | 21 + .../fedora-guest/extra-data.d/15-reddwarf-dep | 48 + .../extra-data.d/20-guest-systemd | 21 + .../fedora-guest/extra-data.d/62-ssh-key | 31 + .../fedora-guest/install.d/15-reddwarf-dep | 30 + .../elements/fedora-guest/install.d/20-etc | 8 + .../elements/fedora-guest/install.d/50-user | 17 + .../fedora-guest/install.d/62-ssh-key | 29 + .../post-install.d/05-ipforwarding | 5 + .../post-install.d/62-trove-guest-sudoers | 15 + .../fedora-guest/post-install.d/90-yum-update | 9 + .../files/elements/fedora-mariadb/README.md | 3 + .../fedora-mariadb/install.d/10-mariadb | 9 + .../pre-install.d/10-percona-copr | 10 + .../files/elements/fedora-mongodb/README.md | 1 + .../fedora-mongodb/install.d/10-mongodb | 24 + .../install.d/25-trove-mongo-dep | 9 + .../files/elements/fedora-mysql/README.md | 3 + .../elements/fedora-mysql/install.d/10-mysql | 16 + .../fedora-mysql/install.d/40-xtrabackup | 10 + .../post-install.d/30-register-mysql-service | 6 + .../install.d/05-percona-server | 17 + .../fedora-percona/install.d/10-mysql | 16 + .../fedora-postgresql/install.d/10-postgresql | 83 + .../files/elements/fedora-redis/README.md | 1 + .../elements/fedora-redis/install.d/10-redis | 9 + .../ubuntu-cassandra/install.d/10-cassandra | 25 + .../ubuntu-couchbase/install.d/10-couchbase | 8 + .../ubuntu-couchdb/install.d/10-couchdb | 19 + .../files/elements/ubuntu-db2/README.md | 36 + .../ubuntu-db2/extra-data.d/20-copy-db2-pkgs | 25 + .../elements/ubuntu-db2/install.d/10-db2 | 52 + .../ubuntu-guest/extra-data.d/15-reddwarf-dep | 48 + .../extra-data.d/20-guest-upstart | 21 + .../ubuntu-guest/extra-data.d/62-ssh-key | 31 + .../ubuntu-guest/install.d/05-base-apps | 10 + .../ubuntu-guest/install.d/15-reddwarf-dep | 31 + .../elements/ubuntu-guest/install.d/20-etc | 8 + .../elements/ubuntu-guest/install.d/50-user | 18 + .../ubuntu-guest/install.d/62-ssh-key | 28 + .../elements/ubuntu-guest/install.d/98-ssh | 8 + .../ubuntu-guest/install.d/99-clean-apt | 11 + .../post-install.d/05-ipforwarding | 4 + .../ubuntu-guest/post-install.d/10-ntp | 10 + .../post-install.d/62-trove-guest-sudoers | 15 + .../post-install.d/90-apt-get-update | 9 + .../ubuntu-guest/pre-install.d/01-trim-pkgs | 117 + .../pre-install.d/04-baseline-tools | 7 + .../files/elements/ubuntu-mariadb/README.md | 3 + .../ubuntu-mariadb/install.d/30-mariadb | 34 + .../pre-install.d/10-percona-apt-key | 40 + .../pre-install.d/20-apparmor-mysql-local | 11 + .../files/elements/ubuntu-mongodb/README.md | 1 + .../ubuntu-mongodb/install.d/10-mongodb-thp | 42 + .../ubuntu-mongodb/install.d/20-mongodb | 8 + .../install.d/25-trove-mongo-dep | 9 + .../ubuntu-mongodb/install.d/30-mongodb-conf | 26 + .../ubuntu-mongodb/install.d/41-mongod-init | 46 + .../ubuntu-mongodb/install.d/42-mongos-init | 30 + .../pre-install.d/10-mongodb-apt-key | 14 + .../files/elements/ubuntu-mysql/README.md | 3 + .../elements/ubuntu-mysql/install.d/30-mysql | 23 + .../pre-install.d/10-percona-apt-key | 40 + .../pre-install.d/20-apparmor-mysql-local | 11 + .../ubuntu-percona/install.d/30-mysql | 17 + .../pre-install.d/10-percona-apt-key | 42 + .../pre-install.d/20-apparmor-mysql-local | 11 + .../ubuntu-postgresql/install.d/10-postgresql | 79 + .../pre-install.d/10-postgresql-repo | 12 + .../elements/ubuntu-pxc/install.d/30-mysql | 14 + .../pre-install.d/10-percona-apt-key | 42 + .../pre-install.d/20-apparmor-mysql-local | 11 + .../files/elements/ubuntu-redis/README.md | 1 + .../elements/ubuntu-redis/install.d/10-redis | 53 + .../files/elements/ubuntu-vertica/README.md | 1 + .../extra-data.d/93-copy-vertica-deb | 14 + .../ubuntu-vertica/install.d/97-vertica | 54 + .../scripts/files/keys/authorized_keys | 1 + integration/scripts/files/keys/id_rsa | 27 + integration/scripts/files/keys/id_rsa.pub | 1 + .../fedora-requirements-default.txt | 31 + .../requirements/fedora-requirements-juno.txt | 19 + .../requirements/fedora-requirements-kilo.txt | 24 + .../fedora-requirements-liberty.txt | 27 + .../fedora-requirements-mitaka.txt | 28 + .../fedora-requirements-newton.txt | 31 + .../ubuntu-requirements-default.txt | 30 + .../requirements/ubuntu-requirements-juno.txt | 19 + .../requirements/ubuntu-requirements-kilo.txt | 24 + .../ubuntu-requirements-liberty.txt | 26 + .../ubuntu-requirements-mitaka.txt | 27 + .../ubuntu-requirements-newton.txt | 30 + .../scripts/files/trove-guest.systemd.conf | 32 + .../scripts/files/trove-guest.upstart.conf | 40 + integration/scripts/functions | 324 ++ integration/scripts/functions_qemu | 174 + integration/scripts/image-projects-list | 3 + .../local.conf.d/ceilometer_cinder.conf.rc | 3 + .../local.conf.d/ceilometer_nova.conf.rc | 3 + .../local.conf.d/ceilometer_services.conf.rc | 3 + integration/scripts/local.conf.d/sample.rc | 42 + .../local.conf.d/trove_services.conf.rc | 24 + integration/scripts/local.conf.d/use_kvm.rc | 4 + .../scripts/local.conf.d/use_uuid_token.rc | 3 + .../scripts/local.conf.d/using_vagrant.rc | 9 + integration/scripts/local.conf.rc | 37 + integration/scripts/localrc.rc | 100 + integration/scripts/projects-list | 12 + integration/scripts/redstack | 1433 ++++++ integration/scripts/redstack.rc | 120 + integration/scripts/reviews.rc | 5 + integration/tests/examples/README | 13 + integration/tests/examples/example_gen.sh | 4 + integration/tests/examples/examples/client.py | 235 + .../examples/examples/example_generation.py | 1042 ++++ .../tests/examples/examples/local.conf | 10 + integration/tests/examples/gendoc.sh | 8 + integration/tests/examples/local.conf | 10 + integration/tests/examples/setup.py | 30 + integration/tests/examples/tox.ini | 16 + integration/tests/integration/core.test.conf | 48 + integration/tests/integration/int_tests.py | 263 + .../tests/integration/localhost.test.conf | 95 + integration/tests/integration/run_local.sh | 57 + integration/tests/integration/setup.py | 30 + integration/tests/integration/tests/README | 1 + .../tests/integration/tests/__init__.py | 27 + .../tests/integration/tests/api/__init__.py | 13 + .../tests/integration/tests/api/delete_all.py | 32 + .../tests/api/instances_pagination.py | 219 + .../integration/tests/api/instances_quotas.py | 47 + .../integration/tests/api/instances_states.py | 76 + .../tests/integration/tests/colorizer.py | 446 ++ .../tests/integration/tests/dns/__init__.py | 0 .../integration/tests/dns/check_domain.py | 174 + .../integration/tests/dns/concurrency.py | 111 + .../tests/integration/tests/dns/conversion.py | 105 + .../tests/integration/tests/dns/dns.py | 104 + .../tests/integration/tests/initialize.py | 176 + .../tests/integration/tests/smoke/__init__.py | 0 .../tests/integration/tests/smoke/instance.py | 103 + .../tests/integration/tests/util/__init__.py | 16 + .../tests/integration/tests/util/report.py | 76 + .../tests/integration/tests/util/rpc.py | 110 + .../tests/integration/tests/util/services.py | 280 + .../integration/tests/volumes/__init__.py | 25 + .../tests/integration/tests/volumes/driver.py | 546 ++ integration/tests/integration/tox.ini | 28 + integration/xsd/common.ent | 72 + integration/xsd/dbaas.wadl | 1177 +++++ integration/xsd/dbaas.xsd | 613 +++ integration/xsd/management.wadl | 625 +++ integration/xsd/management.xsd | 398 ++ tox.ini | 2 +- 351 files changed, 24504 insertions(+), 1 deletion(-) create mode 100644 integration/.gitignore create mode 100644 integration/.gitreview create mode 100644 integration/README.md create mode 100644 integration/apidocs/pom.xml create mode 100644 integration/apidocs/src/resources/cdb-devguide.xml create mode 100644 integration/apidocs/src/resources/cdb-getting-started.xml create mode 100644 integration/apidocs/src/resources/cdb-mgmt-devguide.xml create mode 100644 integration/apidocs/src/resources/cdb-releasenotes.xml create mode 100644 integration/apidocs/src/resources/images/Choose_CS_Image.png create mode 100644 integration/apidocs/src/resources/images/Choose_CS_Image_CCP.png create mode 100644 integration/apidocs/src/resources/images/Choose_Image_CCP.png create mode 100644 integration/apidocs/src/resources/images/Choose_Image_CCP.tiff create mode 100644 integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png create mode 100644 integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.svg create mode 100644 integration/apidocs/src/resources/images/Create_CS.png create mode 100644 integration/apidocs/src/resources/images/phpMyAdmin.png create mode 100644 integration/apidocs/src/resources/samples/db-auth-20.json create mode 100644 integration/apidocs/src/resources/samples/db-auth-20.xml create mode 100644 integration/apidocs/src/resources/samples/db-auth.json create mode 100644 integration/apidocs/src/resources/samples/db-auth.xml create mode 100644 integration/apidocs/src/resources/samples/db-check-root-user-request.json create mode 100644 integration/apidocs/src/resources/samples/db-check-root-user-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-check-root-user-response.json create mode 100644 integration/apidocs/src/resources/samples/db-check-root-user-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-database-instance-response.json create mode 100644 integration/apidocs/src/resources/samples/db-create-database-instance-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-databases-request.json create mode 100644 integration/apidocs/src/resources/samples/db-create-databases-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-databases-response.json create mode 100644 integration/apidocs/src/resources/samples/db-create-databases-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-instance-request.json create mode 100644 integration/apidocs/src/resources/samples/db-create-instance-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-instance-response.json create mode 100644 integration/apidocs/src/resources/samples/db-create-instance-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-users-request.json create mode 100644 integration/apidocs/src/resources/samples/db-create-users-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-create-users-response.json create mode 100644 integration/apidocs/src/resources/samples/db-create-users-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-credentials-20.json create mode 100644 integration/apidocs/src/resources/samples/db-credentials-20.xml create mode 100644 integration/apidocs/src/resources/samples/db-credentials.json create mode 100644 integration/apidocs/src/resources/samples/db-credentials.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-databases-request.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-databases-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-databases-response.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-databases-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-instance-request.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-instance-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-instance-response.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-instance-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-users-request.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-users-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-delete-users-response.json create mode 100644 integration/apidocs/src/resources/samples/db-delete-users-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-enable-root-user-request.json create mode 100644 integration/apidocs/src/resources/samples/db-enable-root-user-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-enable-root-user-response.json create mode 100644 integration/apidocs/src/resources/samples/db-enable-root-user-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-faults-badRequest.json create mode 100644 integration/apidocs/src/resources/samples/db-faults-badRequest.xml create mode 100644 integration/apidocs/src/resources/samples/db-faults-instanceFault.json create mode 100644 integration/apidocs/src/resources/samples/db-faults-instanceFault.xml create mode 100644 integration/apidocs/src/resources/samples/db-faults-itemNotFound.json create mode 100644 integration/apidocs/src/resources/samples/db-faults-itemNotFound.xml create mode 100644 integration/apidocs/src/resources/samples/db-flavors-by-id-request.json create mode 100644 integration/apidocs/src/resources/samples/db-flavors-by-id-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-flavors-by-id-response.json create mode 100644 integration/apidocs/src/resources/samples/db-flavors-by-id-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-flavors-request.json create mode 100644 integration/apidocs/src/resources/samples/db-flavors-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-flavors-response.json create mode 100644 integration/apidocs/src/resources/samples/db-flavors-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.json create mode 100644 integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-guest-update-request.json create mode 100644 integration/apidocs/src/resources/samples/db-guest-update-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-guest-update-response.json create mode 100644 integration/apidocs/src/resources/samples/db-guest-update-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-reboot-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-reboot-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-reboot-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-reboot-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-instance-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-instance-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-instance-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-instance-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-volume-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-volume-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-volume-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-resize-volume-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-restart-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-restart-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-restart-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-restart-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-status-detail-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-status-detail-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-status-detail-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-status-detail-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-update-guest-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-update-guest-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instance-update-guest-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instance-update-guest-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-pagination-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-pagination-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-pagination-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-pagination-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-index-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-paged-index-request.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-paged-index-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-instances-paged-index-response.json create mode 100644 integration/apidocs/src/resources/samples/db-instances-paged-index-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-list-databases-request.json create mode 100644 integration/apidocs/src/resources/samples/db-list-databases-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-list-databases-response.json create mode 100644 integration/apidocs/src/resources/samples/db-list-databases-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-list-users-request.json create mode 100644 integration/apidocs/src/resources/samples/db-list-users-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-list-users-response.json create mode 100644 integration/apidocs/src/resources/samples/db-list-users-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-host-update-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-host-update-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-host-update-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-host-update-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.json create mode 100644 integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-request-types.json create mode 100644 integration/apidocs/src/resources/samples/db-response-types.xml create mode 100644 integration/apidocs/src/resources/samples/db-version-request.json create mode 100644 integration/apidocs/src/resources/samples/db-version-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-version-response.json create mode 100644 integration/apidocs/src/resources/samples/db-version-response.xml create mode 100644 integration/apidocs/src/resources/samples/db-versions-request.json create mode 100644 integration/apidocs/src/resources/samples/db-versions-request.xml create mode 100644 integration/apidocs/src/resources/samples/db-versions-response.json create mode 100644 integration/apidocs/src/resources/samples/db-versions-response.xml create mode 100644 integration/scripts/conf.json.example create mode 100644 integration/scripts/conf/cassandra.conf create mode 100644 integration/scripts/conf/couchbase.conf create mode 100644 integration/scripts/conf/couchdb.conf create mode 100644 integration/scripts/conf/db2.conf create mode 100644 integration/scripts/conf/mariadb.conf create mode 100644 integration/scripts/conf/mongodb.conf create mode 100644 integration/scripts/conf/mysql.conf create mode 100644 integration/scripts/conf/percona.conf create mode 100644 integration/scripts/conf/postgresql.conf create mode 100644 integration/scripts/conf/pxc.conf create mode 100644 integration/scripts/conf/redis.conf create mode 100644 integration/scripts/conf/test_begin.conf create mode 100644 integration/scripts/conf/test_end.conf create mode 100644 integration/scripts/conf/vertica.conf create mode 100755 integration/scripts/create_vm create mode 100644 integration/scripts/files/elements/apt-conf-dir/README.rst create mode 100755 integration/scripts/files/elements/apt-conf-dir/extra-data.d/99-use-host-apt-confd create mode 100755 integration/scripts/files/elements/fedora-guest/extra-data.d/15-reddwarf-dep create mode 100755 integration/scripts/files/elements/fedora-guest/extra-data.d/20-guest-systemd create mode 100755 integration/scripts/files/elements/fedora-guest/extra-data.d/62-ssh-key create mode 100755 integration/scripts/files/elements/fedora-guest/install.d/15-reddwarf-dep create mode 100755 integration/scripts/files/elements/fedora-guest/install.d/20-etc create mode 100755 integration/scripts/files/elements/fedora-guest/install.d/50-user create mode 100755 integration/scripts/files/elements/fedora-guest/install.d/62-ssh-key create mode 100755 integration/scripts/files/elements/fedora-guest/post-install.d/05-ipforwarding create mode 100755 integration/scripts/files/elements/fedora-guest/post-install.d/62-trove-guest-sudoers create mode 100755 integration/scripts/files/elements/fedora-guest/post-install.d/90-yum-update create mode 100644 integration/scripts/files/elements/fedora-mariadb/README.md create mode 100755 integration/scripts/files/elements/fedora-mariadb/install.d/10-mariadb create mode 100755 integration/scripts/files/elements/fedora-mariadb/pre-install.d/10-percona-copr create mode 100644 integration/scripts/files/elements/fedora-mongodb/README.md create mode 100755 integration/scripts/files/elements/fedora-mongodb/install.d/10-mongodb create mode 100755 integration/scripts/files/elements/fedora-mongodb/install.d/25-trove-mongo-dep create mode 100644 integration/scripts/files/elements/fedora-mysql/README.md create mode 100755 integration/scripts/files/elements/fedora-mysql/install.d/10-mysql create mode 100755 integration/scripts/files/elements/fedora-mysql/install.d/40-xtrabackup create mode 100644 integration/scripts/files/elements/fedora-mysql/post-install.d/30-register-mysql-service create mode 100755 integration/scripts/files/elements/fedora-percona/install.d/05-percona-server create mode 100755 integration/scripts/files/elements/fedora-percona/install.d/10-mysql create mode 100755 integration/scripts/files/elements/fedora-postgresql/install.d/10-postgresql create mode 100644 integration/scripts/files/elements/fedora-redis/README.md create mode 100755 integration/scripts/files/elements/fedora-redis/install.d/10-redis create mode 100755 integration/scripts/files/elements/ubuntu-cassandra/install.d/10-cassandra create mode 100755 integration/scripts/files/elements/ubuntu-couchbase/install.d/10-couchbase create mode 100755 integration/scripts/files/elements/ubuntu-couchdb/install.d/10-couchdb create mode 100644 integration/scripts/files/elements/ubuntu-db2/README.md create mode 100755 integration/scripts/files/elements/ubuntu-db2/extra-data.d/20-copy-db2-pkgs create mode 100755 integration/scripts/files/elements/ubuntu-db2/install.d/10-db2 create mode 100755 integration/scripts/files/elements/ubuntu-guest/extra-data.d/15-reddwarf-dep create mode 100755 integration/scripts/files/elements/ubuntu-guest/extra-data.d/20-guest-upstart create mode 100755 integration/scripts/files/elements/ubuntu-guest/extra-data.d/62-ssh-key create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/05-base-apps create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/15-reddwarf-dep create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/20-etc create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/50-user create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/62-ssh-key create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/98-ssh create mode 100755 integration/scripts/files/elements/ubuntu-guest/install.d/99-clean-apt create mode 100755 integration/scripts/files/elements/ubuntu-guest/post-install.d/05-ipforwarding create mode 100755 integration/scripts/files/elements/ubuntu-guest/post-install.d/10-ntp create mode 100755 integration/scripts/files/elements/ubuntu-guest/post-install.d/62-trove-guest-sudoers create mode 100755 integration/scripts/files/elements/ubuntu-guest/post-install.d/90-apt-get-update create mode 100755 integration/scripts/files/elements/ubuntu-guest/pre-install.d/01-trim-pkgs create mode 100755 integration/scripts/files/elements/ubuntu-guest/pre-install.d/04-baseline-tools create mode 100644 integration/scripts/files/elements/ubuntu-mariadb/README.md create mode 100755 integration/scripts/files/elements/ubuntu-mariadb/install.d/30-mariadb create mode 100755 integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/10-percona-apt-key create mode 100755 integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/20-apparmor-mysql-local create mode 100644 integration/scripts/files/elements/ubuntu-mongodb/README.md create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/10-mongodb-thp create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/20-mongodb create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/25-trove-mongo-dep create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/30-mongodb-conf create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/41-mongod-init create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/install.d/42-mongos-init create mode 100755 integration/scripts/files/elements/ubuntu-mongodb/pre-install.d/10-mongodb-apt-key create mode 100644 integration/scripts/files/elements/ubuntu-mysql/README.md create mode 100755 integration/scripts/files/elements/ubuntu-mysql/install.d/30-mysql create mode 100755 integration/scripts/files/elements/ubuntu-mysql/pre-install.d/10-percona-apt-key create mode 100755 integration/scripts/files/elements/ubuntu-mysql/pre-install.d/20-apparmor-mysql-local create mode 100755 integration/scripts/files/elements/ubuntu-percona/install.d/30-mysql create mode 100755 integration/scripts/files/elements/ubuntu-percona/pre-install.d/10-percona-apt-key create mode 100755 integration/scripts/files/elements/ubuntu-percona/pre-install.d/20-apparmor-mysql-local create mode 100755 integration/scripts/files/elements/ubuntu-postgresql/install.d/10-postgresql create mode 100755 integration/scripts/files/elements/ubuntu-postgresql/pre-install.d/10-postgresql-repo create mode 100755 integration/scripts/files/elements/ubuntu-pxc/install.d/30-mysql create mode 100755 integration/scripts/files/elements/ubuntu-pxc/pre-install.d/10-percona-apt-key create mode 100755 integration/scripts/files/elements/ubuntu-pxc/pre-install.d/20-apparmor-mysql-local create mode 100644 integration/scripts/files/elements/ubuntu-redis/README.md create mode 100755 integration/scripts/files/elements/ubuntu-redis/install.d/10-redis create mode 100644 integration/scripts/files/elements/ubuntu-vertica/README.md create mode 100755 integration/scripts/files/elements/ubuntu-vertica/extra-data.d/93-copy-vertica-deb create mode 100755 integration/scripts/files/elements/ubuntu-vertica/install.d/97-vertica create mode 100644 integration/scripts/files/keys/authorized_keys create mode 100644 integration/scripts/files/keys/id_rsa create mode 100644 integration/scripts/files/keys/id_rsa.pub create mode 100644 integration/scripts/files/requirements/fedora-requirements-default.txt create mode 100644 integration/scripts/files/requirements/fedora-requirements-juno.txt create mode 100644 integration/scripts/files/requirements/fedora-requirements-kilo.txt create mode 100644 integration/scripts/files/requirements/fedora-requirements-liberty.txt create mode 100644 integration/scripts/files/requirements/fedora-requirements-mitaka.txt create mode 100644 integration/scripts/files/requirements/fedora-requirements-newton.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-default.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-juno.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-kilo.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-liberty.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-mitaka.txt create mode 100644 integration/scripts/files/requirements/ubuntu-requirements-newton.txt create mode 100644 integration/scripts/files/trove-guest.systemd.conf create mode 100644 integration/scripts/files/trove-guest.upstart.conf create mode 100644 integration/scripts/functions create mode 100644 integration/scripts/functions_qemu create mode 100644 integration/scripts/image-projects-list create mode 100644 integration/scripts/local.conf.d/ceilometer_cinder.conf.rc create mode 100644 integration/scripts/local.conf.d/ceilometer_nova.conf.rc create mode 100644 integration/scripts/local.conf.d/ceilometer_services.conf.rc create mode 100644 integration/scripts/local.conf.d/sample.rc create mode 100644 integration/scripts/local.conf.d/trove_services.conf.rc create mode 100644 integration/scripts/local.conf.d/use_kvm.rc create mode 100644 integration/scripts/local.conf.d/use_uuid_token.rc create mode 100644 integration/scripts/local.conf.d/using_vagrant.rc create mode 100644 integration/scripts/local.conf.rc create mode 100644 integration/scripts/localrc.rc create mode 100644 integration/scripts/projects-list create mode 100755 integration/scripts/redstack create mode 100644 integration/scripts/redstack.rc create mode 100644 integration/scripts/reviews.rc create mode 100644 integration/tests/examples/README create mode 100755 integration/tests/examples/example_gen.sh create mode 100644 integration/tests/examples/examples/client.py create mode 100644 integration/tests/examples/examples/example_generation.py create mode 100644 integration/tests/examples/examples/local.conf create mode 100755 integration/tests/examples/gendoc.sh create mode 100644 integration/tests/examples/local.conf create mode 100644 integration/tests/examples/setup.py create mode 100644 integration/tests/examples/tox.ini create mode 100644 integration/tests/integration/core.test.conf create mode 100644 integration/tests/integration/int_tests.py create mode 100644 integration/tests/integration/localhost.test.conf create mode 100755 integration/tests/integration/run_local.sh create mode 100644 integration/tests/integration/setup.py create mode 100644 integration/tests/integration/tests/README create mode 100644 integration/tests/integration/tests/__init__.py create mode 100644 integration/tests/integration/tests/api/__init__.py create mode 100644 integration/tests/integration/tests/api/delete_all.py create mode 100644 integration/tests/integration/tests/api/instances_pagination.py create mode 100644 integration/tests/integration/tests/api/instances_quotas.py create mode 100644 integration/tests/integration/tests/api/instances_states.py create mode 100644 integration/tests/integration/tests/colorizer.py create mode 100644 integration/tests/integration/tests/dns/__init__.py create mode 100644 integration/tests/integration/tests/dns/check_domain.py create mode 100644 integration/tests/integration/tests/dns/concurrency.py create mode 100644 integration/tests/integration/tests/dns/conversion.py create mode 100644 integration/tests/integration/tests/dns/dns.py create mode 100644 integration/tests/integration/tests/initialize.py create mode 100644 integration/tests/integration/tests/smoke/__init__.py create mode 100644 integration/tests/integration/tests/smoke/instance.py create mode 100644 integration/tests/integration/tests/util/__init__.py create mode 100644 integration/tests/integration/tests/util/report.py create mode 100644 integration/tests/integration/tests/util/rpc.py create mode 100644 integration/tests/integration/tests/util/services.py create mode 100644 integration/tests/integration/tests/volumes/__init__.py create mode 100644 integration/tests/integration/tests/volumes/driver.py create mode 100644 integration/tests/integration/tox.ini create mode 100644 integration/xsd/common.ent create mode 100644 integration/xsd/dbaas.wadl create mode 100644 integration/xsd/dbaas.xsd create mode 100644 integration/xsd/management.wadl create mode 100644 integration/xsd/management.xsd diff --git a/integration/.gitignore b/integration/.gitignore new file mode 100644 index 0000000000..75bff19283 --- /dev/null +++ b/integration/.gitignore @@ -0,0 +1,25 @@ +*.sublime-project +*.sublime-workspace +scripts/.screenrc +scripts/test-def.conf +.tox +*.pyc +*.coverage +tests/integration/Reddwarf_Integration_Tests.egg-info/* +apidocs/target +scripts/.cache/ +scripts/.cinderclient/ +scripts/.mysql_history +scripts/.troveclient/ +conf.json +Vagrantfile +.my.cnf +.vagrant +.bash_history +.rnd +options.rc +.novaclient +*.log +.local.conf +scripts/local.conf.d/local.conf.d +*.config/ diff --git a/integration/.gitreview b/integration/.gitreview new file mode 100644 index 0000000000..725c7456d3 --- /dev/null +++ b/integration/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=openstack/trove-integration.git diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 0000000000..6b1fe76588 --- /dev/null +++ b/integration/README.md @@ -0,0 +1,210 @@ +## Integration dev scripts, tests and docs for Trove. + +*** + +### Steps to setup this environment: + +Install a fresh Ubuntu 14.04 (Trusty Tahr) image ( _We suggest creating a development virtual machine using the image_ ) + +#### Login to the machine as root + +#### Make sure we have git installed: + + # apt-get update + # apt-get install git-core -y + +#### Add a user named ubuntu if you do not already have one: + + # adduser ubuntu + # visudo + + add this line to the file below the root user + + ubuntu ALL=(ALL:ALL) ALL + + **OR use this if you dont want to type your password to sudo a command** + + ubuntu ALL=(ALL) NOPASSWD: ALL + + if /dev/pts/0 does not have read/write for your user + + # chmod 666 /dev/pts/0 + + *Note that this number can change and if you can not connect to the screen session then the /dev/pts/# needs modding like above.* + +#### Login with ubuntu: + + # su ubuntu + $ cd ~ + +#### Clone this repo: + + $ git clone https://github.com/openstack/trove-integration.git + +#### Go into the scripts directory: + + $ cd trove-integration/scripts/ + +#### Running redstack is the core script: +*Run this to get the command list with a short description of each* + + $ ./redstack + +#### Install all the dependencies and then install trove via redstack. +*This brings up trove (rd-api rd-tmgr) and initializes the trove database.* + + $ ./redstack install + +*** + +#### Connecting to the screen session + + $ screen -x stack + +*If that command fails with the error* + + Cannot open your terminal '/dev/pts/1' + +*If that command fails with the error chmod the corresponding /dev/pts/#* + + $ chmod 660 /dev/pts/1 + +#### Navigate the log screens +To produce the list of screens that you can scroll through and select + + ctrl+a then " + +Num Name + +..... (full list ommitted) + +20 c-vol +21 h-eng +22 h-api +23 h-api-cfn +24 h-api-cw +25 tr-api +26 tr-tmgr +27 tr-cond + +Alternatively, to go directly to a specific screen window + + ctrl+a then ' + +then enter a number (like 25) or name (like tr-api) + +#### Detach from the screen session +Allows the services to continue running in the background + + ctrl+a then d + +*** + +#### Kick start the build/test-init/build-image commands +*Add mysql as a parameter to set build and add the mysql guest image. This will also populate /etc/trove/test.conf with appropriate values for running the integration tests.* + + $ ./redstack kick-start mysql + +*Optional commands if you did not run kick-start* + +#### Initialize the test configuration and set up test users (overwrites /etc/trove/test.conf) + + $ ./redstack test-init + +#### Build the image and add it to glance + + $ ./redstack build-image mysql + +*** + +### Reset your environment + +#### Stop all the services running in the screens and refresh the environment: + + $ killall -9 screen + $ screen -wipe + $ RECLONE=yes ./redstack install + $ ./redstack kick-start mysql + + or + + $ RECLONE=yes ./redstack install + $ ./redstack test-init + $ ./redstack build-image mysql + +*** + +### Recover after reboot +If the VM was restarted, then the process for bringing up Openstack and Trove is quite simple + + $./redstack start-deps + $./redstack start + +Use screen to ensure all modules have started without error + + $screen -r stack + +*** + +### Running Integration Tests +Check the values in /etc/trove/test.conf in case it has been re-initialized prior to running the tests. For example, from the previous mysql steps: + + "dbaas_datastore": "%datastore_type%", + "dbaas_datastore_version": "%datastore_version%", + +should be: + + "dbaas_datastore": "mysql", + "dbaas_datastore_version": "5.5", + +Once Trove is running on DevStack, you can use the dev scripts to run the integration tests locally. + + $./redstack int-tests + +This will runs all of the blackbox tests by default. Use the --group option to run a different group: + + $./redstack int-tests --group=simple_blackbox + +You can also specify the TESTS_USE_INSTANCE_ID environment variable to have the integration tests use an existing instance for the tests rather than creating a new one. + + $./TESTS_DO_NOT_DELETE_INSTANCE=True TESTS_USE_INSTANCE_ID=INSTANCE_UUID ./redstack int-tests --group=simple_blackbox + +*** + +### VMware Fusion 5 speed improvement +Running Ubuntu with KVM or Qemu can be extremely slow without certain optimizations. The following are some VMware settings that can improve performance and may also apply to other virtualization platforms. + +1. Shutdown the Ubuntu VM. + +2. Go to VM Settings -> Processors & Memory -> Advanced Options. + Check the "Enable hypervisor applications in this virtual machine" + +3. Go to VM Settings -> Advanced. + Set the "Troubleshooting" option to "None" + +4. After setting these create a snapshot so that in cases where things break down you can revert to a clean snapshot. + +5. Boot up the VM and run the `./redstack install` + +6. To verify that KVM is setup properly after the devstack installation you can run these commands. +``` +ubuntu@ubuntu:~$ kvm-ok +INFO: /dev/kvm exists +KVM acceleration can be used +``` + +### VMware Workstation performance improvements + +In recent versions of VMWare, you can get much better performance if you enable the right virtualization options. For example, in VMWare Workstation (found in version 10.0.2), click on VM->Settings->Processor. + +You should see a box of "Virtualization Engine" options that can be changed only when the VM is shutdown. + +Make sure you check "Virtualize Intel VT-x/EPT or AMD-V/RVI" and "Virtualize CPU performance counters". Set the preferred mode to "Automatic". + +Then boot the VM and ensure that the proper virtualization is enabled. + +``` +ubuntu@ubuntu:~$ kvm-ok +INFO: /dev/kvm exists +KVM acceleration can be used +``` diff --git a/integration/apidocs/pom.xml b/integration/apidocs/pom.xml new file mode 100644 index 0000000000..9351619456 --- /dev/null +++ b/integration/apidocs/pom.xml @@ -0,0 +1,193 @@ + + + 4.0.0 + + com.rackspace.cloud.dbaas + dbaas-docs + 1.0.0 + + Database Public API Spec + jar + + + + Rackspace Research Repositories + + true + + + + rackspace-research + Rackspace Research Repository + http://maven.research.rackspacecloud.com/content/groups/public/ + + + + + rackspace-research + Rackspace Research Repository + http://maven.research.rackspacecloud.com/content/groups/public/ + + + + + + UTF-8 + 1.5.0 + + + + src + + + ../xsd + + + + + com.rackspace.cloud.api + clouddocs-maven-plugin + + + ${doctools.version} + + + g1 + + generate-pdf + generate-webhelp + + generate-sources + + true + src/resources + false + + cdb-mgmt-devguide.xml + ../../cdb-mgmt-devguide-internal.pdf + intranet + mike.asthalter@rackspace.com + + + + + + + + g2 + + generate-pdf + generate-webhelp + + generate-sources + + true + src/resources + false + 2 + + + ../../../cdb-devguide-latest.pdf + 1 + UA-23102455-4 + cdb-devguide.xml + http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content + + + + + + + + + g3 + + generate-pdf + generate-webhelp + + generate-sources + + true + src/resources + false + + cdb-getting-started.xml + ../../../cdb-getting-started.pdf + 1 + UA-23102455-4 + http://docs.rackspace.com/cdb/api/v1.0/cdb-getting-started/content + + + + + + + + + + g4 + + generate-webhelp + generate-pdf + + generate-sources + + true + src/resources + false + ../../../cdb-releasenotes-latest.pdf + cdb-releasenotes.xml + 1 + mike.asthalter@rackspace.com + 1 + 0 + external + http://docs.rackspace.com/cdb/api/v1.0/cdb-releasenotes/content + + + + + + + + + + + g5 + + generate-pdf + generate-webhelp + + generate-sources + + true + src/resources + false + ../../../cdb-releasenotes-latest.pdf + cdb-releasenotes.xml + intranet + mike.asthalter@rackspace.com + 1 + 0 + internal + http://docs-internal.rackspace.com/cdb/api/v1.0/cdb-releasenotes/content + + + + + + + + + + + + 1 + mike.asthalter@rackspace.com + rackspace + + + + + diff --git a/integration/apidocs/src/resources/cdb-devguide.xml b/integration/apidocs/src/resources/cdb-devguide.xml new file mode 100644 index 0000000000..5c2ae69b31 --- /dev/null +++ b/integration/apidocs/src/resources/cdb-devguide.xml @@ -0,0 +1,1675 @@ + + + + + + + + GET'> + PUT'> + POST'> + DELETE'> + + + + + + + + + + + + '> + + + + + + '> +]> + + + + Rackspace Cloud Databases Developer Guide + Rackspace Cloud Databases Developer + Guide + + + + + + + + Rackspace Cloud + + + + 2010 + 2011 + 2012 + Rackspace US, Inc. + + API v1.0 + Rackspace Cloud Databases + 2012-09-04 + + + Copyright details are filled in by the template. + + + + This document is intended for software developers + interested in developing applications using the + Rackspace Cloud Databases Application Programming + Interface (API). + + + + + 2012-09-04 + + + + Added information for pricing and + service level (refer to ). + + + Updated maximum volume size for a + database instance (refer to ). + + + + + + 2012-08-21 + + + + Changed FAILED status for database + instance to ERROR instead (see ). + + + List reserved names that cannot be + used for creating databases (see ) and users (see ). + + + + + + 2012-08-01 + + + + Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases. + + + + + + + + this is a placeholder for the front cover + + + this is a placeholder for the back cover + + + cdb + 2 + + + + Overview + Rackspace Cloud Databases is an OpenStack-based MySQL + relational database service that allows Rackspace + customers to easily provision database instances of + varying virtual resource sizes without the need to + maintain and/or update MySQL. Interactions with Cloud + Databases occur programmatically via the Cloud Databases + API as described in this developer guide. + + Rackspace recommends that Cloud Databases users back + up their data using mysqldump until backups are supported + in Cloud Databases. + + The following figure shows an + overview of Cloud Databases Infrastructure: + + + + + + + + + + + Writer: need to get architecture diagram for DBaaS. + Emailed Daniel 5/1/12. + We welcome feedback, comments, and bug reports at http://feedback.rackspacecloud.com. + Writer: check whether following statement should be + added back in for public (not private) beta: Issues and + bug reports can be directed to your support team via + ticket, chat, email, or phone. + +
+ Intended Audience + This Guide is intended to assist software + developers who want to develop applications using the + Cloud Databases API. It assumes the reader has a + general understanding of databases and is familiar + with: + + + ReSTful web services + + + HTTP/1.1 conventions + + + JSON and/or XML data serialization + formats + + + ATOM Syndication Format + + +
+ +
+ Document Change History + This version of the Developer Guide replaces and + obsoletes all previous versions. The most recent + changes are described in the table below: + +
+
+ Additional Resources + Descriptive information about Cloud Databases is + also published in its Web Application Description + Language (WADL) and XML Schema Definition (XSD). You + are welcome to read this information here: + + + The WADL is . + + + The XSD is . + + + You can download the most current versions of other + API-related documents from http://docs.rackspace.com/. + For more details about Rackspace Cloud Databases, + refer to http://www.rackspace.com/cloud/cloud_hosting_products/databases/. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. + Using this API document, your Rackspace Cloud + account, and at least one Cloud Server, you can get + started whenever you'd like. See the + Getting Started with Rackspace Cloud + Databases and Servers at http://docs.rackspace.com/ for information + about getting started using the API. + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + You can also follow Rackspace updates and + announcements via twitter at: http://www.twitter.com/rackspace. + This API uses standard HTTP 1.1 response codes as + documented at: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. +
+ +
+ Pricing and Service Level + Cloud Databases is part of the Rackspace Cloud and + your use through the API will be billed as per the + pricing schedule at http://www.rackspace.com/cloud/public/databases/pricing. + The Service Level Agreement (SLA) for Cloud + Databases is available at http://www.rackspace.com/cloud/legal/sla/#cloud_databases. +
+
+ + + Concepts + + To use the Cloud Databases API effectively, you should + understand several key concepts: + Reviewer: Daniel Morris is asking Daniel Salinas to + do an initial write-up of this chapter. +
+ Database Instance + A database instance is an isolated MySQL instance in + a single tenant environment on a shared physical host + machine. + Writer: once we support + MSSQL, we need to describe here what is used for MSSQL + in place of database instance. +
+
+ Database + A MySQL database within a database instance. + Writer: once we support + MSSQL, we need to modify the wording here, such as: + The actual database, whether it is in MySQL or + MSSQL. +
+
+ Flavor + A flavor is an available hardware configuration for + a database instance. Each flavor has a unique + combination of memory capacity and priority for CPU + time. +
+
+ Volume + A volume is user-specified storage that contains the + MySQL data directory. Volumes are automatically + provisioned on shared Internet Small Computer System + Interface (iSCSI) storage area networks (SAN) that + provide for increased performance, scalability, + availability and manageability. Applications with high + I/O demands are performance optimized and data is + protected through both local and network RAID-10. + Additionally, network RAID provides synchronous + replication of volumes with automatic failover and + load balancing across available storage + clusters. +
+
+ + General API Information + The Cloud Databases API is implemented using a ReSTful + web service interface. Like other products in the + Rackspace Cloud suite, the Database Service shares a + common token-based authentication system that allows + seamless access between products and services. + + All requests to authenticate against and operate the service are performed using + SSL over HTTP (HTTPS) on TCP port 443. + +
+ Authentication + Every ReST request against the Database Service + requires the inclusion of a specific authorization + token, supplied by the X-Auth-Token HTTP + header. Customers obtain this token by first using the + Rackspace Cloud Authentication Service and supplying a + valid username and API access key. +
+ Geographic Endpoints + The Rackspace Cloud Authentication Service + serves as the entry point to all Rackspace Cloud + APIs and is itself a ReSTful web service. + To access the Authentication Service, you must + know whether your account is US-based or UK-based: + + + US-based accounts authenticate through + &ENDPOINT-US-20;. + + + UK-based accounts authenticate through + &ENDPOINT-UK-20;. + + + Your account may be based in either the US or + the UK; this is not determined by your physical + location but by the location of the Rackspace + retail site which was used to create your account: + + + If your account was created via http://www.rackspacecloud.com, + it is a US-based account. + + + If your account was created via http://www.rackspace.co.uk, it + is a UK-based account. + + + If you are unsure how your account was created, + use the Rackspace contact information at either + site to ask for help. +
+
+ Retrieving the Authentication Token + + + + &POST; + v2.0/tokens + Authenticate to receive a + token and a service catalog. + + + + Normal Response Code(s): + 200, + 203 + + Error Response Code(s): unauthorized + (401), userDisabled + (403), badRequest + (400), authFault + (500), + serviceUnavailable (503) + The authenticate operation provides clients + with an authentication token and a list of + regional cloud endpoints. The sample requests and + responses in this section illustrate a general + case. In your authentication request, use your own + credentials rather than the sample values shown + here for username and + apiKey. When you authenticate + successfully, the response to your authentication + request will include a catalog of the services to + which you have subscribed rather than the sample + values shown here. + + Auth Request: XML + + + + + + + + + + + + Auth Request: JSON + + + + + + + + + + + + + The username supplied here is your + common Rackspace Cloud username. + + + The key is your API access key. The key + can be obtained from the Rackspace Cloud + Control Panel in the <Your + Account>/API + Access section (login + here: Control Panel Login). + + + + Auth Response: XML + + + + + + + + + + + + + + + + + + + + Auth Response: JSON + + + + + + + + + + + + + + + + + + + The information shown in the Auth Response + examples is for US-based accounts. If you + authenticate against the UK-endpoint for auth, + you will see the service catalog information + for UK-based accounts. + + + + + In XML responses only, + a list of namespaces identifies API extensions that add functionality to the core API. + + + + + This token can be presented to a service as evidence of authentication. + Tokens are valid for a finite duration; a token's default lifespan is twenty-four hours. + + The token's expires attribute denotes the time + after which the token will automatically become + invalid. A token may be manually revoked before + the time identified by the expires + attribute; expires predicts a token's + maximum possible lifespan but does not guarantee + that it will reach that lifespan. Clients are + encouraged to cache a token until it expires. + + + + Users can be assigned multiple roles, + with each role providing specific + privileges. In this example, + jsmith is the + administrative user for the account, + holding the fully-privileged + identity:admin role. + Other users might hold other roles with + different privileges. Roles need not be + associated with actual job functions such + as Administrator, Operator, Developer, + Tester, or Trainer. + + + + The service catalog lists the services + this user can access. In this example, the + user can access one database service, one + loadbalancing service, two compute + services (Cloud Servers OpenStack and + Cloud Servers), two object storage + services (Cloud Files Content Distribution + Network (CDN), and Cloud Files), and one + DNS service. The catalog listing for each + service provides at least one endpoint URL + for that service. Other information, such + as regions, versions, and tenants, is + provided if it's relevant to this user's + access to this service. + + + + + The service type attribute identifies services that perform similar functions, whatever those services might be named. + In this example, the services named cloudServers and cloudServersOpenstack are both identified as type="compute", + identifying them as compute services even though the word "compute" does not appear in their names. + + + Use service type as the primary value for + locating a service. If multiple endpoints of the + same service type exist in the same region, use + service name as the tiebreaker. + + + + + + The service name attribute identifies each unique service in the catalog. + Once a service is created, its name does not change. However, new services of the same service type may be added to the catalog with new names. + + + If you are programmatically parsing an authentication + response, use service type rather than service name as + the basis for determining whether a user has access to + a particular kind of service. Service type is stable + across all releases; new service types may be developed, + but existing service types are not renamed. + In this example, + type="compute" identifies all the + available compute services, one of which is named + cloudServers and one of which is named + cloudServersOpenStack. New compute service names may be added + in future releases; whatever the compute services are + named, you can always + recognize them by parsing for + type="compute" in the authentication + response's service catalog. + + + + + A service may expose endpoints in different regions. + Regional endpoints allow clients to provision + resources in a manner that provides high + availability. + Some services are not region-specific. These services supply a single + non-regional endpoint and do not provide access to internal URLs. + + + + Some services recognize specification of a tenant. If a + service does recognize tenants, the format of the + tenant specification is defined only by the + service; for details about whether and how to + specify a tenant, check the documentation for the + service you are using. + + + + + An endpoint can be assigned public and internal URLs. A + public URL is accessible from anywhere. Access to a public + URL usually incurs traffic charges. Internal URLs are only + accessible to services within the same region. Access to + an internal URL is free of charge. + + + + + Authentication tokens are typically valid for 24 + hours. Applications should be designed to + re-authenticate after receiving a 401 + (Unauthorized) response from a service endpoint. + + If you are programmatically parsing an + authentication response, please be aware that + service names are stable for the life of the + particular service and can be used as keys. + You should also be aware that a user's service + catalog can include multiple uniquely-named + services which perform similar functions. For + example, cloudServersOpenStack is the + OpenStack version of compute whereas + cloudServers is the legacy version of compute; + the same user can have access to both + services. In Auth 2.0, the service type + attribute can be used as a key by which to + recognize similar services; see the tip + below. + + + Beginning with Auth 2.0, the service catalog + includes a service type attribute to identify + services that perform similar functions but + have different names; for example, + type="compute" identifies + compute services such as cloudServers and + cloudServersOpenStack. Some developers have + found the service type attribute to be useful + in parsing the service catalog. For additional + information on Auth 2.0 (also known as the + Cloud Identity Service), refer to the + Cloud Identity Client Developer + Guide at http://docs.rackspace.com/. + + Databases service endpoints are published in the + service catalog in the Auth response with the + account number, which is a required element of the + service endpoints. The examples shown here are for + authentication for US customers. Customers with + UK-based accounts will see different values in the + service catalog. Refer to the next section for + more information about service endpoints. +
+
+
+ Service Access/Endpoints + The Database Service is a regionalized service. The + user of the service is therefore responsible for + appropriate replication, caching, and overall + maintenance of Cloud Databases data across regional + boundaries to other Cloud Databases servers. + You can find the available service access/endpoints + for Cloud Databases summarized in the table + below. + + + + + + + + + + + + + + + + + + + + + + + + +
Regionalized Service Endpoints
RegionEndpoint
Chicago (ORD)https://ord.databases.api.rackspacecloud.com/v1.0/1234/
Dallas/Ft. Worth (DFW)https://dfw.databases.api.rackspacecloud.com/v1.0/1234/
London (LON) + https://lon.databases.api.rackspacecloud.com/v1.0/1234/
+
+ + Replace the sample account ID number, + 1234, with your actual + account number returned as part of the authentication + service response. + You will find the actual account number after the + final '/' in the publicURL field returned + by the authentication response. For example, in you can see + from the publicURL field for + cloudServers + ("https://servers.api.rackspacecloud.com/v1.0/1100111") that the account + number is 1100111. +
+
+ Cloud Databases Service Versions + The Cloud Databases version defines the contract + and build information for the API. +
+ Contract Version + The contract version denotes the data model and + behavior that the API supports. The requested + contract version is included in all request URLs. + Different contract versions of the API may be + available at any given time and are not guaranteed + to be compatible with one another. + + + Example Request URL (contract version + in <emphasis role="strong" + >bold</emphasis>) + https://ord.databases.api.rackspacecloud.com/v1.0/1234 + + + This document pertains to contract + version 1.0. + + +
+
+ API Version + The API List Versions call is available to show + the current API version as well as information + about all versions of the API. Refer to for details. +
+
+
+ Request/Response Types + The Cloud Databases API supports both the JSON and + XML data serialization formats. The request format is + specified using the Content-Type header + and is required for calls that + have a request body. The response format can be + specified in requests either by using the + Accept header or by adding an + .xml or .json extension + to the request URI. Note that it is possible for a + response to be serialized using a format different + from the request. If no response format is specified, + JSON is the default. If conflicting formats are + specified using both an Accept header and + a query extension, the query extension takes + precedence. + Some operations support an Atom representation that + can be used to efficiently determine when the state of + services has changed. + Reviewer: the previous sentence will be hidden + for the Private Beta, since it does not appear that + Atom will be supported yet. Correct? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Response Formats
FormatAccept HeaderQuery ExtensionDefault
JSONapplication/json.jsonYes
XMLapplication/xml.xmlNo
ATOMapplication/atom+xml.atomNo
+ Reviewer: the ATOM row in the table above will be + hidden for the Private Beta, since it does not appear + that Atom will be supported yet. Correct? + In the request example below, notice that + Content-Type is set to + application/json, but + application/xml is + requested via the Accept + header: + + Request with Headers: JSON + + Missing code sample! + + Therefore an XML response format is returned: + + Response with Headers: XML + + Missing code sample! + +
+
+ Synchronous and Asynchronous Responses + Reviewer: please give me the updated info for this + section. Need to replace info about callback URL, + etc. + All successful &GET; requests are + synchronous calls, since they + are always retrieving (reading) existing information. + With these requests, the caller waits until the call + returns with the specified code and response body. For + an example, see XXXX. + &PUT;, &POST;, and &DELETE; calls are asynchronous, however, + since they may take some time to process. Therefore they return 202 ACCEPTED + responses containing information with a callback URL, which allows the progress, + status, and/or response information of the call to be retrieved at a later point in + time. The asynchronous response body will look similar to the following examples, + depending on the format requested: + + 202 ACCEPTED Response: XML + + Reviewer: need code example + + + 202 ACCEPTED Response: JSON + + Reviewer: need code example + + The following table shows the attributes for asynchronous responses: + + + + + + + + + + + + + + + + + + + +
Attributes for Asynchronous Responses
AttributeDescription
jobIdAn identifier for the specific request.
callbackUrlResource locator for querying the status of the request.
+ + The status for asynchronous calls is retained for up to 24 hours. + + + If a request body does not pass initial validation or an error condition + arises, you may receive an immediate error response from the request. + + When a request is made to the callback URL provided + and the job is still running, another + 202 ACCEPTED response + is returned with the same information as the previous + one. If the request is complete, the response will be + as if the original call returned as normal, without + waiting. For example, if a Create Database request was + issued and a 202 asynchronous response was returned, + the response from querying the callback URL for a + completed successful database creation would be a + 200 OK and contain the + information for the created database. See XXXX for a + specific example. + If an error occurs during the processing of the + create request, querying the callback URL will return + the details of the error, as if the original call + returned the error response. For example, if a + validation error occurs during the Create Database + request above, the response from querying the callback + URL would be a 400 BAD + REQUEST and contain details regarding the specific + validation error. + + If the response from querying a callback URL is a + 404 NOT FOUND, the details of the error in the + response body will contain information the caller may use to determine whether + the specified job itself was not found, or if the response from the original + request was a 404 NOT FOUND. + + The description of each &PUT;, &POST;, and &DELETE; + request identifies the response codes that can + indicate success or error for that request. For + example, see XXXX immediately below the table for a + list of the successful and error response codes for + the POST /xxxx call. +
+
+ Content Compression + Reviewer: I am hiding this entire section for the + Private Beta, since I'm not sure that it applies. Is + that correct? + Request and response body data may be encoded with gzip compression to accelerate + interactive performance of API calls and responses. This is controlled using the + Accept-Encoding header on the request from the client and indicated + by the Content-Encoding header in the server response. Unless the + header is explicitly set, encoding defaults to disabled. + + + + + + + + + + + + + + + + + + + + + + +
Encoding Headers
Header TypeNameValue
HTTP/1.1 RequestAccept-Encodinggzip
HTTP/1.1 ResponseContent-Encodinggzip
+
+
+ Persistent Connections + Reviewer: I am hiding this entire section for the + Private Beta, since I'm not sure that it applies. Is + that correct? + + By default, the API supports persistent connections + via HTTP/1.1 keepalives. All connections will be kept + alive unless the connection header is set to close. + + + To prevent abuse, HTTP sessions have a timeout of 20 + seconds before being closed. + + + + The server may close the connection at any time + and clients should not rely on this behavior. + + +
+ +
+ Limits + + All accounts, by default, have a preconfigured set of + thresholds (or limits) to manage capacity and prevent + abuse of the system. The system recognizes two kinds + of limits: rate limits and + absolute limits. Rate limits + are thresholds that are reset after a certain amount + of time passes. Absolute limits are fixed. + +
+ Rate Limits + Rate limits are specified in terms of both a + human-readable wild-card URI and a + machine-processable regular expression. The + regular expression boundary matcher '^' takes + effect after the root URI path. For example, the + regular expression ^/v1.0/instances would match + the bolded portion of the following URI: + https://ord.databases.api.rackspacecloud.com/v1.0/instances. + The following table specifies the default rate + limits for all API operations for all &GET;, + &POST;, &PUT;, and &DELETE; calls for databases + and database instances: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Default Rate Limits
VerbURIRegExDefault
&GET; changes-since*/instances/*^/vd+.d+/instances.*3/minute
&POST;*/instances/*^/vd+.d+/instances.*10/minute
&POST; instances*/instances/*^/vd+.d+/instances.*50/day
&PUT;*/instances/*^/vd+.d+/instances.*10/minute
&DELETE;*/instances/*^/vd+.d+/instances.*100/minute
+ Rate limits are applied in order relative to the + verb, going from least to most specific. For + example, although the threshold for &POST; to + /v1.0/* is 10 per minute, one cannot &POST; to + /v1.0/* more than 50 times within a single day. + If you exceed the thresholds established for + your account, a 413 (Rate + Control) HTTP response will be + returned with a Retry-After header to + notify the client when it can attempt to try + again. +
+
+ Absolute Limits + Reviewer: Need to update this entire section. + Please give me your updates. + Refer to the following table for the absolute + limits that are set. + + + + + + + + + + + + + + + + + + + + + +
Absolute Limits
NameDescriptionLimit
InstancesMaximum number of instances allowed + for your account5
Volume SizeMaximum volume size per + instance in gigabytes (GB) for your + account50
+
+
+
+ Date/Time Format + The Database Service uses an ISO-8601 compliant + date format for the display and consumption of + date/time values. + The system timezone is in UTC. MySQL converts TIMESTAMP values from + the current time zone to UTC for storage, and back + from UTC to the current time zone for retrieval. + This does not occur for other types, such as DATETIME. + + + DB Service Date/Time Format + yyyy-MM-dd'T'HH:mm:ss.SSSZ + See the table below for a description of the date/time format codes. + May 19th, 2011 at 8:07:08 AM, GMT-5 would have the following + format: + 2011-05-19T08:07:08-05:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Explanation of Date/Time Format Codes
CodeDescription
yyyyFour digit year
MMTwo digit month
ddTwo digit day of month
TSeparator for date/time
HHTwo digit hour of day (00-23)
mmTwo digit minutes of hour
ssTwo digit seconds of the minute
SSSThree digit milliseconds of the second
ZRFC-822 timezone
+ +
+
+ Pagination + To reduce load on the service, list operations + return a maximum of 20 items at a time. This is + referred to as pagination. Cloud + Databases has separate paging limits for instances, + databases, and users, which are currently all set to + 20. If a request supplies no limit or one that exceeds + the configured default limit, the default is used + instead. + Pagination provides the ability to limit the size + of the returned data as well as retrieve a specified + subset of a large data set. Pagination has two key + concepts: limit and marker. Limit + is the restriction on the maximum number of items for + that type that can be returned. + Marker is the ID of the last + item in the previous list returned. The ID is the UUID + in the case of instances, and the name in the case of + databases and users. For example, a query could + request the next 10 instances after the instance + "1234" as follows: + ?limit=10&marker=1234. Items are + displayed sorted by ID. + Pagination applies only to the calls listed in the + following table: + + + + Verb + URI + Description + + + + + &GET; + /instances/ + Lists the status and + information for all database + instances. + + + &GET; + + /instances/{instanceId}/databases + Lists databases for the + specified instance. + + + &GET; + /instances/{instanceId}/users + Lists the users in the + specified database instance. + + + + If the content returned by a call is paginated, the + response includes a structured link much like an + instance item's links, with the basic structure + {"href": "<url>", "rel": "next"}. + Any response that is truncated by pagination will have + a next link, which points to the + next item in the collection. If there are no more + items, no next link is + returned. + See the examples of paged List Instances calls that + follow. + + List Instances Paged Request: XML + + + + + + + List Instances Paged Request: JSON + + + + + + Notice that the paged request examples above set the + limit to 2 (?limit=2), so the responses + that follow each show 2 instances and return a + marker set to the UUID of the + last item in the returned list + (?marker=4137d6a4-03b7-4b66-b0ef-8c7c35c470d3). + Also a link is provided to retrieve the next 2 results + (limit=2) in the link element + identified by the attribute rel="next" + (XML) or "rel":"next" (JSON): + + List Instances Paged Response: XML + + + + + + + List Instances Paged Response: JSON + + + + + +
+
+ Efficient Polling with the + <parameter>Changes-Since</parameter> + Parameter + Reviewer: I have hidden this section, since it + does not appear that it will be supported for Private + Beta. Correct? + The ReST API allows you to poll for the status of + certain operations by performing a &GET; on various + URIs. Rather than re-downloading and re-parsing the + full status at each polling interval, your ReST client + may use the changes-since + parameter to check for changes since a previous + request. The changes-since time + is specified as Unix time (the number of seconds since + January 1, 1970, 00:00:00 UTC, not counting leap + seconds). If nothing has changed since the + changes-since time, a + 304 (Not Modified) + response will be returned. If data has changed, only + the items changed since the specified time will be + returned in the response. + Reviewer: does the following sentence apply, and + should it be included? + For example, performing a &GET; against + https://api.servers.rackspacecloud.com/v1.0/224532/servers?changes-since=1244012982 + would list all servers that have changed since Wed, 03 + Jun 2009 07:09:42 UTC. +
+
+ Faults + When an error occurs, the Database Service returns + a fault object containing an HTTP error response code + that denotes the type of error. In the body of the + response, the system will return additional + information about the fault. + The following table lists possible fault types with their associated error codes + and descriptions. + + + + Fault Type + Associated Error Code + Description + + + + + badRequest + 400 + There was one or more errors in the user request. + + + unauthorized + 401 + The supplied token is not authorized to access the resources, either it's expired or invalid. + + + forbidden + 403 + Access to the requested + resource was denied. + + + itemNotFound + 404 + The back-end services did not + find anything matching the + Request-URI. + + + badMethod + 405 + The request method is not allowed for this resource. + + + overLimit + 413 + Either the number of entities in the request is larger than + allowed limits, or the user has exceeded allowable request rate limits. + See the details element for more specifics. Contact support + if you think you need higher request rate limits. + + + badMediaType + 415 + The requested content type is not supported by this service. + + + unprocessableEntity + 422 + The requested resource could + not be processed on at the moment. + + + instanceFault + 500 + This is a generic server error and the message contains the reason for the error. This error could wrap several error messages and is a catch all. + + + notImplemented + 501 + The requested method or resource is not implemented. + + + serviceUnavailable + 503 + The Database Service is not + available. + + + + The following two instanceFault + examples show errors when the server has erred or + cannot perform the requested operation: + + + Example instanceFault Response: XML + + + + + + + Example Fault Response: JSON + + + + + + The error code (code) is returned in the body of the response for + convenience. The message element returns a human-readable message that + is appropriate for display to the end user. The details element is + optional and may contain information that is useful for tracking down an error, such + as a stack trace. The details element may or may not be appropriate for + display to an end user, depending on the role and experience of the end user. + The fault's root element (for example, + instanceFault) may change depending + on the type of error. + The following two badRequest examples + show errors when the volume size is invalid: + + Example badRequest Fault on Volume Size Errors: + XML + + + + + + + Example badRequest Fault on Volume Size Errors: + JSON + + + + + + The next two examples show + itemNotFound errors: + + Example itemNotFound Fault: XML + + + + + + + Example itemNotFound Fault: JSON + + + + + +
+
+ Database Instance Status + When making an API call to create, list, or delete + database instance(s), the following database instance + status values are possible: + + + BUILD – The database instance is being provisioned. + + + REBOOT – The database instance is + rebooting. + + + ACTIVE – The database instance is + online and available to take requests. + + + BLOCKED – The database instance is + unresponsive at the moment. + + + RESIZE – The database instance is being + resized at the moment. + + + SHUTDOWN – The database instance is + terminating services. Also, SHUTDOWN is + returned if for any reason the MySQL instance + is shut down but not the actual server. + + + If MySQL has crashed (causing the + SHUTDOWN status), please call support + for assistance. + + + + + ERROR – The last operation for the + database instance failed due to an + error. + + +
+
+ Database Instance Accessibility + Database instances are directly accessible only on + the internal ServiceNet network and using a Cloud + resource within the same regional datacenter. For + example, a database instance in DFW can only be + accessed by a Cloud Server in DFW. + Note that using a public Cloud Load Balancer allows + you to access your ServiceNet database instance from + the public network by performing the following steps: + + Using the Cloud Databases API, create a + database instance. For details see . + + + Using the Cloud Load Balancers API, + create a public load balancer and specify + your database instance hostname as a node, + or use the API to add your instance + hostname as a node to an existing public + load balancer. For details refer to the + Cloud Load Balancers + Developer Guide at http://docs.rackspace.com/api/. + + +
+
+ + + API Operations + + Do not use trailing slashes (/) at the end of calls + to API operations, since this may cause the call to + fail. For example, do not use &GET; /instances/detail/ + (with the trailing slash at the end). Rather, use + &GET; /instances/detail instead. + + +
+ API Versions + This section describes the versions that are + supported for the Cloud Databases API. + + + + + + + + + +
+ +
+ Database Instances + This section describes the operations that are supported for database instances. + + + + + + + + + + + + + + + +
+ +
+ Database Instance Actions + This section describes the actions that are supported for database instances. + + + + + + + +
+ +
+ Databases + This section describes the operations that are + supported for databases. + + + + + + + + + +
+ +
+ Users + This section describes the operations that are + supported for managing database users. + + + + + + + + + +
+ +
+ Flavors + This section describes the operations that are + supported for flavors. + + + + + + + + + +
+
+ + Glossary + + database + + A MySQL database within a database instance. + + + + database instance + + A database instance is an isolated MySQL instance in a single tenant environment on a + shared physical host machine. Also referred to as instance. + + + + flavor + + A flavor is an available hardware configuration for a database instance. Each flavor has a unique combination of memory capacity and priority for CPU time. + + + + volume + + A volume is user-specified storage that contains the MySQL data directory. + Volumes are automatically provisioned on shared Internet Small Computer System Interface (iSCSI) + storage area networks (SAN) that provide for increased performance, scalability, availability + and manageability. Applications with high I/O demands are performance optimized and data is + protected through both local and network RAID-10. Additionally, network RAID provides synchronous + replication of volumes with automatic failover and load balancing across available storage clusters. + + + +
diff --git a/integration/apidocs/src/resources/cdb-getting-started.xml b/integration/apidocs/src/resources/cdb-getting-started.xml new file mode 100644 index 0000000000..ba3275b356 --- /dev/null +++ b/integration/apidocs/src/resources/cdb-getting-started.xml @@ -0,0 +1,1131 @@ + + + + + + + + + GET'> + PUT'> + POST'> + DELETE'> + + + + + + + + + MAY'> + SHOULD'> + MUST'> + MUST NOT'> +]> + + + + + Rackspace Cloud Databases Getting Started Guide + Rackspace Cloud Databases and Servers Getting Started Guide + + + + + 2011 + 2012 + Rackspace US, Inc. + + API v1.0 + Rackspace Cloud Databases + 2012-09-04 + + + Copyright details are filled in by the template. + + + + + 2012-09-04 + + + + Added information for pricing and + service level (refer to ). + + + + + + 2012-08-02 + + + + Corrected request examples for + authentication (refer to ). + + + + + + 2012-08-01 + + + + Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases. + + + + + + + cdb + 1 + + + + Document Change History + This version of the Getting Started replaces and + obsoletes all previous versions. The most recent changes + are described in the table below: + + + + Overview + Follow the steps described in this guide to use the + Rackspace Cloud Databases API and the Cloud + Servers section of the Rackspace Cloud Control + Panel to create and access your database instances via + Rackspace Cloud Servers. + For details about using the Cloud Databases API, refer + to the Cloud Databases Developer + Guide at http://docs.rackspace.com/. + For more details about Rackspace Cloud Databases, refer + to http://www.rackspace.com/cloud/cloud_hosting_products/databases/. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. + Please visit our Product Feedback Forum and let us know what + you think about Cloud Databases! +
+ Prerequisites for Running Examples + In order to run the examples in this guide, you must + have the following prerequisites: + + Rackspace Cloud account + + + Rackspace Cloud username and password, + as specified during registration + + +
+
+ Pricing and Service Level + Cloud Databases is part of the Rackspace Cloud and + your use through the API will be billed as per the + pricing schedule at http://www.rackspace.com/cloud/public/databases/pricing. + Cloud Servers is also part of the Rackspace Cloud and + your use through the Control Panel will be billed as + per the pricing schedule at http://www.rackspace.com/cloud/public/servers/pricing. + The Service Level Agreements (SLAs) for Cloud + Databases and Cloud Servers are available at http://www.rackspace.com/cloud/legal/sla/#cloud_databases + and http://www.rackspace.com/cloud/legal/sla + respectively. +
+
+ + Send Requests to the API + You have several options for sending requests through an + API: + + + Developers and testers may prefer to use cURL, + the command-line tool from http://curl.haxx.se/. + With cURL you can send HTTP requests and receive + responses back from the command line. + + + If you like to use a more graphical interface, + the ReST client for Firefox also works well for + testing and trying out commands, see https://addons.mozilla.org/en-US/firefox/addon/restclient/. + + + You can also download and install rest-client, a + Java application to test ReSTful web services, + from http://code.google.com/p/rest-client/. + + +
+ Send API Requests Using cURL + cURL is a command-line tool that is available on + most UNIX®-like environments and Mac OS X® and can be + downloaded for Windows® in order to interact with the + ReST interfaces. For more information on cURL, visit + http://curl.haxx.se/. + cURL allows you to transmit and receive HTTP requests and responses from the + command-line or from within a shell script. This makes it possible to work with the ReST API + directly without using one of the client APIs. + The following cURL command-line options will be used + in this guide to run the examples: + + cURL Command-Line Options + + + + Sends the specified data in a post + request to the HTTP server. + + + + + + Includes the HTTP header in the + output. + + + + + + Specify an HTTP header in the request. + + + + + If you have the tools, you can run the cURL JSON + request examples with the following options to + format the output from cURL: <curl + JSON request example> | python + -mjson.tool. + +
+
+ Copying and Pasting cURL Request Examples into a + Terminal Window + To run the cURL request examples shown in this guide + on Linux or Mac systems, copy and paste each example + from the HTML version of this guide into an ASCII + editor (for example vi or TextEdit). Then modify each example + with your required account information and so forth, + as detailed in this guide. + + The carriage returns in the cURL request + examples that are part of the cURL syntax are + escaped with a backslash ('\') in order to avoid + prematurely terminating the command. However you + should not escape carriage returns inside the xml + or json message within the command. + + Consider the following cURL Authenticate Request: + XML example that is described in detail in : + + cURL Authenticate Request: XML + + curl \ +'<?xml version="1.0" encoding="UTF-8"?> + <auth>    + <apiKeyCredentials      + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0"      + username="your_username"      + apiKey="your_api_key"/>    + </auth> \ +'https://identity.api.rackspacecloud.com/v2.0/tokens' + + You can see that the lines that are part of the cURL + command syntax have all been escaped with a backslash + ('\') to indicate that the command continues on the + next line: + curl \ + +    +(... lines within the xml portion of the message are not shown in this example) +(... the example only shows lines that are part of cURL syntax)      +      +     +    + </auth> \ +'https://identity.api.rackspacecloud.com/v2.0/tokens' + However the lines within the + xml portion of the message are + not escaped with a backslash + ('\') in order to avoid issues with the xml + processing: + '<?xml version="1.0" encoding="UTF-8"?> + <auth>    + <apiKeyCredentials      + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0"      + username="your_username"      + apiKey="your_api_key"/>    + </auth> \ + + + The final line of the xml message is escaped + since the backslash lies + outside the xml message + and continues the cURL command to the next + line. + + After you are finished modifying the text for the + cURL request example with your information (for + example your_username and + your_api_key), paste it + into your terminal window. Then execute the cURL + command by pressing Enter. + If you have trouble copying and pasting the examples + as described, try typing the entire example on one + long line, removing all the backslash line + continuation characters. +
+
+ + Generate an Authentication Token + You need to generate a token whether you use cURL or a + ReST client. + In order to use the ReST API, you will first need to + obtain an authentication token, which will need to be + passed in for each request using the + X-Auth-Token header. + The following example demonstrates how to use cURL to + obtain the authentication token and the account number. + You will need to supply the authentication token and + account number when making subsequent Cloud Databases API + calls. + Remember to replace the names in the Authenticate + Request examples below with their respective + values: + + your_username — The + username is your common Rackspace Cloud + username, as supplied during + registration. + + + your_api_key — The key + is your API access key. The key can be + obtained from the Rackspace Cloud Control + Panel in the <Your + Account> / API + Keys section (login here: + Control Panel Login). + + + To access the Authentication Service, you must know + whether your account is US-based or UK-based: + + + US-based accounts authenticate through &ENDPOINT-US-20;. + + + UK-based accounts authenticate through &ENDPOINT-UK-20;. + + + Your account may be based in either the US or the UK; + this is not determined by your physical location but by + the location of the Rackspace retail site which was used + to create your account: + + + If your account was created via http://www.rackspacecloud.com, it is a + US-based account. + + + If your account was created via http:/www.rackspace.co.uk, it is a + UK-based account. + + + If you are unsure how your account was created, use the + Rackspace contact information at either site to ask for + help. + Notice that you authenticate using a special URL for + Cloud authentication services. For example, for US-based + accounts, you use + https://identity.api.rackspacecloud.com/v2.0/tokens, + as shown in the following Authenticate Request examples. + Note that the v2.0 component in the URL + indicates that you are using version 2.0 of the Cloud Auth + API. + + cURL Authenticate Request: XML + + curl \ +'<?xml version="1.0" encoding="UTF-8"?> +<auth> + <apiKeyCredentials + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + username="your_username" + apiKey="your_api_key"/> +</auth>' \ + \ + \ +'https://identity.api.rackspacecloud.com/v2.0/tokens' + + + cURL Authenticate Request: JSON + + curl \ +'{ + "auth": + { + "RAX-KSKEY:apiKeyCredentials": + { + "username": "your_username", + "apiKey": "your_api_key"} + } +}' \ + \ +'https://identity.api.rackspacecloud.com/v2.0/tokens' + + + For UK-based accounts, you would need to modify the + URL shown in the last line of each of the Authenticate + Request examples above to be + 'https://lon.identity.api.rackspacecloud.com/v2.0/tokens' + instead. + + + Authenticate Response: XML + Missing code sample! + + + Authenticate Response: JSON + Missing code sample! + + The authentication token id is returned + along with an expires attribute that + specifies when the token expires. + + Notes + + + For all response examples in this guide, the + field values you receive in your responses + will vary from those shown here since they + will be specific to your account. + + + The information shown in the Authenticate + Response examples above is for US-based + accounts. If you authenticate against the + UK-endpoint for auth, you will see the service + catalog information for UK-based + accounts. + + + The id attribute in the + Authenticate Response specifies the + authentication token. Tokens are valid for a + finite duration. + Remember to supply your authentication token + wherever you see the field your_auth_token in + the examples in this guide. + + + The expires attribute denotes + the time after which the token will + automatically become invalid. A token may be + manually revoked before the time identified by + the expires attribute; expires + predicts a token's maximum possible lifespan + but does not guarantee that it will reach that + lifespan. Clients are encouraged to cache a + token until it expires. + + + Applications should be designed to + re-authenticate after receiving a 401 + (Unauthorized) response from a service + endpoint. + + + + The publicURL endpoints for + cloudDatabases (for example + https://ord.databases.api.rackspacecloud.com/v1.0/1100111) + are also returned in the response. + You will find the actual account number after the final + '/' in the publicURL field. In this example, + you can see that the account number is 1100111. You need + to specify your account number on most of the Cloud + Databases API calls, wherever you see the field your_acct_id specified in the + examples in this guide. + After authentication, you can use cURL to perform &GET;, + &DELETE;, and &POST; requests for the Cloud Databases + API. + + + Service Access/Endpoints + The endpoints to use for your Cloud Databases API calls + are summarized in the table below. + + + + + + + + + + + + + + + + + + + + + + + + +
Regionalized Service Endpoints
RegionEndpoint
Chicago (ORD)https://ord.databases.api.rackspacecloud.com/v1.0/1234/ +
Dallas/Ft. Worth (DFW)https://dfw.databases.api.rackspacecloud.com/v1.0/1234/ +
London (LON) + https://lon.databases.api.rackspacecloud.com/v1.0/1234/ +
+
+ + Notes: + + + Choose the endpoint from the table for the + datacenter where your Cloud resources are + located. + + + The Cloud Server that you use in must be + located in the same datacenter where your + database resides. + + + All examples in this guide assume that you + are operating against the ORD datacenter, + however if you are using a different + datacenter, be sure to use the associated + endpoint from the table above instead. + + + + Replace the sample account ID number, + 1234, with your actual account + number returned as part of the authentication response. + Use your actual account number wherever you see the field + your_acct_id + specified in this guide. Refer to . + When making a Cloud Databases API call, place the + endpoint at the beginning of the request URL, for example: + (https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/), + as you can see in the cURL List Flavors Details Request + examples in . +
+ + List Flavors + A flavor is an available hardware configuration for a + database instance. Each flavor has a unique combination of + memory capacity and priority for CPU time. The larger the + flavor size you use, the larger the amount of RAM and + priority for CPU time your database instance will + receive. + You need to use the List Flavors API call + (/flavors) to find the available + configurations for your database instance, and then decide + which size you need. + This operation does not require a request body. + The following examples show the cURL requests for List + Flavors: + + cURL List Flavors Request: XML + curl -i \ +-H 'X-Auth-Token: your_auth_token' \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/flavors' + + + cURL List Flavors Request: JSON + curl -i \ +-H 'X-Auth-Token: your_auth_token' \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/flavors' + + Remember to replace the names in the examples above with + their actual respective values for all the cURL examples + that follow: + + your_auth_token — as + returned in your authentication response (see + the response examples in ) + + + your_acct_id — as + returned in your authentication response (must + be replaced in the request URL) + + + The following examples show the responses for List + Flavors: + + List Flavors Response: XML + Missing code sample! + + + List Flavors Response: JSON + Missing code sample! + + In the previous examples, you can see from the flavor + name that there are multiple flavors + available, including medium (with 1 virtual + CPU and 2 gigabytes of memory) and tiny (with + 1 virtual CPU and 0.5 gigabytes of memory). + In this example, assume that you decide to use the tiny + flavor (id 1) to provide the needed capacity for your + database instance. + Notice that there are two kinds of link relations + associated with flavor resources. A self link + contains a versioned link to the + flavor resource. These links should be used in cases where + the link will be followed immediately (as you will see in + the next section). A bookmark link provides a + permanent link to a flavor resource that is appropriate + for long term storage and works across API + versions. + + + + Create a Database Instance with a Database and a + User + A database instance is an isolated MySQL instance in + a single tenant environment on a shared physical host + machine. In the example below, you create a database + instance with a database and a user. The example instance + uses the tiny flavor and a volume size of 2 gigabytes + (GB). + Refer to Create Database in the Cloud + Databases Developer Guide for the + restrictions for choosing the database name. + The example creates a database instance + myrackinstance, with the following: + + the tiny flavor + + + volume size of 2 gigabytes (GB) + + + a database named sampledb with: + + utf8 character + set + + + utf8_general_ci + collation + + + + + a user simplestUser with + password password + + + + Notice that the request specifies the flavor reference + (flavorRef) to the tiny flavor (id 1) + that was returned by the List Flavors Details call: + "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1". + This is the self link that is the versioned + link to the flavor resource. Refer to the response + examples in . + The following examples show the cURL requests for Create + Instance: + + cURL Create Instance Request: XML + curl \ +'<?xml version="1.0" ?> +<instance xmlns="http://docs.openstack.org/database/api/v1.0" + name="myrackinstance" + flavorRef= + "https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/flavors/1"> + <databases> + <database name="sampledb" character_set="utf8" + collate="utf8_general_ci" /> + </databases> + <users> + <user name="simplestUser" password="password"> + <databases> + <database name="sampledb"/> + </databases> + </user> + </users> + <volume size="2" /> +</instance>' \ +-H 'X-Auth-Token: your_auth_token' \ + \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances' + + + cURL Create Instance Request: JSON + curl \ +'{ + "instance": { + "databases": [ + { + "character_set": "utf8", + "collate": "utf8_general_ci", + "name": "sampledb" + } + ], + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/flavors/1", + "name": "myrackinstance", + "users": [ + { + "databases": [ + { + "name": "sampledb" + } + ], + "name": "simplestUser", + "password": "password" + } + ], + "volume": + { + "size": 2 + } + } +}' \ +-H 'X-Auth-Token: your_auth_token' \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances' + + Remember to replace the names in the examples above with + their actual respective values for all the cURL examples + that follow: + + your_auth_token — as + returned in your authentication response (see + the response examples in ) + + + your_acct_id — as + returned in your authentication response (must + be replaced in the request URL) + + + Remember to replace the account id shown in the + flavorRef property for the examples with + your actual account id: + "https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/flavors/1". + Rather than the flavor URI shown in the previous sentence, + you can also pass the flavor id (integer) as the value for + flavorRef. For example, the flavor id for the flavor URI + shown above is "1". + The following examples show the Create Instance + responses: + + + Create Instance Response: XML + Missing code sample! + + + + Create Instance Response: JSON + Missing code sample! + + You will need to specify the instance id returned (in + the response examples above: + id="d379ba5c-9a1f-4aa9-9a17-afe237d04c65") + on subsequent API calls that require it, for example List + Databases for Instance. + Note that the database and user are not listed in the + Create Instance responses. Next you will verify that both + were successfully created. + The operation of creating the database instance + may take up to several minutes. You will not be + able to perform the operations to List Databases + for Instance and List Users for Instance in the + sections that follow until the instance has ACTIVE + status. + + + + List Databases for Instance + In this section you will list the databases in the + specified database instance. + This operation does not require a request body. + The following + examples show the cURL requests for List Databases for + Instance: + + cURL List Databases for Instance Request: + XML + curl \ +-H 'X-Auth-Token: your_auth_token' \ + \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances/instance_id/databases' + + + + cURL List Databases for Instance Request: + JSON + curl \ +-H 'X-Auth-Token: your_auth_token' \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances/instance_id/databases' + + Remember to replace the names in the examples above with + their actual respective values: + + your_auth_token — as + returned in your authentication response (see + the examples in ) + + + your_acct_id — as + returned in your authentication response (see + the examples in ) + + + instance_id + — as returned in your create instance + response (see the examples in ) + + + The following examples show the responses for Create + User: + + List Databases for Instance Response: XML + HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 109 +Date: Thu, 05 Apr 2012 18:20:18 GMT + +<databases xmlns="http://docs.openstack.org/database/api/v1.0"> + <database name="sampledb"/> +</databases> + + + + List Databases for Instance Response: JSON + HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 37 +Date: Thu, 05 Apr 2012 18:13:53 GMT + +{ + "databases": [ + { + "name": "sampledb" + } + ] +} + + You can see that the database sampledb was + successfully created. Next you will list the users. + + + List Users in Database Instance + In this section you will list the users in the specified + database instance. + This operation does not require a request body. + The following examples show the cURL requests for List + Users in Database Instance: + + cURL List Users in Database Instance Request: + XML + curl \ +-H 'X-Auth-Token: your_auth_token' \ + \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances/instance_id/users' + + + + cURL List Users in Database Instance Request: + JSON + curl \ +-H 'X-Auth-Token: your_auth_token' \ + \ +'https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/instances/instance_id/users' + + Remember to replace the names in the examples above with + their actual respective values: + + your_auth_token — as + returned in your authentication response (see + the examples in ) + + + your_acct_id — as + returned in your authentication response (see + the examples in ) + + + instance_id + — as returned in your create instance + response (see the examples in ) + + + The following examples show the responses for List + Users in Database Instance: + + List Users in Database Instance Response: + XML + HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 109 +Date: Thu, 05 Apr 2012 18:20:18 GMT + +<?xml version="1.0" ?> +<users xmlns="http://docs.openstack.org/database/api/v1.0"> + <user name="simplestUser"> + <databases> + <database> + <name> + sampledb + </name> + </database> + </databases> + </user> +</users> + + + + List Users in Database Instance Response: + JSON + HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 113 +Date: Thu, 05 Apr 2012 18:13:53 GMT + +{ + "users": [ + { + "databases": [ + { + "name": "sampledb" + } + ], + "name": "simplestUser" + } + ] +} + + You can see that the user simplestUser was + successfully created. + + + Create a New Cloud Server + Create a Cloud Server (or use an existing Cloud Server) + to access your database. + + If you are using an existing Cloud Server on your + account, skip this step and go directly to . + + Create a Cloud Server using the Cloud Servers section of + the Cloud Control Panel (login here: Control Panel Login). + + To create a Cloud Server using the Cloud Control + Panel: + + Click Servers to view + the Cloud Servers page. + + + Using the Region drop-down + menu, select the appropriate region, depending on + whether you want to create a first generation + Cloud Server or a next generation Cloud Server. + (The type of Cloud Server does not matter for this + exercise.) + + + Select an image from a list of different + operating systems, including Linux Distributions + and Windows Images: + + + + + + + + Specify the Server Name + and select a Size for your + Cloud Server, then click Create + Server. + + + + You can also create a Cloud Server using the Cloud + Servers API. Refer to the Next Generation Cloud + Servers Developer Guide for + details. + + + + Configuring an Application to Use Your Cloud + Database + If you are configuring an application to use your + database on a Cloud Server, you need to configure the + application with the hostname for the database instance + and user name / password for the database. Refer to the + response examples in + for the hostname returned and the request + examples for the user name and password you + specified. + + + MySQL GUI Administration + If you want to access your database using the command + line MySQL client, then you have now completed the + Getting Started. + Otherwise, you can use a GUI tool such as phpMyAdmin to + interact with your database instance. Common operations + include managing databases, tables, fields, relations, + indexes, users, and permissions. Included below is a + procedure to set up phpMyAdmin on an Ubuntu 11.04 Cloud + Server. + For more detailed installation configuration + instructions see the phpMyAdmin documentation at: http://www.phpmyadmin.net/documentation/. + Rackspace does not provide phpMyAdmin support, + and the user is responsible for any security + related configuration. + + + To install and configure phpMyAdmin on an Ubuntu + 11.04 Cloud Server: + + Install phpMyAdmin: + + sudo apt-get install + phpmyadmin + + + Set up a symbolic link to the phpmyadmin config + file: + sudo ln -s /etc/phpmyadmin/apache.conf + /etc/apache2/conf.d/phpmyadmin.conf + + + + Edit the + /etc/phpmyadmin/config-db.php + config file to point to your database + instance: + $dbserver='<cloud database + hostname>'; + + + + Restart apache: + sudo apachectl restart + + + Access phpMyAdmin at + http://<your_ipaddress>/phpMyAdmin: + + + + + + + + This concludes the Getting + Started. Thank you for using Rackspace Cloud + products. + +
diff --git a/integration/apidocs/src/resources/cdb-mgmt-devguide.xml b/integration/apidocs/src/resources/cdb-mgmt-devguide.xml new file mode 100644 index 0000000000..ea7d784cbb --- /dev/null +++ b/integration/apidocs/src/resources/cdb-mgmt-devguide.xml @@ -0,0 +1,1267 @@ + + + + + + + + GET'> + PUT'> + POST'> + DELETE'> + + + + + + '> + + + + + + '> +]> + + + + Rackspace Cloud Databases Developer Guide for Service + Management + + Rackspace Cloud Databases Management Developer + Guide + + + + + + + + Rackspace Cloud + + + + 2011 + 2012 + Rackspace US, Inc. + + API v1.0 + Rackspace Cloud Databases + 2012-10-04 + + + Copyright details are filled in by the template. + + + + This document is intended for software developers + interested in developing service management + applications using the Rackspace Cloud Databases + Application Programming Interface + (API). + + + + + 2012-10-04 + + + + Added new API call List All Active + Accounts (see ). + + + + + + 2012-10-02 + + + + Added new API calls Get Hardware + Info (see ) and Update All Instances on Host + (see ). + + + Added tenant_id field to sample + responses for List All Instances for a + Host API call (see ). + + + + + + 2012-08-21 + + + + Changed FAILED status for database + instance to ERROR instead (see ). + + + + + + 2012-08-01 + + + + Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases. + + + + + + + + this is a placeholder for the front cover + + + this is a placeholder for the back cover + + + + Overview + Rackspace Cloud Databases is a database available to + Rackspace Open Cloud customers. Interactions with Cloud + Databases occur programmatically via the Cloud Databases + API as described in this Cloud Databases + Developer Guide for Service + Management. + Writer: need to synch up Overview section with + marketing blurb for web. These should be + consistent. + The API operations described in this manual relate to + support of Cloud Databases service activities on behalf of + a specific customer. The scope of these operations is + narrow, relating to the needs of a single customer. A + customer might directly request these operations, however + only Rackers are authorized to perform them. + The following figure shows an + overview of Cloud Databases Infrastructure: + + + + + + + + + + + Writer: need to get architecture diagram for DBaaS. + Daniel emailed 11/17 that he would work on this during the + Private Beta and have it done for the Public + Beta. + We welcome feedback, comments, and bug reports at http://feedback.rackspacecloud.com. + Writer: check whether following statement should be + added back in for public (not private) beta: Issues and + bug reports can be directed to your support team via + ticket, chat, email, or phone. +
+ Intended Audience + Two APIs provide access to Rackspace Cloud + Databases: + + + The management API, + available only to developers, Support + personnel, and Operations administrators + within Rackspace, depending on the granted + LDAP roles/permissions, is the subject of this + document. + + + The public API, + available to developers within Rackspace and + Rackspace customers, is the subject of a + companion document, the Cloud + Databases Developer + Guide. + + + To use the information provided here, you should + first have a general understanding of the Database + service. You should also be familiar with: + + + Database terminology + + + ReSTful web services + + + HTTP/1.1 conventions + + + JSON and/or XML data serialization + formats + + + ATOM Syndication Format + + +
+ +
+ Document Change History + This version of the Developer Guide replaces and + obsoletes all previous versions. The most recent + changes are described in the table below: + +
+
+ Additional Resources + Descriptive information about Cloud Databases is + also published in its Web Application Description + Language (WADL) and XML Schema Definition (XSD). You + are welcome to read this information here: + + + The WADL is . + + + The XSD is . + + + You can download the most current versions of other + API-related documents from http://docs.rackspace.com/. + For information about getting started using Cloud + Databases and Cloud Servers, refer to + Getting Started with Rackspace Cloud + Databases and Servers. + For more details about Rackspace Cloud Databases, + refer to http://www.rackspace.com/cloud/cloud_hosting_products/databases/. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. + Please visit our Product Feedback Forum to find out what + customers think about Cloud Databases. + You can also follow Rackspace updates and + announcements via twitter at http://www.twitter.com/rackspace. + This API uses standard HTTP 1.1 response codes as + documented at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. + +
+
+ + Concepts + + To use the Cloud Databases API effectively, you should + understand several key concepts: + Reviewer: Daniel Morris is asking Daniel Salinas to + do an initial write-up of this chapter. +
+ Database Instance + A database instance is an isolated MySQL instance in + a single tenant environment on a shared physical host + machine. + Writer: once we support + MSSQL, we need to describe here what is used for MSSQL + in place of database instance. +
+
+ Database + A MySQL database within a database instance. + Writer: once we support + MSSQL, we need to modify the wording here, such as: + The actual database, whether it is in MySQL or + MSSQL. +
+
+ Flavor + A flavor is an available hardware configuration for + a database instance. Each flavor has a unique + combination of memory capacity and priority for CPU + time. +
+
+ Volume + A volume is user-specified storage that contains the + MySQL data directory. Volumes are automatically + provisioned on dedicated Internet Small Computer + System Interface (iSCSI) storage area networks (SAN) + that provide for increased performance, scalability, + availability and manageability. Applications with high + I/O demands are performance optimized and data is + protected through both local and network RAID-10. + Additionally, network RAID provides synchronous + replication of volumes with automatic failover and + load balancing across available storage + clusters. +
+
+ + General API Information + The Cloud Databases Management API is implemented using + a ReSTful web service interface. Most functions of the + customer-facing Cloud Databases API may be accessed on + behalf of a customer. The Cloud Databases Management API + also extends this functionality to include operations for + managing features of the public APIas well as + additional data fields not available to + customers. +
+ Authentication + Every ReST request against the Cloud Databases + Management API requires that the caller be + authenticated with HTTP Basic Auth. + + If you cannot access the Cloud Databases + Management API, please email the Cloud DB team at + clouddb_all@rackspace.com to + request access. Access will be granted on an + as-needed basis until access to the Management API + is integrated with Global Auth. + +
+
+ Service Access + The Database Service is a regionalized service. The + user of the service is therefore responsible for + appropriate replication, caching, and overall + maintenance of Cloud Databases data across regional + boundaries to other Cloud Databases servers. + + Replace the sample account ID number, + 1234, as shown in the URLs + in this guide, with your actual account number + returned as part of the authentication service + response. + You will find the actual account number after the + final '/' in the publicURL field returned + by the authentication response. In the following + example, you can see from the publicURL + field for cloudServers + (publicURL="https://servers.api.rackspacecloud.com/v1.0/322781") + that the account number is 322781. +
+
+ Request/Response Types + The Cloud Databases API supports both the JSON and + XML data serialization formats. The request format is + specified using the Content-Type header + and is required for calls that + have a request body. The response format can be + specified in requests either by using the + Accept header or by adding an + .xml or .json extension + to the request URI. Note that it is possible for a + response to be serialized using a format different + from the request. If no response format is specified, + JSON is the default. If conflicting formats are + specified using both an Accept header and + a query extension, the query extension takes + precedence. + Some operations support an Atom representation that + can be used to efficiently determine when the state of + services has changed. + Reviewer: the previous sentence will be hidden + for the Private Beta, since it does not appear that + Atom will be supported yet. Correct? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Response Formats
FormatAccept HeaderQuery ExtensionDefault
JSONapplication/json.jsonYes
XMLapplication/xml.xmlNo
ATOMapplication/atom+xml.atomNo
+ Reviewer: the ATOM row in the table above will be + hidden for the Private Beta, since it does not appear + that Atom will be supported yet. Correct? + In the request example below, notice that + Content-Type is set to + application/json, but + application/xml is + requested via the Accept + header: + + Request with Headers: JSON + + Missing code sample! + + Therefore an XML response format is returned: + + Response with Headers: XML + + Missing code sample! + +
+
+ Synchronous and Asynchronous Responses + Reviewer: please give me the updated info for this + section. Need to replace info about callback URL, + etc. + All successful &GET; requests are + synchronous calls, since they + are always retrieving (reading) existing information. + With these requests, the caller waits until the call + returns with the specified code and response body. For + an example, see XXXX. + &PUT;, &POST;, and &DELETE; calls are asynchronous, however, + since they may take some time to process. Therefore they return 202 ACCEPTED + responses containing information with a callback URL, which allows the progress, + status, and/or response information of the call to be retrieved at a later point in + time. The asynchronous response body will look similar to the following examples, + depending on the format requested: + + 202 ACCEPTED Response: XML + + Reviewer: need code example + + + 202 ACCEPTED Response: JSON + + Reviewer: need code example + + The following table shows the attributes for asynchronous responses: + + + + + + + + + + + + + + + + + + + +
Attributes for Asynchronous Responses
AttributeDescription
jobIdAn identifier for the specific request.
callbackUrlResource locator for querying the status of the request.
+ + The status for asynchronous calls is retained for up to 24 hours. + + + If a request body does not pass initial validation or an error condition + arises, you may receive an immediate error response from the request. + + When a request is made to the callback URL provided + and the job is still running, another + 202 ACCEPTED response + is returned with the same information as the previous + one. If the request is complete, the response will be + as if the original call returned as normal, without + waiting. For example, if a Create Database request was + issued and a 202 asynchronous response was returned, + the response from querying the callback URL for a + completed successful database creation would be a + 200 OK and contain the + information for the created database. See XXXX for a + specific example. + If an error occurs during the processing of the + create request, querying the callback URL will return + the details of the error, as if the original call + returned the error response. For example, if a + validation error occurs during the Create Database + request above, the response from querying the callback + URL would be a 400 BAD + REQUEST and contain details regarding the specific + validation error. + + If the response from querying a callback URL is a + 404 NOT FOUND, the details of the error in the + response body will contain information the caller may use to determine whether + the specified job itself was not found, or if the response from the original + request was a 404 NOT FOUND. + + The description of each &PUT;, &POST;, and &DELETE; + request identifies the response codes that can + indicate success or error for that request. For + example, see XXXX immediately below the table for a + list of the successful and error response codes for + the POST /xxxx call. +
+
+ Content Compression + Request and response body data may be encoded with gzip compression to accelerate + interactive performance of API calls and responses. This is controlled using the + Accept-Encoding header on the request from the client and indicated + by the Content-Encoding header in the server response. Unless the + header is explicitly set, encoding defaults to disabled. + + + + + + + + + + + + + + + + + + + + + + +
Encoding Headers
Header TypeNameValue
HTTP/1.1 RequestAccept-Encodinggzip
HTTP/1.1 ResponseContent-Encodinggzip
+
+
+ Persistent Connections + + By default, the API supports persistent connections + via HTTP/1.1 keepalives. All connections will be kept + alive unless the connection header is set to close. + + + To prevent abuse, HTTP sessions have a timeout of 20 + seconds before being closed. + + + + The server may close the connection at any time + and clients should not rely on this behavior. + + +
+ +
+ Limits + + All accounts, by default, have a preconfigured set of + thresholds (or limits) to manage capacity and prevent + abuse of the system. The system recognizes two kinds + of limits: rate limits and + absolute limits. Rate limits + are thresholds that are reset after a certain amount + of time passes. Absolute limits are fixed. + +
+ Rate Limits + Rate limits are specified in terms of both a + human-readable wild-card URI and a + machine-processable regular expression. The + regular expression boundary matcher '^' takes + effect after the root URI path. For example, the + regular expression ^/v1.0/instances would match + the bolded portion of the following URI: + https://ord.databases.api.rackspacecloud.com/v1.0/instances. + The following table specifies the default rate + limits for all API operations for all &GET;, + &POST;, &PUT;, and &DELETE; calls for databases + and database instances: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Default Rate Limits
VerbURIRegExDefault
&GET; changes-since*/instances/*^/vd+.d+/instances.*3/minute
&POST;*/instances/*^/vd+.d+/instances.*10/minute
&POST; instances*/instances/*^/vd+.d+/instances.*50/day
&PUT;*/instances/*^/vd+.d+/instances.*10/minute
&DELETE;*/instances/*^/vd+.d+/instances.*100/minute
+ Rate limits are applied in order relative to the + verb, going from least to most specific. For + example, although the threshold for &POST; to + /v1.0/* is 10 per minute, one cannot &POST; to + /v1.0/* more than 50 times within a single day. + If you exceed the thresholds established for + your account, a 413 (Rate + Control) HTTP response will be + returned with a Retry-After header to + notify the client when it can attempt to try + again. +
+
+ Absolute Limits + Reviewer: Need to update this entire section. + Please give me your updates. + Refer to the following table for the absolute + limits that are set. + + + + + + + + + + + + + + + + + + + + + +
Absolute Limits
NameDescriptionLimit
InstancesMaximum number of instances allowed + for your account5
Volume SizeMaximum volume size per + instance in gigabytes (GB) for your + account25
+
+
+
+ Date/Time Format + The Database Service uses an ISO-8601 compliant + date format for the display and consumption of + date/time values. + The system timezone is in UTC. MySQL converts + TIMESTAMP values from the current time zone to UTC for + storage, and back from UTC to the current time zone + for retrieval. This does not occur for other types, + such as DATETIME. + + DB Service Date/Time Format + yyyy-MM-dd'T'HH:mm:ss.SSSZ + See the table below for a description of the date/time format codes. + May 19th, 2011 at 8:07:08 AM, GMT-5 would have the following + format: + 2011-05-19T08:07:08-05:00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Explanation of Date/Time Format Codes
CodeDescription
yyyyFour digit year
MMTwo digit month
ddTwo digit day of month
TSeparator for date/time
HHTwo digit hour of day (00-23)
mmTwo digit minutes of hour
ssTwo digit seconds of the minute
SSSThree digit milliseconds of the second
ZRFC-822 timezone
+ +
+
+ Pagination + Reviewer: please review and give me your updates + for this section. Dev needs to determine which API + calls support pagination and then supply examples. I + have hidden this entire section for now, since we do + not believe that it will be supported for Private + Beta. Correct? + To reduce load on the service, list operations will + return a maximum of 100 items at a time. This is + referred to as pagination. + Pagination is the ability to limit the size of the returned data as well as + retrieve a specified subset of a large data set. Pagination has two key concepts: + limit and offset. Limit is the restriction on the maximum + number of items for that type that can be returned. Offset is + the starting point for the return data. For example, an offset of 50 specifies that + the items that are returned should start with item number 51 (since the numbering is + one-based) in the collection. + It is important to note that offset must be a multiple of the + limit (or zero), otherwise a Bad Request Exception will be thrown. Both limit and + offset are specified via request parameters on the URI. The parameters are named + limit and offset respectively, and both apply only to + &GET; calls. If unspecified, they default to limit=100 and + offset=0. See the examples that follow. + + Examples of Limits and Offsets for Paging Calls + + Reviewer: Need code example. + + Pagination applies only to the calls listed in the following table: + Reviewer: need to update the table of paginated API + calls below: + + + + Verb + URI + Description + + + + + &GET; + /URI/ + List all databases manageable + by the account specified. + + + + &GET; + /URI/?name=Name + Filter databases. + + + + &GET; + /URI/ID + List details of the specified + database. + + + + &GET; + /URI/ID/xxx + List details xxx. + + + + &GET; + /URI/ID/yyy + List details yyy. + + + + + + + See the following section for examples of paged List + Databases calls. +
+ Pagination Elements and Attributes + Reviewer: please review and give me your + updates for this section. + For any collection in a result, there is a totalEntries attribute + representing the total number of entries there are for this item type. If the + number of items requested in the &GET; call is less then the total number of + items for this type, then there will be pagination links previous + and/or next, specifying how to get to the previous and/or next set + of records. + + The previous and/or next link elements are + displayed only if there are items available in the corresponding link. See + the following examples for details. + + + List Databases Request with limit: + XML + + Reviewer: Need code example. + + + List Databases Request with limit: + JSON + + Reviewer: Need code example. + + + List Databases Response with totalEntries: + XML + + Reviewer: Need code example. + + + List Databases Response with totalEntries: + JSON + + Reviewer: Need code example. + + In the previous two response examples, note that + totalEntries=112 and that a link has been provided to retrieve + the next 3 results (limit=3) in the link element identified by the + attribute rel="next" (XML) or "rel":"next" (JSON). + The following example shows links to both previous and next results in the + responses, since the request specified to start with the fourth item in the + collection (offset=3): + + List Databases Request with limit and + offset: XML + + Reviewer: Need code example. + + + List Databases Request with limit and + offset: JSON + + Reviewer: Need code example. + + + List Databases Response with Links to + previous and next Results: XML + + Reviewer: Need code example. + + + List Databases Response with Links to + previous and next Results: JSON + + Reviewer: Need code example. + + + In the previous two response examples, note that + totalEntries=112 and two links have been provided to: + + Retrieve the next 3 results (limit=3) via the link + element identified by the attribute rel="next" (XML) or + "rel":"next" (JSON) + + + Retrieve the previous 3 results via the link element identified by + the attribute rel="previous" (XML) or + "rel":"previous" (JSON) + + +
+
+
+ Efficient Polling with the + <parameter>Changes-Since</parameter> + Parameter + Reviewer: I have hidden this section, since it + does not appear that it will be supported for Private + Beta. Correct? + The ReST API allows you to poll for the status of + certain operations by performing a &GET; on various + URIs. Rather than re-downloading and re-parsing the + full status at each polling interval, your ReST client + may use the changes-since + parameter to check for changes since a previous + request. The changes-since time + is specified as Unix time (the number of seconds since + January 1, 1970, 00:00:00 UTC, not counting leap + seconds). If nothing has changed since the + changes-since time, a + 304 (Not Modified) + response will be returned. If data has changed, only + the items changed since the specified time will be + returned in the response. + Reviewer: does the following sentence apply, and + should it be included? + For example, performing a &GET; against + https://api.servers.rackspacecloud.com/v1.0/224532/servers?changes-since=1244012982 + would list all servers that have changed since Wed, 03 + Jun 2009 07:09:42 UTC. +
+
+ Faults + Reviewer: need to update this section as needed + for Cloud Databases. + When an error occurs, the Database Service returns + a fault object containing an HTTP error response code + that denotes the type of error. In the body of the + response, the system will return additional + information about the fault. + The following table lists possible fault types with their associated error codes + and descriptions. + + + + Fault Type + Associated Error Code + Description + + + + + badRequest + 400 + There was one or more errors in the user request. + + + unauthorized + 401 + The supplied token is not authorized to access the resources, either it's expired or invalid. + + + forbidden + 403 + Access to the requested + resource was denied. + + + itemNotFound + 404 + The back-end services did not + find anything matching the + Request-URI. + + + badMethod + 405 + The request method is not allowed for this resource. + + + overLimit + 413 + Either the number of entities in the request is larger than + allowed limits, or the user has exceeded allowable request rate limits. + See the details element for more specifics. Contact support + if you think you need higher request rate limits. + + + badMediaType + 415 + The requested content type is not supported by this service. + + + unprocessableEntity + 422 + The requested resource could + not be processed on at the moment. + + + instanceFault + 500 + This is a generic server error and the message contains the reason for the error. This error could wrap several error messages and is a catch all. + + + notImplemented + 501 + The requested method or resource is not implemented. + + + serviceUnavailable + 503 + The Database Service is not + available. + + + + The following two instanceFault + examples show errors when the server has erred or + cannot perform the requested operation: + + + Example instanceFault Response: XML + + + + + + + Example instanceFault Response: JSON + + + + + + The error code (code) is returned in the body of the response for + convenience. The message element returns a human-readable message that + is appropriate for display to the end user. The details element is + optional and may contain information that is useful for tracking down an error, such + as a stack trace. The details element may or may not be appropriate for + display to an end user, depending on the role and experience of the end user. + The fault's root element (for example, + instanceFault) may change depending + on the type of error. + The following two badRequest examples + show errors when the volume size is invalid: + + Example badRequest Fault on Volume Size Errors: + XML + + + + + + + Example badRequest Fault on Volume Size Errors: + JSON + + + + + + The next two examples show + itemNotFound errors: + + Example itemNotFound Fault: XML + + + + + + + Example itemNotFound Fault: JSON + + + + + +
+
+ Database Instance Status + When making an API call to create, list, or delete + database instance(s), the following database instance + status values are possible: + + + BUILD – The database instance is being provisioned. + + + REBOOT – The database instance is + rebooting. + + + ACTIVE – The database instance is + online and available to take requests. + + + BLOCKED – The database instance is + unresponsive at the moment. + + + RESIZE – The database instance is being + resized at the moment. + + + SHUTDOWN – The database instance is + terminating services. Also, SHUTDOWN is + returned if for any reason the MySQL instance + is shut down but not the actual server. + + + If MySQL has crashed (causing the + SHUTDOWN status), you can try + rebooting the database instance to see + if that corrects the problem. If the + instance still does not recover, you + can escalate the issue to + NebOps. + + + + + ERROR – The last operation for the + database instance failed due to an + error. + + +
+
+ + + API Operations + +
+ Database Instance Management + This section describes the operations that are supported for managing database instances. + + + + + + + + + + + + + + + + + +
+ +
+ Host Information + This section describes the operations that are supported for getting host information. + + + + + + + + + + + +
+ +
+ Management Instance Actions + This section describes the actions that are supported for database instances. + + + + + + +
+ +
+ Storage Management + This section describes the operations that are supported for getting information about storage devices. + + + + + +
+ +
+ Account Information + This section describes the operations that are + supported for accounts. + + + + + + + + +
+ +
+
diff --git a/integration/apidocs/src/resources/cdb-releasenotes.xml b/integration/apidocs/src/resources/cdb-releasenotes.xml new file mode 100644 index 0000000000..205ff6530e --- /dev/null +++ b/integration/apidocs/src/resources/cdb-releasenotes.xml @@ -0,0 +1,711 @@ + + + + + Rackspace Cloud Databases Release Notes + Rackspace Cloud DBs Rel Notes + + + + + + + + Rackspace Cloud + + + + 2010 + 2011 + 2012 + Rackspace US, Inc. + + API v1.0 + Rackspace Cloud Databases + 2012-10-02 + + + Copyright details are filled in by the template. + + + + This document is intended for software developers + interested in developing applications using the + Rackspace Cloud Databases Application Program + Interface (API). + + + + 2012-10-02 + + Updated for v1.0.13. + + + + 2012-09-17 + + Updated for v1.0.12. + + + + 2012-09-13 + + Updated for v1.0.11. + + + + 2012-08-21 + + Updated for v1.0.8. + + + + 2012-08-02 + + Updated for v1.0.7. + + + + 2012-08-01 + + Initial Unlimited Availability (UA) release + for Rackspace Cloud Databases. + + + + + cdb + 10 + + + + Document Change History + This version of the Release Notes replaces and obsoletes all + previous versions. The most recent changes are described + in the table below: + + + + v1.0.13, October 2, 2012 + + Bug Fixes + + D-08245: Formatted code to comply with + pep8 code style. + + + D-08357: Initialized and updated usage + data for instances that existed prior to billing + enablement. + + + D-08648: Fixed guest agent to properly + address lost connections with message bus. + + + + Resources + + Get started using the Cloud Databases API to + create databases in the Getting Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in + the Cloud Databases + Developer Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also File + a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + v1.0.12 (Hotfix), September 17, 2012 + + Bug Fixes + + D-08422: Fixed billing code to + properly send events to Yagi and fixed billing + timestamp format. + + + + Known Issues / Limitations + + List Versions API call does not work and returns + a "403 Forbidden" response. + + + Users' database privileges are not modifiable. + Users must be deleted and recreated to add + additional database privileges. + + + + Resources + + Get started using the Cloud Databases API to + create databases in the Getting Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in + the Cloud Databases + Developer Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also File + a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + v1.0.11, September 13, 2012 + + Bug Fixes + + D-08051: Fixed issue preventing users + from increasing/decreasing their instance flavor + size. + + + D-06654: Resizing instance using same + volume size no longer generates misleading error + message. + + + D-07897: Fixed issue where instance in + ERROR fails to delete. + + + D-07819: Fixed issue causing usage + information to be applied when instance goes into + error status after failed provisioning. + + + D-07213: Fixed guest update Mgmt API + call. + + + + Maintenance + + D-08217: Corrected yagi logrotate + configuration to prevent log file handle from + disappearing. + + + B-27788: Updated usage modify events + (resize volume/flavor). + + + B-29438: Added Environment Variable to + usage event. + + + + Known Issues / Limitations + + List Versions API call does not work and returns + a "403 Forbidden" response. + + + Users' database privileges are not modifiable. Users must be + deleted and recreated to add additional database + privileges. + + + + Resources + + Get started using the Cloud Databases API to + create databases in the Getting Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in + the Cloud Databases + Developer Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also File + a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + v1.0.8, August 21, 2012 + + What's New + + Added reserved database names that cannot be used for + creating databases (see http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createDatabase__version___accountId__instances__instanceId__databases_.html + for details). + + + Added reserved user names that cannot be used for creating + users (see http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createUser__version___accountId__instances__instanceId__users_.html + for details). + + + Added support for the InnoDB plugin. + + + + Bug Fixes + + D-07436: Added updated + attribute to the response from the List Versions + API call. + + + D-07488: Increased + max_allowed_packet default to 16 + MB. + + + D-07278: Fixed + open_files_limit based on flavor + size. + + + D-06636: Fixed issue where rabbit + restart causes delete_queue to fail. + + + D-07273: Added check for account id in + Mgmt API calls. + + + D-07644: Fixed the issue where the + Usage events for "Exists" do not have a "T" in the + date/time format. + + + D-06783: Split usage events of over 24 + hours into "exists" events. + + + D-07366: Simplified ioprio + calculation. + + + Story:317533: Added cpulimit + overcommit multiplier. + + + Fixed the trove-api issues with rabbit + connections. Using openstack-common rpc. + + + Updated the usage event with + the environment set to STAGE instead of + STAGING. + + + Changed set_cpus to not use a multiplier so that values in + the flavors database have the correct vcpus that + are then applied to the containers. + + + + Known Issues / Limitations + + List Versions API call does not work and returns + a "403 Forbidden" response. + + + Users' database privileges are not modifiable. +  Users must be deleted and recreated to add + additional database privileges. + + + + Resources + + Get started using the Cloud Databases API to + create databases in the Getting Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in + the Cloud Databases + Developer Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also File + a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + v1.0.7, August 2, 2012 + + What's New + + Multi-cab scheduler. + + + + Bug Fixes + + Fixed usage bug with httplib2. + + + + Resources + + Get started using the Cloud Databases API to + create databases in the Getting Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in + the Cloud Databases + Developer Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also File + a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + v1.0.6, August 1, 2012 + We’re pleased to announce the availability of Rackspace Cloud + Databases to all US and UK Open Cloud customers. These + release notes cover the Unlimited Availability (UA) + release of Rackspace Cloud Databases. + + Product Features + + + Performance Optimized MySQL + Instances + + Provision MySQL database instances of varying + flavor (RAM) sizes without the need to maintain + and/or update MySQL. Optionally create users and + databases during instance creation for quick + deployment. + + + + + High-performance block level + storage + + Critical loads with high I/O demands are + performance-optimized and protected with local + RAID 10 and network RAID 10. Additionally, network + RAID provides synchronous replication of volumes + with automatic failover and load balancing across + available storage clusters. + + + + + Storage + Management + + Increase storage allocation seamlessly without + downtime as your dataset size increases. + + + + + Database Instance + Management + + Increase or decrease flavor (RAM) size as + database input/output (I/O) demands + increase. + + + + + Database + Management + + Create, list, and delete databases on your MySQL + database instance. + + + + + User Management + + Control database access by adding and removing + MySQL users. Optionally enable root user access + for additional control. + + + + What's New + + Performance improvements. + + + Documentation updates to the CDB Developer Guide. + + + + Bug Fixes + + Removed root user from the users list. + + + List users no longer shows root (or any other + specified users we define). + + + XML Pagination works for list instances. + + + Kernel openvz updates. + + + Added Environment field to usage payload + xml. + + + Separate the modify event into modify + volume/flavor types. + + + Added categories searching for atom hopper + billing. + + + + Known Issues / Limitations + + Users' database privileges are not modifiable. +  Users must be deleted and recreated to add + additional database privileges. + + + + Resources + + Get started using the Cloud Databases API to create + databases in the Getting + Started Guide at: http://docs.rackspace.com/api/. + + + Get reference information and examples in the Cloud Databases Developer + Guide at: http://docs.rackspace.com/api/. + + + Support for Cloud Databases is available for US and UK + customers 24x7x365 via phone, chat, or you may + also File a Ticket. + + + Please visit our Product Feedback Forum and let us know + what you think about Cloud Databases! + + + + + diff --git a/integration/apidocs/src/resources/images/Choose_CS_Image.png b/integration/apidocs/src/resources/images/Choose_CS_Image.png new file mode 100644 index 0000000000000000000000000000000000000000..b60a9df8a9851ae57acbbe71b1a4c07dab3640ee GIT binary patch literal 85201 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 literal 0 HcmV?d00001 diff --git a/integration/apidocs/src/resources/images/Choose_Image_CCP.png b/integration/apidocs/src/resources/images/Choose_Image_CCP.png new file mode 100644 index 0000000000000000000000000000000000000000..14a555722bf63862af4d34c468a6044c715ec955 GIT binary patch literal 21253 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 literal 0 HcmV?d00001 diff --git a/integration/apidocs/src/resources/images/Choose_Image_CCP.tiff b/integration/apidocs/src/resources/images/Choose_Image_CCP.tiff new file mode 100644 index 0000000000000000000000000000000000000000..79d5a45faf0449ac14eb4e06830d6519fb2105a8 GIT binary patch literal 26244 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 literal 0 HcmV?d00001 diff --git a/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png b/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7a00ae001b8577ac97b0ab662c2568a9e1e0c272 GIT binary patch literal 66336 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

>#oVFRw{=qN395!DyO^$9{a1O$DSC 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

ccaqml5tzMi@b_yO*i`#F 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 + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/images/Create_CS.png b/integration/apidocs/src/resources/images/Create_CS.png new file mode 100644 index 0000000000000000000000000000000000000000..32c291b347a029e498d2c13c78404ebf22c87860 GIT binary patch literal 31902 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 r(EeyiW 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≻GP%p=MJ_=0qP>j^W%z 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

+ This is the main index XML Schema document + for DBaaS API Schema Types Version 1.0. +

+ + + + + + +

A MySQL User.

+
+
+
+ + + +

A MySQL User.

+
+
+
+ + + +

A Database Instance.

+
+
+
+ + + +

Database Instances.

+
+
+
+ + + +

Settings such as memory used to create a database instance.

+
+
+
+ + + +

A list of flavors.

+
+
+
+ + + +

A Database.

+
+
+
+ + + +

List of Databases.

+
+
+
+ + + +

Restart the Database

+
+
+
+ + + +

Resize an Instance

+
+
+
+ + + + + + + +

A list of database user names.

+
+
+
+
+
+ + + + + + +

A list of databases.

+
+
+
+
+ + + +

The name for the user.

+
+
+
+ + + +

The password for the user.

+
+
+
+ + + +

The status of the user.

+
+
+
+
+ + + + + + +

A list of database instances.

+
+
+
+
+
+ + + + + + +

A list of optional databases.

+
+
+
+ + + +

A list of optional users.

+
+
+
+ + + +

The links for the type of instance.

+
+
+
+ + + +

The volume attached to the instance.

+
+
+
+
+ + + +

A unique database instance id.

+
+
+
+ + + +

Date/Time the instance was created.

+
+
+
+ + + +

Date/Time the instance was last updated.

+
+
+
+ + + +

The name for the instance.

+
+
+
+ + + +

The flavor reference of the instance.

+
+
+
+ + + +

The status of the instance.

+
+
+
+ + + +

The hostname attached to the instance.

+
+
+
+ + + +

Whether or not root is enabled for the instance.

+
+
+
+
+ + + + + +

The type of link.

+
+
+
+ + + +

The URL.

+
+
+
+ + + +

The type of link.

+
+
+
+
+ + + + + + +

A list of flavors.

+
+
+
+
+
+ + + + + + +

A list of links.

+
+
+
+
+ + + +

A unique flavor id.

+
+
+
+ + + +

The name for the instance.

+
+
+
+ + + +

The RAM in megabytes.

+
+
+
+
+ + + + + + +

A list of links.

+
+
+
+
+
+ + + + + + +

A list of databases.

+
+
+
+
+
+ + + + + +

The Database character set.

+
+
+
+ + + +

The name for the instance.

+
+
+
+ + + +

The Collation type of the database.

+
+
+
+
+ + + + + +

Whether or not root is enabled for the given instance.

+
+
+
+
+ + + + + +

Size of the volume in GBs.

+
+
+
+ + + +

Used space on the attached volume in GBs.

+
+
+
+
+ + + + + +

A unique flavor id.

+
+
+
+
+ + + + + + +

The volume attached to the instance.

+
+
+
+ + + +

New flavorRef to size the instance.

+
+
+
+
+
+ + + + + +

The instance status.

+
+
+ + + + +

Healthy status.

+
+
+
+ + + +

Down status.

+
+
+
+ + + +

unavailable status.

+
+
+
+
+
+ + + + +

The mysql user status.

+
+
+ + + + +

Enabled status.

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + +

+ A human readable message that is appropriate for display + to the end user. +

+
+
+
+ + + +

+ The optional <details> element may contain useful + information for tracking down errors (e.g a stack + trace). This information may or may not be appropriate + for display to an end user. +

+
+
+
+ +
+ + + +

+ The HTTP status code associated with the current fault. +

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ An optional dateTime denoting when an operation should + be retried. +

+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/xsd/management.wadl b/integration/xsd/management.wadl new file mode 100644 index 0000000000..2271219252 --- /dev/null +++ b/integration/xsd/management.wadl @@ -0,0 +1,625 @@ + + + %common; +]> + + + + + + + + + + + + + + The host ID for the specified host. + + + + + + + + + + + + + The instance ID for the specified database instance. + + + + + + + + + + + + + + + + + + + + + + + The account ID for the specified account. + + + + + + + + + + + + Lists all of the accounts owning at least one instance that is not deleted. + + This operation returns a list of all the accounts across all locations that own at least one instance that has not been deleted, that is, all accounts of current users. + + + + + The following examples show the List All Active Accounts requests: + + + + + + + + + + + + + The following examples show the List All Active Accounts responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists all of the compute hosts. + + Reviewer: in the DNS project, we have been requested by the customer to provide a table of parameters (should be pulled automatically if parms defined in wadl) and a table of attributes (for calls that allow detailed info about the object created to be specified. No doubt our DB customers will want this too. + Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description. + This operation returns a list of all the hosts from the database that are running the binary 'nova-compute'. + + + + + The following examples show the List All Compute Hosts requests: + + + + + + + + + + + + + The following examples show the List All Compute Hosts responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists all of the instances for the specified host. + + Reviewer: in the DNS project, we have been requested by the customer to provide a table of parameters (should be pulled automatically if parms defined in wadl) and a table of attributes (for calls that allow detailed info about the object created to be specified. No doubt our DB customers will want this too. + Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description. + This operation returns the following information: + + + host name + + + percent of RAM used + + + total RAM on host + + + used RAM on host + + + a list of the compute instances running on the given host + + + If the host name does not exist, a 404 not found error is returned. + + + + + The following examples show the List All Instances for a Host requests: + + + + + + + + + + + + + The following examples show the List All Instances for a Host responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists all of the database instances, optionally filtered by deleted status. + + + + + + This query parameter specifies whether or not to list deleted instances. + If true, deleted instances are listed. If false, deleted instances are not listed. + + + + + + The following examples show the List All Instances requests: + + + + + + + + + + + + + The following examples show the List All Instances response: + + + + + + + By default, all instances (both deleted and not deleted) are displayed. + Use the deleted query parameter to list only the deleted or not deleted instances. + + + + &commonFaults; + &getFaults; + + + + + + Returns detailed information for the specified Database Instance. + + Reviewer: in the DNS project, we have been requested by the customer to provide a table of parameters (should be pulled automatically if parms defined in wadl) and a table of attributes (for calls that allow detailed info about the object created to be specified. No doubt our DB customers will want this too. + Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description. + This operation returns detailed information about the status and details for the specified database instance. + + + + + The following examples show the List Database Instance Status and Details requests: + + + + + + + + + + + + + The following examples show the List Database Instance Status and Details responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Perform a reboot of the instance. + This operation will reboot the underlying OS along with the mysql instance. + This operation returns a 202 Accepted response. + + + + + The following examples show the Reboot Instance requests: + + + + + + + + + + + + + The following examples show the Reboot Instance responses: + + + + + + + + + + &commonFaults; + &getFaults; + &postPutFaults; + + + + + Update all instances on a specified host. + This operation will call on all guest agents in a specified host to install new versions of itself. + This operation returns a 202 Accepted response. + + + + + The following examples show the Update all Instances on Host requests: + + + + + + + + + + + + + The following examples show the Update all Instances on Host responses: + + + + + + + + + + &commonFaults; + &getFaults; + &postPutFaults; + + + + + + Returns detailed information for the storage device. + + Reviewer: in the DNS project, we have been requested by the customer to provide a table of parameters (should be pulled automatically if parms defined in wadl) and a table of attributes (for calls that allow detailed info about the object created to be specified. No doubt our DB customers will want this too. + Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description. + This operation returns detailed information about the details for the storage device. + + + + + The following examples show the List Storage Device Details requests: + + + + + + + + + + + + + The following examples show the List Storage Device Details responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Returns detailed information for the specified account. + + Reviewer: This section needs examples. + This operation returns: + + + account name + + + id + + + list of hosts that have the id + + + instance name + + + status of each instance on the hosts + + + + + + + The following examples show the List Account Details requests: + + + + + + + + + + + + + The following examples show the List Account Details responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Gets the root flag details for the specified instance. + + Reviewer: Need the description for the summary table above and the detailed description for the next paragraph. + This operation shows the root flag details for the specified instance. + + + + + The following examples show the Get Root Details requests: + + + + + + + + + + + + + The following examples show the Get Root Details responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Get the instance agent and other instance diagnostics info. + + This operation provides the following info for the guest agent managing the specified instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Return Attributes for Get Diagnostics Info
NameDescription
versionThe version of the guest installed on the instance.
fdSizeNumber of file descriptor slots currently allocated.
vmSizeVirtual memory size.
vmPeakPeak virtual memory size.
vmRssResident set size.
vmHwmPeak resident set size ("high water mark").
threadsNumber of threads in process containing this thread.
+
+ + + + The following examples show the Get Diagnostics Info requests: + + + + + + + + + + + + + The following examples show the Get Diagnostics Info requests: + + + + + + + + + + &commonFaults; + &getFaults; +
+ + + + + Get the instance hardware info from the guest agent. + + This operation provides the following info from the guest agent managing the specified instance. + + + + + + + + + + + + + + + + + + +
Return Attributes for Get Hardware Info
NameDescription
mem_totalTotal memory an instance sees.
num_cpusNumber of CPUs an instance sees.
+
+ + + + The following examples show the Get Hardware Info requests: + + + + + + + + + + + + + The following examples show the Get Hardware Info responses: + + + + + + + + + + &commonFaults; + &getFaults; +
+
diff --git a/integration/xsd/management.xsd b/integration/xsd/management.xsd new file mode 100644 index 0000000000..4b69123f5b --- /dev/null +++ b/integration/xsd/management.xsd @@ -0,0 +1,398 @@ + + + + + + + + Dbaas Management + + +

This schema defines entity contains entities related to the dbaas Management API.

+
+
+ + + + +

Information about a Host.

+
+
+
+ + + +

List of Hosts.

+
+
+
+ + + + +

Information about storage devices.

+
+
+
+ + + + +

Information about Account.

+
+
+
+ + + +

Details of a configuration item

+
+
+
+ + + +

List of Configs.

+
+
+
+ + + + +

Details of a configuration item

+
+
+
+ + + + +

Diagnostic details of a guest.

+
+
+
+ + + + +

Reboot an Instance

+
+
+
+ + + + + + + +

A list of Host names.

+
+
+
+
+
+ + + + + + +

Timestamp of the first time MySQL root was enabled for the instance.

+
+
+
+ + + +

ID of the first user to enable MySQL root for the instance.

+
+
+
+
+
+ + + + + +

A list of database instances, with additional management information.

+
+
+ + +
+
+ + + + + + + + + + + + + + + + +

A single database instance, with some additional management information.

+
+ + + + + + + + + +
+
+
+ + + + + +

A list of database index instances, with some additional management information.

+
+ + +
+
+
+ + + + + +

A list of database instances on the Host.

+
+
+ +
+ + + +

The name of the Host.

+
+
+
+ + + +

The percent of the used RAM on the Host.

+
+
+
+ + + +

The total amount of RAM on the Host.

+
+
+
+ + + +

The used amount of RAM on the Host.

+
+
+
+
+ + + + + + +

A storage device on the Host.

+
+
+
+
+
+ + + + + +

The id of the storage device.

+
+
+
+ + + +

The name of the storage device.

+
+
+
+ + + +

The type of the storage device.

+
+
+
+ + + +

The available size of the storage device.

+
+
+
+ + + +

The total size of the storage device.

+
+
+
+
+ + + + + +

The name of the account.

+
+
+
+ + + + +

List of hosts that the Account has instances running on.

+
+
+
+
+
+ + + + + + +

A configuration entry

+
+
+
+
+
+ + + + + +

The configuration entry key

+
+
+
+ + + +

The value of the configuration entry

+
+
+
+ + + +

The description of the configuration entry

+
+
+
+
+ + + + + +

The instance id of that is checked by the call.

+
+
+
+ + + +

The date timestamp that a user enabled root on the instance.

+
+
+
+ + + +

The user that enabled root on the instance.

+
+
+
+
+ + + + + +

The version of the guest installed on the instance.

+
+
+
+ + + +

Number of file descriptor slots currently allocated.

+
+
+
+ + + +

Virtual memory size.

+
+
+
+ + + +

Peak virtual memory size.

+
+
+
+ + + +

Resident set size.

+
+
+
+ + + +

Peak resident set size ("high water mark").

+
+
+
+ + + +

Number of threads in process containing this thread.

+
+
+
+
+ +
diff --git a/tox.ini b/tox.ini index 85d5f69f2c..ec32ad81cf 100644 --- a/tox.ini +++ b/tox.ini @@ -73,7 +73,7 @@ show-source = True # The rest of the ignores are TODOs. ignore = F821,H237,H301,H404,H405,H501 builtins = _ -exclude=.venv,.tox,.git,dist,doc,*egg,tools,etc,build,*.po,*.pot +exclude=.venv,.tox,.git,dist,doc,*egg,tools,etc,build,*.po,*.pot,integration filename=*.py,trove-* [testenv:api-ref]

;Uj|-_yDYNly6uaZn&G#LU^xPd|8#@Uwq`SV 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-auth.json b/integration/apidocs/src/resources/samples/db-auth.json new file mode 100644 index 0000000000..583b584c0b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-auth.json @@ -0,0 +1,72 @@ +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 477 +Date: Thu, 12 Apr 2012 18:45:13 GMT + +{ + "auth": { + "serviceCatalog": { + "cloudDNS": [ + { + "publicURL": "https://dns.api.rackspacecloud.com/v1.0/1234" + } + ], + "cloudDatabases": [ + { + "publicURL": "https://dfw.databases.api.rackspacecloud.com/v1.0/1234", + "region": "DFW" + }, + { + "publicURL": "https://ord.databases.api.rackspacecloud.com/v1.0/1234", + "region": "ORD" + } + ], + "cloudFiles": [ + { + "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/ MossoCloudFS_aaaa-bbbbb-cccc-ddddd ", + "publicURL": "https://storage101.dfw1.clouddrive.com/v1/ MossoCloudFS_aaaa-bbbbb-cccc-ddddd ", + "region": "DFW", + "v1Default": true + } + ], + "cloudFilesCDN": [ + { + "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_aaaa-bbbbb-cccc-ddddd", + "region": "DFW", + "v1Default": true + } + ], + "cloudLoadBalancers": [ + { + "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1234", + "region": "ORD" + }, + { + "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234", + "region": "DFW" + } + ], + "cloudMonitoring": [ + { + "publicURL": "https://monitoring.api.rackspacecloud.com/v1.0/1234" + } + ], + "cloudServers": [ + { + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1234", + "v1Default": true + } + ], + "cloudServersOpenStack": [ + { + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/1234", + "region": "DFW" + } + ] + }, + "token": { + "expires": "2012-04-12T13:15:52.000-05:00", + "id": "aaaaa-bbbbbb-cccccc-ddddd" + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-auth.xml b/integration/apidocs/src/resources/samples/db-auth.xml new file mode 100644 index 0000000000..de97e59a0b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-auth.xml @@ -0,0 +1,50 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=UTF-8 +Content-Length: 477 +Date: Thu, 12 Apr 2012 18:50:20 GMT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-check-root-user-request.json b/integration/apidocs/src/resources/samples/db-check-root-user-request.json new file mode 100644 index 0000000000..79a6d18e1e --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-check-root-user-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/root HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-check-root-user-request.xml b/integration/apidocs/src/resources/samples/db-check-root-user-request.xml new file mode 100644 index 0000000000..46f2cf2918 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-check-root-user-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/root HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-check-root-user-response.json b/integration/apidocs/src/resources/samples/db-check-root-user-response.json new file mode 100644 index 0000000000..3e121e4e76 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-check-root-user-response.json @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 21 +Date: Wed, 25 Jan 2012 21:58:13 GMT + +{ + "rootEnabled": true +} diff --git a/integration/apidocs/src/resources/samples/db-check-root-user-response.xml b/integration/apidocs/src/resources/samples/db-check-root-user-response.xml new file mode 100644 index 0000000000..fd75a745f2 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-check-root-user-response.xml @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 90 +Date: Wed, 25 Jan 2012 21:58:14 GMT + +True diff --git a/integration/apidocs/src/resources/samples/db-create-database-instance-response.json b/integration/apidocs/src/resources/samples/db-create-database-instance-response.json new file mode 100644 index 0000000000..78b1743243 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-database-instance-response.json @@ -0,0 +1,41 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 632 +Date: Mon, 28 Nov 2011 21:35:41 GMT + +{ + "instance": { + "created": "2011-11-03T15:55:26Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostname": "d735f61b985bb003a61dc72948dbf4e7174da12c.rackspaceclouddb.com", + "id": "00ff11ee-22dd-33cc-44bb-55aa66007799", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/00ff11ee-22dd-33cc-44bb-55aa66007799", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/00ff11ee-22dd-33cc-44bb-55aa66007799", + "rel": "bookmark" + } + ], + "name": "myrackinstance", + "status": "BUILD", + "updated": "2011-11-03T15:55:27Z", + "volume": { + "size": "2" + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-create-database-instance-response.xml b/integration/apidocs/src/resources/samples/db-create-database-instance-response.xml new file mode 100644 index 0000000000..9f0cc3550f --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-database-instance-response.xml @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 639 +Date: Mon, 28 Nov 2011 16:19:34 GMT + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-create-databases-request.json b/integration/apidocs/src/resources/samples/db-create-databases-request.json new file mode 100644 index 0000000000..1ef2bab6b5 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-databases-request.json @@ -0,0 +1,19 @@ +POST /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + +{ + "databases": [ + { + "character_set": "utf8", + "collate": "utf8_general_ci", + "name": "testingdb" + }, + { + "name": "sampledb" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-create-databases-request.xml b/integration/apidocs/src/resources/samples/db-create-databases-request.xml new file mode 100644 index 0000000000..a8e584d0d1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-databases-request.xml @@ -0,0 +1,13 @@ +POST /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-create-databases-response.json b/integration/apidocs/src/resources/samples/db-create-databases-response.json new file mode 100644 index 0000000000..6a83f7fcb1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-databases-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:18 GMT diff --git a/integration/apidocs/src/resources/samples/db-create-databases-response.xml b/integration/apidocs/src/resources/samples/db-create-databases-response.xml new file mode 100644 index 0000000000..3a2ce5f652 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-databases-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:18 GMT diff --git a/integration/apidocs/src/resources/samples/db-create-instance-request.json b/integration/apidocs/src/resources/samples/db-create-instance-request.json new file mode 100644 index 0000000000..cbcfc90dc9 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-instance-request.json @@ -0,0 +1,37 @@ +POST /v1.0/1234/instances HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: d6cafa5b-e0c7-4ab8-948e-7c95f2acd031 +Accept: application/json +Content-Type: application/json + +{ + "instance": { + "databases": [ + { + "character_set": "utf8", + "collate": "utf8_general_ci", + "name": "sampledb" + }, + { + "name": "nextround" + } + ], + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "name": "json_rack_instance", + "users": [ + { + "databases": [ + { + "name": "sampledb" + } + ], + "name": "demouser", + "password": "demopassword" + } + ], + "volume": { + "size": 2 + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-create-instance-request.xml b/integration/apidocs/src/resources/samples/db-create-instance-request.xml new file mode 100644 index 0000000000..7f19a4e748 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-instance-request.xml @@ -0,0 +1,23 @@ +POST /v1.0/1234/instances HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: d6cafa5b-e0c7-4ab8-948e-7c95f2acd031 +Accept: application/xml +Content-Type: application/xml + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-create-instance-response.json b/integration/apidocs/src/resources/samples/db-create-instance-response.json new file mode 100644 index 0000000000..f5383ce2fd --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-instance-response.json @@ -0,0 +1,41 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 636 +Date: Wed, 25 Jan 2012 21:53:10 GMT + +{ + "instance": { + "created": "2012-01-25T21:53:09Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com", + "id": "dea5a2f7-3ec7-4496-adab-0abb5a42d635", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/dea5a2f7-3ec7-4496-adab-0abb5a42d635", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/dea5a2f7-3ec7-4496-adab-0abb5a42d635", + "rel": "bookmark" + } + ], + "name": "json_rack_instance", + "status": "BUILD", + "updated": "2012-01-25T21:53:10Z", + "volume": { + "size": 2 + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-create-instance-response.xml b/integration/apidocs/src/resources/samples/db-create-instance-response.xml new file mode 100644 index 0000000000..8ed9e306ec --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-instance-response.xml @@ -0,0 +1,19 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 748 +Date: Wed, 25 Jan 2012 21:53:23 GMT + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-create-users-request.json b/integration/apidocs/src/resources/samples/db-create-users-request.json new file mode 100644 index 0000000000..022a12b2d1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-users-request.json @@ -0,0 +1,32 @@ +POST /v1.0/1234/instances/1c59bdb8-03b6-4079-a7db-ba92d23a98b3/users HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: bb64d788-2dec-4a6b-a670-7151d108cacf +Accept: application/json +Content-Type: application/json + +{ + "users": [ + { + "databases": [ + { + "name": "databaseA" + } + ], + "name": "dbuser3", + "password": "password" + }, + { + "databases": [ + { + "name": "databaseB" + }, + { + "name": "databaseC" + } + ], + "name": "dbuser4", + "password": "password" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-create-users-request.xml b/integration/apidocs/src/resources/samples/db-create-users-request.xml new file mode 100644 index 0000000000..2f24166560 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-users-request.xml @@ -0,0 +1,21 @@ +POST /v1.0/1234/instances/1f19ee9e-6124-46b3-a6c6-fc46a5756814/users HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: bb64d788-2dec-4a6b-a670-7151d108cacf +Accept: application/xml +Content-Type: application/xml + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-create-users-response.json b/integration/apidocs/src/resources/samples/db-create-users-response.json new file mode 100644 index 0000000000..6a83f7fcb1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-users-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:18 GMT diff --git a/integration/apidocs/src/resources/samples/db-create-users-response.xml b/integration/apidocs/src/resources/samples/db-create-users-response.xml new file mode 100644 index 0000000000..9b6fa1dc90 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-create-users-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:19 GMT diff --git a/integration/apidocs/src/resources/samples/db-credentials-20.json b/integration/apidocs/src/resources/samples/db-credentials-20.json new file mode 100644 index 0000000000..1ae90cd4a3 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-credentials-20.json @@ -0,0 +1,18 @@ +POST /v2.0/tokens HTTP/1.1 +User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6 +Host: identity.api.rackspacecloud.com +Accept: application/json +Content-Type: application/json +Content-Length: 54 + +{ + "auth": + { + "RAX-KSKEY:apiKeyCredentials": + { + "username": "jsmith", + "apiKey": "aaaaa-bbbbb-ccccc-12345678" + } + } +} + diff --git a/integration/apidocs/src/resources/samples/db-credentials-20.xml b/integration/apidocs/src/resources/samples/db-credentials-20.xml new file mode 100644 index 0000000000..cd3298b4d0 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-credentials-20.xml @@ -0,0 +1,14 @@ +POST /v2.0/tokens HTTP/1.1 +User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6 +Host: identity.api.rackspacecloud.com +Accept: application/xml +Content-Type: application/xml +Content-Length: 88 + + + + + diff --git a/integration/apidocs/src/resources/samples/db-credentials.json b/integration/apidocs/src/resources/samples/db-credentials.json new file mode 100644 index 0000000000..bf15936f9c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-credentials.json @@ -0,0 +1,13 @@ +POST /v1.1/auth HTTP/1.1 +User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6 +Host: auth.api.rackspacecloud.com +Accept: application/json +Content-Type: application/json +Content-Length: 54 + +{ + "credentials" : { + "username" : "hub_cap", + "key" : "a86850deb2742ec3cb41518e26aa2d89" + } +} diff --git a/integration/apidocs/src/resources/samples/db-credentials.xml b/integration/apidocs/src/resources/samples/db-credentials.xml new file mode 100644 index 0000000000..ae7f8971e1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-credentials.xml @@ -0,0 +1,11 @@ +POST /v1.1/auth HTTP/1.1 +User-Agent: curl/7.21.0 (x86_64-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.15 libssh2/1.2.6 +Host: auth.api.rackspacecloud.com +Accept: application/xml +Content-Type: application/xml +Content-Length: 88 + + + diff --git a/integration/apidocs/src/resources/samples/db-delete-databases-request.json b/integration/apidocs/src/resources/samples/db-delete-databases-request.json new file mode 100644 index 0000000000..eb958e9549 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-databases-request.json @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases/exampledb HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-delete-databases-request.xml b/integration/apidocs/src/resources/samples/db-delete-databases-request.xml new file mode 100644 index 0000000000..94cfc0bd96 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-databases-request.xml @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases/exampledb HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-delete-databases-response.json b/integration/apidocs/src/resources/samples/db-delete-databases-response.json new file mode 100644 index 0000000000..6a83f7fcb1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-databases-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:18 GMT diff --git a/integration/apidocs/src/resources/samples/db-delete-databases-response.xml b/integration/apidocs/src/resources/samples/db-delete-databases-response.xml new file mode 100644 index 0000000000..3a2ce5f652 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-databases-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:18 GMT diff --git a/integration/apidocs/src/resources/samples/db-delete-instance-request.json b/integration/apidocs/src/resources/samples/db-delete-instance-request.json new file mode 100644 index 0000000000..fdf11e6494 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-instance-request.json @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-delete-instance-request.xml b/integration/apidocs/src/resources/samples/db-delete-instance-request.xml new file mode 100644 index 0000000000..b960fbf927 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-instance-request.xml @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-delete-instance-response.json b/integration/apidocs/src/resources/samples/db-delete-instance-response.json new file mode 100644 index 0000000000..a1ecb28dac --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-instance-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:15:51 GMT diff --git a/integration/apidocs/src/resources/samples/db-delete-instance-response.xml b/integration/apidocs/src/resources/samples/db-delete-instance-response.xml new file mode 100644 index 0000000000..3dcda464bf --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-instance-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:15:51 GMT diff --git a/integration/apidocs/src/resources/samples/db-delete-users-request.json b/integration/apidocs/src/resources/samples/db-delete-users-request.json new file mode 100644 index 0000000000..76ba9a600d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-users-request.json @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/users/testuser HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-delete-users-request.xml b/integration/apidocs/src/resources/samples/db-delete-users-request.xml new file mode 100644 index 0000000000..3be87be7ea --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-users-request.xml @@ -0,0 +1,8 @@ +DELETE /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/users/testuser HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-delete-users-response.json b/integration/apidocs/src/resources/samples/db-delete-users-response.json new file mode 100644 index 0000000000..e5f311463d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-users-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:19 GMT diff --git a/integration/apidocs/src/resources/samples/db-delete-users-response.xml b/integration/apidocs/src/resources/samples/db-delete-users-response.xml new file mode 100644 index 0000000000..9b6fa1dc90 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-delete-users-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:19 GMT diff --git a/integration/apidocs/src/resources/samples/db-enable-root-user-request.json b/integration/apidocs/src/resources/samples/db-enable-root-user-request.json new file mode 100644 index 0000000000..85ba63792a --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-enable-root-user-request.json @@ -0,0 +1,8 @@ +POST /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/root HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-enable-root-user-request.xml b/integration/apidocs/src/resources/samples/db-enable-root-user-request.xml new file mode 100644 index 0000000000..66485f815f --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-enable-root-user-request.xml @@ -0,0 +1,8 @@ +POST /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/root HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-enable-root-user-response.json b/integration/apidocs/src/resources/samples/db-enable-root-user-response.json new file mode 100644 index 0000000000..a7868ee712 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-enable-root-user-response.json @@ -0,0 +1,11 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 78 +Date: Wed, 25 Jan 2012 21:58:11 GMT + +{ + "user": { + "name": "root", + "password": "d4311cb1-d912-45be-8517-e8a46f54df66" + } +} diff --git a/integration/apidocs/src/resources/samples/db-enable-root-user-response.xml b/integration/apidocs/src/resources/samples/db-enable-root-user-response.xml new file mode 100644 index 0000000000..c2f99a9784 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-enable-root-user-response.xml @@ -0,0 +1,7 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 120 +Date: Wed, 25 Jan 2012 21:58:12 GMT + + + diff --git a/integration/apidocs/src/resources/samples/db-faults-badRequest.json b/integration/apidocs/src/resources/samples/db-faults-badRequest.json new file mode 100644 index 0000000000..fcb4571afc --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-badRequest.json @@ -0,0 +1,11 @@ +HTTP/1.1 400 None +Content-Length: 120 +Content-Type: application/json; charset=UTF-8 +Date: Tue, 29 Nov 2011 00:33:48 GMT + +{ + "badRequest": { + "code": 400, + "message": "Volume 'size' needs to be a positive integer value, -1.0 cannot be accepted." + } +} diff --git a/integration/apidocs/src/resources/samples/db-faults-badRequest.xml b/integration/apidocs/src/resources/samples/db-faults-badRequest.xml new file mode 100644 index 0000000000..6067a4d9b9 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-badRequest.xml @@ -0,0 +1,10 @@ +HTTP/1.1 400 None +Content-Type: application/xml +Content-Length: 121 +Date: Mon, 28 Nov 2011 18:19:37 GMT + + + + Volume 'size' needs to be a positive integer value, -1.0 cannot be accepted. + + diff --git a/integration/apidocs/src/resources/samples/db-faults-instanceFault.json b/integration/apidocs/src/resources/samples/db-faults-instanceFault.json new file mode 100644 index 0000000000..97299fc4fd --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-instanceFault.json @@ -0,0 +1,11 @@ +HTTP/1.1 500 Internal Server Error +Content-Length: 120 +Content-Type: application/json; charset=UTF-8 +Date: Tue, 29 Nov 2011 00:33:48 GMT + +{ + "instanceFault": { + "code": 500, + "message": "The server has either erred or is incapable of performing the requested operation." + } +} diff --git a/integration/apidocs/src/resources/samples/db-faults-instanceFault.xml b/integration/apidocs/src/resources/samples/db-faults-instanceFault.xml new file mode 100644 index 0000000000..4864b0b483 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-instanceFault.xml @@ -0,0 +1,10 @@ +HTTP/1.1 500 Internal Server Error +Content-Type: application/xml +Content-Length: 121 +Date: Mon, 28 Nov 2011 18:19:37 GMT + + + + The server has either erred or is incapable of performing the requested operation. + + diff --git a/integration/apidocs/src/resources/samples/db-faults-itemNotFound.json b/integration/apidocs/src/resources/samples/db-faults-itemNotFound.json new file mode 100644 index 0000000000..866329b5e5 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-itemNotFound.json @@ -0,0 +1,11 @@ +HTTP/1.1 404 Not Found +Content-Length: 78 +Content-Type: application/json; charset=UTF-8 +Date: Tue, 29 Nov 2011 00:35:24 GMT + +{ + "itemNotFound": { + "code": 404, + "message": "The resource could not be found." + } +} diff --git a/integration/apidocs/src/resources/samples/db-faults-itemNotFound.xml b/integration/apidocs/src/resources/samples/db-faults-itemNotFound.xml new file mode 100644 index 0000000000..806bc2c7cb --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-faults-itemNotFound.xml @@ -0,0 +1,10 @@ +HTTP/1.1 404 Not Found +Content-Length: 147 +Content-Type: application/xml; charset=UTF-8 +Date: Mon, 28 Nov 2011 19:50:15 GMT + + + + The resource could not be found. + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-by-id-request.json b/integration/apidocs/src/resources/samples/db-flavors-by-id-request.json new file mode 100644 index 0000000000..1944f3ae66 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-by-id-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/flavors/1 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-by-id-request.xml b/integration/apidocs/src/resources/samples/db-flavors-by-id-request.xml new file mode 100644 index 0000000000..d2beb31de8 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-by-id-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/flavors/1 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-by-id-response.json b/integration/apidocs/src/resources/samples/db-flavors-by-id-response.json new file mode 100644 index 0000000000..685d02ca41 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-by-id-response.json @@ -0,0 +1,22 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 209 +Date: Wed, 25 Jan 2012 21:53:05 GMT + +{ + "flavor": { + "id": 1, + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ], + "name": "m1.tiny", + "ram": 512 + } +} diff --git a/integration/apidocs/src/resources/samples/db-flavors-by-id-response.xml b/integration/apidocs/src/resources/samples/db-flavors-by-id-response.xml new file mode 100644 index 0000000000..d1d5fec0a8 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-by-id-response.xml @@ -0,0 +1,11 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 284 +Date: Wed, 25 Jan 2012 21:53:05 GMT + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-request.json b/integration/apidocs/src/resources/samples/db-flavors-request.json new file mode 100644 index 0000000000..0d6817a504 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/flavors HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-request.xml b/integration/apidocs/src/resources/samples/db-flavors-request.xml new file mode 100644 index 0000000000..7981dd7577 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/flavors HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-flavors-response.json b/integration/apidocs/src/resources/samples/db-flavors-response.json new file mode 100644 index 0000000000..901d65ab6c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-response.json @@ -0,0 +1,69 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 1768 +Date: Tue, 19 Jun 2012 19:52:45 GMT + +{ + "flavors": [ + { + "id": 1, + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ], + "name": "m1.tiny", + "ram": 512 + }, + { + "id": 2, + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/2", + "rel": "bookmark" + } + ], + "name": "m1.small", + "ram": 1024 + }, + { + "id": 3, + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/3", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/3", + "rel": "bookmark" + } + ], + "name": "m1.medium", + "ram": 2048 + }, + { + "id": 4, + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/4", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/4", + "rel": "bookmark" + } + ], + "name": "m1.large", + "ram": 4096 + }, + ] +} diff --git a/integration/apidocs/src/resources/samples/db-flavors-response.xml b/integration/apidocs/src/resources/samples/db-flavors-response.xml new file mode 100644 index 0000000000..516da13703 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-flavors-response.xml @@ -0,0 +1,32 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 2300 +Date: Tue, 19 Jun 2012 19:52:45 GMT + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.json b/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.json new file mode 100644 index 0000000000..69f69b4107 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.json @@ -0,0 +1,41 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 756 +Date: Thu, 05 Apr 2012 16:48:44 GMT + +{ + "instance": { + "status": "BUILD", + "updated": "2012-04-05T16:48:44Z", + "name": "myrackinstance", + "links": [ + { + "href": "http://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/d379ba5c-9a1f-4aa9-9a17-afe237d04c65", + "rel": "self" + }, + { + "href": "http://ord.databases.api.rackspacecloud.com/instances/d379ba5c-9a1f-4aa9-9a17-afe237d04c65", + "rel": "bookmark" + } + ], + "created": "2012-04-05T16:48:44Z", + "hostname": "ca9fa2985e47b351915c75f1a8e95d0729068892.rackspaceclouddb.com", + "volume": { + "size": 2 + }, + "flavor": { + "id": "1", + "links": [ + { + "href": "http://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { "href": "http://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "id": "d379ba5c-9a1f-4aa9-9a17-afe237d04c65" + } +} + diff --git a/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.xml b/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.xml new file mode 100644 index 0000000000..967a5dd8e6 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-gs-create-database-instance-response.xml @@ -0,0 +1,22 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 870 +Date: Thu, 05 Apr 2012 16:17:29 GMT + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-guest-update-request.json b/integration/apidocs/src/resources/samples/db-guest-update-request.json new file mode 100644 index 0000000000..9d03d2bad6 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-guest-update-request.json @@ -0,0 +1,10 @@ +POST /v1.0/1234/mgmt/instances/3b16f448-4387-44f5-b464-45111dd7a934/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 570ef429-78eb-43ec-ad69-de9456cd88c9 +Accept: application/json +Content-Type: application/json + +{ + "update": {} +} diff --git a/integration/apidocs/src/resources/samples/db-guest-update-request.xml b/integration/apidocs/src/resources/samples/db-guest-update-request.xml new file mode 100644 index 0000000000..7d64bdda04 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-guest-update-request.xml @@ -0,0 +1,10 @@ +POST /v1.0/1234/mgmt/instances/f3a9e303-1542-4663-9dca-bfdcd7abe095/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 570ef429-78eb-43ec-ad69-de9456cd88c9 +Accept: application/xml +Content-Type: application/xml + + + + diff --git a/integration/apidocs/src/resources/samples/db-guest-update-response.json b/integration/apidocs/src/resources/samples/db-guest-update-response.json new file mode 100644 index 0000000000..1b029992e4 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-guest-update-response.json @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Mon, 05 Mar 2012 23:48:46 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-guest-update-response.xml b/integration/apidocs/src/resources/samples/db-guest-update-response.xml new file mode 100644 index 0000000000..99f5908ace --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-guest-update-response.xml @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Mon, 05 Mar 2012 23:48:47 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-instance-reboot-request.json b/integration/apidocs/src/resources/samples/db-instance-reboot-request.json new file mode 100644 index 0000000000..892243ae58 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-reboot-request.json @@ -0,0 +1,10 @@ +POST /v1.0/1234/mgmt/instances/0617673d-a280-40c3-8151-29800a8f9d70/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 73d60021-96e1-4d06-843d-72d20fa5377b +Accept: application/json +Content-Type: application/json + +{ + "reboot": {} +} diff --git a/integration/apidocs/src/resources/samples/db-instance-reboot-request.xml b/integration/apidocs/src/resources/samples/db-instance-reboot-request.xml new file mode 100644 index 0000000000..b93a9f9eca --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-reboot-request.xml @@ -0,0 +1,10 @@ +POST /v1.0/1234/mgmt/instances/475baf88-53b8-4ebb-932a-a2518227a6c6/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 73d60021-96e1-4d06-843d-72d20fa5377b +Accept: application/xml +Content-Type: application/xml + + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-reboot-response.json b/integration/apidocs/src/resources/samples/db-instance-reboot-response.json new file mode 100644 index 0000000000..2308734cac --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-reboot-response.json @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Tue, 07 Feb 2012 23:56:52 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-instance-reboot-response.xml b/integration/apidocs/src/resources/samples/db-instance-reboot-response.xml new file mode 100644 index 0000000000..b8ad7430da --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-reboot-response.xml @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Tue, 07 Feb 2012 23:56:55 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.json b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.json new file mode 100644 index 0000000000..c80b418c34 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.json @@ -0,0 +1,12 @@ +POST /v1.0/1234/instances/aeb0b280-7c21-42cc-938f-275c0cc83c08/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: d45346d7-b313-41fb-a29b-e8f5f7c59de2 +Accept: application/json +Content-Type: application/json + +{ + "resize": { + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/3" + } +} diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.xml b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.xml new file mode 100644 index 0000000000..274ef34fdf --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-request.xml @@ -0,0 +1,10 @@ +POST /v1.0/1234/instances/51a5576d-c2cb-449b-b2be-176d65566ac3/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: d45346d7-b313-41fb-a29b-e8f5f7c59de2 +Accept: application/xml +Content-Type: application/xml + + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.json b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.json new file mode 100644 index 0000000000..fe14f4dd49 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:14:20 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.xml b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.xml new file mode 100644 index 0000000000..6271164b1f --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-flavor-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:14:20 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.json b/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.json new file mode 100644 index 0000000000..ec1ef6d5e1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.json @@ -0,0 +1,12 @@ +POST /v1.0/1234/instances/23a3d4fb-3731-497b-afd4-bf25bde2b5fc/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 2eeb3252-0164-40f5-8fb7-85df5faa2698 +Accept: application/json +Content-Type: application/json + +{ + "resize": { + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2" + } +} diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.xml b/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.xml new file mode 100644 index 0000000000..fd51dc85cd --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-instance-request.xml @@ -0,0 +1,12 @@ +POST /v1.0/1234/instances/5d891bb6-6c61-4b0a-8b85-26f4ee461c9d/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 2eeb3252-0164-40f5-8fb7-85df5faa2698 +Accept: application/xml +Content-Type: application/xml + + + + https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2 + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.json b/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.json new file mode 100644 index 0000000000..48a2e91a0a --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.json @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Mon, 06 Feb 2012 21:28:10 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.xml b/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.xml new file mode 100644 index 0000000000..549a5f80ed --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-instance-response.xml @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Mon, 06 Feb 2012 21:28:11 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.json b/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.json new file mode 100644 index 0000000000..7cec8fd88c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.json @@ -0,0 +1,14 @@ +POST /v1.0/1234/instances/23a3d4fb-3731-497b-afd4-bf25bde2b5fc/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 2eeb3252-0164-40f5-8fb7-85df5faa2698 +Accept: application/json +Content-Type: application/json + +{ + "resize": { + "volume": { + "size": 4 + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.xml b/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.xml new file mode 100644 index 0000000000..fe567b353e --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-volume-request.xml @@ -0,0 +1,12 @@ +POST /v1.0/1234/instances/5d891bb6-6c61-4b0a-8b85-26f4ee461c9d/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 2eeb3252-0164-40f5-8fb7-85df5faa2698 +Accept: application/xml +Content-Type: application/xml + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.json b/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.json new file mode 100644 index 0000000000..319bbadbbd --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:12:20 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.xml b/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.xml new file mode 100644 index 0000000000..e7c611031d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-resize-volume-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:12:20 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-restart-request.json b/integration/apidocs/src/resources/samples/db-instance-restart-request.json new file mode 100644 index 0000000000..ee27681a34 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-restart-request.json @@ -0,0 +1,10 @@ +POST /v1.0/1234/instances/13d940c4-70bb-4ff4-8866-6ee9ab5e5cae/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + +{ + "restart": {} +} diff --git a/integration/apidocs/src/resources/samples/db-instance-restart-request.xml b/integration/apidocs/src/resources/samples/db-instance-restart-request.xml new file mode 100644 index 0000000000..1f2165afc0 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-restart-request.xml @@ -0,0 +1,9 @@ +POST /v1.0/1234/instances/ab585bea-2b42-4f95-8655-4afdd5037966/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-restart-response.json b/integration/apidocs/src/resources/samples/db-instance-restart-response.json new file mode 100644 index 0000000000..e5f311463d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-restart-response.json @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/json +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:19 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-restart-response.xml b/integration/apidocs/src/resources/samples/db-instance-restart-response.xml new file mode 100644 index 0000000000..9b6fa1dc90 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-restart-response.xml @@ -0,0 +1,4 @@ +HTTP/1.1 202 Accepted +Content-Type: application/xml +Content-Length: 0 +Date: Wed, 27 Jun 2012 23:11:19 GMT diff --git a/integration/apidocs/src/resources/samples/db-instance-status-detail-request.json b/integration/apidocs/src/resources/samples/db-instance-status-detail-request.json new file mode 100644 index 0000000000..b76d404463 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-status-detail-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-instance-status-detail-request.xml b/integration/apidocs/src/resources/samples/db-instance-status-detail-request.xml new file mode 100644 index 0000000000..08090d3348 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-status-detail-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-instance-status-detail-response.json b/integration/apidocs/src/resources/samples/db-instance-status-detail-response.json new file mode 100644 index 0000000000..e4e2de53fa --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-status-detail-response.json @@ -0,0 +1,42 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 685 +Date: Wed, 28 Mar 2012 21:37:29 GMT + +{ + "instance": { + "created": "2012-03-28T21:31:02Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com", + "id": "2450c73f-7805-4afe-a42c-4094ab42666b", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/2450c73f-7805-4afe-a42c-4094ab42666b", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/2450c73f-7805-4afe-a42c-4094ab42666b", + "rel": "bookmark" + } + ], + "name": "xml_rack_instance", + "status": "ACTIVE", + "updated": "2012-03-28T21:34:25Z", + "volume": { + "size": 2, + "used": 0.124542236328125 + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-instance-status-detail-response.xml b/integration/apidocs/src/resources/samples/db-instance-status-detail-response.xml new file mode 100644 index 0000000000..2b839e2e2b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-status-detail-response.xml @@ -0,0 +1,20 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 796 +Date: Wed, 28 Mar 2012 21:37:31 GMT + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-update-guest-request.json b/integration/apidocs/src/resources/samples/db-instance-update-guest-request.json new file mode 100644 index 0000000000..c73ff9f200 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-update-guest-request.json @@ -0,0 +1,11 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + +{ + "update": { + } +} diff --git a/integration/apidocs/src/resources/samples/db-instance-update-guest-request.xml b/integration/apidocs/src/resources/samples/db-instance-update-guest-request.xml new file mode 100644 index 0000000000..a146f7887c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-update-guest-request.xml @@ -0,0 +1,10 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + + + diff --git a/integration/apidocs/src/resources/samples/db-instance-update-guest-response.json b/integration/apidocs/src/resources/samples/db-instance-update-guest-response.json new file mode 100644 index 0000000000..23bd9c1201 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-update-guest-response.json @@ -0,0 +1,8 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Wed, 29 Feb 2012 23:53:42 GMT + +202 Accepted + +The request is accepted for processing. diff --git a/integration/apidocs/src/resources/samples/db-instance-update-guest-response.xml b/integration/apidocs/src/resources/samples/db-instance-update-guest-response.xml new file mode 100644 index 0000000000..23bd9c1201 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instance-update-guest-response.xml @@ -0,0 +1,8 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Wed, 29 Feb 2012 23:53:42 GMT + +202 Accepted + +The request is accepted for processing. diff --git a/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.json b/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.json new file mode 100644 index 0000000000..4a55cfb7e0 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances?limit=2 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.xml b/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.xml new file mode 100644 index 0000000000..bd9a930c1e --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-pagination-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances?limit=2 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.json b/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.json new file mode 100644 index 0000000000..bdaf6b4f91 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.json @@ -0,0 +1,45 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 868 +Date: Tue, 19 Jun 2012 15:44:54 GMT + +{ + "instances": [ + { + "id": "13d940c4-70bb-4ff4-8866-6ee9ab5e5cae", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/13d940c4-70bb-4ff4-8866-6ee9ab5e5cae", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/13d940c4-70bb-4ff4-8866-6ee9ab5e5cae", + "rel": "bookmark" + } + ], + "name": "xml_rack_instance", + "status": "ACTIVE" + }, + { + "id": "4137d6a4-03b7-4b66-b0ef-8c7c35c470d3", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/4137d6a4-03b7-4b66-b0ef-8c7c35c470d3", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/4137d6a4-03b7-4b66-b0ef-8c7c35c470d3", + "rel": "bookmark" + } + ], + "name": "xml_rack_instance", + "status": "ACTIVE" + } + ], + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances?marker=4137d6a4-03b7-4b66-b0ef-8c7c35c470d3&limit=2", + "rel": "next" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.xml b/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.xml new file mode 100644 index 0000000000..4aab91cc97 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-pagination-response.xml @@ -0,0 +1,23 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 887 +Date: Tue, 19 Jun 2012 15:44:54 GMT + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-instances-index-request.json b/integration/apidocs/src/resources/samples/db-instances-index-request.json new file mode 100644 index 0000000000..ef941e56bf --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-instances-index-request.xml b/integration/apidocs/src/resources/samples/db-instances-index-request.xml new file mode 100644 index 0000000000..f03169746c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-instances-index-response.json b/integration/apidocs/src/resources/samples/db-instances-index-response.json new file mode 100644 index 0000000000..5a885c741a --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-response.json @@ -0,0 +1,71 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 1150 +Date: Tue, 19 Jun 2012 19:53:04 GMT + +{ + "instances": [ + { + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "id": "28d1b8f3-172a-4f6d-983d-36021508444a", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/28d1b8f3-172a-4f6d-983d-36021508444a", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/28d1b8f3-172a-4f6d-983d-36021508444a", + "rel": "bookmark" + } + ], + "name": "json_rack_instance", + "status": "ACTIVE", + "volume": { + "size": 2 + } + }, + { + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "id": "8fb081af-f237-44f5-80cc-b46be1840ca9", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/8fb081af-f237-44f5-80cc-b46be1840ca9", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/8fb081af-f237-44f5-80cc-b46be1840ca9", + "rel": "bookmark" + } + ], + "name": "xml_rack_instance", + "status": "ACTIVE", + "volume": { + "size": 2 + } + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-instances-index-response.xml b/integration/apidocs/src/resources/samples/db-instances-index-response.xml new file mode 100644 index 0000000000..c5e3ea129d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-index-response.xml @@ -0,0 +1,34 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 1492 +Date: Tue, 19 Jun 2012 19:53:04 GMT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-instances-paged-index-request.json b/integration/apidocs/src/resources/samples/db-instances-paged-index-request.json new file mode 100644 index 0000000000..d74ea74cea --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-paged-index-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances?marker=369018b0-30de-2775-ab39-a19bd20006e9&limit=5 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-instances-paged-index-request.xml b/integration/apidocs/src/resources/samples/db-instances-paged-index-request.xml new file mode 100644 index 0000000000..d7fd876308 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-paged-index-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances?marker=369018b0-30de-2775-ab39-a19bd20006e9&limit=5 HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-instances-paged-index-response.json b/integration/apidocs/src/resources/samples/db-instances-paged-index-response.json new file mode 100644 index 0000000000..d8de6465e8 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-paged-index-response.json @@ -0,0 +1,90 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 648 +Date: Wed, 25 Jan 2012 21:58:14 GMT + +{ + "instances": [ + { + "id": "28290db6-2f47-80d4-1b62-92bde6ff1a6e", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/28290db6-2f47-80d4-1b62-92bde6ff1a6e", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/28290db6-2f47-80d4-1b62-92bde6ff1a6e", + "rel": "bookmark" + } + ], + "name": "example_rack_instance", + "status": "ACTIVE" + }, + { + "id": "ae939ae2-3827-d005-4de6-0028a5f438d0", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/ae939ae2-3827-d005-4de6-0028a5f438d0", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/ae939ae2-3827-d005-4de6-0028a5f438d0", + "rel": "bookmark" + } + ], + "name": "example_rack_instance", + "status": "ACTIVE" + }, + { + "id": "006e4db2-5729-3f8e-77b0-075a1723ad37", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/006e4db2-5729-3f8e-77b0-075a1723ad37", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/006e4db2-5729-3f8e-77b0-075a1723ad37", + "rel": "bookmark" + } + ], + "name": "example_rack_instance", + "status": "ACTIVE" + }, + { + "id": "2806d85e-d363-2a27-f7fd-4e9911ebd8a9", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/2806d85e-d363-2a27-f7fd-4e9911ebd8a9", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/2806d85e-d363-2a27-f7fd-4e9911ebd8a9", + "rel": "bookmark" + } + ], + "name": "example_rack_instance", + "status": "ACTIVE" + }, + { + "id": "e0846e3b-3df4-398f-1e9a-d53650573dd1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/e0846e3b-3df4-398f-1e9a-d53650573dd1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/e0846e3b-3df4-398f-1e9a-d53650573dd1", + "rel": "bookmark" + } + ], + "name": "example_rack_instance", + "status": "ACTIVE" + } + ], + "links": [ + { + "rel": "next", + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances?marker=e0846e3b-3df4-398f-1e9a-d53650573dd1&limit=5" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-instances-paged-index-response.xml b/integration/apidocs/src/resources/samples/db-instances-paged-index-response.xml new file mode 100644 index 0000000000..05dc5261fb --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-instances-paged-index-response.xml @@ -0,0 +1,41 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 828 +Date: Wed, 25 Jan 2012 21:58:14 GMT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-list-databases-request.json b/integration/apidocs/src/resources/samples/db-list-databases-request.json new file mode 100644 index 0000000000..b7a3db2fa5 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-databases-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-list-databases-request.xml b/integration/apidocs/src/resources/samples/db-list-databases-request.xml new file mode 100644 index 0000000000..d0e2bb86ce --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-databases-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/databases HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-list-databases-response.json b/integration/apidocs/src/resources/samples/db-list-databases-response.json new file mode 100644 index 0000000000..879da3d0ab --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-databases-response.json @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 136 +Date: Wed, 25 Jan 2012 21:58:01 GMT + +{ + "databases": [ + { + "name": "anotherexampledb" + }, + { + "name": "exampledb" + }, + { + "name": "nextround" + }, + { + "name": "sampledb" + }, + { + "name": "testingdb" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-list-databases-response.xml b/integration/apidocs/src/resources/samples/db-list-databases-response.xml new file mode 100644 index 0000000000..d6643d6be4 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-databases-response.xml @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 248 +Date: Wed, 25 Jan 2012 21:58:02 GMT + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-list-users-request.json b/integration/apidocs/src/resources/samples/db-list-users-request.json new file mode 100644 index 0000000000..43c813d086 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-users-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/users HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-list-users-request.xml b/integration/apidocs/src/resources/samples/db-list-users-request.xml new file mode 100644 index 0000000000..0b9a2bb583 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-users-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f/users HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-list-users-response.json b/integration/apidocs/src/resources/samples/db-list-users-response.json new file mode 100644 index 0000000000..e2a94c60f9 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-users-response.json @@ -0,0 +1,28 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 152 +Date: Wed, 21 Mar 2012 17:46:46 GMT + +{ + "users": [ + { + "databases": [ + { + "name": "databaseA" + } + ], + "name": "dbuser3" + }, + { + "databases": [ + { + "name": "databaseB" + }, + { + "name": "databaseC" + } + ], + "name": "dbuser4" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-list-users-response.xml b/integration/apidocs/src/resources/samples/db-list-users-response.xml new file mode 100644 index 0000000000..69a60eded7 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-list-users-response.xml @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 501 +Date: Wed, 27 Jun 2012 21:56:06 GMT + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.json b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.json new file mode 100644 index 0000000000..fc35c39785 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/accounts/1234 HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.xml new file mode 100644 index 0000000000..467501c28c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/accounts/1234 HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.json b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.json new file mode 100644 index 0000000000..8080f2e8de --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.json @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 280 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "account": { + "id": "1234", + "instances": [ + { + "host": "hostname", + "id": "6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "name": "json_rack_instance", + "status": "ACTIVE" + }, + { + "host": "hostname", + "id": "72eed896-134e-4212-9f25-f81b4e0795e2", + "name": "xml_rack_instance", + "status": "ACTIVE" + } + ] + } +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.xml new file mode 100644 index 0000000000..ae735efee6 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-account-details-response.xml @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 358 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.json b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.json new file mode 100644 index 0000000000..b30ee85b15 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/hosts/hostname HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.xml new file mode 100644 index 0000000000..d2b20fd825 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/hosts/hostname HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.json b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.json new file mode 100644 index 0000000000..6466a5efef --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.json @@ -0,0 +1,29 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 411 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "host": { + "instances": [ + { + "id": "6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "name": "json_rack_instance", + "server_id": "64438fb6-4b98-4bd8-975e-222bc11daf74", + "status": "ACTIVE", + "tenant_id": "1234" + }, + { + "id": "72eed896-134e-4212-9f25-f81b4e0795e2", + "name": "xml_rack_instance", + "server_id": "6d93bc48-f736-41db-9ad7-c8594aac25fb", + "status": "ACTIVE", + "tenant_id": "1234" + } + ], + "name": "hostname", + "percentUsed": 51, + "totalRAM": 2004, + "usedRAM": 1024 + } +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.xml new file mode 100644 index 0000000000..c12da22a1d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-host-detail-response.xml @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 480 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.json b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.json new file mode 100644 index 0000000000..b0de95071d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.xml new file mode 100644 index 0000000000..dd2ff53caf --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/72eed896-134e-4212-9f25-f81b4e0795e2 HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.json b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.json new file mode 100644 index 0000000000..3843ad8323 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.json @@ -0,0 +1,51 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 903 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "instance": { + "created": "2012-07-24T19:48:27", + "deleted": false, + "deleted_at": null, + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "guest_status": { + "state_description": "running" + }, + "host": "hostname", + "id": "6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "rel": "bookmark" + } + ], + "local_id": 0, + "name": "json_rack_instance", + "server_id": "64438fb6-4b98-4bd8-975e-222bc11daf74", + "status": "ACTIVE", + "task_description": "No tasks for the instance.", + "tenant_id": "1234", + "updated": "2012-07-24T19:48:29", + "volume": { + "id": "VOL_0fb3073c-15de-483b-bb85-376a8997dc8a", + "size": 2 + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.xml new file mode 100644 index 0000000000..eaa95339cb --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-instance-details-response.xml @@ -0,0 +1,20 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 1008 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.json b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.json new file mode 100644 index 0000000000..551d6f0808 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b/root HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.xml new file mode 100644 index 0000000000..72f4c15661 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/72eed896-134e-4212-9f25-f81b4e0795e2/root HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.json b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.json new file mode 100644 index 0000000000..3c5d815037 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.json @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 102 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "root_history": { + "enabled": "Never", + "id": "6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "user": "Nobody" + } +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.xml new file mode 100644 index 0000000000..87458a104c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-root-details-response.xml @@ -0,0 +1,7 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 140 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.json b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.json new file mode 100644 index 0000000000..28a7a894e5 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/storage HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.xml new file mode 100644 index 0000000000..6b8ba36065 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/storage HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.json b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.json new file mode 100644 index 0000000000..3f5e7e7d16 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.json @@ -0,0 +1,23 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 177 +Date: Tue, 24 Jul 2012 19:48:35 GMT + +{ + "devices": [ + { + "capacity": { + "available": 90, + "total": 100 + }, + "name": "fake_storage", + "provision": { + "available": 40, + "percent": 10, + "total": 50 + }, + "type": "test_type", + "used": 10 + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.xml new file mode 100644 index 0000000000..e154a26940 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-get-storage-response.xml @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 254 +Date: Tue, 24 Jul 2012 19:48:35 GMT + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.json b/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.json new file mode 100644 index 0000000000..56f6ce432d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.json @@ -0,0 +1,12 @@ +POST /v1.0/1234/mgmt/hosts/hostname/instances/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 73d60021-96e1-4d06-843d-72d20fa5377b +Accept: application/json +Content-Type: application/json + +{ + "update": {} +} + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.xml new file mode 100644 index 0000000000..2662bd28a7 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-host-update-request.xml @@ -0,0 +1,10 @@ +POST /v1.0/1234/mgmt/hosts/hostname/instances/action HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 73d60021-96e1-4d06-843d-72d20fa5377b +Accept: application/xml +Content-Type: application/xml + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.json b/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.json new file mode 100644 index 0000000000..0574b12a5b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.json @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Tue, 07 Feb 2012 23:56:52 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.xml new file mode 100644 index 0000000000..bcea5bff07 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-host-update-response.xml @@ -0,0 +1,6 @@ +HTTP/1.1 202 Accepted +Content-Type: text/plain; charset=UTF-8 +Content-Length: 58 +Date: Tue, 07 Feb 2012 23:56:55 GMT + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.json new file mode 100644 index 0000000000..89ba631133 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b/diagnostics HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.xml new file mode 100644 index 0000000000..cfaa3ea029 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/72eed896-134e-4212-9f25-f81b4e0795e2/diagnostics HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.json new file mode 100644 index 0000000000..2755708db5 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.json @@ -0,0 +1,16 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 125 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "diagnostics": { + "fdSize": 64, + "threads": 2, + "version": "1", + "vmHwm": 2872, + "vmPeak": 29160, + "vmRss": 2872, + "vmSize": 29096 + } +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.xml new file mode 100644 index 0000000000..edd8f0e1e9 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.xml @@ -0,0 +1,7 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 159 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.json new file mode 100644 index 0000000000..c879a96d00 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b/hwinfo HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.xml new file mode 100644 index 0000000000..18243538ff --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances/72eed896-134e-4212-9f25-f81b4e0795e2/hwinfo HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.json new file mode 100644 index 0000000000..613f82c83f --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.json @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 48 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "hwinfo": { + "mem_total": 524288, + "num_cpus": 1 + } +} + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.xml new file mode 100644 index 0000000000..129d41f0ff --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.xml @@ -0,0 +1,15 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 160 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + 1 + + + 524288 + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.json new file mode 100644 index 0000000000..0b8e59ebb6 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances?deleted=false HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.xml new file mode 100644 index 0000000000..0de1eb4cf7 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/instances?deleted=false HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.json b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.json new file mode 100644 index 0000000000..9486f6bbd8 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.json @@ -0,0 +1,89 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 1586 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "instances": [ + { + "created": "2012-07-24T19:48:27", + "deleted": false, + "deleted_at": null, + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "host": "hostname", + "id": "6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b", + "rel": "bookmark" + } + ], + "local_id": 0, + "name": "json_rack_instance", + "server_id": "64438fb6-4b98-4bd8-975e-222bc11daf74", + "status": "ACTIVE", + "task_description": "No tasks for the instance.", + "tenant_id": "1234", + "updated": "2012-07-24T19:48:29", + "volume": { + "size": 2 + } + }, + { + "created": "2012-07-24T19:48:27", + "deleted": false, + "deleted_at": null, + "flavor": { + "id": "1", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/flavors/1", + "rel": "bookmark" + } + ] + }, + "host": "hostname", + "id": "72eed896-134e-4212-9f25-f81b4e0795e2", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/72eed896-134e-4212-9f25-f81b4e0795e2", + "rel": "self" + }, + { + "href": "https://ord.databases.api.rackspacecloud.com/instances/72eed896-134e-4212-9f25-f81b4e0795e2", + "rel": "bookmark" + } + ], + "local_id": 0, + "name": "xml_rack_instance", + "server_id": "6d93bc48-f736-41db-9ad7-c8594aac25fb", + "status": "ACTIVE", + "task_description": "No tasks for the instance.", + "tenant_id": "1234", + "updated": "2012-07-24T19:48:30", + "volume": { + "size": 2 + } + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.xml new file mode 100644 index 0000000000..c607012d5c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-instance-index-response.xml @@ -0,0 +1,34 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 1896 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.json b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.json new file mode 100644 index 0000000000..6d253741cf --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/accounts HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.xml new file mode 100644 index 0000000000..08641a9eb1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/accounts HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.json b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.json new file mode 100644 index 0000000000..d65c4e2056 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.json @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 51 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "accounts": [ + { + "id": "1234", + "num_instances": 2 + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.xml new file mode 100644 index 0000000000..99dded2c5d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-accounts-response.xml @@ -0,0 +1,9 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 119 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.json b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.json new file mode 100644 index 0000000000..a8f7b2c200 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.json @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/hosts HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.xml b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.xml new file mode 100644 index 0000000000..a7c26dd586 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/1234/mgmt/hosts HTTP/1.1 +User-Agent: python-troveclient +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.json b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.json new file mode 100644 index 0000000000..db62c38114 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.json @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 54 +Date: Tue, 24 Jul 2012 19:48:33 GMT + +{ + "hosts": [ + { + "instanceCount": 2, + "name": "hostname" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.xml b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.xml new file mode 100644 index 0000000000..42af1c3f2d --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-mgmt-list-hosts-response.xml @@ -0,0 +1,9 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 116 +Date: Tue, 24 Jul 2012 19:48:33 GMT + + + + + diff --git a/integration/apidocs/src/resources/samples/db-request-types.json b/integration/apidocs/src/resources/samples/db-request-types.json new file mode 100644 index 0000000000..44bd6d67ac --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-request-types.json @@ -0,0 +1,15 @@ +POST /v1.0/1234/instances HTTP/1.1 +Host: ord.databases.api.rackspacecloud.com +Content-Type: application/json +Accept: application/xml +X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb + +{ + "instance": { + "name": "'my_instance_name'", + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1", + "volume": { + "size": "2" + } + } +} diff --git a/integration/apidocs/src/resources/samples/db-response-types.xml b/integration/apidocs/src/resources/samples/db-response-types.xml new file mode 100644 index 0000000000..957b65c197 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-response-types.xml @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 380 +Date: Thu, 17 Nov 2011 02:37:37 GMT + + + + + + + + + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-version-request.json b/integration/apidocs/src/resources/samples/db-version-request.json new file mode 100644 index 0000000000..45e1b1b6ff --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-version-request.json @@ -0,0 +1,8 @@ +GET /v1.0/ HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-version-request.xml b/integration/apidocs/src/resources/samples/db-version-request.xml new file mode 100644 index 0000000000..eb4ce6c716 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-version-request.xml @@ -0,0 +1,8 @@ +GET /v1.0/ HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-version-response.json b/integration/apidocs/src/resources/samples/db-version-response.json new file mode 100644 index 0000000000..d5618703af --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-version-response.json @@ -0,0 +1,18 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 150 +Date: Wed, 25 Jan 2012 21:53:04 GMT + +{ + "version": { + "id": "v1.0", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/", + "rel": "self" + } + ], + "status": "CURRENT", + "updated": "2012-01-01T00:00:00Z" + } +} diff --git a/integration/apidocs/src/resources/samples/db-version-response.xml b/integration/apidocs/src/resources/samples/db-version-response.xml new file mode 100644 index 0000000000..d978f23295 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-version-response.xml @@ -0,0 +1,10 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 218 +Date: Wed, 25 Jan 2012 21:53:04 GMT + + + + + + diff --git a/integration/apidocs/src/resources/samples/db-versions-request.json b/integration/apidocs/src/resources/samples/db-versions-request.json new file mode 100644 index 0000000000..719a55667b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-versions-request.json @@ -0,0 +1,8 @@ +GET / HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/json +Content-Type: application/json + + diff --git a/integration/apidocs/src/resources/samples/db-versions-request.xml b/integration/apidocs/src/resources/samples/db-versions-request.xml new file mode 100644 index 0000000000..0b9d413a0b --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-versions-request.xml @@ -0,0 +1,8 @@ +GET / HTTP/1.1 +User-Agent: python-example-client +Host: ord.databases.api.rackspacecloud.com +X-Auth-Token: 87c6033c-9ff6-405f-943e-2deb73f278b7 +Accept: application/xml +Content-Type: application/xml + + diff --git a/integration/apidocs/src/resources/samples/db-versions-response.json b/integration/apidocs/src/resources/samples/db-versions-response.json new file mode 100644 index 0000000000..0028b4e4b1 --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-versions-response.json @@ -0,0 +1,20 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 153 +Date: Wed, 25 Jan 2012 21:53:04 GMT + +{ + "versions": [ + { + "id": "v1.0", + "links": [ + { + "href": "https://ord.databases.api.rackspacecloud.com/v1.0/", + "rel": "self" + } + ], + "status": "CURRENT", + "updated": "2012-01-01T00:00:00Z" + } + ] +} diff --git a/integration/apidocs/src/resources/samples/db-versions-response.xml b/integration/apidocs/src/resources/samples/db-versions-response.xml new file mode 100644 index 0000000000..6ec42452ae --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-versions-response.xml @@ -0,0 +1,12 @@ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 261 +Date: Wed, 25 Jan 2012 21:53:04 GMT + + + + + + + + diff --git a/integration/scripts/conf.json.example b/integration/scripts/conf.json.example new file mode 100644 index 0000000000..ee6bc7bf0a --- /dev/null +++ b/integration/scripts/conf.json.example @@ -0,0 +1,12 @@ +{ + "devstack":null, + "glance":null, + "horizon":null, + "keystone":null, + "nova":null, + "python_openstackclient":null, + "python_novaclient":null, + "trove":null, + "python_troveclient":null, + "tempest":null +} diff --git a/integration/scripts/conf/cassandra.conf b/integration/scripts/conf/cassandra.conf new file mode 100644 index 0000000000..5a2f0accce --- /dev/null +++ b/integration/scripts/conf/cassandra.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.large-5", + "instance_bigger_flavor_name": "test.large-5.resize", + "instance_eph_flavor_name": "test.eph.large-5", + "instance_bigger_eph_flavor_name": "test.eph.large-5.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/couchbase.conf b/integration/scripts/conf/couchbase.conf new file mode 100644 index 0000000000..5a2f0accce --- /dev/null +++ b/integration/scripts/conf/couchbase.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.large-5", + "instance_bigger_flavor_name": "test.large-5.resize", + "instance_eph_flavor_name": "test.eph.large-5", + "instance_bigger_eph_flavor_name": "test.eph.large-5.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/couchdb.conf b/integration/scripts/conf/couchdb.conf new file mode 100644 index 0000000000..65bd382c90 --- /dev/null +++ b/integration/scripts/conf/couchdb.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.tiny-3", + "instance_bigger_flavor_name": "test.tiny-3.resize", + "instance_eph_flavor_name": "test.eph.tiny-3", + "instance_bigger_eph_flavor_name": "test.eph.tiny-3.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/db2.conf b/integration/scripts/conf/db2.conf new file mode 100644 index 0000000000..a842ea02f8 --- /dev/null +++ b/integration/scripts/conf/db2.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.small-5", + "instance_bigger_flavor_name": "test.small-5.resize", + "instance_eph_flavor_name": "test.eph.small-5", + "instance_bigger_eph_flavor_name": "test.eph.small-5.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/mariadb.conf b/integration/scripts/conf/mariadb.conf new file mode 100644 index 0000000000..0e29d1140a --- /dev/null +++ b/integration/scripts/conf/mariadb.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.small-4", + "instance_bigger_flavor_name": "test.small-4.resize", + "instance_eph_flavor_name": "test.eph.small-4", + "instance_bigger_eph_flavor_name": "test.eph.small-4.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/mongodb.conf b/integration/scripts/conf/mongodb.conf new file mode 100644 index 0000000000..0e0aae02a2 --- /dev/null +++ b/integration/scripts/conf/mongodb.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.large-5", + "instance_bigger_flavor_name": "test.large-5.resize", + "instance_eph_flavor_name": "test.eph.large-5", + "instance_bigger_eph_flavor_name": "test.eph.large-5.resize", + "trove_volume_support": true, + "trove_volume_size": 5, diff --git a/integration/scripts/conf/mysql.conf b/integration/scripts/conf/mysql.conf new file mode 100644 index 0000000000..eac3c5790e --- /dev/null +++ b/integration/scripts/conf/mysql.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.small-3", + "instance_bigger_flavor_name": "test.small-3.resize", + "instance_eph_flavor_name": "test.eph.small-3", + "instance_bigger_eph_flavor_name": "test.eph.small-3.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/percona.conf b/integration/scripts/conf/percona.conf new file mode 100644 index 0000000000..eac3c5790e --- /dev/null +++ b/integration/scripts/conf/percona.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.small-3", + "instance_bigger_flavor_name": "test.small-3.resize", + "instance_eph_flavor_name": "test.eph.small-3", + "instance_bigger_eph_flavor_name": "test.eph.small-3.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/postgresql.conf b/integration/scripts/conf/postgresql.conf new file mode 100644 index 0000000000..8033f5826b --- /dev/null +++ b/integration/scripts/conf/postgresql.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.medium-4", + "instance_bigger_flavor_name": "test.medium-4.resize", + "instance_eph_flavor_name": "test.eph.medium-4", + "instance_bigger_eph_flavor_name": "test.eph.medium-4.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/pxc.conf b/integration/scripts/conf/pxc.conf new file mode 100644 index 0000000000..eac3c5790e --- /dev/null +++ b/integration/scripts/conf/pxc.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.small-3", + "instance_bigger_flavor_name": "test.small-3.resize", + "instance_eph_flavor_name": "test.eph.small-3", + "instance_bigger_eph_flavor_name": "test.eph.small-3.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/redis.conf b/integration/scripts/conf/redis.conf new file mode 100644 index 0000000000..65bd382c90 --- /dev/null +++ b/integration/scripts/conf/redis.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.tiny-3", + "instance_bigger_flavor_name": "test.tiny-3.resize", + "instance_eph_flavor_name": "test.eph.tiny-3", + "instance_bigger_eph_flavor_name": "test.eph.tiny-3.resize", + "trove_volume_support": true, + "trove_volume_size": 1, diff --git a/integration/scripts/conf/test_begin.conf b/integration/scripts/conf/test_begin.conf new file mode 100644 index 0000000000..eff96dea6f --- /dev/null +++ b/integration/scripts/conf/test_begin.conf @@ -0,0 +1,101 @@ +{ + "dbaas_url":"http://%service_host%:8779/v1.0", + "version_url":"http://%service_host%:8779", + "trove_auth_url":"http://%service_host%:35357/v2.0/tokens", + "trove_client_insecure":false, + "auth_strategy":null, + "trove_client_region_name": "%region_name%", + + "nova_client": { + "url":"http://%service_host%:8774/v1.1", + "auth_url":"http://%service_host%:35357/v2.0", + "nova_service_type":"compute", + "volume_service_type":"volume" + }, + + "flavors": null, + + "white_box":false, + "start_services": true, + "test_mgmt":false, + "use_local_ovz":false, + "use_venv":false, + "glance_code_root":"%glance_path%", + "glance_api_conf":"/vagrant/conf/glance-api.conf", + "glance_reg_conf":"/vagrant/conf/glance-reg.conf", + "glance_images_directory": "/glance_images", + "glance_image": "debian-squeeze-x86_64-openvz.tar.gz", + "report_directory":"%report_directory%", + "usr_bin_dir":"%bin_path%", + "nova_code_root":"%nova_path%", + "nova_conf":"/home/vagrant/nova.conf", + "keystone_code_root":"%keystone_path%", + "keystone_conf":"/etc/keystone/keystone.conf", + "keystone_use_combined":true, + "trove_code_root":"%trove_path%", + "trove_conf":"/tmp/trove.conf", + "trove_version":"v1.0", + "trove_api_updated":"2012-08-01T00:00:00Z", + "trove_max_accepted_volume_size": 1000, + "trove_max_instances_per_user": 55, + "trove_max_volumes_per_user": 100, + "use_reaper":false, + "users": [ + { "auth_user":"admin", + "auth_key":"%admin_password%", + "tenant":"admin", + "tenant_id":"%admin_tenant_id%", + "requirements": { + "is_admin":true, + "services": ["trove"] + } + }, + { "auth_user":"alt_demo", + "auth_key":"%admin_password%", + "tenant":"alt_demo", + "tenant_id":"%alt_demo_tenant_id%", + "requirements": { + "is_admin":false, + "services": ["trove"] + } + }, + { "auth_user":"admin_alt_demo", + "auth_key":"%admin_password%", + "tenant":"alt_demo", + "tenant_id":"%alt_demo_tenant_id%", + "requirements": { + "is_admin":true, + "services": ["swift"] + } + }, + { "auth_user":"demo", + "auth_key":"%admin_password%", + "tenant":"demo", + "tenant_id":"%demo_tenant_id%", + "requirements": { + "is_admin":false, + "services": ["nova", "trove"] + } + } + ], + "root_removed_from_instance_api": true, + "root_timestamp_disabled": false, + "openvz_disabled": true, + "management_api_disabled": true, + "dbaas_image": 1, + "dns_driver":"trove.dns.rsdns.driver.RsDnsDriver", + "dns_instance_entry_factory":"trove.dns.rsdns.driver.RsDnsInstanceEntryFactory", + "trove_dns_support": false, + "databases_page_size": 20, + "instances_page_size": 20, + "users_page_size": 20, + "rabbit_runs_locally": true, + "dbaas_datastore": "%datastore_type%", + "dbaas_datastore_version": "%datastore_version%", + "neutron_enabled": %neutron_enabled%, + "shared_network": "%shared_network%", + "shared_network_subnet": "%shared_network_subnet%", + "instance_fault_1_flavor_name": "test.fault_1-1", + "instance_fault_1_eph_flavor_name": "test.eph.fault_1-1", + "instance_fault_2_flavor_name": "test.fault_2-5", + "instance_fault_2_eph_flavor_name": "test.eph.fault_2-5", diff --git a/integration/scripts/conf/test_end.conf b/integration/scripts/conf/test_end.conf new file mode 100644 index 0000000000..10fc2de14a --- /dev/null +++ b/integration/scripts/conf/test_end.conf @@ -0,0 +1,2 @@ + "sentinel": null +} diff --git a/integration/scripts/conf/vertica.conf b/integration/scripts/conf/vertica.conf new file mode 100644 index 0000000000..242d7fa140 --- /dev/null +++ b/integration/scripts/conf/vertica.conf @@ -0,0 +1,6 @@ + "instance_flavor_name": "test.large-10", + "instance_bigger_flavor_name": "test.large-10.resize", + "instance_eph_flavor_name": "test.eph.large-10", + "instance_bigger_eph_flavor_name": "test.eph.large-10.resize", + "trove_volume_support": true, + "trove_volume_size": 5, diff --git a/integration/scripts/create_vm b/integration/scripts/create_vm new file mode 100755 index 0000000000..7b1b376779 --- /dev/null +++ b/integration/scripts/create_vm @@ -0,0 +1,84 @@ +#!/usr/bin/env python +""" +Sets up a VM with hardcoded paths to multiple source trees. +Creates a script for use with VMWare or a Vagrantfile. + +Uses a configuration file (in JSON format) that stores the paths to checked-out +copies of OpenStack projects on the host machine. If the path is None then +it lets devstack download them. +""" + +import json + + +class Config(object): + """ + Very simple configuration file thats just some JSON. + """ + + vm_paths = { + 'devstack':"/devstack", + 'glance': '/opt/stack/glance', + 'horizon': '/opt/stack/horizon', + 'keystone': "/opt/stack/keystone", + 'nova': "/opt/stack/nova", + 'python_openstackclient': "/opt/stack/python-openstackclient", + 'python_novaclient': "/opt/stack/python-novaclient", + 'trove':"/opt/stack/trove", + 'python_troveclient':"/opt/stack/python-troveclient", + 'tempest':"/opt/stack/tempest" + } + + def __init__(self, **kwargs): + for name in Config.vm_paths.keys(): + if name not in kwargs: + raise RuntimeError('Missing configuration value "%s".' % name) + value = kwargs[name] + if value is not None and type(value) is not str \ + and type(value) is not unicode: + raise RuntimeError('Path "%s" must be a string or None but is ' + 'of type %s.' % (name, type(value))) + setattr(self, name, kwargs[name]) + self.vagrant_path = kwargs.get("vagrant_path", "Vagrantfile") + + @staticmethod + def load(file_path): + file_contents = open(file_path, "r").read() + dict = json.loads(file_contents); + return Config(**dict) + + def write_vagrant_file(self): + with open(self.vagrant_path, 'w') as file: + file.write(""" +Vagrant::Config.run do |global_config| + # Host config + global_config.vm.define :host do |config| + + config.vm.network "33.33.44.11" + + config.vm.box = "precise" + config.vm.host_name = "host" + + config.ssh.timeout = 3600 + config.vm.customize do |vm| + vm.memory_size = 2048 + end + + config.vm.share_folder "integration", "/integration", "../" + +""") + for key in Config.vm_paths.keys(): + local_path = getattr(self, key) + vm_path = Config.vm_paths[key] + if local_path is not None: + file.write('\tconfig.vm.share_folder "%s", "%s", "%s" \n' + % (key, vm_path, local_path)) + file.write(""" + + end +end + """) + +if __name__=="__main__": + conf = Config.load("conf.json") + conf.write_vagrant_file() diff --git a/integration/scripts/files/elements/apt-conf-dir/README.rst b/integration/scripts/files/elements/apt-conf-dir/README.rst new file mode 100644 index 0000000000..c94e00ea33 --- /dev/null +++ b/integration/scripts/files/elements/apt-conf-dir/README.rst @@ -0,0 +1,16 @@ +============ +apt-conf-dir +============ + +This element overrides the default apt.conf.d directory for APT based systems. + +Environment Variables +--------------------- + +DIB_APT_CONF_DIR: + :Required: No + :Default: None + :Description: To override `DIB_APT_CONF_DIR`, set it to the path to your + apt.conf.d. The new apt.conf.d will take effect at build time + and run time. + :Example: ``DIB_APT_CONF_DIR=/etc/apt/apt.conf`` diff --git a/integration/scripts/files/elements/apt-conf-dir/extra-data.d/99-use-host-apt-confd b/integration/scripts/files/elements/apt-conf-dir/extra-data.d/99-use-host-apt-confd new file mode 100755 index 0000000000..e286d684e1 --- /dev/null +++ b/integration/scripts/files/elements/apt-conf-dir/extra-data.d/99-use-host-apt-confd @@ -0,0 +1,21 @@ +#!/bin/bash +# Override the default /etc/apt/apt.conf.d directory with $DIB_APT_CONF_DIR + +if [ ${DIB_DEBUG_TRACE:-0} -gt 0 ]; then + set -x +fi +set -eu +set -o pipefail + +# exit directly if DIB_APT_CONF_DIR is not defined properly +if [ -z "${DIB_APT_CONF_DIR:-}" ] ; then + echo "DIB_APT_CONF_DIR is not set - no apt.conf.d will be copied in" + exit 0 +elif [ ! -d "$DIB_APT_CONF_DIR" ] ; then + echo "$DIB_APT_CONF_DIR is not a valid apt.conf.d directory." + echo "You should assign a proper apt.conf.d directory in DIB_APT_CONF_DIR" + exit 1 +fi + +# copy the apt.conf to cloudimg +sudo cp -L -f -R $DIB_APT_CONF_DIR $TMP_MOUNT_PATH/etc/apt diff --git a/integration/scripts/files/elements/fedora-guest/extra-data.d/15-reddwarf-dep b/integration/scripts/files/elements/fedora-guest/extra-data.d/15-reddwarf-dep new file mode 100755 index 0000000000..97a5e438bc --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/extra-data.d/15-reddwarf-dep @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: Setup the requirements file for use by 15-reddwarf-dep + +source $_LIB/die + +BRANCH_OVERRIDE=${BRANCH_OVERRIDE:-default} +ADD_BRANCH=$(basename ${BRANCH_OVERRIDE}) +REQUIREMENTS_FILE=${REDSTACK_SCRIPTS}/files/requirements/fedora-requirements-${ADD_BRANCH}.txt + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" +[ -e ${REQUIREMENTS_FILE} ] || die "Requirements not found" +[ -n "$HOST_USERNAME" ] || die "HOST_USERNAME not set" + +sudo -Hiu ${HOST_USERNAME} dd if=${REQUIREMENTS_FILE} of=${TMP_HOOKS_PATH}/requirements.txt + +# Grab the upper constraints file, but don't fail if we can't find it. +# If we are running in the CI environment, $DEST will be set and stackrc +# will use $DEST/requirements as the location for the requirements repo. +# Use that as it will help us chain a job with something that is changing UC. + +UC_FILE=upper-constraints.txt + +if [ -f "${DEST}/requirements/${UC_FILE}" ]; then + echo "Found ${DEST}/requirements/${UC_FILE}, using that" + sudo -Hiu ${HOST_USERNAME} dd if="${DEST}/requirements/${UC_FILE}" \ + of="${TMP_HOOKS_PATH}/${UC_FILE}" +else + UC_DIR=$(pwd) + UC_BRANCH=${BRANCH_OVERRIDE} + if [ "${ADD_BRANCH}" == "default" ]; then + UC_BRANCH=master + fi + + set +e + curl -o "${UC_DIR}/${UC_FILE}" \ + https://git.openstack.org/cgit/openstack/requirements/plain/${UC_FILE}?h=${UC_BRANCH} + set -e + + if [ -f "${UC_DIR}/${UC_FILE}" ]; then + sudo -Hiu ${HOST_USERNAME} dd if="${UC_DIR}/${UC_FILE}" of=${TMP_HOOKS_PATH}/${UC_FILE} + rm -f "${UC_DIR}/${UC_FILE}" + fi +fi diff --git a/integration/scripts/files/elements/fedora-guest/extra-data.d/20-guest-systemd b/integration/scripts/files/elements/fedora-guest/extra-data.d/20-guest-systemd new file mode 100755 index 0000000000..960342e350 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/extra-data.d/20-guest-systemd @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: stages the bootstrap file and upstart conf file while replacing variables so that guest image is properly +# configured + +source $_LIB/die + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${HOST_SCP_USERNAME}" ] || die "HOST_SCP_USERNAME needs to be set to the user for the host instance" +[ -n "${CONTROLLER_IP}" ] || die "CONTROLLER_IP needs to be set to the ip address that guests will use to contact the controller" +[ -n "${ESCAPED_PATH_TROVE}" ] || die "ESCAPED_PATH_TROVE needs to be set to the path to the trove directory on the redstack host" +[ -n "${REDSTACK_SCRIPTS}" ] || die "REDSTACK_SCRIPTS needs to be set to the trove-integration scripts dir" +[ -n "${ESCAPED_GUEST_LOGDIR}" ] || die "ESCAPED_GUEST_LOGDIR must be set to the escaped guest log dir" + +sed "s/GUEST_USERNAME/${GUEST_USERNAME}/g;s/GUEST_LOGDIR/${ESCAPED_GUEST_LOGDIR}/g;s/HOST_SCP_USERNAME/${HOST_SCP_USERNAME}/g;s/CONTROLLER_IP/${CONTROLLER_IP}/g;s/PATH_TROVE/${ESCAPED_PATH_TROVE}/g" ${REDSTACK_SCRIPTS}/files/trove-guest.systemd.conf > ${TMP_HOOKS_PATH}/trove-guest.service diff --git a/integration/scripts/files/elements/fedora-guest/extra-data.d/62-ssh-key b/integration/scripts/files/elements/fedora-guest/extra-data.d/62-ssh-key new file mode 100755 index 0000000000..63453a75c6 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/extra-data.d/62-ssh-key @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: creates the SSH key on the host if it doesn't exist. Then this copies the keys over to a staging area where +# they will be duplicated in the guest VM. +# This process allows the host to log into the guest but more importantly the guest phones home to get the trove +# source + +source $_LIB/die + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" + +[ -n "${HOST_USERNAME}" ] || die "HOST_USERNAME needs to be set to the user for the current user on the host" + +if [ `whoami` = "root" ]; then + die "This should not be run as root" +fi + +# copy files over the "staging" area for the guest image (they'll later be put in the correct location by the guest user +# not these keys should not be overridden otherwise a) you won't be able to ssh in and b) the guest won't be able to +# rsync the files +if [ -e ${SSH_DIR}/authorized_keys ]; then + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/authorized_keys of=${TMP_HOOKS_PATH}/ssh-authorized-keys + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/id_rsa of=${TMP_HOOKS_PATH}/id_rsa + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/id_rsa.pub of=${TMP_HOOKS_PATH}/id_rsa.pub +else + die "SSH Authorized Keys file must exist along with pub and private key" +fi diff --git a/integration/scripts/files/elements/fedora-guest/install.d/15-reddwarf-dep b/integration/scripts/files/elements/fedora-guest/install.d/15-reddwarf-dep new file mode 100755 index 0000000000..98fb24ba18 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/install.d/15-reddwarf-dep @@ -0,0 +1,30 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install trove guest python dependencies - see redstack functions_qemu + +set -e +set -o xtrace + +dnf install -y python-devel libxml2-devel libxslt-devel python-setuptools \ + python-sqlalchemy python-lxml \ + python-routes python-eventlet python-webob \ + python-kombu python-paste-deploy python-paste python-netaddr \ + python-httplib2 python-iso8601 python-pip python-mysql \ + python-migrate python-anyjson gcc python-pexpect + +# pick up the requirements file left for us by +# extra-data.d/15-reddwarf-dep + +TMP_HOOKS_DIR="/tmp/in_target.d" + +UPPER_CONSTRAINTS= +if [ -f ${TMP_HOOKS_DIR}/upper-constraints.txt ]; then + UPPER_CONSTRAINTS=" -c ${TMP_HOOKS_DIR}/upper-constraints.txt" +fi + +pip install -q --upgrade -r ${TMP_HOOKS_DIR}/requirements.txt ${UPPER_CONSTRAINTS} + +echo "diagnostic pip freeze output follows" +pip freeze +echo "diagnostic pip freeze output above" diff --git a/integration/scripts/files/elements/fedora-guest/install.d/20-etc b/integration/scripts/files/elements/fedora-guest/install.d/20-etc new file mode 100755 index 0000000000..bec065ef32 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/install.d/20-etc @@ -0,0 +1,8 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: take "staged" trove-guest.conf file and put it in the init directory on guest image + +dd if=/tmp/in_target.d/trove-guest.service of=/usr/lib/systemd/system/trove-guest.service + +systemctl enable trove-guest.service diff --git a/integration/scripts/files/elements/fedora-guest/install.d/50-user b/integration/scripts/files/elements/fedora-guest/install.d/50-user new file mode 100755 index 0000000000..a4b666bf3b --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/install.d/50-user @@ -0,0 +1,17 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Add the guest image user that will own the trove agent source...if the user does not already exist + +set -e +set -o xtrace + +# Difference from apt, -G admin option +if ! id -u ${GUEST_USERNAME} >/dev/null 2>&1; then + echo "Adding ${GUEST_USERNAME} user" + useradd -m ${GUEST_USERNAME} -s /bin/bash + passwd ${GUEST_USERNAME} <<_EOF_ +${GUEST_USERNAME} +${GUEST_USERNAME} +_EOF_ +fi \ No newline at end of file diff --git a/integration/scripts/files/elements/fedora-guest/install.d/62-ssh-key b/integration/scripts/files/elements/fedora-guest/install.d/62-ssh-key new file mode 100755 index 0000000000..80c1e65c50 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/install.d/62-ssh-key @@ -0,0 +1,29 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: take "staged" ssh keys (see extra-data.d/62-ssh-key) and put them in the GUEST_USERS home directory + +set -e +set -o xtrace + +SSH_DIR="/home/${GUEST_USERNAME}/.ssh" +TMP_HOOKS_DIR="/tmp/in_target.d" + +if [ -e "${TMP_HOOKS_DIR}/ssh-authorized-keys" ]; then + if [ ! -e ${SSH_DIR} ]; then + # this method worked more reliable in vmware fusion over doing sudo -Hiu ${GUEST_USERNAME} + mkdir ${SSH_DIR} + chown ${GUEST_USERNAME}:${GUEST_USERNAME} ${SSH_DIR} + fi + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/authorized_keys conv=notrunc if=${TMP_HOOKS_DIR}/ssh-authorized-keys + sudo -Hiu ${GUEST_USERNAME} chmod 600 ${SSH_DIR}/authorized_keys + if [ ! -e "${SSH_DIR}/id_rsa" ]; then + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/id_rsa if=${TMP_HOOKS_DIR}/id_rsa + # perms have to be right on this file for ssh to work + sudo -Hiu ${GUEST_USERNAME} chmod 600 ${SSH_DIR}/id_rsa + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/id_rsa.pub if=${TMP_HOOKS_DIR}/id_rsa.pub + fi +else + echo "SSH Keys were not staged by host" + exit -1 +fi diff --git a/integration/scripts/files/elements/fedora-guest/post-install.d/05-ipforwarding b/integration/scripts/files/elements/fedora-guest/post-install.d/05-ipforwarding new file mode 100755 index 0000000000..4824cfcf7b --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/post-install.d/05-ipforwarding @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +set -o xtrace +sed -i -r 's/^\s*#(net\.ipv4\.ip_forward=1.*)/\1/' /etc/sysctl.conf +echo 1 > /proc/sys/net/ipv4/ip_forward diff --git a/integration/scripts/files/elements/fedora-guest/post-install.d/62-trove-guest-sudoers b/integration/scripts/files/elements/fedora-guest/post-install.d/62-trove-guest-sudoers new file mode 100755 index 0000000000..0581fd2b56 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/post-install.d/62-trove-guest-sudoers @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +set -o xtrace + +# CONTEXT: HOST after IMAGE BUILD as SCRIPT USER +# PURPOSE: add the guest user account to the /etc/sudoers files with NOPASSWD + +# Adds user to the sudoers file so they can do everything w/o a pass +# Some binaries might be under /sbin or /usr/sbin, so make sure sudo will +# see them by forcing PATH +TEMPFILE=`mktemp` +echo "${GUEST_USERNAME} ALL=(ALL) NOPASSWD:ALL" > $TEMPFILE +chmod 0440 $TEMPFILE +sudo chown root:root $TEMPFILE +sudo mv $TEMPFILE /etc/sudoers.d/60_trove_guest diff --git a/integration/scripts/files/elements/fedora-guest/post-install.d/90-yum-update b/integration/scripts/files/elements/fedora-guest/post-install.d/90-yum-update new file mode 100755 index 0000000000..cd2992c107 --- /dev/null +++ b/integration/scripts/files/elements/fedora-guest/post-install.d/90-yum-update @@ -0,0 +1,9 @@ +#!/bin/bash + +# CONTEXT: GUEST after packages installed +# PURPOSE: do dnf update to save each instance having to do all the work + +set -e +set -o xtrace + +dnf -y update diff --git a/integration/scripts/files/elements/fedora-mariadb/README.md b/integration/scripts/files/elements/fedora-mariadb/README.md new file mode 100644 index 0000000000..757f00b864 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mariadb/README.md @@ -0,0 +1,3 @@ +Sets up a MariaDB server install in the image. + +TODO: auto-tune settings based on host resources or metadata service. diff --git a/integration/scripts/files/elements/fedora-mariadb/install.d/10-mariadb b/integration/scripts/files/elements/fedora-mariadb/install.d/10-mariadb new file mode 100755 index 0000000000..a5cc2c0e67 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mariadb/install.d/10-mariadb @@ -0,0 +1,9 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +dnf -y install mariadb-server percona-xtrabackup diff --git a/integration/scripts/files/elements/fedora-mariadb/pre-install.d/10-percona-copr b/integration/scripts/files/elements/fedora-mariadb/pre-install.d/10-percona-copr new file mode 100755 index 0000000000..bcc552052a --- /dev/null +++ b/integration/scripts/files/elements/fedora-mariadb/pre-install.d/10-percona-copr @@ -0,0 +1,10 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup COPR Percona repository + +set -e +set -o xtrace + +# install from Fedora repos +dnf -y install percona-xtrabackup diff --git a/integration/scripts/files/elements/fedora-mongodb/README.md b/integration/scripts/files/elements/fedora-mongodb/README.md new file mode 100644 index 0000000000..2518abf234 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mongodb/README.md @@ -0,0 +1 @@ +Sets up a MongoDB install in the image. \ No newline at end of file diff --git a/integration/scripts/files/elements/fedora-mongodb/install.d/10-mongodb b/integration/scripts/files/elements/fedora-mongodb/install.d/10-mongodb new file mode 100755 index 0000000000..3b53a18b6c --- /dev/null +++ b/integration/scripts/files/elements/fedora-mongodb/install.d/10-mongodb @@ -0,0 +1,24 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +cat > "/etc/rc.local" << _EOF_ +# Make sure to disable Linux kernel feature transparent huge pages, +# it will affect greatly both memory usage and latency in a negative way. +# See: http://docs.mongodb.org/manual/tutorial/transparent-huge-pages/ +if test -f /sys/kernel/mm/redhat_transparent_hugepage/defrag; then + echo never > /sys/kernel/mm/redhat_transparent_hugepage/defrag +fi +if test -f /sys/kernel/mm/redhat_transparent_hugepage/enabled; then + echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled +fi + +exit \$? + +_EOF_ + +dnf -y install mongodb-server diff --git a/integration/scripts/files/elements/fedora-mongodb/install.d/25-trove-mongo-dep b/integration/scripts/files/elements/fedora-mongodb/install.d/25-trove-mongo-dep new file mode 100755 index 0000000000..a3e2633b22 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mongodb/install.d/25-trove-mongo-dep @@ -0,0 +1,9 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install trove guest python dependencies - see redstack functions_qemu + +set -e +set -o xtrace + +pip install pymongo>=3.0.2,!=3.1 diff --git a/integration/scripts/files/elements/fedora-mysql/README.md b/integration/scripts/files/elements/fedora-mysql/README.md new file mode 100644 index 0000000000..39a6ab8c66 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mysql/README.md @@ -0,0 +1,3 @@ +Sets up a MySQL server install in the image. + +TODO: auto-tune settings based on host resources or metadata service. diff --git a/integration/scripts/files/elements/fedora-mysql/install.d/10-mysql b/integration/scripts/files/elements/fedora-mysql/install.d/10-mysql new file mode 100755 index 0000000000..ff7efa365e --- /dev/null +++ b/integration/scripts/files/elements/fedora-mysql/install.d/10-mysql @@ -0,0 +1,16 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +dnf -y install https://repo.mysql.com/mysql-community-release-fc22.rpm +dnf -y install mysql-community-server + +# move the config dir for now but leave /etc/my.cnf alone +# ln -s creates problems for the systemd script +mkdir /etc/mysql +mv /etc/my.cnf.d /etc/mysql/conf.d +chown -R mysql:mysql /etc/mysql diff --git a/integration/scripts/files/elements/fedora-mysql/install.d/40-xtrabackup b/integration/scripts/files/elements/fedora-mysql/install.d/40-xtrabackup new file mode 100755 index 0000000000..9c9709ca41 --- /dev/null +++ b/integration/scripts/files/elements/fedora-mysql/install.d/40-xtrabackup @@ -0,0 +1,10 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +dnf -y install percona-xtrabackup + diff --git a/integration/scripts/files/elements/fedora-mysql/post-install.d/30-register-mysql-service b/integration/scripts/files/elements/fedora-mysql/post-install.d/30-register-mysql-service new file mode 100644 index 0000000000..a7db5d926e --- /dev/null +++ b/integration/scripts/files/elements/fedora-mysql/post-install.d/30-register-mysql-service @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +set -o xtrace + +# DO NOT enable or start mysqld for systemd, let the guestagent coordinate startup diff --git a/integration/scripts/files/elements/fedora-percona/install.d/05-percona-server b/integration/scripts/files/elements/fedora-percona/install.d/05-percona-server new file mode 100755 index 0000000000..9c43c6ef7a --- /dev/null +++ b/integration/scripts/files/elements/fedora-percona/install.d/05-percona-server @@ -0,0 +1,17 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup apt-repo list so that we can connect to Percona's repo + +set -e +set -o xtrace + +curl -o /etc/pki/rpm-gpg/RPM-GPG-KEY-percona http://www.percona.com/downloads/RPM-GPG-KEY-percona +cat < /etc/yum.repos.d/Percona.repo +[percona] +name = CentOS \$releasever - Percona +baseurl=http://repo.percona.com/centos/latest/os/\$basearch/ +enabled = 1 +gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-percona +gpgcheck = 1 +EOL \ No newline at end of file diff --git a/integration/scripts/files/elements/fedora-percona/install.d/10-mysql b/integration/scripts/files/elements/fedora-percona/install.d/10-mysql new file mode 100755 index 0000000000..284e81ac1f --- /dev/null +++ b/integration/scripts/files/elements/fedora-percona/install.d/10-mysql @@ -0,0 +1,16 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +# The fix to make versions of percona-xtrabackup > v2.2 work with Trove +# was put into the mysql guestagent code for Mitaka. There are no current +# plans to backport so we need to make sure the guest generated when the +# tests are run for Kilo or Liberty get the 2.2 verson of PXB +if [[ $BRANCH_OVERRIDE == "stable/kilo" || $BRANCH_OVERRIDE == "stable/liberty" ]]; then + PXB_VERSION_OVERRIDE="-22" +fi +dnf -y install percona-toolkit Percona-Server-shared-55 Percona-Server-server-55 Percona-Server-test-55 Percona-Server-client-55 percona-xtrabackup${PXB_VERSION_OVERRIDE} diff --git a/integration/scripts/files/elements/fedora-postgresql/install.d/10-postgresql b/integration/scripts/files/elements/fedora-postgresql/install.d/10-postgresql new file mode 100755 index 0000000000..a2ee463390 --- /dev/null +++ b/integration/scripts/files/elements/fedora-postgresql/install.d/10-postgresql @@ -0,0 +1,83 @@ +#!/bin/sh + +set -e +set -o xtrace + +cat > "/etc/sysctl.d/10-postgresql-performance.conf" << _EOF_ +# See 'http://www.postgresql.org/docs/9.3/static/kernel-resources.html' +# for best practices. +# It is recommended to disable memory overcommit, +# but the Python interpreter may require it on smaller flavors. +# We therefore stick with the heuristic overcommit setting. +vm.overcommit_memory=0 + +_EOF_ + +cat > "/etc/rc.local" << _EOF_ +# See 'http://www.postgresql.org/docs/9.3/static/kernel-resources.html' +# Disable Linux kernel transparent huge pages. This feature is not supported by +# by Postgres 9.3 and may negatively impact performance of the database. +if test -f /sys/kernel/mm/redhat_transparent_hugepage/defrag; then + echo never > /sys/kernel/mm/redhat_transparent_hugepage/defrag +fi +if test -f /sys/kernel/mm/redhat_transparent_hugepage/enabled; then + echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled +fi + +exit \$? + +_EOF_ + +dnf install -y http://yum.postgresql.org/9.4/fedora/fedora-22-x86_64/pgdg-fedora94-9.4-4.noarch.rpm + +dnf install -y postgresql94-server postgresql94-contrib postgresql94-devel gcc + +########################################### +# Hack alert: +# For Postgresql 9.4, pg_rewind is not in the main source tree and +# no packages exist in the repos, so it must be compiled manually +# and installed on the image until we can move to 9.5 +# See README at +# https://github.com/vmware/pg_rewind/tree/REL9_4_STABLE + +dev_pkgs="readline-devel zlib-devel krb5-devel openssl-devel pam-devel libxml2-devel libxslt-devel" + +yum install -y $dev_pkgs + +# We need pg_config to be accessible on the path +mkdir -p /tmp/build +cd /tmp/build +git clone https://github.com/vmware/pg_rewind.git --branch REL9_4_STABLE +git clone https://github.com/postgres/postgres.git --branch REL9_4_STABLE + +ln -s /usr/pgsql-9.4/bin/pg_config /usr/bin/pg_config + +cd pg_rewind +make USE_PGXS=1 top_srcdir=/tmp/build/postgres install +ln -s /usr/pgsql-9.4/bin/pg_rewind /usr/bin/pg_rewind + +# Cleanup + +cd +rm -rf /tmp/build +yum remove -y $dev_pkgs + + +# Though /var/lib/pgsql is the preferred directory, need to move it as +# this is where the volume will be mounted +su - postgres -c "/usr/pgsql-9.4/bin/initdb /var/lib/pgsql/9.4/data" +mv /var/lib/pgsql /var/lib/postgresql + +mv /lib/systemd/system/postgresql-9.4.service /lib/systemd/system/postgresql.service + +sed -i 's/PGDATA=\/var\/lib\/pgsql\/9.4\/data/PGDATA=\/var\/lib\/postgresql\/9.4\/data/' /lib/systemd/system/postgresql.service + +# Create a volatile directory for runtime files. +echo "d /var/run/postgresql/ 0755 postgres postgres" > /lib/tmpfiles.d/postgresql.conf + +# Install the native Python client. +dnf install -y postgresql-devel python-devel +pip install psycopg2 + +systemctl enable postgresql.service +systemctl start postgresql.service diff --git a/integration/scripts/files/elements/fedora-redis/README.md b/integration/scripts/files/elements/fedora-redis/README.md new file mode 100644 index 0000000000..426072cfb7 --- /dev/null +++ b/integration/scripts/files/elements/fedora-redis/README.md @@ -0,0 +1 @@ +Sets up a redis server install in the image. diff --git a/integration/scripts/files/elements/fedora-redis/install.d/10-redis b/integration/scripts/files/elements/fedora-redis/install.d/10-redis new file mode 100755 index 0000000000..8c23b5f63f --- /dev/null +++ b/integration/scripts/files/elements/fedora-redis/install.d/10-redis @@ -0,0 +1,9 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -ex + +dnf -y install redis +service redis start diff --git a/integration/scripts/files/elements/ubuntu-cassandra/install.d/10-cassandra b/integration/scripts/files/elements/ubuntu-cassandra/install.d/10-cassandra new file mode 100755 index 0000000000..2bd40181c1 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-cassandra/install.d/10-cassandra @@ -0,0 +1,25 @@ +#!/bin/bash + +set -ex +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get install -qy curl +echo "deb http://debian.datastax.com/community stable main" >> /etc/apt/sources.list.d/cassandra.sources.list +curl -L http://debian.datastax.com/debian/repo_key | apt-key add - +apt-get update +apt-get install -qy openjdk-7-jdk expect python-dev +apt-get install -qy libxml2-dev ntp mc +apt-get install -qy libxslt1-dev python-pexpect +apt-get install -qy python-migrate build-essential + +apt-get install dsc21=2.1.* cassandra=2.1.* -qy + +# The Python Driver 2.0 for Apache Cassandra. +pip install cassandra-driver +# Sorted sets support for the Python driver. +pip install blist + +service cassandra stop +rm -rf /var/lib/cassandra/data/system/* +service cassandra start diff --git a/integration/scripts/files/elements/ubuntu-couchbase/install.d/10-couchbase b/integration/scripts/files/elements/ubuntu-couchbase/install.d/10-couchbase new file mode 100755 index 0000000000..0dc67d8bd8 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-couchbase/install.d/10-couchbase @@ -0,0 +1,8 @@ +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get install -qy curl +apt-get install -qy libssl0.9.8 +curl -O http://packages.couchbase.com/releases/2.2.0/couchbase-server-community_2.2.0_x86_64.deb +INSTALL_DONT_START_SERVER=1 dpkg -i couchbase-server-community_2.2.0_x86_64.deb diff --git a/integration/scripts/files/elements/ubuntu-couchdb/install.d/10-couchdb b/integration/scripts/files/elements/ubuntu-couchdb/install.d/10-couchdb new file mode 100755 index 0000000000..77871d3f33 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-couchdb/install.d/10-couchdb @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +# install the ppa-finding tool for ubuntu 12.0.4 release +apt-get install -y python-software-properties +add-apt-repository -y ppa:couchdb/stable +# update cached list of packages +apt-get update -y +# remove any existing couchdb binaries +apt-get remove -yf couchdb couchdb-bin couchdb-common +# install couchdb +apt-get install -yV couchdb +# install curl to provide a way to interact with CouchDB +# over HTTP REST API +apt-get install -qy curl diff --git a/integration/scripts/files/elements/ubuntu-db2/README.md b/integration/scripts/files/elements/ubuntu-db2/README.md new file mode 100644 index 0000000000..b488f2dbe9 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-db2/README.md @@ -0,0 +1,36 @@ + Creates an image for DB2 Express-C v10.5 + + The packages for DB2 Express-C can be downloaded from: + http://www-01.ibm.com/software/data/db2/express-c/download.html + and click on the link "DB2 Express-C for Linux 64-bit". + New users can either get an IBM ID or click on the "Proceed without an + IBM ID". User is provided with a registration form which needs to be + completed in order to proceed further to download the DB2 Express-C + packages. After accepting the license agreement, user can download the + the DB2 Express-C package (.tar.gz file). + + There are 2 options for making the DB2 Express-C package accessible to + the Trove disk-image building process: + - place the package in a private repository and set the environment + variable DATASTORE_PKG_LOCATION with the url to this private + repository. + e.g. export DATASTORE_PKG_LOCATION="http://www.foo.com/db2/v10.5_linuxx64_expc.tar.gz" + + - download the package and place it in any directory on the local + filesystem that the trove-integration scripts can access. Set the + environment variable DATASTORE_PKG_LOCATION with the full path to + the downloaded package. + e.g. export DATASTORE_PKG_LOCATION="/home/stack/db2/v10.5_linuxx64_expc.tar.gz" + + The environment variables used are as follows: + + DATASTORE_PKG_LOCATION - is the place where user stores the DB2 + Express-C package after registration. This can either be a + url to a private repository or the full path to the + downloaded package on a local filesystem. + DATASTORE_DOWNLOAD_OPTS - defines any wget options user wants to specify + like user,password, etc. This is an optional variable and is + needed only if specifying a private repository to download + the packages from. + e.g. export DATASTORE_DOWNLOAD_OPTS="--user=foo --password='secret'" + diff --git a/integration/scripts/files/elements/ubuntu-db2/extra-data.d/20-copy-db2-pkgs b/integration/scripts/files/elements/ubuntu-db2/extra-data.d/20-copy-db2-pkgs new file mode 100755 index 0000000000..f82fd32ad5 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-db2/extra-data.d/20-copy-db2-pkgs @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: Download the DB2 Express-C v10.5 packages to a directory on the local filesystem or +# to a private repository. The download location is specified using the env variable: +# DATASTORE_PKG_LOCATION + +[ -n "${TMP_HOOKS_PATH}" ] || die "Temp hook path not set" +[ -n "${DATASTORE_PKG_LOCATION}" ] || die "DATASTORE_PKG_LOCATION not set" + +# First check if the package is available on the local filesystem. +if [ -f "${DATASTORE_PKG_LOCATION}" ]; then + echo "Found the DB2 Express-C packages in ${DATASTORE_PKG_LOCATION}." + dd if="${DATASTORE_PKG_LOCATION}" of=${TMP_HOOKS_PATH}/db2.tar.gz +# else, check if the package is available for download in a private repository. +elif wget ${DATASTORE_DOWNLOAD_OPTS} "${DATASTORE_PKG_LOCATION}" -O ${TMP_HOOKS_PATH}/db2.tar.gz; then + echo "Downloaded the DB2 Express-C package from the private repository" +else + echo "Unable to find the DB2 package at ${DATASTORE_PKG_LOCATION}" + echo "Please register and download the DB2 Express-C packages to a private repository or local filesystem." + exit -1 +fi diff --git a/integration/scripts/files/elements/ubuntu-db2/install.d/10-db2 b/integration/scripts/files/elements/ubuntu-db2/install.d/10-db2 new file mode 100755 index 0000000000..63bc3bf739 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-db2/install.d/10-db2 @@ -0,0 +1,52 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Uncompress the DB2 packages and install and configure DB2 on Ubuntu. + +# DB2_PKG_LOCATION points to the directory where the DB2 packages +# are located to install. +DB2_PKG_LOCATION="/db2" +mkdir ${DB2_PKG_LOCATION} +cd ${DB2_PKG_LOCATION} + +# DB2 install requires the hostname to be resolved correctly +host_name=`hostname` +echo "127.0.0.1 ${host_name}" >> /etc/hosts + +tar -xvzf /tmp/in_target.d/db2.tar.gz + +# installing dependencies +apt-get install libaio1 +apt-get install libstdc++6 + +# start the installation process. Accepts the default installation directory '/opt/ibm/db2/V10.5' +${DB2_PKG_LOCATION}/expc/db2_install -b /opt/ibm/db2/V10.5 -f sysreq -l ${DB2_PKG_LOCATION}/db2_install.log + +# create the DB2 users. +# DB2 instance owner - db2inst1 +# DB2 fence user - db2fenc1 +# DB2 admin user - db2das1 +useradd -m db2inst1 +useradd -m db2fenc1 +useradd -m db2das1 + +# Create the DB2 server instance +/opt/ibm/db2/V10.5/instance/db2icrt -a server -u db2fenc1 db2inst1 +/opt/ibm/db2/V10.5/cfg/db2ln + +# Configure DB2 server instance to communicate via TCP/IP on a particulat port. +echo 'db2c_db2inst1 50000/tcp # DB2 connection service port' >> /etc/services + +# Configure DB2 to use the TCP/IP settings defined above. +su - db2inst1 -c "db2 update database manager configuration using svcename db2c_db2inst1" + +# Start the actual TCP/IP communication. +su - db2inst1 -c "db2set DB2COMM=tcpip" + +# DB2 requires the hostname to be resolved correctly. Delete this entry from the +# /etc/hosts since this is the hostname of the instance where the image is being +# built. The correct hostname will be set in the guest agent. +sed -i "/127.0.0.1[[:space:]]*${host_name}/d" /etc/hosts diff --git a/integration/scripts/files/elements/ubuntu-guest/extra-data.d/15-reddwarf-dep b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/15-reddwarf-dep new file mode 100755 index 0000000000..33b42306de --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/15-reddwarf-dep @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: Setup the requirements file for use by 15-reddwarf-dep + +source $_LIB/die + +BRANCH_OVERRIDE=${BRANCH_OVERRIDE:-default} +ADD_BRANCH=$(basename ${BRANCH_OVERRIDE}) +REQUIREMENTS_FILE=${REDSTACK_SCRIPTS}/files/requirements/ubuntu-requirements-${ADD_BRANCH}.txt + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" +[ -e ${REQUIREMENTS_FILE} ] || die "Requirements not found" +[ -n "$HOST_USERNAME" ] || die "HOST_USERNAME not set" + +sudo -Hiu ${HOST_USERNAME} dd if=${REQUIREMENTS_FILE} of=${TMP_HOOKS_PATH}/requirements.txt + +# Grab the upper constraints file, but don't fail if we can't find it. +# If we are running in the CI environment, $DEST will be set and stackrc +# will use $DEST/requirements as the location for the requirements repo. +# Use that as it will help us chain a job with something that is changing UC. + +UC_FILE=upper-constraints.txt + +if [ -f "${DEST}/requirements/${UC_FILE}" ]; then + echo "Found ${DEST}/requirements/${UC_FILE}, using that" + sudo -Hiu ${HOST_USERNAME} dd if="${DEST}/requirements/${UC_FILE}" \ + of="${TMP_HOOKS_PATH}/${UC_FILE}" +else + UC_DIR=$(pwd) + UC_BRANCH=${BRANCH_OVERRIDE} + if [ "${ADD_BRANCH}" == "default" ]; then + UC_BRANCH=master + fi + + set +e + curl -o "${UC_DIR}/${UC_FILE}" \ + https://git.openstack.org/cgit/openstack/requirements/plain/${UC_FILE}?h=${UC_BRANCH} + set -e + + if [ -f "${UC_DIR}/${UC_FILE}" ]; then + sudo -Hiu ${HOST_USERNAME} dd if="${UC_DIR}/${UC_FILE}" of=${TMP_HOOKS_PATH}/${UC_FILE} + rm -f "${UC_DIR}/${UC_FILE}" + fi +fi diff --git a/integration/scripts/files/elements/ubuntu-guest/extra-data.d/20-guest-upstart b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/20-guest-upstart new file mode 100755 index 0000000000..2b2215920d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/20-guest-upstart @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: stages the bootstrap file and upstart conf file while replacing variables so that guest image is properly +# configured + +source $_LIB/die + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${HOST_SCP_USERNAME}" ] || die "HOST_SCP_USERNAME needs to be set to the user for the host instance" +[ -n "${CONTROLLER_IP}" ] || die "CONTROLLER_IP needs to be set to the ip address that guests will use to contact the controller" +[ -n "${ESCAPED_PATH_TROVE}" ] || die "ESCAPED_PATH_TROVE needs to be set to the path to the trove directory on the redstack host" +[ -n "${REDSTACK_SCRIPTS}" ] || die "REDSTACK_SCRIPTS needs to be set to the trove-integration scripts dir" +[ -n "${ESCAPED_GUEST_LOGDIR}" ] || die "ESCAPED_GUEST_LOGDIR must be set to the escaped guest log dir" + +sed "s/GUEST_USERNAME/${GUEST_USERNAME}/g;s/GUEST_LOGDIR/${ESCAPED_GUEST_LOGDIR}/g;s/HOST_SCP_USERNAME/${HOST_SCP_USERNAME}/g;s/CONTROLLER_IP/${CONTROLLER_IP}/g;s/PATH_TROVE/${ESCAPED_PATH_TROVE}/g" ${REDSTACK_SCRIPTS}/files/trove-guest.upstart.conf > ${TMP_HOOKS_PATH}/trove-guest.conf diff --git a/integration/scripts/files/elements/ubuntu-guest/extra-data.d/62-ssh-key b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/62-ssh-key new file mode 100755 index 0000000000..63453a75c6 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/extra-data.d/62-ssh-key @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e +set -o xtrace + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: creates the SSH key on the host if it doesn't exist. Then this copies the keys over to a staging area where +# they will be duplicated in the guest VM. +# This process allows the host to log into the guest but more importantly the guest phones home to get the trove +# source + +source $_LIB/die + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" + +[ -n "${HOST_USERNAME}" ] || die "HOST_USERNAME needs to be set to the user for the current user on the host" + +if [ `whoami` = "root" ]; then + die "This should not be run as root" +fi + +# copy files over the "staging" area for the guest image (they'll later be put in the correct location by the guest user +# not these keys should not be overridden otherwise a) you won't be able to ssh in and b) the guest won't be able to +# rsync the files +if [ -e ${SSH_DIR}/authorized_keys ]; then + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/authorized_keys of=${TMP_HOOKS_PATH}/ssh-authorized-keys + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/id_rsa of=${TMP_HOOKS_PATH}/id_rsa + sudo -Hiu ${HOST_USERNAME} dd if=${SSH_DIR}/id_rsa.pub of=${TMP_HOOKS_PATH}/id_rsa.pub +else + die "SSH Authorized Keys file must exist along with pub and private key" +fi diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/05-base-apps b/integration/scripts/files/elements/ubuntu-guest/install.d/05-base-apps new file mode 100755 index 0000000000..5cd392b2c4 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/05-base-apps @@ -0,0 +1,10 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install basic services and applications + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get -y install ntp apparmor-utils diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/15-reddwarf-dep b/integration/scripts/files/elements/ubuntu-guest/install.d/15-reddwarf-dep new file mode 100755 index 0000000000..8d989fe0cd --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/15-reddwarf-dep @@ -0,0 +1,31 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install trove guest python dependencies - see redstack functions_qemu + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get -y install python-dev libxml2-dev libxslt1-dev python-setuptools \ + python-pip python-sqlalchemy python-lxml \ + python-routes python-eventlet python-webob \ + python-pastedeploy python-paste python-netaddr \ + python-httplib2 python-iso8601 python-pexpect python-mysqldb python-migrate + + +# pick up the requirements file left for us by +# extra-data.d/15-reddwarf-dep + +TMP_HOOKS_DIR="/tmp/in_target.d" + +UPPER_CONSTRAINTS= +if [ -f ${TMP_HOOKS_DIR}/upper-constraints.txt ]; then + UPPER_CONSTRAINTS=" -c ${TMP_HOOKS_DIR}/upper-constraints.txt" +fi + +pip install -q --upgrade -r ${TMP_HOOKS_DIR}/requirements.txt ${UPPER_CONSTRAINTS} + +echo "diagnostic pip freeze output follows" +pip freeze +echo "diagnostic pip freeze output above" diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/20-etc b/integration/scripts/files/elements/ubuntu-guest/install.d/20-etc new file mode 100755 index 0000000000..8ac19f7cdd --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/20-etc @@ -0,0 +1,8 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: take "staged" trove-guest.conf file and put it in the init directory on guest image + +dd if=/tmp/in_target.d/trove-guest.conf of=/etc/init/trove-guest.conf + + diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/50-user b/integration/scripts/files/elements/ubuntu-guest/install.d/50-user new file mode 100755 index 0000000000..99f68966b6 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/50-user @@ -0,0 +1,18 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Add the guest image user that will own the trove agent source...if the user does not already exist + +set -e +set -o xtrace + +if ! id -u ${GUEST_USERNAME} >/dev/null 2>&1; then + echo "Adding ${GUEST_USERNAME} user" + useradd -G sudo -m ${GUEST_USERNAME} -s /bin/bash + chown ${GUEST_USERNAME}:${GUEST_USERNAME} /home/${GUEST_USERNAME} + passwd ${GUEST_USERNAME} <<_EOF_ +${GUEST_USERNAME} +${GUEST_USERNAME} +_EOF_ +fi + diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/62-ssh-key b/integration/scripts/files/elements/ubuntu-guest/install.d/62-ssh-key new file mode 100755 index 0000000000..8085cbaaca --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/62-ssh-key @@ -0,0 +1,28 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: take "staged" ssh keys (see extra-data.d/62-ssh-key) and put them in the GUEST_USERS home directory + +set -e +set -o xtrace + +SSH_DIR="/home/${GUEST_USERNAME}/.ssh" +TMP_HOOKS_DIR="/tmp/in_target.d" + +if [ -e "${TMP_HOOKS_DIR}/ssh-authorized-keys" ]; then + if [ ! -e ${SSH_DIR} ]; then + # this method worked more reliable in vmware fusion over doing sudo -Hiu ${GUEST_USERNAME} + mkdir ${SSH_DIR} + chown ${GUEST_USERNAME}:${GUEST_USERNAME} ${SSH_DIR} + fi + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/authorized_keys conv=notrunc if=${TMP_HOOKS_DIR}/ssh-authorized-keys + if [ ! -e "${SSH_DIR}/id_rsa" ]; then + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/id_rsa if=${TMP_HOOKS_DIR}/id_rsa + # perms have to be right on this file for ssh to work + sudo -Hiu ${GUEST_USERNAME} chmod 600 ${SSH_DIR}/id_rsa + sudo -Hiu ${GUEST_USERNAME} dd of=${SSH_DIR}/id_rsa.pub if=${TMP_HOOKS_DIR}/id_rsa.pub + fi +else + echo "SSH Keys were not staged by host" + exit -1 +fi diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/98-ssh b/integration/scripts/files/elements/ubuntu-guest/install.d/98-ssh new file mode 100755 index 0000000000..2134c19889 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/98-ssh @@ -0,0 +1,8 @@ +#!/bin/bash +# Regenerate host keys now. XXX: Really should be a cloud-init task, should get +# that working. + +set -e +set -o xtrace + +dpkg-reconfigure openssh-server diff --git a/integration/scripts/files/elements/ubuntu-guest/install.d/99-clean-apt b/integration/scripts/files/elements/ubuntu-guest/install.d/99-clean-apt new file mode 100755 index 0000000000..cc348c5cb9 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/install.d/99-clean-apt @@ -0,0 +1,11 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Delete contents of apt cache on guest (saves image disk space) + +set -e +set -o xtrace + +apt-get clean + + diff --git a/integration/scripts/files/elements/ubuntu-guest/post-install.d/05-ipforwarding b/integration/scripts/files/elements/ubuntu-guest/post-install.d/05-ipforwarding new file mode 100755 index 0000000000..48b7ad0d37 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/post-install.d/05-ipforwarding @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +set -o xtrace +sed -i -r 's/^\s*#(net\.ipv4\.ip_forward=1.*)/\1/' /etc/sysctl.conf diff --git a/integration/scripts/files/elements/ubuntu-guest/post-install.d/10-ntp b/integration/scripts/files/elements/ubuntu-guest/post-install.d/10-ntp new file mode 100755 index 0000000000..bc494ae356 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/post-install.d/10-ntp @@ -0,0 +1,10 @@ +#!/bin/bash +ntpfile=`mktemp` +cat << EOF > $ntpfile +server ntp.ubuntu.com iburst +server 127.127.1.0 +fudge 127.127.1.0 stratum 10 +EOF + +mv /etc/ntp.conf /etc/ntp.conf.orig +mv $ntpfile /etc/ntp.conf diff --git a/integration/scripts/files/elements/ubuntu-guest/post-install.d/62-trove-guest-sudoers b/integration/scripts/files/elements/ubuntu-guest/post-install.d/62-trove-guest-sudoers new file mode 100755 index 0000000000..0581fd2b56 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/post-install.d/62-trove-guest-sudoers @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +set -o xtrace + +# CONTEXT: HOST after IMAGE BUILD as SCRIPT USER +# PURPOSE: add the guest user account to the /etc/sudoers files with NOPASSWD + +# Adds user to the sudoers file so they can do everything w/o a pass +# Some binaries might be under /sbin or /usr/sbin, so make sure sudo will +# see them by forcing PATH +TEMPFILE=`mktemp` +echo "${GUEST_USERNAME} ALL=(ALL) NOPASSWD:ALL" > $TEMPFILE +chmod 0440 $TEMPFILE +sudo chown root:root $TEMPFILE +sudo mv $TEMPFILE /etc/sudoers.d/60_trove_guest diff --git a/integration/scripts/files/elements/ubuntu-guest/post-install.d/90-apt-get-update b/integration/scripts/files/elements/ubuntu-guest/post-install.d/90-apt-get-update new file mode 100755 index 0000000000..6cc087a7db --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/post-install.d/90-apt-get-update @@ -0,0 +1,9 @@ +#!/bin/bash + +# CONTEXT: GUEST after packages installed +# PURPOSE: do apt-get update to save each instance having to do all the work + +set -e +set -o xtrace + +apt-get update diff --git a/integration/scripts/files/elements/ubuntu-guest/pre-install.d/01-trim-pkgs b/integration/scripts/files/elements/ubuntu-guest/pre-install.d/01-trim-pkgs new file mode 100755 index 0000000000..8787df7b48 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/pre-install.d/01-trim-pkgs @@ -0,0 +1,117 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install basic services and applications + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get -y purge acpid\ + apport\ + apport-symptoms\ + apt-transport-https\ + apt-xapian-index\ + aptitude\ + at\ + bash-completion\ + bc\ + bind9-host\ + bsdmainutils\ + busybox-static\ + byobu\ + command-not-found\ + command-not-found-data\ + curl\ + dbus\ + dmidecode\ + dosfstools\ + ed\ + fonts-ubuntu-font-family-console\ + friendly-recovery\ + ftp\ + fuse\ + geoip-database\ + groff-base\ + hdparm\ + info\ + install-info\ + iptables\ + iputils-tracepath\ + irqbalance\ + landscape-client\ + landscape-common\ + language-selector-common\ + laptop-detect\ + libaccountsservice0\ + libbind9-90\ + libclass-accessor-perl\ + libcwidget3\ + libdns100\ + libept1.4.12\ + libevent-2.0-5\ + libgc1c2\ + libgeoip1\ + libio-string-perl\ + libisc95\ + liblwres90\ + libnfnetlink0\ + libparse-debianchangelog-perl\ + libparted0debian1\ + libpcap0.8\ + libpci3\ + libpipeline1\ + libpolkit-gobject-1-0\ + libsasl2-modules\ + libsigc++-2.0-0c2a\ + libsub-name-perl\ + libusb-1.0-0\ + libxapian22\ + lshw\ + lsof\ + ltrace\ + man-db\ + mlocate\ + mtr-tiny\ + nano\ + ntfs-3g\ + parted\ + patch\ + plymouth-theme-ubuntu-text\ + popularity-contest\ + powermgmt-base\ + ppp\ + pppoeconf\ + python-debian\ + python-gdbm\ + python-pam\ + python-twisted-bin\ + python-xapian\ + python-zope.interface\ + screen\ + shared-mime-info\ + strace\ + tasksel\ + tcpdump\ + telnet\ + time\ + tmux\ + ubuntu-standard\ + ufw\ + update-manager-core\ + update-notifier-common\ + usbutils\ + uuid-runtime\ + w3m + +# The following packages cannot be removed as they cause cloud-init to be +# uninstalled in Ubuntu 14.04 +# gir1.2-glib-2.0 +# libdbus-glib-1-2 +# libgirepository-1.0-1 +# python-chardet +# python-serial +# xz-utils + +apt-get -y autoremove + diff --git a/integration/scripts/files/elements/ubuntu-guest/pre-install.d/04-baseline-tools b/integration/scripts/files/elements/ubuntu-guest/pre-install.d/04-baseline-tools new file mode 100755 index 0000000000..1a8647f510 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-guest/pre-install.d/04-baseline-tools @@ -0,0 +1,7 @@ +#!/bin/bash +# Install baseline packages and tools. + +set -e +set -o xtrace + +apt-get install -y language-pack-en python-software-properties \ No newline at end of file diff --git a/integration/scripts/files/elements/ubuntu-mariadb/README.md b/integration/scripts/files/elements/ubuntu-mariadb/README.md new file mode 100644 index 0000000000..757f00b864 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mariadb/README.md @@ -0,0 +1,3 @@ +Sets up a MariaDB server install in the image. + +TODO: auto-tune settings based on host resources or metadata service. diff --git a/integration/scripts/files/elements/ubuntu-mariadb/install.d/30-mariadb b/integration/scripts/files/elements/ubuntu-mariadb/install.d/30-mariadb new file mode 100755 index 0000000000..065c2f9870 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mariadb/install.d/30-mariadb @@ -0,0 +1,34 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +# NOTE(vkmc): Using MariaDB repositories is required +# https://mariadb.com/kb/en/mariadb/installing-mariadb-deb-files/ +apt-get -y install software-properties-common +apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db +add-apt-repository 'deb http://ftp.osuosl.org/pub/mariadb/repo/10.1/ubuntu trusty main' + +# Pin MariaDB repository +sudo echo -e "Package: *\nPin: origin ftp.osuosl.org\nPin-Priority: 1000" > /etc/apt/preferences.d/mariadb.pref + +apt-get -y update +# The fix to make versions of percona-xtrabackup > v2.2 work with Trove +# was put into the mysql guestagent code for Mitaka. There are no current +# plans to backport so we need to make sure the guest generated when the +# tests are run for Kilo or Liberty get the 2.2 verson of PXB +if [[ $BRANCH_OVERRIDE == "stable/kilo" || $BRANCH_OVERRIDE == "stable/liberty" ]]; then + PXB_VERSION_OVERRIDE="-22" +fi +apt-get -y install socat percona-xtrabackup${PXB_VERSION_OVERRIDE} +apt-get -y install libmariadbclient18 mariadb-server + +cat >/etc/mysql/conf.d/no_perf_schema.cnf <<_EOF_ +[mysqld] +performance_schema = off +_EOF_ diff --git a/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/10-percona-apt-key b/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/10-percona-apt-key new file mode 100755 index 0000000000..ec1d89d561 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/10-percona-apt-key @@ -0,0 +1,40 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup apt-repo list so that we can connect to Percona's repo + +set -e +set -o xtrace + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${RELEASE}" ] || die "RELEASE must be set to either Trusty or Precise" + +# Add Percona GPG key +mkdir -p /home/${GUEST_USERNAME}/.gnupg + +# sometimes the primary key server is unavailable and we should try an +# alternate. see +# https://bugs.launchpad.net/percona-server/+bug/907789. Disable +# shell errexit so we can interrogate the exit code and take action +# based on the exit code. We will reenable it later. +set +e +apt-key adv --keyserver hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A + +if [ "$?" -ne "0" ]; +then + echo "Trying alternate keyserver hkp://keyserver.ubuntu.com" + set -e + apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 1C4CBDCDCD2EFD2A +fi + +set -e + +# Add Percona repo +# Creates the Percona sources list +cat < /etc/apt/sources.list.d/percona.list +deb http://repo.percona.com/apt $RELEASE main +deb-src http://repo.percona.com/apt $RELEASE main +EOL + +# Force an update +apt-get -y update diff --git a/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/20-apparmor-mysql-local b/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/20-apparmor-mysql-local new file mode 100755 index 0000000000..a3e1dc7c7d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mariadb/pre-install.d/20-apparmor-mysql-local @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +#CONTEXT: chroot on host +#PURPOSE: Allows mysqld to create temporary files when restoring backups + +cat <>/etc/apparmor.d/local/usr.sbin.mysqld + /tmp/ rw, + /tmp/** rwk, +EOF diff --git a/integration/scripts/files/elements/ubuntu-mongodb/README.md b/integration/scripts/files/elements/ubuntu-mongodb/README.md new file mode 100644 index 0000000000..5b9e33bb6a --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/README.md @@ -0,0 +1 @@ +Sets up a MongoDB install in the image. diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/10-mongodb-thp b/integration/scripts/files/elements/ubuntu-mongodb/install.d/10-mongodb-thp new file mode 100755 index 0000000000..48d6c8bf76 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/10-mongodb-thp @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +cat > /etc/init.d/disable-transparent-hugepages << '_EOF_' +#!/bin/sh +### BEGIN INIT INFO +# Provides: disable-transparent-hugepages +# Required-Start: $local_fs +# Required-Stop: +# X-Start-Before: mongod mongodb-mms-automation-agent +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Disable Linux transparent huge pages +# Description: Disable Linux transparent huge pages, to improve +# database performance. +### END INIT INFO + +case $1 in + start) + if [ -d /sys/kernel/mm/transparent_hugepage ]; then + thp_path=/sys/kernel/mm/transparent_hugepage + elif [ -d /sys/kernel/mm/redhat_transparent_hugepage ]; then + thp_path=/sys/kernel/mm/redhat_transparent_hugepage + else + return 0 + fi + + echo 'never' > ${thp_path}/enabled + echo 'never' > ${thp_path}/defrag + + unset thp_path + ;; +esac +_EOF_ + +chmod 755 /etc/init.d/disable-transparent-hugepages + +update-rc.d disable-transparent-hugepages defaults diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/20-mongodb b/integration/scripts/files/elements/ubuntu-mongodb/install.d/20-mongodb new file mode 100755 index 0000000000..6a95d2f03d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/20-mongodb @@ -0,0 +1,8 @@ +#!/bin/sh + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +apt-get -y install mongodb-org=3.2.6 diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/25-trove-mongo-dep b/integration/scripts/files/elements/ubuntu-mongodb/install.d/25-trove-mongo-dep new file mode 100755 index 0000000000..a3e2633b22 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/25-trove-mongo-dep @@ -0,0 +1,9 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install trove guest python dependencies - see redstack functions_qemu + +set -e +set -o xtrace + +pip install pymongo>=3.0.2,!=3.1 diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/30-mongodb-conf b/integration/scripts/files/elements/ubuntu-mongodb/install.d/30-mongodb-conf new file mode 100755 index 0000000000..afb582847d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/30-mongodb-conf @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e +set -o xtrace + +# Remove the default pid file +rm -f /var/run/mongodb.pid + + +cat > /etc/mongod.conf << '_EOF_' +storage.dbPath: /var/lib/mongodb +security.authorization: enabled +storage.engine: wiredTiger +storage.journal.enabled: true +systemLog.destination: file +systemLog.logAppend: true +systemLog.path: /var/log/mongodb/mongod.log +_EOF_ + + +cat > /etc/mongos.conf << '_EOF_' +security.authorization: enabled +systemLog.destination: file +systemLog.logAppend: true +systemLog.path: /var/log/mongodb/mongos.log +_EOF_ diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/41-mongod-init b/integration/scripts/files/elements/ubuntu-mongodb/install.d/41-mongod-init new file mode 100755 index 0000000000..77a1292723 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/41-mongod-init @@ -0,0 +1,46 @@ +#!/bin/sh + +set -e +set -o xtrace + +cat > /etc/init/mongod.conf << '_EOF_' +limit fsize unlimited unlimited +limit cpu unlimited unlimited +limit as unlimited unlimited +limit nofile 64000 64000 +limit rss unlimited unlimited +limit nproc 64000 64000 + +kill timeout 300 # wait 300s between SIGTERM and SIGKILL. + +pre-start script + mkdir -p /var/run/mongodb/ + touch /var/run/mongodb/mongod.pid + chown mongodb -R /var/run/mongodb/ +end script + +start on runlevel [2345] +stop on runlevel [06] + +script + CONF=/etc/mongod.conf + + # Handle NUMA access to CPUs (SERVER-3574) + # This verifies the existence of numactl as well as testing that the command works + NUMACTL_ARGS="--interleave=all" + if which numactl >/dev/null 2>/dev/null && numactl $NUMACTL_ARGS ls / >/dev/null 2>/dev/null + then + NUMACTL="$(which numactl) -- $NUMACTL_ARGS" + DAEMON_OPTS=${DAEMON_OPTS:-"--config $CONF"} + else + NUMACTL="" + DAEMON_OPTS="-- "${DAEMON_OPTS:-"--config $CONF"} + fi + + exec start-stop-daemon --start \ + --chuid mongodb \ + --pidfile /var/run/mongod.pid \ + --make-pidfile \ + --exec $NUMACTL /usr/bin/mongod $DAEMON_OPTS +end script +_EOF_ diff --git a/integration/scripts/files/elements/ubuntu-mongodb/install.d/42-mongos-init b/integration/scripts/files/elements/ubuntu-mongodb/install.d/42-mongos-init new file mode 100755 index 0000000000..dc91fdbac8 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/install.d/42-mongos-init @@ -0,0 +1,30 @@ +#!/bin/sh + +set -e +set -o xtrace + +cat > /etc/init/mongos.conf << '_EOF_' +limit fsize unlimited unlimited +limit cpu unlimited unlimited +limit as unlimited unlimited +limit nofile 64000 64000 +limit rss unlimited unlimited +limit nproc 64000 64000 + +pre-start script + mkdir -p /var/run/mongodb/ + touch /var/run/mongodb/mongos.pid + chown mongodb -R /var/run/mongodb/ +end script + +start on runlevel [2345] +stop on runlevel [06] + +script + exec start-stop-daemon --start \ + --chuid mongodb \ + --pidfile /var/run/mongos.pid \ + --make-pidfile \ + --exec /usr/bin/mongos -- --config /etc/mongos.conf +end script +_EOF_ diff --git a/integration/scripts/files/elements/ubuntu-mongodb/pre-install.d/10-mongodb-apt-key b/integration/scripts/files/elements/ubuntu-mongodb/pre-install.d/10-mongodb-apt-key new file mode 100755 index 0000000000..1538d61cb0 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mongodb/pre-install.d/10-mongodb-apt-key @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e +set -o xtrace + +[ -n "${RELEASE}" ] || die "RELEASE must be set to either Precise or Quantal" + +apt-get -y install software-properties-common + +apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927 + +echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list + +apt-get -y update diff --git a/integration/scripts/files/elements/ubuntu-mysql/README.md b/integration/scripts/files/elements/ubuntu-mysql/README.md new file mode 100644 index 0000000000..39a6ab8c66 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mysql/README.md @@ -0,0 +1,3 @@ +Sets up a MySQL server install in the image. + +TODO: auto-tune settings based on host resources or metadata service. diff --git a/integration/scripts/files/elements/ubuntu-mysql/install.d/30-mysql b/integration/scripts/files/elements/ubuntu-mysql/install.d/30-mysql new file mode 100755 index 0000000000..d31292eca0 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mysql/install.d/30-mysql @@ -0,0 +1,23 @@ +#!/bin/bash + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +# The fix to make versions of percona-xtrabackup > v2.2 work with Trove +# was put into the mysql guestagent code for Mitaka. There are no current +# plans to backport so we need to make sure the guest generated when the +# tests are run for Kilo or Liberty get the 2.2 verson of PXB +if [[ $BRANCH_OVERRIDE == "stable/kilo" || $BRANCH_OVERRIDE == "stable/liberty" ]]; then + PXB_VERSION_OVERRIDE="-22" +fi +apt-get -y install libmysqlclient18 mysql-server-5.6 percona-xtrabackup${PXB_VERSION_OVERRIDE} + +cat >/etc/mysql/conf.d/no_perf_schema.cnf <<_EOF_ +[mysqld] +performance_schema = off +_EOF_ diff --git a/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/10-percona-apt-key b/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/10-percona-apt-key new file mode 100755 index 0000000000..2a03ad5054 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/10-percona-apt-key @@ -0,0 +1,40 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup apt-repo list so that we can connect to Percona's repo + +set -e +set -o xtrace + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${RELEASE}" ] || die "RELEASE must be set to either Precise or Quantal" + +# Add Percona GPG key +mkdir -p /home/${GUEST_USERNAME}/.gnupg + +# sometimes the primary key server is unavailable and we should try an +# alternate. see +# https://bugs.launchpad.net/percona-server/+bug/907789. Disable +# shell errexit so we can interrogate the exit code and take action +# based on the exit code. We will reenable it later. +set +e +apt-key adv --keyserver hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A + +if [ "$?" -ne "0" ]; +then + echo "Trying alternate keyserver hkp://keyserver.ubuntu.com" + set -e + apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 1C4CBDCDCD2EFD2A +fi + +set -e + +# Add Percona repo +# Creates the percona sources list +cat < /etc/apt/sources.list.d/percona.list +deb http://repo.percona.com/apt $RELEASE main +deb-src http://repo.percona.com/apt $RELEASE main +EOL + +# Force an update +apt-get update diff --git a/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/20-apparmor-mysql-local b/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/20-apparmor-mysql-local new file mode 100755 index 0000000000..a3e1dc7c7d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-mysql/pre-install.d/20-apparmor-mysql-local @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +#CONTEXT: chroot on host +#PURPOSE: Allows mysqld to create temporary files when restoring backups + +cat <>/etc/apparmor.d/local/usr.sbin.mysqld + /tmp/ rw, + /tmp/** rwk, +EOF diff --git a/integration/scripts/files/elements/ubuntu-percona/install.d/30-mysql b/integration/scripts/files/elements/ubuntu-percona/install.d/30-mysql new file mode 100755 index 0000000000..5d5b4265a6 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-percona/install.d/30-mysql @@ -0,0 +1,17 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +# The fix to make versions of percona-xtrabackup > v2.2 work with Trove +# was put into the mysql guestagent code for Mitaka. There are no current +# plans to backport so we need to make sure the guest generated when the +# tests are run for Kilo or Liberty get the 2.2 verson of PXB +if [[ $BRANCH_OVERRIDE == "stable/kilo" || $BRANCH_OVERRIDE == "stable/liberty" ]]; then + PXB_VERSION_OVERRIDE="-22" +fi +apt-get -y install percona-toolkit percona-server-common-5.6 percona-server-server-5.6 percona-server-test-5.6 percona-server-client-5.6 percona-xtrabackup${PXB_VERSION_OVERRIDE} diff --git a/integration/scripts/files/elements/ubuntu-percona/pre-install.d/10-percona-apt-key b/integration/scripts/files/elements/ubuntu-percona/pre-install.d/10-percona-apt-key new file mode 100755 index 0000000000..c2b686c4c0 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-percona/pre-install.d/10-percona-apt-key @@ -0,0 +1,42 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup apt-repo list so that we can connect to Percona's repo + +set -e +set -o xtrace + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${RELEASE}" ] || die "RELEASE must be set to either Precise or Quantal" + +#5 add Percona GPG key +if [ ! -e /home/${GUEST_USERNAME}/.gnupg ]; then + mkdir -p /home/${GUEST_USERNAME}/.gnupg +fi + +# sometimes the primary key server is unavailable and we should try an +# alternate. see +# https://bugs.launchpad.net/percona-server/+bug/907789. Disable +# shell errexit so we can interrogate the exit code and take action +# based on the exit code. We will reenable it later. +set +e +apt-key adv --keyserver hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A + +if [ "$?" -ne "0" ]; +then + echo "Trying alternate keyserver hkp://keyserver.ubuntu.com" + set -e + apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 1C4CBDCDCD2EFD2A +fi + +set -e + +# add Percona repo +# creates the percona sources list +cat < /etc/apt/sources.list.d/percona.list +deb http://repo.percona.com/apt $RELEASE main +deb-src http://repo.percona.com/apt $RELEASE main +EOL + +# force an update +apt-get update diff --git a/integration/scripts/files/elements/ubuntu-percona/pre-install.d/20-apparmor-mysql-local b/integration/scripts/files/elements/ubuntu-percona/pre-install.d/20-apparmor-mysql-local new file mode 100755 index 0000000000..a3e1dc7c7d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-percona/pre-install.d/20-apparmor-mysql-local @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +#CONTEXT: chroot on host +#PURPOSE: Allows mysqld to create temporary files when restoring backups + +cat <>/etc/apparmor.d/local/usr.sbin.mysqld + /tmp/ rw, + /tmp/** rwk, +EOF diff --git a/integration/scripts/files/elements/ubuntu-postgresql/install.d/10-postgresql b/integration/scripts/files/elements/ubuntu-postgresql/install.d/10-postgresql new file mode 100755 index 0000000000..cd85325667 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-postgresql/install.d/10-postgresql @@ -0,0 +1,79 @@ +#!/bin/sh + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive + +cat > "/etc/sysctl.d/10-postgresql-performance.conf" << _EOF_ +# See 'http://www.postgresql.org/docs/9.4/static/kernel-resources.html' +# for best practices. +# It is recommended to disable memory overcommit, +# but the Python interpreter may require it on smaller flavors. +# We therefore stick with the heuristic overcommit setting. +vm.overcommit_memory=0 +vm.nr_hugepages=64 + +_EOF_ + +cat > "/etc/rc.local" << _EOF_ +# See 'http://www.postgresql.org/docs/9.4/static/kernel-resources.html' +# Postgres 9.4 added support for THP. Using huge pages reduces overhead when +# using large contiguous chunks of memory, like PostgreSQL does. +if test -f /sys/kernel/mm/transparent_hugepage/defrag; then + echo never > /sys/kernel/mm/transparent_hugepage/defrag +fi +if test -f /sys/kernel/mm/transparent_hugepage/enabled; then + echo always > /sys/kernel/mm/transparent_hugepage/enabled +fi + +exit \$? + +_EOF_ + +apt-get -y install postgresql-9.4 postgresql-contrib-9.4 postgresql-server-dev-9.4 + +########################################### +# Hack alert: +# For Postgresql 9.4, pg_rewind is not in the main source tree and +# no packages exist in the repos, so it must be compiled manually +# and installed on the image until we can move to 9.5 +# See README at +# https://github.com/vmware/pg_rewind/tree/REL9_4_STABLE + +tmpdir=/tmp/build +mkdir -p $tmpdir +cd $tmpdir +git clone https://github.com/postgres/postgres.git --branch REL9_4_STABLE +cd postgres/contrib +git clone https://github.com/vmware/pg_rewind.git --branch REL9_4_STABLE + +dev_pkgs="libreadline-dev libkrb5-dev libssl-dev libpam-dev libxml2-dev libxslt-dev libedit-dev libselinux1-dev bison flex" + +apt-get install $dev_pkgs -y + +# Unfortunately, on ubuntu, was not able to get pg_rewind to build +# outside of the pgsql source tree. Configure and compile postgres +# but only call make install against the contrib/pg_rewind directory +# so that support library is accessible to the server +cd $tmpdir/postgres +./configure +make +cd contrib/pg_rewind +make install + +# Make the pg_rewind binary and the library used by the +# pg_rewind stored procedures accessible +ln -s /usr/local/pgsql/bin/pg_rewind /usr/bin/pg_rewind +ln -s /usr/local/pgsql/lib/pg_rewind_support.so /usr/lib/postgresql/9.4/lib/pg_rewind_support.so + +cd +rm -rf $tmpdir +apt-get remove -y $dev_pkgs + +# End hack +################################ + +# Install the native Python client. +apt-get -y install libpq-dev +pip install psycopg2 diff --git a/integration/scripts/files/elements/ubuntu-postgresql/pre-install.d/10-postgresql-repo b/integration/scripts/files/elements/ubuntu-postgresql/pre-install.d/10-postgresql-repo new file mode 100755 index 0000000000..48a25d1d37 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-postgresql/pre-install.d/10-postgresql-repo @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e +set -o xtrace + +cat < /etc/apt/sources.list.d/postgresql.list +deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main +EOL + +wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - + +apt-get update diff --git a/integration/scripts/files/elements/ubuntu-pxc/install.d/30-mysql b/integration/scripts/files/elements/ubuntu-pxc/install.d/30-mysql new file mode 100755 index 0000000000..ae658957e2 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-pxc/install.d/30-mysql @@ -0,0 +1,14 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace + +export DEBIAN_FRONTEND=noninteractive +apt-get -y install percona-xtradb-cluster-server-5.6 percona-xtradb-cluster-client-5.6 percona-xtrabackup + +# Don't auto start mysql (we'll start it up in guest) +update-rc.d mysql defaults +update-rc.d mysql disable diff --git a/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/10-percona-apt-key b/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/10-percona-apt-key new file mode 100755 index 0000000000..c2b686c4c0 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/10-percona-apt-key @@ -0,0 +1,42 @@ +#!/bin/sh + +# CONTEXT: GUEST during PRE-CONSTRUCTION as ROOT +# PURPOSE: Setup apt-repo list so that we can connect to Percona's repo + +set -e +set -o xtrace + +[ -n "${GUEST_USERNAME}" ] || die "GUEST_USERNAME needs to be set to the user for the guest image" +[ -n "${RELEASE}" ] || die "RELEASE must be set to either Precise or Quantal" + +#5 add Percona GPG key +if [ ! -e /home/${GUEST_USERNAME}/.gnupg ]; then + mkdir -p /home/${GUEST_USERNAME}/.gnupg +fi + +# sometimes the primary key server is unavailable and we should try an +# alternate. see +# https://bugs.launchpad.net/percona-server/+bug/907789. Disable +# shell errexit so we can interrogate the exit code and take action +# based on the exit code. We will reenable it later. +set +e +apt-key adv --keyserver hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A + +if [ "$?" -ne "0" ]; +then + echo "Trying alternate keyserver hkp://keyserver.ubuntu.com" + set -e + apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 1C4CBDCDCD2EFD2A +fi + +set -e + +# add Percona repo +# creates the percona sources list +cat < /etc/apt/sources.list.d/percona.list +deb http://repo.percona.com/apt $RELEASE main +deb-src http://repo.percona.com/apt $RELEASE main +EOL + +# force an update +apt-get update diff --git a/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/20-apparmor-mysql-local b/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/20-apparmor-mysql-local new file mode 100755 index 0000000000..a3e1dc7c7d --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-pxc/pre-install.d/20-apparmor-mysql-local @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +#CONTEXT: chroot on host +#PURPOSE: Allows mysqld to create temporary files when restoring backups + +cat <>/etc/apparmor.d/local/usr.sbin.mysqld + /tmp/ rw, + /tmp/** rwk, +EOF diff --git a/integration/scripts/files/elements/ubuntu-redis/README.md b/integration/scripts/files/elements/ubuntu-redis/README.md new file mode 100644 index 0000000000..426072cfb7 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-redis/README.md @@ -0,0 +1 @@ +Sets up a redis server install in the image. diff --git a/integration/scripts/files/elements/ubuntu-redis/install.d/10-redis b/integration/scripts/files/elements/ubuntu-redis/install.d/10-redis new file mode 100755 index 0000000000..57fcdf9035 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-redis/install.d/10-redis @@ -0,0 +1,53 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT +# PURPOSE: Install controller base required packages + +set -ex + +export DEBIAN_FRONTEND=noninteractive + +cat > "/etc/sysctl.d/10-redis-performance.conf" << _EOF_ +# See 'http://redis.io/topics/admin' for best practices. +# Make sure to set the Linux kernel overcommit memory setting to 1. +vm.overcommit_memory=1 + +# Linux kernel will silently truncate 'tcp-backlog' to the value of +# '/proc/sys/net/core/somaxconn' so make sure to raise both the value of +# 'somaxconn' and 'tcp_max_syn_backlog' in order to get the desired effect. +net.ipv4.tcp_max_syn_backlog=1024 +net.core.somaxconn=1024 + +_EOF_ + +cat > "/etc/rc.local" << _EOF_ +# Make sure to disable Linux kernel feature transparent huge pages, +# it will affect greatly both memory usage and latency in a negative way. +if test -f /sys/kernel/mm/transparent_hugepage/defrag; then + echo never > /sys/kernel/mm/transparent_hugepage/defrag +fi +if test -f /sys/kernel/mm/transparent_hugepage/enabled; then + echo never > /sys/kernel/mm/transparent_hugepage/enabled +fi + +_EOF_ + +add-apt-repository -y ppa:chris-lea/redis-server +apt-get -y update +apt-get install -y redis-server + +cat > "/etc/default/redis-server" << _EOF_ +# Call ulimit -n with this argument prior to invoking Redis itself. +# This may be required for high-concurrency environments. Redis itself cannot +# alter its limits as it is not being run as root. +ULIMIT=65536 + +_EOF_ + +# Install Python driver for Redis ('redis-py'). +pip install redis + +# By default, redis-py will attempt to use the HiredisParser if installed. +# Using Hiredis can provide up to a 10x speed improvement in parsing responses +# from the Redis server. +pip install hiredis diff --git a/integration/scripts/files/elements/ubuntu-vertica/README.md b/integration/scripts/files/elements/ubuntu-vertica/README.md new file mode 100644 index 0000000000..8620237967 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-vertica/README.md @@ -0,0 +1 @@ +Sets up a Vertica CE 7.1 debian package and other dependencies install in the image. diff --git a/integration/scripts/files/elements/ubuntu-vertica/extra-data.d/93-copy-vertica-deb b/integration/scripts/files/elements/ubuntu-vertica/extra-data.d/93-copy-vertica-deb new file mode 100755 index 0000000000..56c02cda9e --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-vertica/extra-data.d/93-copy-vertica-deb @@ -0,0 +1,14 @@ +#!/bin/bash + +# CONTEXT: HOST prior to IMAGE BUILD as SCRIPT USER +# PURPOSE: Stages the package installer file from DATASTORE_PKG_LOCATION, +# so that guest image has the package file. + +set -e +set -o xtrace + +source $_LIB/die + +[ -n "$TMP_HOOKS_PATH" ] || die "Temp hook path not set" +[ -f "$DATASTORE_PKG_LOCATION" ] || die "Datastore package installer file not found at:" $DATASTORE_PKG_LOCATION +dd if=${DATASTORE_PKG_LOCATION} of=${TMP_HOOKS_PATH}/vertica.deb diff --git a/integration/scripts/files/elements/ubuntu-vertica/install.d/97-vertica b/integration/scripts/files/elements/ubuntu-vertica/install.d/97-vertica new file mode 100755 index 0000000000..2d50bc6a78 --- /dev/null +++ b/integration/scripts/files/elements/ubuntu-vertica/install.d/97-vertica @@ -0,0 +1,54 @@ +#!/bin/sh + +# CONTEXT: GUEST during CONSTRUCTION as ROOT - install.d +# PURPOSE: Install controller base required packages + +set -e +set -o xtrace +export DEBIAN_FRONTEND=noninteractive + + +# Copy the package file to the image, +# as it needs to be used later during configuration. +dd if=/tmp/in_target.d/vertica.deb of=/vertica.deb + +# Install base packages +apt-get install -qy build-essential bc iptables +apt-get install -qy curl sysstat pstack mcelog +apt-get install -qy python-dev g++ unixODBC unixODBC-dev dialog +apt-get install -qy dialog libbz2-dev libboost-all-dev libcurl4-gnutls-dev +apt-get install -qy openjdk-7-jdk + +# Install Vertica package +dpkg -i /vertica.deb + +# Creating dbadmin user and verticadba group +groupadd verticadba +useradd -g verticadba -d /home/dbadmin -s /bin/bash -m dbadmin +echo "export PATH=/opt/vertica/bin:\$PATH" >> ~dbadmin/.profile +echo "export TZ=`date +%Z`" >> ~dbadmin/.profile + +# Create base directory for to be used for database creation +mkdir /var/lib/vertica +chown dbadmin:verticadba /var/lib/vertica + +# Backup /etc/hosts +cp -p /etc/hosts /etc/hosts.bkp + +# Compile the SDK examples - the supplied UDFs can then be loaded +cd /opt/vertica/sdk/examples +TMPDIR=/tmp JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64 make +cd + +cat > "/etc/rc.local" << _EOF_ +# Vertica requires THP to be turned off +if test -f /sys/kernel/mm/transparent_hugepage/defrag; then + echo never > /sys/kernel/mm/transparent_hugepage/defrag +fi +if test -f /sys/kernel/mm/transparent_hugepage/enabled; then + echo never > /sys/kernel/mm/transparent_hugepage/enabled +fi + +exit \$? + +_EOF_ diff --git a/integration/scripts/files/keys/authorized_keys b/integration/scripts/files/keys/authorized_keys new file mode 100644 index 0000000000..4093a7145c --- /dev/null +++ b/integration/scripts/files/keys/authorized_keys @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmQeA/uyEyFf9DsmwR+OztWb7Hb/uTC+R3xG1QgBvRwhSbpBnyBESGMZZ07bIw5Ib7BUSDzwoeryUqNAhAhir2KLeIYODS39UmTwOIl+rIvhlTxhsIoQHV90pewD2qw0T8KgVMPUDsQ0Bd98E6e5dbxciZp67ihVD0r7srhdSRo8PIc56hJWrD52j5FeiIGEmLXHXiZLOyma1M7j/EmiV81wHAzgql6sihWSZHm3xPZZ712JtXbmHhe3RLFIK13u9PSb3XbuEIdGwkZdzP+vYNE0CsYqwjXjVRrY/APsiEkbSNVzHI5p2W1L7ZMtSOMUqZ1Ve+sytVb+YcIJ9L8y07 trove@devstack \ No newline at end of file diff --git a/integration/scripts/files/keys/id_rsa b/integration/scripts/files/keys/id_rsa new file mode 100644 index 0000000000..041e61fdfd --- /dev/null +++ b/integration/scripts/files/keys/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA5kHgP7shMhX/Q7JsEfjs7Vm+x2/7kwvkd8RtUIAb0cIUm6QZ +8gREhjGWdO2yMOSG+wVEg88KHq8lKjQIQIYq9ii3iGDg0t/VJk8DiJfqyL4ZU8Yb +CKEB1fdKXsA9qsNE/CoFTD1A7ENAXffBOnuXW8XImaeu4oVQ9K+7K4XUkaPDyHOe +oSVqw+do+RXoiBhJi1x14mSzspmtTO4/xJolfNcBwM4KperIoVkmR5t8T2We9dib +V25h4Xt0SxSCtd7vT0m9127hCHRsJGXcz/r2DRNArGKsI141Ua2PwD7IhJG0jVcx +yOadltS+2TLUjjFKmdVXvrMrVW/mHCCfS/MtOwIDAQABAoIBAQCTAQHbjmwuHu8e +2F24x7Tz//UWPT9fbRtK/6RO3ctlCsS/bXCHHARnrGcDdfHq1yv6PS21/UvXtThE +Dn4qO75X9DzgnAFNgEwELjPyVBM4YG2pF3SQ+MJESaI4hgGY8Rws5eMF/qFhdbo1 +hATggqFqnQZqWy0DP9wkq8ESk1nYNICehj0d6Mo5uW190TDMD6QrfHg4rDYbgITf +SCmsRdybCASlgOHCrYgjweG9czNoFimhaG8WwP59yfCX0A1TrDJ0toryyopupIre +A+5HHBM2Dk3KylrtVBAPRsM9eGUo4bmz8p6hRkuw7mr321d416MHdIXcHK38EWR0 +ZvUWM8QJAoGBAPlPe3ggR9xhEbQJQkOohnZ1hkogE/uxZrcRiUI3pnDNcCXy9Ogt +SYfwxYnHLa5kuynbmCCzSLOtq2DN9QwJ5o+zgRjX9T8DAWub6KFdEnTHhq/ZLF/w +PWPg3Oe8dYDEEcrPLvN25aetY2LrWKsRPFL8//WLJc1+LFRG6Vc1ATftAoGBAOxv +hGxNGrcD5c8g0ZcyeKVbCCRGfp0+mwonnQ25mDyIXNH+PEHa7SPs5dVexA5r0/Ky +lM/jQgs756EvslwA2oukqVz7ehDhJI4RE92OPjpYrAu7HF6eN/fAhUMghs/vAZ6c +YCM6i9emHYHM4mU6H/yLIr+0e2JNf+479bB5hQTHAoGBAOGnFUQXQ7OukE16C+Yd +RQc3PIMfIbcwTJ3qW2f54sY6zAUtMIptYx6NyN35z9kHB3jNb5Y5b9ZhnLqT7/Yj +h/INMQ4BedK8r65sgVR8X1YfukKzuLxlP8uFHa0KIPiZftkoSYDH3vmzsD86cRj1 +ErqykCH4/hBO4WSugkkSirXJAoGARXvQcvOF8lsW4nRGpCSVCCNklSSSeSu47JcP +tMTiVIfOn3gTxVbNck1gjgA3pfVSaHTK/v1On3aPb/NQe3FUyM0vaMAO8372+zlR +mT8AUq1Ugm4OvE/LKuhNQZkBhYI7+50BM9k0179d1JOdxRn75IAPSj+EMzOLcTv1 +zFMqIGkCgYBm5xT3Gu8fJh/8ztelzrDkGga6UpYKKYjHGFHpaqmDn9sjjCu8X2pG +JUGgyUVj1NkJAtHMS45Ud3upQwxpy2aNmMaQbwzHybvX7EYZHHVuCwsSzaXRtwj2 +Q6mG1Ghi0UQ76SPKQr0Vu8Uu+0CAzYAK4IEKeH6BCRjrzHggSpdNzQ== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/integration/scripts/files/keys/id_rsa.pub b/integration/scripts/files/keys/id_rsa.pub new file mode 100644 index 0000000000..4093a7145c --- /dev/null +++ b/integration/scripts/files/keys/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmQeA/uyEyFf9DsmwR+OztWb7Hb/uTC+R3xG1QgBvRwhSbpBnyBESGMZZ07bIw5Ib7BUSDzwoeryUqNAhAhir2KLeIYODS39UmTwOIl+rIvhlTxhsIoQHV90pewD2qw0T8KgVMPUDsQ0Bd98E6e5dbxciZp67ihVD0r7srhdSRo8PIc56hJWrD52j5FeiIGEmLXHXiZLOyma1M7j/EmiV81wHAzgql6sihWSZHm3xPZZ712JtXbmHhe3RLFIK13u9PSb3XbuEIdGwkZdzP+vYNE0CsYqwjXjVRrY/APsiEkbSNVzHI5p2W1L7ZMtSOMUqZ1Ve+sytVb+YcIJ9L8y07 trove@devstack \ No newline at end of file diff --git a/integration/scripts/files/requirements/fedora-requirements-default.txt b/integration/scripts/files/requirements/fedora-requirements-default.txt new file mode 100644 index 0000000000..c976bfac52 --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-default.txt @@ -0,0 +1,31 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.22.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +python-keystoneclient>=2.0.0,!=2.1.0 # Apache-2.0 +kombu>=2.5.0 +babel>=1.3 +python-heatclient>=0.3.0 +passlib +jinja2>=2.6 +PyMySQL>=0.6.2 # MIT License +python-neutronclient>=2.3.11,<3 +netifaces>=0.10.4 +oslo.config>=1.9.3 # Apache-2.0 +oslo.messaging>=1.8.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.serialization>=1.4.0 # Apache-2.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.4.0 # Apache-2.0 +oslo.log>=1.8.0 # Apache-2.0 +osprofiler>=0.3.0 +oslo.concurrency>=1.8.0 # Apache-2.0 +pexpect>=3.1,!=3.3 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain +xmltodict>=0.10.1 # MIT diff --git a/integration/scripts/files/requirements/fedora-requirements-juno.txt b/integration/scripts/files/requirements/fedora-requirements-juno.txt new file mode 100644 index 0000000000..a3ddea3a0f --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-juno.txt @@ -0,0 +1,19 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools>=0.9.34,!=1.4.0,<=1.5.0 +extras +python-novaclient>=2.18.0,<=2.20.0 +python-swiftclient>=2.2.0,<=2.3.1 +python-cinderclient>=1.1.0,<=1.1.1 +kombu>=2.5.0,<=3.0.7 +six>=1.7.0,<=1.9.0 +Babel>=1.3,<=1.3 +python-heatclient>=0.2.9,<0.3.0 +passlib<=1.6.2 +Jinja2<=2.7.2 +python-neutronclient>=2.3.6,<2.4.0 +netifaces>=0.10.4,<=0.10.4 +oslo.config>=1.4.0,<=1.6.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 diff --git a/integration/scripts/files/requirements/fedora-requirements-kilo.txt b/integration/scripts/files/requirements/fedora-requirements-kilo.txt new file mode 100644 index 0000000000..48bda11ba9 --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-kilo.txt @@ -0,0 +1,24 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools>=0.9.36,!=1.2.0 +extras +python-novaclient>=2.22.0,<2.24.0 +python-swiftclient>=2.2.0,<2.5.0 +python-cinderclient>=1.1.0,<1.2.0 +kombu>=2.5.0 +six>=1.9.0 +Babel>=1.3 +python-heatclient>=0.3.0,<0.5.0 +passlib +Jinja2>=2.6 # BSD License (3 clause) +python-neutronclient>=2.3.11,<2.5.0 +netifaces>=0.10.4 +oslo.config>=1.9.3,<1.10.0 # Apache-2.0 +oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 +oslo.serialization>=1.4.0,<1.5.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 +oslo.concurrency>=1.8.0,<1.9.0 # Apache-2.0 +oslo.messaging>=1.8.0,<1.9.0 # Apache-2.0 +osprofiler>=0.3.0 # Apache-2.0 diff --git a/integration/scripts/files/requirements/fedora-requirements-liberty.txt b/integration/scripts/files/requirements/fedora-requirements-liberty.txt new file mode 100644 index 0000000000..f47cbadab5 --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-liberty.txt @@ -0,0 +1,27 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.22.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +kombu>=2.5.0 +babel>=1.3 +python-heatclient>=0.3.0 +passlib +jinja2>=2.6 +python-neutronclient>=2.3.11,<3 +netifaces>=0.10.4 +oslo.context>=0.2.0,<=1.0.0 +oslo.config>=1.9.3,<1.10.0 # Apache-2.0 +oslo.messaging>=1.8.0 # Apache-2.0 +oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 +oslo.serialization>=1.4.0,<1.5.0 # Apache-2.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 +oslo.log>=1.8.0 # Apache-2.0 +osprofiler>=0.3.0 +oslo.concurrency>=1.8.0,<1.9.0 # Apache-2.0 +pexpect>=3.1,!=3.3 diff --git a/integration/scripts/files/requirements/fedora-requirements-mitaka.txt b/integration/scripts/files/requirements/fedora-requirements-mitaka.txt new file mode 100644 index 0000000000..6f8c478cd6 --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-mitaka.txt @@ -0,0 +1,28 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.22.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +kombu>=2.5.0 +babel>=1.3 +python-heatclient>=0.3.0 +passlib +jinja2>=2.6 +python-neutronclient>=2.3.11,<3 +netifaces>=0.10.4 +oslo.config>=1.9.3 # Apache-2.0 +oslo.messaging>=1.8.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.serialization>=1.4.0 # Apache-2.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.4.0 # Apache-2.0 +oslo.log>=1.8.0 # Apache-2.0 +osprofiler>=0.3.0 +oslo.concurrency>=1.8.0 # Apache-2.0 +pexpect>=3.1,!=3.3 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain diff --git a/integration/scripts/files/requirements/fedora-requirements-newton.txt b/integration/scripts/files/requirements/fedora-requirements-newton.txt new file mode 100644 index 0000000000..c976bfac52 --- /dev/null +++ b/integration/scripts/files/requirements/fedora-requirements-newton.txt @@ -0,0 +1,31 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.22.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +python-keystoneclient>=2.0.0,!=2.1.0 # Apache-2.0 +kombu>=2.5.0 +babel>=1.3 +python-heatclient>=0.3.0 +passlib +jinja2>=2.6 +PyMySQL>=0.6.2 # MIT License +python-neutronclient>=2.3.11,<3 +netifaces>=0.10.4 +oslo.config>=1.9.3 # Apache-2.0 +oslo.messaging>=1.8.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.serialization>=1.4.0 # Apache-2.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.4.0 # Apache-2.0 +oslo.log>=1.8.0 # Apache-2.0 +osprofiler>=0.3.0 +oslo.concurrency>=1.8.0 # Apache-2.0 +pexpect>=3.1,!=3.3 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain +xmltodict>=0.10.1 # MIT diff --git a/integration/scripts/files/requirements/ubuntu-requirements-default.txt b/integration/scripts/files/requirements/ubuntu-requirements-default.txt new file mode 100644 index 0000000000..9607060b4b --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-default.txt @@ -0,0 +1,30 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.18.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +python-keystoneclient>=2.0.0,!=2.1.0 # Apache-2.0 +kombu>=2.5.0 +six>=1.7.0 +babel +python-heatclient>=0.2.9 +passlib +jinja2 +PyMySQL>=0.6.2 # MIT License +python-neutronclient>=2.3.6,<3 +netifaces>=0.10.4 +oslo.config>=1.4.0 # Apache-2.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.i18n>=1.0.0 +oslo.serialization>=1.0.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.1.0 +osprofiler>=0.3.0 +oslo.concurrency>=0.3.0 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain +xmltodict>=0.10.1 # MIT diff --git a/integration/scripts/files/requirements/ubuntu-requirements-juno.txt b/integration/scripts/files/requirements/ubuntu-requirements-juno.txt new file mode 100644 index 0000000000..a3ddea3a0f --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-juno.txt @@ -0,0 +1,19 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools>=0.9.34,!=1.4.0,<=1.5.0 +extras +python-novaclient>=2.18.0,<=2.20.0 +python-swiftclient>=2.2.0,<=2.3.1 +python-cinderclient>=1.1.0,<=1.1.1 +kombu>=2.5.0,<=3.0.7 +six>=1.7.0,<=1.9.0 +Babel>=1.3,<=1.3 +python-heatclient>=0.2.9,<0.3.0 +passlib<=1.6.2 +Jinja2<=2.7.2 +python-neutronclient>=2.3.6,<2.4.0 +netifaces>=0.10.4,<=0.10.4 +oslo.config>=1.4.0,<=1.6.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 diff --git a/integration/scripts/files/requirements/ubuntu-requirements-kilo.txt b/integration/scripts/files/requirements/ubuntu-requirements-kilo.txt new file mode 100644 index 0000000000..48bda11ba9 --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-kilo.txt @@ -0,0 +1,24 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools>=0.9.36,!=1.2.0 +extras +python-novaclient>=2.22.0,<2.24.0 +python-swiftclient>=2.2.0,<2.5.0 +python-cinderclient>=1.1.0,<1.2.0 +kombu>=2.5.0 +six>=1.9.0 +Babel>=1.3 +python-heatclient>=0.3.0,<0.5.0 +passlib +Jinja2>=2.6 # BSD License (3 clause) +python-neutronclient>=2.3.11,<2.5.0 +netifaces>=0.10.4 +oslo.config>=1.9.3,<1.10.0 # Apache-2.0 +oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 +oslo.serialization>=1.4.0,<1.5.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 +oslo.concurrency>=1.8.0,<1.9.0 # Apache-2.0 +oslo.messaging>=1.8.0,<1.9.0 # Apache-2.0 +osprofiler>=0.3.0 # Apache-2.0 diff --git a/integration/scripts/files/requirements/ubuntu-requirements-liberty.txt b/integration/scripts/files/requirements/ubuntu-requirements-liberty.txt new file mode 100644 index 0000000000..1f3d0d7b61 --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-liberty.txt @@ -0,0 +1,26 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.18.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +kombu>=2.5.0 +six>=1.7.0 +babel +python-heatclient>=0.2.9 +passlib +jinja2 +python-neutronclient>=2.3.6,<3 +netifaces>=0.10.4 +oslo.context>=0.2.0,<=1.0.0 +oslo.config>=1.4.0 # Apache-2.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.i18n>=1.0.0 +oslo.serialization>=1.0.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.1.0 +osprofiler>=0.3.0 +oslo.concurrency>=0.3.0 diff --git a/integration/scripts/files/requirements/ubuntu-requirements-mitaka.txt b/integration/scripts/files/requirements/ubuntu-requirements-mitaka.txt new file mode 100644 index 0000000000..8724d163fb --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-mitaka.txt @@ -0,0 +1,27 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.18.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +kombu>=2.5.0 +six>=1.7.0 +babel +python-heatclient>=0.2.9 +passlib +jinja2 +python-neutronclient>=2.3.6,<3 +netifaces>=0.10.4 +oslo.config>=1.4.0 # Apache-2.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.i18n>=1.0.0 +oslo.serialization>=1.0.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.1.0 +osprofiler>=0.3.0 +oslo.concurrency>=0.3.0 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain diff --git a/integration/scripts/files/requirements/ubuntu-requirements-newton.txt b/integration/scripts/files/requirements/ubuntu-requirements-newton.txt new file mode 100644 index 0000000000..9607060b4b --- /dev/null +++ b/integration/scripts/files/requirements/ubuntu-requirements-newton.txt @@ -0,0 +1,30 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +unittest2 +testtools +extras +python-novaclient>=2.18.0 +python-swiftclient>=2.2.0 +python-cinderclient>=1.1.0 +python-keystoneclient>=2.0.0,!=2.1.0 # Apache-2.0 +kombu>=2.5.0 +six>=1.7.0 +babel +python-heatclient>=0.2.9 +passlib +jinja2 +PyMySQL>=0.6.2 # MIT License +python-neutronclient>=2.3.6,<3 +netifaces>=0.10.4 +oslo.config>=1.4.0 # Apache-2.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.i18n>=1.0.0 +oslo.serialization>=1.0.0 +oslo.service>=0.1.0 # Apache-2.0 +oslo.utils>=1.1.0 +osprofiler>=0.3.0 +oslo.concurrency>=0.3.0 +enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD +pycrypto>=2.6 # Public Domain +xmltodict>=0.10.1 # MIT diff --git a/integration/scripts/files/trove-guest.systemd.conf b/integration/scripts/files/trove-guest.systemd.conf new file mode 100644 index 0000000000..47bc2cf82d --- /dev/null +++ b/integration/scripts/files/trove-guest.systemd.conf @@ -0,0 +1,32 @@ +[Unit] +Description=Trove Guest +After=syslog.target +After=network.target + +[Service] +Type=simple +User=GUEST_USERNAME +Group=GUEST_USERNAME + +ExecStartPre=/bin/bash -c "sudo mkdir -p GUEST_LOGDIR ; sudo chown GUEST_USERNAME:root GUEST_LOGDIR" + +# If ~/trove-installed does not exist, copy the trove source from +# the user's development environment, then touch the sentinel file +ExecStartPre=/bin/bash -c "test -d /home/GUEST_USERNAME/trove-installed || sudo -u GUEST_USERNAME rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' -avz --exclude='.*' HOST_SCP_USERNAME@CONTROLLER_IP:PATH_TROVE/ /home/GUEST_USERNAME/trove && touch /home/GUEST_USERNAME/trove-installed" + +# If /etc/trove does not exist, create it and then copy the trove-guestagent.conf +# from /etc/trove on the user's development environment, +ExecStartPre=/bin/bash -c "test -d /etc/trove/conf.d || sudo mkdir -p /etc/trove/conf.d && sudo -u GUEST_USERNAME rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' -avz --exclude='.*' HOST_SCP_USERNAME@CONTROLLER_IP:/etc/trove/trove-guestagent.conf ~GUEST_USERNAME/ && sudo mv ~GUEST_USERNAME/trove-guestagent.conf /etc/trove/conf.d/trove-guestagent.conf" + +ExecStartPre=/bin/bash -c "sudo chown -R GUEST_USERNAME:root /etc/trove" + +ExecStart=/home/GUEST_USERNAME/trove/contrib/trove-guestagent --config-dir=/etc/trove/conf.d + +# Give a reasonable amount of time for the server to start up/shut down +TimeoutSec=300 + +# PgSql doesn't play nice with PrivateTmp +PrivateTmp=false + +[Install] +WantedBy=multi-user.target diff --git a/integration/scripts/files/trove-guest.upstart.conf b/integration/scripts/files/trove-guest.upstart.conf new file mode 100644 index 0000000000..2e20d6cfb9 --- /dev/null +++ b/integration/scripts/files/trove-guest.upstart.conf @@ -0,0 +1,40 @@ +description "Trove Guest" +author "Auto-Gen" + +start on (filesystem and net-device-up IFACE!=lo) +stop on runlevel [016] +chdir /var/run +pre-start script + mkdir -p /var/run/trove + chown GUEST_USERNAME:root /var/run/trove/ + + mkdir -p /var/lock/trove + chown GUEST_USERNAME:root /var/lock/trove/ + + mkdir -p GUEST_LOGDIR + chown GUEST_USERNAME:root GUEST_LOGDIR + + # Copy the trove source from the user's development environment + if [ ! -d /home/GUEST_USERNAME/trove ]; then + sudo -u GUEST_USERNAME rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' -avz --exclude='.*' HOST_SCP_USERNAME@CONTROLLER_IP:PATH_TROVE/ /home/GUEST_USERNAME/trove + fi + + # Ensure conf dir exists and is readable + mkdir -p /etc/trove/conf.d + chmod -R +r /etc/trove +end script + +script + # For backwards compatibility until https://review.openstack.org/#/c/100381 merges + TROVE_CONFIG="--config-dir=/etc/trove/conf.d" + if [ ! -f /etc/trove/conf.d/guest_info ] && [ ! -f /etc/trove/conf.d/trove-guestagent.conf ]; then + + chmod +r /etc/guest_info + sudo -u GUEST_USERNAME rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' -avz --exclude='.*' HOST_SCP_USERNAME@CONTROLLER_IP:/etc/trove/trove-guestagent.conf ~GUEST_USERNAME/ + mv ~GUEST_USERNAME/trove-guestagent.conf /etc/trove/trove-guestagent.conf + TROVE_CONFIG="--config-file=/etc/guest_info --config-file=/etc/trove/trove-guestagent.conf" + + fi + + exec su -c "/home/GUEST_USERNAME/trove/contrib/trove-guestagent $TROVE_CONFIG" GUEST_USERNAME +end script diff --git a/integration/scripts/functions b/integration/scripts/functions new file mode 100644 index 0000000000..1a09f55201 --- /dev/null +++ b/integration/scripts/functions @@ -0,0 +1,324 @@ +#!/bin/bash +# This file format was stolen from devstack <3 + +# This method was stolen from devstack +# git clone only if directory doesn't exist already. Since ``DEST`` might not +# be owned by the installation user, we create the directory and change the +# ownership to the proper user. +# Set global RECLONE=yes to simulate a clone when dest-dir exists +# git_clone remote dest-dir branch +function git_clone { + [[ "$OFFLINE" = "True" ]] && return + + GIT_REMOTE=$1 + GIT_DEST=$2 + GIT_BRANCH=$3 + + if echo $GIT_BRANCH | egrep -q "^refs"; then + # If our branch name is a gerrit style refs/changes/... + if [[ ! -d $GIT_DEST ]]; then + git_timed clone $GIT_REMOTE $GIT_DEST + fi + cd $GIT_DEST + git_timed fetch $GIT_REMOTE $GIT_BRANCH && git_timed checkout FETCH_HEAD + else + # do a full clone only if the directory doesn't exist + if [[ ! -d $GIT_DEST ]]; then + git_timed clone $GIT_REMOTE $GIT_DEST + cd $GIT_DEST + # This checkout syntax works for both branches and tags + git_timed checkout $GIT_BRANCH + elif [[ "$RECLONE" == "yes" ]]; then + # if it does exist then simulate what clone does if asked to RECLONE + cd $GIT_DEST + # set the url to pull from and fetch + git_timed remote set-url origin $GIT_REMOTE + git_timed fetch origin + # remove the existing ignored files (like pyc) as they cause breakage + # (due to the py files having older timestamps than our pyc, so python + # thinks the pyc files are correct using them) + find $GIT_DEST -name '*.pyc' -delete + git_timed checkout -f origin/$GIT_BRANCH + # a local branch might not exist + git_timed branch -D $GIT_BRANCH || true + git_timed checkout -b $GIT_BRANCH + fi + fi +} + +# Determinate is the given option present in the INI file +# ini_has_option config-file section option +function ini_has_option() { + local file=$1 + local section=$2 + local option=$3 + local line + line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" "$file") + [ -n "$line" ] +} + +# Get an option from an INI file +# iniget config-file section option +function iniget() { + local file=$1 + local section=$2 + local option=$3 + local line + line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" "$file") + echo ${line#*=} +} + +# Set an option in an INI file +# iniset config-file section option value +function iniset() { + local file=$1 + local section=$2 + local option=$3 + local value=$4 + if ! grep -q "^\[$section\]" "$file"; then + # Add section at the end + echo -e "\n[$section]" >>"$file" + fi + if ! ini_has_option "$file" "$section" "$option"; then + # Add it + sed -i -e "/^\[$section\]/ a\\ +$option = $value +" "$file" + else + # Replace it + sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=[ \t]*\).*$|\1$value|" "$file" + fi +} + +# Determine OS Vendor, Release and Update +# Tested with OS/X, Ubuntu, RedHat, CentOS, Fedora +# Returns results in global variables: +# os_VENDOR - vendor name +# os_RELEASE - release +# os_UPDATE - update +# os_PACKAGE - package type +# os_CODENAME - vendor's codename for release +# GetOSVersion +GetOSVersion() { + # Figure out which vendor we are + if [[ -x "`which sw_vers 2>/dev/null`" ]]; then + # OS/X + os_VENDOR=`sw_vers -productName` + os_RELEASE=`sw_vers -productVersion` + os_UPDATE=${os_RELEASE##*.} + os_RELEASE=${os_RELEASE%.*} + os_PACKAGE="" + if [[ "$os_RELEASE" =~ "10.7" ]]; then + os_CODENAME="lion" + elif [[ "$os_RELEASE" =~ "10.6" ]]; then + os_CODENAME="snow leopard" + elif [[ "$os_RELEASE" =~ "10.5" ]]; then + os_CODENAME="leopard" + elif [[ "$os_RELEASE" =~ "10.4" ]]; then + os_CODENAME="tiger" + elif [[ "$os_RELEASE" =~ "10.3" ]]; then + os_CODENAME="panther" + else + os_CODENAME="" + fi + elif [[ -x $(which lsb_release 2>/dev/null) ]]; then + os_VENDOR=$(lsb_release -i -s) + os_RELEASE=$(lsb_release -r -s) + os_UPDATE="" + os_PACKAGE="rpm" + if [[ "Debian,Ubuntu,LinuxMint" =~ $os_VENDOR ]]; then + os_PACKAGE="deb" + elif [[ "SUSE LINUX" =~ $os_VENDOR ]]; then + lsb_release -d -s | grep -q openSUSE + if [[ $? -eq 0 ]]; then + os_VENDOR="openSUSE" + fi + elif [[ $os_VENDOR == "openSUSE project" ]]; then + os_VENDOR="openSUSE" + elif [[ $os_VENDOR =~ Red.*Hat ]]; then + os_VENDOR="Red Hat" + fi + os_CODENAME=$(lsb_release -c -s) + elif [[ -r /etc/redhat-release ]]; then + # Red Hat Enterprise Linux Server release 5.5 (Tikanga) + # Red Hat Enterprise Linux Server release 7.0 Beta (Maipo) + # CentOS release 5.5 (Final) + # CentOS Linux release 6.0 (Final) + # Fedora release 16 (Verne) + # XenServer release 6.2.0-70446c (xenenterprise) + os_CODENAME="" + for r in "Red Hat" CentOS Fedora XenServer; do + os_VENDOR=$r + if [[ -n "`grep \"$r\" /etc/redhat-release`" ]]; then + ver=`sed -e 's/^.* \([0-9].*\) (\(.*\)).*$/\1\|\2/' /etc/redhat-release` + os_CODENAME=${ver#*|} + os_RELEASE=${ver%|*} + os_UPDATE=${os_RELEASE##*.} + os_RELEASE=${os_RELEASE%.*} + break + fi + os_VENDOR="" + done + os_PACKAGE="rpm" + elif [[ -r /etc/SuSE-release ]]; then + for r in openSUSE "SUSE Linux"; do + if [[ "$r" = "SUSE Linux" ]]; then + os_VENDOR="SUSE LINUX" + else + os_VENDOR=$r + fi + + if [[ -n "`grep \"$r\" /etc/SuSE-release`" ]]; then + os_CODENAME=`grep "CODENAME = " /etc/SuSE-release | sed 's:.* = ::g'` + os_RELEASE=`grep "VERSION = " /etc/SuSE-release | sed 's:.* = ::g'` + os_UPDATE=`grep "PATCHLEVEL = " /etc/SuSE-release | sed 's:.* = ::g'` + break + fi + os_VENDOR="" + done + os_PACKAGE="rpm" + # If lsb_release is not installed, we should be able to detect Debian OS + elif [[ -f /etc/debian_version ]] && [[ $(cat /proc/version) =~ "Debian" ]]; then + os_VENDOR="Debian" + os_PACKAGE="deb" + os_CODENAME=$(awk '/VERSION=/' /etc/os-release | sed 's/VERSION=//' | sed -r 's/\"|\(|\)//g' | awk '{print $2}') + os_RELEASE=$(awk '/VERSION_ID=/' /etc/os-release | sed 's/VERSION_ID=//' | sed 's/\"//g') + fi + export os_VENDOR os_RELEASE os_UPDATE os_PACKAGE os_CODENAME +} + + +# Translate the OS version values into common nomenclature +# Sets ``DISTRO`` from the ``os_*`` values +function GetDistro() { + GetOSVersion + if [[ "$os_VENDOR" =~ (Ubuntu) || "$os_VENDOR" =~ (Debian) ]]; then + # 'Everyone' refers to Ubuntu / Debian releases by the code name adjective + DISTRO=$os_CODENAME + elif [[ "$os_VENDOR" =~ (Fedora) ]]; then + # For Fedora, just use 'f' and the release + DISTRO="f$os_RELEASE" + elif [[ "$os_VENDOR" =~ (openSUSE) ]]; then + DISTRO="opensuse-$os_RELEASE" + elif [[ "$os_VENDOR" =~ (SUSE LINUX) ]]; then + # For SLE, also use the service pack + if [[ -z "$os_UPDATE" ]]; then + DISTRO="sle${os_RELEASE}" + else + DISTRO="sle${os_RELEASE}sp${os_UPDATE}" + fi + elif [[ "$os_VENDOR" =~ (Red Hat) || "$os_VENDOR" =~ (CentOS) ]]; then + # Drop the . release as we assume it's compatible + DISTRO="rhel${os_RELEASE::1}" + elif [[ "$os_VENDOR" =~ (XenServer) ]]; then + DISTRO="xs$os_RELEASE" + else + # Catch-all for now is Vendor + Release + Update + DISTRO="$os_VENDOR-$os_RELEASE.$os_UPDATE" + fi + export DISTRO +} + + +# Determine if current distribution is a Fedora-based distribution +# (Fedora, RHEL, CentOS, etc). +# is_fedora +function is_fedora { + if [[ -z "$os_VENDOR" ]]; then + GetOSVersion + fi + + [ "$os_VENDOR" = "Fedora" ] || [ "$os_VENDOR" = "Red Hat" ] || [ "$os_VENDOR" = "CentOS" ] +} + + +# Determine if current distribution is a SUSE-based distribution +# (openSUSE, SLE). +# is_suse +function is_suse { + if [[ -z "$os_VENDOR" ]]; then + GetOSVersion + fi + + [ "$os_VENDOR" = "openSUSE" ] || [ "$os_VENDOR" = "SUSE LINUX" ] +} + +# Get the path to the direcotry where python executables are installed. +# get_python_exec_prefix +function get_python_exec_prefix() { + if is_fedora || is_suse; then + echo "/usr/bin" + else + echo "/usr/local/bin" + fi +} + +# Returns 'true' if 'true', 'yes', 'on' or '1' - false, otherwise. +# Converts values to lower case first. +# If no default is provided, assumes false. +function get_bool() { + local VARIABLE="$1" + local DEFAULT=${2:-false} + + VALUE=${!VARIABLE:-$DEFAULT} + + VALUE=$(eval echo "$VALUE" | tr '[:upper:]' '[:lower:]') + if [[ "1 yes true on" =~ "$VALUE" ]]; then + VALUE=true + else + VALUE=false + fi + + echo $VALUE +} + +# Get the project branch to switch to. Uses PROJECT_BRANCH_NAME, +# then BRANCH_DEFAULT, then 'master' +function get_project_branch() { + local PROJECT_BRANCH_NAME=$1 + local BRANCH_DEFAULT=${2:-master} + + PROJECT_BRANCH_OVERRIDE=${!PROJECT_BRANCH_NAME} + + BRANCH=${PROJECT_BRANCH_OVERRIDE:-$BRANCH_DEFAULT} + + echo "$BRANCH" +} + +# Try to switch to a branch or commit in a repo +# Fails if the branch/commit doesn't exist +function git_checkout() { + local PROJECT=$1 + local REPO_DIR=$2 + local REPO_BRANCH=$3 + local REPO_BRANCH_VAR_NAME=$4 + + if [ -n "$REPO_BRANCH" ]; then + pushd "$REPO_DIR" + if [ $PROJECT == "diskimage-builder" ] || [ $PROJECT == "tripleo-image-elements" ]; then + REPO_BRANCH=master + fi + CURRENT_BRANCH=$(git branch | grep "\*" | awk '{print $2}') + GIT_STATUS=$(git checkout "$REPO_BRANCH" &> /dev/null || echo "failed") + if [[ "$GIT_STATUS" = "failed" ]]; then + exclaim "${COLOR_RED}Could not switch to branch/commit '$REPO_BRANCH' in $PROJECT, exiting${COLOR_NONE}" + echo "Please set '$REPO_BRANCH_VAR_NAME' to a valid branch/commit and try again." + if [[ "$CURRENT_BRANCH" != "master" ]]; then + echo "Note: This repo is currently on branch ${CURRENT_BRANCH} - if this is correct," + echo "you should set $REPO_BRANCH_VAR_NAME=${CURRENT_BRANCH} and re-run your command." + else + echo "Note: This error may also mean that there are modified files in $PROJECT." + echo " If that is the case, please stash them and re-run your command." + fi + exit 1 + else + if [[ "$REPO_BRANCH" != "$CURRENT_BRANCH" ]]; then + exclaim "${COLOR_BLUE}Switched to $PROJECT branch '$REPO_BRANCH'${COLOR_NONE}" + else + echo "Using $PROJECT branch '$REPO_BRANCH'" + fi + fi + popd + fi +} + diff --git a/integration/scripts/functions_qemu b/integration/scripts/functions_qemu new file mode 100644 index 0000000000..62875eec0a --- /dev/null +++ b/integration/scripts/functions_qemu @@ -0,0 +1,174 @@ +#!/bin/bash +# +# Additional functions that would mostly just pertain to a Ubuntu + Qemu setup +# + +function build_vm() { + exclaim "Actually building the image, this can take up to 15 minutes" + + # set variables here and ensure they are not changed during the duration of this script + readonly HOMEDIR=$1 + readonly HOST_USERNAME=$2 + GUEST_USERNAME=${GUEST_USERNAME:-$2} + HOST_SCP_USERNAME=${HOST_SCP_USERNAME:-$2} + VM=$3 + DISTRO=$4 + SERVICE_TYPE=$5 + + readonly SSH_DIR=${KEY_DIR:-${HOMEDIR}/.ssh} + manage_ssh_keys + + if [ $DISTRO == 'ubuntu' ]; then + export RELEASE=trusty + export DIB_RELEASE=$RELEASE + export DIB_CLOUD_IMAGES=cloud-images.ubuntu.com + # Use the apt sources.list on the build host, its almost always preferred + if [ -f /etc/apt/sources.list ]; then + export DIB_APT_SOURCES=/etc/apt/sources.list + EXTRA_ELEMENTS="apt-sources apt-conf-dir" + fi + fi + if [ $DISTRO == 'fedora' ]; then + EXTRA_ELEMENTS=selinux-permissive + fi + + export HOST_USERNAME + export HOST_SCP_USERNAME + export GUEST_USERNAME + export CONTROLLER_IP + export REDSTACK_SCRIPTS + export SERVICE_TYPE + export PATH_TROVE + export ESCAPED_PATH_TROVE + export SSH_DIR + export GUEST_LOGDIR + export ESCAPED_GUEST_LOGDIR + export ELEMENTS_PATH=$REDSTACK_SCRIPTS/files/elements + export ELEMENTS_PATH+=:$PATH_DISKIMAGEBUILDER/elements + export ELEMENTS_PATH+=:$PATH_TRIPLEO_ELEMENTS/elements + export DIB_CLOUD_INIT_DATASOURCES="ConfigDrive" + export DATASTORE_PKG_LOCATION + export BRANCH_OVERRIDE + export DIB_APT_CONF_DIR=/etc/apt/apt.conf.d + export DIB_CLOUD_INIT_ETC_HOSTS=true + local QEMU_IMG_OPTIONS=$(! $(qemu-img | grep -q 'version 1') && echo "--qemu-img-options compat=0.10") + ${PATH_DISKIMAGEBUILDER}/bin/disk-image-create -a amd64 -o "${VM}" \ + -x ${QEMU_IMG_OPTIONS} ${DISTRO} ${EXTRA_ELEMENTS} vm heat-cfntools \ + cloud-init-datasources ${DISTRO}-guest ${DISTRO}-${SERVICE_TYPE} +} + +function build_guest_image() { + exclaim "Building an image for use with development and integration tests." + if [ -z "$1" ] + then + echo "You must pass an image type to build, like mysql" + exit 1 + fi + SERVICE_TYPE=$1 + VALID_SERVICES='mysql percona mariadb redis cassandra couchbase mongodb postgresql couchdb vertica db2 pxc' + if ! [[ " $VALID_SERVICES " =~ " $SERVICE_TYPE " ]]; then + exclaim "You did not pass in a valid image type. Valid types are:" $VALID_SERVICES + exit 1 + fi + + GUEST_LOGDIR=$(iniget $PATH_TROVE/etc/trove/trove-guestagent.conf.sample DEFAULT log_dir) + GUEST_LOGFILE=$(iniget $PATH_TROVE/etc/trove/trove-guestagent.conf.sample DEFAULT log_file) + + if [ -z $GUEST_LOGDIR ] || [ -z $GUEST_LOGFILE ] + then + exclaim "error: log_dir and log_file are required in: " $PATH_TROVE/etc/trove/trove-guestagent.conf.sample + exit 1 + fi + + ESCAPED_GUEST_LOGDIR=`echo $GUEST_LOGDIR | sed 's/\//\\\\\//g'` + + USERNAME=`whoami` + # To change the distro, edit the redstack.rc file + readonly IMAGENAME=${DISTRO}_${SERVICE_TYPE} + readonly VM_PATH=$USERHOME/images/${IMAGENAME} + readonly VM_PATH_NAME=${VM_PATH}/${IMAGENAME} + mkdir -p $VM_PATH + + # If the path does not exist, build it, otherwise just upload it + # (unless we're explicitly told to rebuild it) + REBUILD_IMAGE=$(echo "${REBUILD_IMAGE}" | tr '[:upper:]' '[:lower:]') + if [ "${REBUILD_IMAGE}" = "true" ] || [ ! -d $VM_PATH ] || [ `ls -1 $VM_PATH | wc -l` -eq '0' ] + then + if [ "${REBUILD_IMAGE}" = "true" ] + then + exclaim "Rebuilding image" + rm -rf "${VM_PATH}" + fi + build_vm $USERHOME $USERNAME $VM_PATH_NAME $DISTRO $SERVICE_TYPE + touch -c "${VM_PATH}" + else + exclaim "Found image in $VM_PATH - using the qcow2 image found here..." + ELEMENTS_DIR="files/elements/${DISTRO}-${SERVICE_TYPE}" + ELEMENTS_DIR_GUEST="files/elements/${DISTRO}-guest" + # Print out a warning on all the elements files that are newer than the image. + # Directories are not excluded as that is the only way to determine if a file + # has been removed. + # The rebuild is not automatically triggered as there are valid reasons for a + # new file to be present (rollback of change, inadvertent .log files present, + # feature half implemented, etc.). + IMAGE_OLD= + while IFS= read -r -d '' ELEMENT_FILE + do + if [ "${ELEMENT_FILE}" -nt "${VM_PATH}" ] + then + IMAGE_OLD=true + exclaim "${COLOR_RED}WARNING: Element file '${ELEMENT_FILE}' is newer than cached image${COLOR_NONE}" + fi + done < <(find "${ELEMENTS_DIR}" "${ELEMENTS_DIR_GUEST}" -depth -print0) + if [ "${IMAGE_OLD}" = "true" ] + then + exclaim "${COLOR_RED}Use ${COLOR_NONE}REBUILD_IMAGE=True${COLOR_RED} to rebuild image${COLOR_NONE}" + fi + + fi +} + +function clean_instances() { + LIST=`virsh -q list|awk '{print $1}'` + for i in $LIST; do sudo virsh destroy $i; done +} + +function manage_ssh_keys() { + if [ -e ${SSH_DIR} ]; then + echo "${SSH_DIR} already exists" + else + echo "Creating ${SSH_DIR} for ${HOST_USERNAME}" + sudo -Hiu ${HOST_USERNAME} mkdir -m go-w -p ${SSH_DIR} + fi + + if [ ! -f ${SSH_DIR}/id_rsa.pub ]; then + sudo ${PKG_MGR} ${PKG_GET_ARGS} install expect + generate_empty_passphrase_ssh_key ${HOST_USERNAME} + fi + + add_host_key_to_authorizedkeys +} + +function generate_empty_passphrase_ssh_key () { + echo "generating a empty passphrase DEV ONLY rsa key" + expect -c " +spawn sudo -Hiu ${HOST_USERNAME} /usr/bin/ssh-keygen -f ${SSH_DIR}/id_rsa -q +expect \"empty for no passphrase\" +send \n +expect assphrase +send \n +expect eof" +} + +function add_host_key_to_authorizedkeys () { + # test to see if the host key is already in its own authorized_keys file - if not then add it. This is then later copied + # to the guest image + is_in_keyfile=`cat ${SSH_DIR}/id_rsa.pub | grep -f - ${SSH_DIR}/authorized_keys | wc -l` + if [ $is_in_keyfile == 0 ]; then + echo "Adding keyfile to authorized_keys, it does not yet exist" + cat ${SSH_DIR}/id_rsa.pub >> ${SSH_DIR}/authorized_keys + chmod 600 ${SSH_DIR}/authorized_keys + else + echo "Keyfile already exists in authorized_keys - skipping" + fi +} diff --git a/integration/scripts/image-projects-list b/integration/scripts/image-projects-list new file mode 100644 index 0000000000..76d0da45b8 --- /dev/null +++ b/integration/scripts/image-projects-list @@ -0,0 +1,3 @@ +diskimage-builder +tripleo-image-elements +trove diff --git a/integration/scripts/local.conf.d/ceilometer_cinder.conf.rc b/integration/scripts/local.conf.d/ceilometer_cinder.conf.rc new file mode 100644 index 0000000000..9b80b6c690 --- /dev/null +++ b/integration/scripts/local.conf.d/ceilometer_cinder.conf.rc @@ -0,0 +1,3 @@ +[[post-config|\$CINDER_CONF]] +[DEFAULT] +notification_driver = messagingv2 diff --git a/integration/scripts/local.conf.d/ceilometer_nova.conf.rc b/integration/scripts/local.conf.d/ceilometer_nova.conf.rc new file mode 100644 index 0000000000..fe48b0224b --- /dev/null +++ b/integration/scripts/local.conf.d/ceilometer_nova.conf.rc @@ -0,0 +1,3 @@ +[[post-config|\$NOVA_CONF]] +[DEFAULT] +instance_usage_audit = True diff --git a/integration/scripts/local.conf.d/ceilometer_services.conf.rc b/integration/scripts/local.conf.d/ceilometer_services.conf.rc new file mode 100644 index 0000000000..ce33948ef2 --- /dev/null +++ b/integration/scripts/local.conf.d/ceilometer_services.conf.rc @@ -0,0 +1,3 @@ +[[post-config|\$CEILOMETER_CONF]] +[notification] +store_events = True diff --git a/integration/scripts/local.conf.d/sample.rc b/integration/scripts/local.conf.d/sample.rc new file mode 100644 index 0000000000..78753bc7d7 --- /dev/null +++ b/integration/scripts/local.conf.d/sample.rc @@ -0,0 +1,42 @@ +# +# Files in this directory are automatically added to the devstack +# local.conf file, between a specific set of tags. +# +# Filenames must end with '.rc' to be recognized; sample.rc is +# ignored. +# +# A '\' is required in front of any devstack variables since all +# .rc files are parsed first (using eval). +# +# Meta section headings must be included in each file, such as: +# [[local|localrc]] +# as the order of inserting the files is not guaranteed. +# +# All files are inherently included by default - to exclude a file, +# add a variable 'FILENAME_IN_UPPERCASE_MINUS_RC=false' in redstack.rc +# For Example: USING_VAGRANT=false (for the using_vagrant.rc file). +# +# Symbolic links are followed, so additional files can be loaded +# by placing them in an external directory and linking it in +# local.conf.d (this should allow complete flexibility in setting +# up testing options). +# For Example: +# cd /path/to/trove-integration/scripts/local.conf.d +# ln -s $HOME/local.conf.d local.conf.d +# cp /path/to/my_conf.rc $HOME/local.conf.d + + +[[local|localrc]] +# Put regular devstack variables under this meta section heading. +# This section is written out to a file and sourced by devstack, +# so it can contain logic as well. + +# The following section types should only contain ini file style +# section headings and name=value pairs +[[post-config|\$TROVE_CONF]] + +[[post-config|\$TROVE_TASKMANAGER_CONF]] + +[[post-config|\$TROVE_CONDUCTOR_CONF]] + +[[post-config|\$TROVE_API_PASTE_INI]] diff --git a/integration/scripts/local.conf.d/trove_services.conf.rc b/integration/scripts/local.conf.d/trove_services.conf.rc new file mode 100644 index 0000000000..6eedc1c30b --- /dev/null +++ b/integration/scripts/local.conf.d/trove_services.conf.rc @@ -0,0 +1,24 @@ +[[post-config|\$TROVE_CONF]] +[profiler] +enabled = $ENABLE_PROFILER +trace_sqlalchemy = $PROFILER_TRACE_SQL + +[[post-config|\$TROVE_TASKMANAGER_CONF]] +[profiler] +enabled = $ENABLE_PROFILER +trace_sqlalchemy = $PROFILER_TRACE_SQL + +[[post-config|\$TROVE_CONDUCTOR_CONF]] +[profiler] +enabled = $ENABLE_PROFILER +trace_sqlalchemy = $PROFILER_TRACE_SQL + +[[post-config|\$TROVE_GUESTAGENT_CONF]] +[profiler] +enabled = $ENABLE_PROFILER +trace_sqlalchemy = $PROFILER_TRACE_SQL + +[[post-config|\$TROVE_API_PASTE_INI]] +[filter:osprofiler] +enabled = $ENABLE_PROFILER +hmac_keys = $PROFILER_HMAC_KEYS diff --git a/integration/scripts/local.conf.d/use_kvm.rc b/integration/scripts/local.conf.d/use_kvm.rc new file mode 100644 index 0000000000..06bc2ebcb2 --- /dev/null +++ b/integration/scripts/local.conf.d/use_kvm.rc @@ -0,0 +1,4 @@ +[[local|localrc]] + +# force kvm as the libvirt type. +LIBVIRT_TYPE=kvm diff --git a/integration/scripts/local.conf.d/use_uuid_token.rc b/integration/scripts/local.conf.d/use_uuid_token.rc new file mode 100644 index 0000000000..587a4064c8 --- /dev/null +++ b/integration/scripts/local.conf.d/use_uuid_token.rc @@ -0,0 +1,3 @@ +[[local|localrc]] + +KEYSTONE_TOKEN_FORMAT=UUID diff --git a/integration/scripts/local.conf.d/using_vagrant.rc b/integration/scripts/local.conf.d/using_vagrant.rc new file mode 100644 index 0000000000..7333cd466b --- /dev/null +++ b/integration/scripts/local.conf.d/using_vagrant.rc @@ -0,0 +1,9 @@ +[[local|localrc]] + +# This is similar to code found at +# https://github.com/bcwaldon/vagrant_devstack/blob/master/Vagrantfile +# and seems to make instances ping'able in VirtualBox. +FLAT_INTERFACE=eth1 +PUBLIC_INTERFACE=eth1 +FLOATING_RANGE=`ip_chunk eth0 1`.`ip_chunk eth0 2`.`ip_chunk eth0 3`.128/28 +HOST_IP=`ip_chunk eth0 1`.`ip_chunk eth0 2`.`ip_chunk eth0 3`.`ip_chunk eth0 4` diff --git a/integration/scripts/local.conf.rc b/integration/scripts/local.conf.rc new file mode 100644 index 0000000000..6b9eb0ad75 --- /dev/null +++ b/integration/scripts/local.conf.rc @@ -0,0 +1,37 @@ +$TROVE_PRESENT_TAG +# Set some arguments for devstack. +# +# Note: This file contains autogenerated parts. +# All lines are removed from between the tag/end of tag +# markers (lines with '$MARKER_TOKEN' at beginning and end) and +# are replaced by trove-integration. +# Edits to these sections will not persist. +# +# See the '$USER_OPTS_TAG' section +# for ways to insert user args into this file. +# + +# +# This section is for things that belong in localrc +# It comes from $DEFAULT_LOCALRC +# +[[local|localrc]] + +$LOCALRC_OPTS_TAG +$LOCALRC_OPTS_TAG_END + +# +# User options here were inserted from the file USER_LOCAL_CONF +# (defaults to $USERHOME/.$LOCAL_CONF) +# + +$USER_OPTS_TAG +$USER_OPTS_TAG_END + +# +# Additional options here were inserted by trove-integration +# automatically from files in $LOCAL_CONF_D +# + +$ADD_OPTS_TAG +$ADD_OPTS_TAG_END diff --git a/integration/scripts/localrc.rc b/integration/scripts/localrc.rc new file mode 100644 index 0000000000..94c598ee6e --- /dev/null +++ b/integration/scripts/localrc.rc @@ -0,0 +1,100 @@ +# These passwords originally come from redstack.rc. +MYSQL_PASSWORD=$MYSQL_PASSWORD +RABBIT_PASSWORD=$RABBIT_PASSWORD +SERVICE_TOKEN=$SERVICE_TOKEN +ADMIN_PASSWORD=$ADMIN_PASSWORD +SERVICE_PASSWORD=$SERVICE_PASSWORD + +PUBLIC_INTERFACE=eth0 +TROVE_LOGDIR=$TROVE_LOGDIR +TROVE_AUTH_CACHE_DIR=$TROVE_AUTH_CACHE_DIR + +# Enable the Trove plugin for devstack +if [[ $USE_DEVSTACK_TROVE_PLUGIN = true ]]; then + enable_plugin trove $TROVE_REPO $TROVE_BRANCH +fi + +# Enable Trove, Swift, and Heat +ENABLED_SERVICES+=,trove,tr-api,tr-tmgr,tr-cond +ENABLED_SERVICES+=,s-proxy,s-object,s-container,s-account +ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng +ENABLED_SERVICES+=,-n-novnc,-n-xvnc +if [[ $ENABLE_NEUTRON = true ]]; then + ENABLED_SERVICES+=,neutron,q-svc,q-agt,q-dhcp,q-l3,q-meta + disable_service n-net +else + enable_service n-net + disable_service neutron q-svc q-agt q-dhcp q-l3 q-meta +fi +# OSprofiler depends on Ceilometer +if [[ $ENABLE_CEILOMETER = true ]] || [[ $ENABLE_PROFILER = true ]]; then + CEILOMETER_BACKEND=mysql + CEILOMETER_NOTIFICATION_TOPICS=notifications,profiler + PROJ_BRANCH=$(get_project_branch CEILOMETER_BRANCH $PROJECT_BRANCH) + enable_plugin ceilometer ${GIT_OPENSTACK}/ceilometer.git $PROJ_BRANCH +fi + +# Enable Mistral, if configured +if [[ $ENABLE_MISTRAL = true ]]; then + PROJ_BRANCH=$(get_project_branch MISTRAL_BRANCH $PROJECT_BRANCH) + enable_plugin mistral ${GIT_OPENSTACK}/mistral.git $PROJ_BRANCH +fi + +# Use Git repositories for client components +LIBS_FROM_GIT=python-troveclient +if [[ $LIBS_FROM_GIT_ALL_CLIENTS = true ]]; then + LIBS_FROM_GIT+=,python-ceilometerclient + LIBS_FROM_GIT+=,python-cinderclient + LIBS_FROM_GIT+=,python-glanceclient + LIBS_FROM_GIT+=,python-heatclient + LIBS_FROM_GIT+=,python-keystoneclient + LIBS_FROM_GIT+=,python-mistralclient + LIBS_FROM_GIT+=,python-neutronclient + LIBS_FROM_GIT+=,python-novaclient + LIBS_FROM_GIT+=,python-openstackclient + LIBS_FROM_GIT+=,python-swiftclient +else + if [[ $ENABLE_NEUTRON = true ]]; then + LIBS_FROM_GIT+=,python-neutronclient + fi + if [[ $ENABLE_MISTRAL = true ]]; then + LIBS_FROM_GIT+=,python-mistralclient + fi + if [[ $ENABLE_CEILOMETER = true ]]; then + LIBS_FROM_GIT+=,python-ceilometerclient + fi +fi +# Add Git repositories for libraries +if [[ $LIBS_FROM_GIT_ALL_OSLO = true ]]; then + LIBS_FROM_GIT+=,cliff,futurist + LIBS_FROM_GIT+=,debtcollector,automaton + LIBS_FROM_GIT+=,oslo.cache,oslo.concurrency + LIBS_FROM_GIT+=,oslo.config,oslo.context + LIBS_FROM_GIT+=,oslo.db,oslo.i18n + LIBS_FROM_GIT+=,oslo.log,oslo.messaging + LIBS_FROM_GIT+=,oslo.middleware,oslo.policy + LIBS_FROM_GIT+=,oslo.reports,oslo.rootwrap + LIBS_FROM_GIT+=,oslo.serialization,oslo.service + LIBS_FROM_GIT+=,oslo.utils,oslo.versionedobjects + LIBS_FROM_GIT+=,oslo.vmware + LIBS_FROM_GIT+=,pycadf,stevedore + LIBS_FROM_GIT+=,taskflow,tooz + LIBS_FROM_GIT+=,pbr +fi + +NOVNC_FROM_PACKAGE=false +SWIFT_HASH=$SWIFT_HASH +# Set Cinder Volume from Redstack so that later Redstack can help manage +# reconnecting Volume Group to Backing File +DEST=$DEST +DATA_DIR=$DATA_DIR +SERVICE_DIR=$SERVICE_DIR +VOLUME_GROUP=${VOLUME_GROUP} +VOLUME_BACKING_FILE=${VOLUME_BACKING_FILE} +VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE} +# The lock_path is by default /opt/stack/nova; if this path is a shared +# folder in VirtualBox things seem to break. We fix it by setting EXTRA_OPS +# to force lock_path to /tmp. +EXTRA_OPTS=(lock_path=$USERHOME/nova_locks rescan_timeout=180 resizefs_timeout=240 force_dhcp_release=False) +UNDO_REQUIREMENTS=False + diff --git a/integration/scripts/projects-list b/integration/scripts/projects-list new file mode 100644 index 0000000000..eacd4d8e8b --- /dev/null +++ b/integration/scripts/projects-list @@ -0,0 +1,12 @@ +keystone +nova +glance +horizon +swift +neutron +heat +python-openstackclient +python-novaclient +python-troveclient +python-neutronclient +python-heatclient diff --git a/integration/scripts/redstack b/integration/scripts/redstack new file mode 100755 index 0000000000..105aceb97c --- /dev/null +++ b/integration/scripts/redstack @@ -0,0 +1,1433 @@ +#!/usr/bin/env bash +############################################################################### +# RedStack, the Trove Dev Machine Controller # +############################################################################### +# # +# This script provides all the functionality to run all the steps from # +# setting up the environment, resetting the nova database to running the # +# test. # +# # +############################################################################### + +REDSTACK_SCRIPTS=${REDSTACK_SCRIPTS:-`pwd`} +REDSTACK_TESTS=$REDSTACK_SCRIPTS/../tests/ + +DEFAULT_LOCAL_CONF=local.conf.rc +DEFAULT_LOCALRC=localrc.rc +LOCAL_CONF=local.conf +LOCALRC=localrc +LOCALRC_AUTO=.localrc.auto +USER_LOCAL_CONF_NAME=.devstack.$LOCAL_CONF + +USERHOME=$HOME +# Load options not checked into VCS. +if [ -f $USERHOME/.redstack.options.rc ]; then + . $USERHOME/.redstack.options.rc +fi +if [ -f $REDSTACK_SCRIPTS/options.rc ]; then + . $REDSTACK_SCRIPTS/options.rc +fi + +# NOTE(mriedem): The gate-trove-functional-dsvm-* job config in project-config +# sets this value for Jenkins runs. +BRANCH_OVERRIDE=${BRANCH_OVERRIDE:-default} +if [[ $BRANCH_OVERRIDE == "default" && $OVERRIDE_ZUUL_BRANCH != "master" ]]; then + BRANCH_OVERRIDE=$OVERRIDE_ZUUL_BRANCH +fi + +# Bail on errors. +set -e + +# Get default host ip from interface +function get_default_host_ip() { + host_iface=$(ip route | sed -n '/^default/{ s/.*dev \(\w\+\)\s\+.*/\1/; p; }' | head -1) + echo `LC_ALL=C ip -f inet addr show ${host_iface} | awk '/inet/ {split($2,parts,"/"); print parts[1]}' | head -1` +} + +# Load functions devstack style +. $REDSTACK_SCRIPTS/functions +. $REDSTACK_SCRIPTS/functions_qemu + +# Load global configuration variables. +. $REDSTACK_SCRIPTS/redstack.rc +. $REDSTACK_SCRIPTS/reviews.rc + +# allow overrides from devstack if already set +[[ -f $PATH_DEVSTACK_SRC/functions-common ]] && source $PATH_DEVSTACK_SRC/functions-common +[[ -f $PATH_DEVSTACK_SRC/functions ]] && source $PATH_DEVSTACK_SRC/functions + +# Source the old-style localrc, or new-style .local.auto - only one should exist. +# Note: The devstack localrc's have references to 'enable_plugin' which causes +# errors when sourcing them in the stable/juno and stable/kilo branches. +# These errors are safe to ignore when sourcing these files. +set +e +[[ -f $PATH_DEVSTACK_SRC/$LOCALRC ]] && source $PATH_DEVSTACK_SRC/$LOCALRC +[[ -f $PATH_DEVSTACK_SRC/$LOCALRC_AUTO ]] && source $PATH_DEVSTACK_SRC/$LOCALRC_AUTO +set -e + +# Set up variables for the CONF files - this has to happen after loading redstack.rc, since +# TROVE_CONF_DIR is defined there - these will be used by devstack too +export TROVE_CONF=$TROVE_CONF_DIR/trove.conf +export TROVE_TASKMANAGER_CONF=$TROVE_CONF_DIR/trove-taskmanager.conf +export TROVE_CONDUCTOR_CONF=$TROVE_CONF_DIR/trove-conductor.conf +export TROVE_GUESTAGENT_CONF=$TROVE_CONF_DIR/trove-guestagent.conf +export TROVE_API_PASTE_INI=$TROVE_CONF_DIR/api-paste.ini +export TEST_CONF=$TROVE_CONF_DIR/test.conf + +# Public facing bits +SERVICE_PROTOCOL=${SERVICE_PROTOCOL:-http} +NETWORK_INTERFACE=${NETWORK_INTERFACE:-eth0} +NETWORK_SUBNET=${NETWORK_SUBNET:-10.0.0.0/24} +NETWORK_GATEWAY=${NETWORK_GATEWAY:-10.0.0.1} +BRIDGE_IP=${BRIDGE_IP:-172.24.4.1} +KEYSTONE_AUTH_HOST=${KEYSTONE_AUTH_HOST:-$SERVICE_HOST} +KEYSTONE_AUTH_PROTOCOL=${KEYSTONE_AUTH_PROTOCOL:-$SERVICE_PROTOCOL} +KEYSTONE_AUTH_PORT=${KEYSTONE_AUTH_PORT:-35357} +GLANCE_HOSTPORT=${GLANCE_HOSTPORT:-$SERVICE_HOST:9292} +GLANCE_SERVICE_PROTOCOL=${GLANCE_SERVICE_PROTOCOL:-http} +TROVE_AUTH_ENDPOINT=$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT/v2.0 + +# The following depends on whether neutron is used or nova-network +# neutron uses a bridge, nova-network does not +[[ $ENABLE_NEUTRON = true ]] && CONTROLLER_IP=$BRIDGE_IP || CONTROLLER_IP=$NETWORK_GATEWAY + +# PATH_TROVE more than likely has file separators, which sed does not like +# This will escape them +ESCAPED_PATH_TROVE=$(echo $PATH_TROVE | sed 's/\//\\\//g') +ESCAPED_REDSTACK_SCRIPTS=$(echo $REDSTACK_SCRIPTS | sed 's/\//\\\//g') +TROVE_AUTH_CACHE_DIR=${TROVE_AUTH_CACHE_DIR:-/var/cache/trove} +TROVE_LOGDIR=${TROVE_LOGDIR:-$DEST/logs} +# DATASTORE_PKG_LOCATION defines the location from where the datastore packages +# can be accessed by the DIB elements. This is applicable only for datastores +# that do not have a public repository from where their packages can be accessed. +# This can either be a url to a private repository or a location on the local +# filesystem that contains the datastore packages. +DATASTORE_PKG_LOCATION=${DATASTORE_PKG_LOCATION:-} + +# Support entry points installation of console scripts +if [[ -d $PATH_TROVE/bin ]]; then + TROVE_BIN_DIR=$PATH_TROVE/bin +else + TROVE_BIN_DIR=$(get_python_exec_prefix) +fi + +# Allow sourcing config values from env.rc for overrides +if [ -f /tmp/integration/env.rc ]; then + source /tmp/integration/env.rc +fi + +# set up respective package managers +if is_fedora; then + PKG_INSTALL_OPTS="" + PKG_MGR=dnf + PKG_GET_ARGS="-y" +else + PKG_INSTALL_OPTS="DEBIAN_FRONTEND=noninteractive" + PKG_MGR=apt-get + PKG_GET_ARGS="-y --allow-unauthenticated --force-yes" +fi +PKG_INSTALL_ARG="install" +PKG_UPDATE_ARG="update" + +############################################################################### +# Utility functions +############################################################################### + +# Colors that can be used in 'exclaim' +COLOR_RED='\033[0;31m' +COLOR_GREEN='\033[0;32m' +COLOR_BLUE='\033[0;34m' +COLOR_NONE='\033[0m' + +function exclaim () { + echo "*******************************************************************************" + echo -e "$@" + echo "*******************************************************************************" +} + +# Set the location of the Trove setup commands file for devstack - either in +# the devstack repo, or the Trove one. Also sets the flag which activates the +# devstack trove plugin, if required. We'll use the devstack version if it +# exists, otherwise we assume the plugin method. +function set_trove_plugin_vars () { + FAIL_IF_MISSING=${1:-true} + + TROVE_SETUP_CMD_FILE="$PATH_DEVSTACK_SRC/lib/trove" + if [ -f "$TROVE_SETUP_CMD_FILE" ]; then + USE_DEVSTACK_TROVE_PLUGIN=false + else + TROVE_SETUP_CMD_FILE="$DEST/trove/devstack/plugin.sh" + USE_DEVSTACK_TROVE_PLUGIN=true + fi + # Only complain if we have a devstack directory and are told to + if [ "$FAIL_IF_MISSING" = "true" ] && [ -d "$PATH_DEVSTACK_SRC" ] && [ ! -f "$TROVE_SETUP_CMD_FILE" ]; then + exclaim "${COLOR_RED}Trove setup file '${TROVE_SETUP_CMD_FILE}' not found!${COLOR_NONE}" + exit 1 + fi +} + +function pkg_install () { + echo Installing $@... + sudo -E $PKG_INSTALL_OPTS $HTTP_PROXY $PKG_MGR $PKG_GET_ARGS $PKG_INSTALL_ARG $@ +} + +function pkg_update () { + echo Updating $@... + sudo -E $PKG_INSTALL_OPTS $HTTP_PROXY $PKG_MGR $PKG_GET_ARGS $PKG_UPDATE_ARG $@ +} + +function set_home_dir() { + exclaim "set_home_dir has been nooped." + exit 1 +} + +function set_http_proxy() { + if [ ! "${http_proxy}" = '' ]; then + HTTP_PROXY="http_proxy=$http_proxy https_proxy=$https_proxy" + fi +} + +function get_ip_for_device() { + /sbin/ifconfig $1 | awk '/inet addr/{gsub(/addr:/,"");print $2}' +} + +function ip_chunk() { + # Given 1-4 returns a bit of where the ip range starts. + # Full IP= `ip_chunk 1`.`ip_chunk 2`.`ip_chunk 3`.`ip_chunk 4` + get_ip_for_device $1 | cut -d. -f$2 +} + +# Add a flavor and a corresponding flavor.resize +# (flavor.resize adds 16 to the memory and one more vcpu) +function add_flavor() { + local FLAVOR_NAME=$1 + local FLAVOR_ID=$2 + local FLAVOR_MEMORY_MB=$3 + local FLAVOR_ROOT_GB=$4 + local FLAVOR_VCPUS=$5 + + credentials="--os-username=admin --os-password=$ADMIN_PASSWORD --os-tenant-name=admin --os-auth-url=$TROVE_AUTH_ENDPOINT" + if [[ -z "$FLAVOR_LIST_FOR_ADD" ]]; then + FLAVOR_LIST_FOR_ADD=$(nova $credentials flavor-list | cut -d'|' -f3 | sed -e's/ /,/g') + fi + + base_id=${FLAVOR_ID} + base_name_prefix=test + ephemeral_name_prefix=${base_name_prefix}.eph + for name_prefix in $base_name_prefix $ephemeral_name_prefix; do + reg_name=${name_prefix}.${FLAVOR_NAME}-${FLAVOR_ROOT_GB} + resize_name=${reg_name}.resize + ephemeral=0 + if [[ $name_prefix == $ephemeral_name_prefix ]]; then + ephemeral=1 + fi + for name in ${reg_name} ${resize_name}; do + id=$base_id + memory=${FLAVOR_MEMORY_MB} + vcpus=${FLAVOR_VCPUS} + if [[ $ephemeral != 0 ]]; then + if [[ $BRANCH_OVERRIDE == "stable/juno" || $BRANCH_OVERRIDE == "stable/kilo" ]]; then + id=1${id} + else + id=${id}e + fi + fi + if [[ $name == ${resize_name} ]]; then + if [[ $BRANCH_OVERRIDE == "stable/juno" || $BRANCH_OVERRIDE == "stable/kilo" ]]; then + id=2${id} + else + id=${id}r + fi + memory=$((${FLAVOR_MEMORY_MB} + 16)) + vcpus=$((${FLAVOR_VCPUS} + 1)) + fi + if [[ $FLAVOR_LIST_FOR_ADD != *",$name,"* ]]; then + nova $credentials flavor-create $name $id $memory $FLAVOR_ROOT_GB $vcpus --ephemeral $ephemeral + fi + done + done +} + +function get_attribute_id() { + openstack --os-auth-url=$TROVE_AUTH_ENDPOINT --os-username=admin --os-password=$ADMIN_PASSWORD --os-project-name=admin $1 list | grep " $2" | get_field $3 +} + + +############################################################################### +# Install all the required dependencies +############################################################################### + +function install_prep_packages() { + # Called before devstack + exclaim 'Updating dependencies (part 1a)...' + pkg_update + exclaim 'Installing dependencies (part 1b)...' + pkg_install python-pip + if is_fedora; then + pkg_install git gettext + else + pkg_install git-core kvm-ipxe gettext + fi + sudo -H $HTTP_PROXY pip install --upgrade pip dib-utils +} + +function install_devstack_code() { + exclaim "Installing devstack..." + # Installs devstack (if needed). + if [ ! -d $PATH_DEVSTACK_SRC ]; then + echo "DevStack not in a shared folder, cloning from git." + mkdir -p $PATH_DEVSTACK_SRC + git clone $DEVSTACK_REPO $PATH_DEVSTACK_SRC + fi + + source $PATH_DEVSTACK_SRC/functions-common + source $PATH_DEVSTACK_SRC/functions + + # Switch to a branch if specified. The order the variables are checked is: + # DEVSTACK_BRANCH then PROJECT_BRANCH + BRANCH_SPECIFIED=$(test -z "${DEVSTACK_BRANCH}${PROJECT_BRANCH}" || echo 'True') + if [[ "${BRANCH_SPECIFIED}" = "True" ]]; then + PROJ_BRANCH=$(get_project_branch DEVSTACK_BRANCH $PROJECT_BRANCH) + ENV_VARS="DEVSTACK_BRANCH' or 'PROJECT_BRANCH" + git_checkout "devstack" "$PATH_DEVSTACK_SRC" "$PROJ_BRANCH" "$ENV_VARS" + fi + + exclaim "Installing devstack projects..." + # Ensures present user can get to the devstack dirs + sudo mkdir -p $PATH_DEVSTACK_OUTPUT + if [ ! -w $PATH_DEVSTACK_OUTPUT ]; then + sudo chown `whoami` $PATH_DEVSTACK_OUTPUT + fi + # Clones all of the code to where devstack expects it to be + pushd $PATH_DEVSTACK_OUTPUT + cmd_clone_projects do_not_force_update $REDSTACK_SCRIPTS/projects-list \ + $REDSTACK_SCRIPTS/image-projects-list + popd +} + +function install_reviews_on_top_of_devstack() { + exclaim "Putting gerrit review code on top of the existing devstack code" + run_review_for nova $PATH_NOVA $REVIEW_NOVA + run_review_for python-novaclient $PATH_PYTHON_NOVACLIENT $REVIEW_PYTHON_NOVACLIENT + run_review_for keystone $PATH_KEYSTONE $REVIEW_KEYSTONE + run_review_for python-keystoneclient $PATH_KEYSTONECLIENT $REVIEW_PYTHON_KEYSTONECLIENT + run_review_for python-openstackclient $PATH_OPENSTACKCLIENT $REVIEW_PYTHON_OPENSTACKCLIENT + run_review_for glance $PATH_GLANCE $REVIEW_GLANCE + run_review_for swift $PATH_SWIFT $REVIEW_SWIFT + run_review_for python-swiftclient $PATH_PYTHON_SWIFTCLIENT $REVIEW_PYTHON_SWIFTCLIENT + run_review_for trove $PATH_TROVE $REVIEW_TROVE + run_review_for python-troveclient $PATH_PYTHON_TROVECLIENT $REVIEW_PYTHON_TROVECLIENT +} + +function run_review_for() { + # Splits based on colon in the REVIEW_ARG and pulls from + GIT_NAME=$1 + PATH_ARG=$2 + REVIEW_ARG=$3 + for review in `echo $REVIEW_ARG| tr ":" "\n"` + do + # This should be the ref spec for what we pull + pushd $PATH_ARG + git_timed pull https://review.openstack.org/p/openstack/$GIT_NAME refs/changes/$review + popd + done +} + +function fixup_broken_devstack() { + # Nothing to do here, devstack is working + : +} + +# Delete all the lines from FILE_NAME between START_TAG and END_TAG +# Tags must appear at the beginning of a line +function clear_file_lines() { + local FILE_NAME=$1 + local START_TAG=$2 + local END_TAG=$3 + + sed -i "/^$START_TAG$/,/^$END_TAG$/{/^$START_TAG/!{/^$END_TAG/!d;}}" "$FILE_NAME" +} + +# Checks to see if a variable with the same name as FILE_NAME exists. +# Returns 'true' if no varable exists or if the value of the variable +# is set to 'true' - returns the VAR_NAME to set otherwise. +# FILE_NAME is first converted to uppercase, the extension is removed +# and all remaining '.' and spaces are replaced with '_' +function check_filename_var() { + local FILE_NAME=$1 + + DEREF_VALUE=false + if [ -f "$FILE_NAME" ]; then + VAR_NAME=$(basename "$FILE_NAME" ".rc" | tr '[:lower:][:blank:][:punct:]' '[:upper:]__') + DEREF_VALUE=$(get_bool "$VAR_NAME" "true") + if [ "$DEREF_VALUE" != "true" ]; then + DEREF_VALUE=$VAR_NAME + fi + fi + echo "$DEREF_VALUE" +} + +# Add the contents of one file to another, after the given tag +# Run through 'eval' if PARSE_FILE is true (defaults to true) +# Start with a blank line if BLANK_LINE_TO_START is true (defaults to false) +function add_file_contents() { + local FILE_NAME=$1 + local FILE_TO_ADD=$2 + local TAG=$3 + local PARSE_FILE=${4:-true} + local BLANK_LINE_TO_START=${5:-false} + + TEMP_FILE=".redstack.$$" + rm -f "$TEMP_FILE" + if [ "$BLANK_LINE_TO_START" = "true" ]; then + echo "" > "$TEMP_FILE" + fi + if [ -f "$FILE_TO_ADD" ]; then + echo "Adding $FILE_TO_ADD to $FILE_NAME" + echo "# Contents from $FILE_TO_ADD" >> "$TEMP_FILE" + if [ "$PARSE_FILE" = "true" ]; then + eval echo "\"$(cat "$FILE_TO_ADD")\"" >> "$TEMP_FILE" + else + cat "$FILE_TO_ADD" >> "$TEMP_FILE" + fi + echo "# End Of Contents from $FILE_TO_ADD" >> "$TEMP_FILE" + fi + echo "" >> "$TEMP_FILE" + sed -i "/^$TAG/r $TEMP_FILE" "$FILE_NAME" + rm -f "$TEMP_FILE" +} + +function run_devstack() { + exclaim "Running devstack..." + + # (Re)Creating this lock directory seems sure-fire. + rm -rf "$USERHOME/nova_locks" + mkdir -p "$USERHOME/nova_locks" + + TROVE_PRESENT_TAG="# Trove-integration" + LOCAL_CONF_D=local.conf.d + CONF_MATCH="*.rc" + MARKER_TOKEN="#####" + USER_LOCAL_CONF=$(readlink -f "${USER_LOCAL_CONF:-$USERHOME/$USER_LOCAL_CONF_NAME}") + LOCALRC_OPTS_TAG="$MARKER_TOKEN Redstack Localrc Options $MARKER_TOKEN" + LOCALRC_OPTS_TAG_END="$MARKER_TOKEN End Of Redstack Localrc Options $MARKER_TOKEN" + USER_OPTS_TAG="$MARKER_TOKEN User Specified Options $MARKER_TOKEN" + USER_OPTS_TAG_END="$MARKER_TOKEN End Of User Specified Options $MARKER_TOKEN" + ADD_OPTS_TAG="$MARKER_TOKEN Additional Options $MARKER_TOKEN" + ADD_OPTS_TAG_END="$MARKER_TOKEN End Of Additional Options $MARKER_TOKEN" + set_trove_plugin_vars false + + pushd "$PATH_DEVSTACK_SRC" + DEVSTACK_LOCAL_CONF=$LOCAL_CONF + # remain backwards compatible with existing localrc files + if [ -f "$LOCALRC" ]; then + DEVSTACK_LOCAL_CONF=$LOCALRC + echo "Old-style devstack config file $PATH_DEVSTACK_SRC/$DEVSTACK_LOCAL_CONF found." + echo "Consider removing to generate the preferred-sytle config file $LOCAL_CONF." + fi + if [ -f "$DEVSTACK_LOCAL_CONF" ]; then + # Check if we have already configured the devstack config file + already_in_conf=$(grep "$TROVE_PRESENT_TAG" "$DEVSTACK_LOCAL_CONF" | wc -l) + if [ "$already_in_conf" == 0 ]; then + # We can no longer append to an existing old-style localrc file + if [ "$DEVSTACK_LOCAL_CONF" == "$LOCALRC" ]; then + echo "The devstack config file $PATH_DEVSTACK_SRC/$DEVSTACK_LOCAL_CONF is too old to append to." + echo "Please remove and try again." + exit 1 + fi + # Otherwise append the redstack version to the existing file + eval echo "\"$(cat "$REDSTACK_SCRIPTS/$DEFAULT_LOCALRC")\"" >> "$DEVSTACK_LOCAL_CONF" + fi + else + # If a devstack config file doesn't exist, create it + eval echo "\"$(cat "$REDSTACK_SCRIPTS/$DEFAULT_LOCAL_CONF")\"" > "$DEVSTACK_LOCAL_CONF" + fi + + # We can only replace sections from the LOCAL_CONF style files + if [ "$DEVSTACK_LOCAL_CONF" == "$LOCAL_CONF" ]; then + # Clear out all the options + clear_file_lines "$DEVSTACK_LOCAL_CONF" "$LOCALRC_OPTS_TAG" "$LOCALRC_OPTS_TAG_END" + clear_file_lines "$DEVSTACK_LOCAL_CONF" "$USER_OPTS_TAG" "$USER_OPTS_TAG_END" + clear_file_lines "$DEVSTACK_LOCAL_CONF" "$ADD_OPTS_TAG" "$ADD_OPTS_TAG_END" + + # Add the main localrc file + PARSE_FILE="true" + BLANK_LINE_TO_START="true" + if [ -f "$REDSTACK_SCRIPTS/$DEFAULT_LOCALRC" ]; then + add_file_contents "$DEVSTACK_LOCAL_CONF" "$REDSTACK_SCRIPTS/$DEFAULT_LOCALRC" "$LOCALRC_OPTS_TAG" "$PARSE_FILE" "$BLANK_LINE_TO_START" + fi + + # Add any user options + PARSE_FILE="false" + BLANK_LINE_TO_START="true" + if [ -f "$USER_LOCAL_CONF" ]; then + add_file_contents "$DEVSTACK_LOCAL_CONF" "$USER_LOCAL_CONF" "$USER_OPTS_TAG" "$PARSE_FILE" "$BLANK_LINE_TO_START" + fi + + # Add all the files in the LOCAL_CONF_D directory that match CONF_MATCH (except for sample files) + # and that aren't excluded. Files are excluded by having a variable + # 'FILENAME_IN_UPPERCASE_MINUS_RC=false' in redstack.rc + # For Example: USING_VAGRANT=false (for the using_vagrant.rc file). + PARSE_FILE="true" + BLANK_LINE_TO_START="false" + while IFS= read -r -d '' CONF_FILE + do + FILE_NAME_VAR=$(check_filename_var "$CONF_FILE") + if [ "$FILE_NAME_VAR" = "true" ]; then + add_file_contents "$DEVSTACK_LOCAL_CONF" "$CONF_FILE" "$ADD_OPTS_TAG" "$PARSE_FILE" "$BLANK_LINE_TO_START" + else + echo "Skipping $CONF_FILE" + echo "Use $FILE_NAME_VAR=true to include" + fi + done < <(find "$REDSTACK_SCRIPTS/${LOCAL_CONF_D}" -name "${CONF_MATCH}" -follow -not -name "sample*.rc" -type f -print0) + # this is to add a blank line for readability + add_file_contents "$DEVSTACK_LOCAL_CONF" "" "$ADD_OPTS_TAG" + fi + ./stack.sh + popd +} + + +function cmd_install() { + sudo mkdir -p $TROVE_LOGDIR # Creates TROVE_LOGDIR if it does not exist + if [ ! -w $TROVE_LOGDIR ]; then + sudo chown `whoami` $TROVE_LOGDIR + fi + install_prep_packages + install_devstack_code + install_reviews_on_top_of_devstack + fixup_broken_devstack + run_devstack + exclaim "${COLOR_GREEN}FINISHED INSTALL${COLOR_NONE}" +} + + +############################################################################### +# Build the image +# see functions_qemu +############################################################################### + +# Grab a numbered field from python prettytable output +# Fields are numbered starting with 1 +# Reverse syntax is supported: -1 is the last field, -2 is second to last, etc. +# get_field field-number +function get_field() { + while read data; do + if [ "$1" -lt 0 ]; then + field="(\$(NF$1))" + else + field="\$$(($1 + 1))" + fi + echo "$data" | awk -F'[ \t]*\\|[ \t]*' "{print $field}" + done +} + +function get_glance_id () { + echo `$@ | grep ' id ' | get_field 2` +} + +function set_bin_path() { + if is_fedora; then + sed -i "s|%bin_path%|/usr/bin|g" $TEST_CONF + else + sed -i "s|%bin_path%|/usr/local/bin|g" $TEST_CONF + fi +} + +function set_mysql_pkg() { + if is_fedora; then + MYSQL_PKG="mysql-community-server" + else + MYSQL_PKG="mysql-server-5.6" + fi +} + + +function cmd_set_datastore() { + IMAGEID=$1 + DATASTORE_TYPE=$2 + # rd_manage datastore_update + rd_manage datastore_update "$DATASTORE_TYPE" "" + PACKAGES=${PACKAGES:-""} + if [ "$DATASTORE_TYPE" == "mysql" ]; then + set_mysql_pkg + PACKAGES=${PACKAGES:-$MYSQL_PKG} + VERSION="5.6" + elif [ "$DATASTORE_TYPE" == "percona" ]; then + PACKAGES=${PACKAGES:-"percona-server-server-5.6"} + VERSION="5.6" + elif [ "$DATASTORE_TYPE" == "pxc" ]; then + PACKAGES=${PACKAGES:-"percona-xtradb-cluster-server-5.6"} + VERSION="5.6" + elif [ "$DATASTORE_TYPE" == "mariadb" ]; then + PACKAGES=${PACKAGES:-"mariadb-server"} + VERSION="10.1" + elif [ "$DATASTORE_TYPE" == "mongodb" ]; then + PACKAGES=${PACKAGES:-"mongodb-org"} + VERSION="3.2" + elif [ "$DATASTORE_TYPE" == "redis" ]; then + PACKAGES=${PACKAGES:-"redis-server"} + VERSION="3.0" + elif [ "$DATASTORE_TYPE" == "cassandra" ]; then + PACKAGES=${PACKAGES:-"cassandra"} + VERSION="2.1.0" + elif [ "$DATASTORE_TYPE" == "couchbase" ]; then + PACKAGES=${PACKAGES:-"couchbase-server"} + VERSION="2.2.0" + elif [ "$DATASTORE_TYPE" == "postgresql" ]; then + PACKAGES=${PACKAGES:-"postgresql-9.4"} + VERSION="9.4" + elif [ "$DATASTORE_TYPE" == "couchdb" ]; then + PACKAGES=${PACKAGES:-"couchdb"} + VERSION="1.6.1" + elif [ "$DATASTORE_TYPE" == "vertica" ]; then + PACKAGES=${PACKAGES:-"vertica"} + VERSION="7.1" + elif [ "$DATASTORE_TYPE" == "db2" ]; then + PACKAGES=${PACKAGES:-""} + VERSION="10.5" + else + echo "Unrecognized datastore type. ($DATASTORE_TYPE)" + exit 1 + fi + + sed -i "s/%datastore_type%/$DATASTORE_TYPE/g" $TEST_CONF + sed -i "s/%datastore_version%/$VERSION/g" $TEST_CONF + + #rd_manage datastore_version_update + rd_manage datastore_version_update "$DATASTORE_TYPE" "$VERSION" "$DATASTORE_TYPE" $IMAGEID "$PACKAGES" 1 + rd_manage datastore_version_update "$DATASTORE_TYPE" "inactive_version" "manager1" $IMAGEID "" 0 + rd_manage datastore_update "$DATASTORE_TYPE" "$VERSION" + rd_manage datastore_update Test_Datastore_1 "" + + if [ -f "$PATH_TROVE"/trove/templates/$DATASTORE_TYPE/validation-rules.json ]; then + # add the configuration parameters to the database for the kick-start datastore + rd_manage db_load_datastore_config_parameters "$DATASTORE_TYPE" "$VERSION" "$PATH_TROVE"/trove/templates/$DATASTORE_TYPE/validation-rules.json + fi + + cmd_stop + iniset $TROVE_CONF DEFAULT default_datastore "$DATASTORE_TYPE" + sleep 1.5 + cmd_start +} + +############################################################################### +# Run Unit Tests +############################################################################### + +function cmd_unit_tests() { + exclaim "Running Trove Unit Tests..." + $PATH_TROVE/run_tests.sh -N +} + +############################################################################### +# Start various OpenStack daemons interactively in a screen session +############################################################################### + +function cmd_start_deps() { + if ! sudo vgs $VOLUME_GROUP; then + exclaim "Reconnecting Volume Group to Backing File" + sudo losetup -f --show ${VOLUME_BACKING_FILE} + fi + + if ! egrep -q ${SWIFT_DATA_DIR}/drives/sdb1 /proc/mounts; then + exclaim "Re-mounting Swift Disk Image" + sudo mount -t xfs -o loop,noatime,nodiratime,nobarrier,logbufs=8 ${SWIFT_DISK_IMAGE} ${SWIFT_DATA_DIR}/drives/sdb1 || true + fi + + if [[ -e $PATH_DEVSTACK_SRC/stack-screenrc ]]; then + screen -dmS stack -c $PATH_DEVSTACK_SRC/stack-screenrc + fi +} + +function cmd_stop_deps() { + if screen -ls | grep -q stack; then + screen -S stack -X quit + rm -f $DEST/status/stack/* + fi +} + + +############################################################################### +# Initialize Trove +############################################################################### + +function rd_manage() { + pushd $PATH_TROVE + $TROVE_BIN_DIR/trove-manage --config-file=$TROVE_CONF "$@" + popd +} + +function install_test_packages() { + sudo -H $HTTP_PROXY pip install openstack.nose_plugin proboscis pexpect +} + +function mod_confs() { + DATASTORE_TYPE=$1 + + sudo install -b --mode 0664 $REDSTACK_SCRIPTS/conf/test_begin.conf $TEST_CONF + TROVE_REPORT_DIR=$REDSTACK_SCRIPTS/../report/ + EXTRA_CONF=$REDSTACK_SCRIPTS/conf/test.extra.conf + if [ -e $EXTRA_CONF ]; then + cat $EXTRA_CONF >> $TEST_CONF + fi + # Append datastore specific configuration file + DATASTORE_CONF=$REDSTACK_SCRIPTS/conf/$DATASTORE_TYPE.conf + if [ ! -f $DATASTORE_CONF ]; then + exclaim "Datastore configuration file ${DATASTORE_CONF} not found" + exit 1 + fi + cat $DATASTORE_CONF | sudo tee -a $TEST_CONF > /dev/null + cat $REDSTACK_SCRIPTS/conf/test_end.conf | sudo tee -a $TEST_CONF > /dev/null + + #Add the paths to the test conf + sed -i "s,%report_directory%,$TROVE_REPORT_DIR,g" $TEST_CONF + sed -i "s,%keystone_path%,$PATH_KEYSTONE,g" $TEST_CONF + sed -i "s,%nova_path%,$PATH_NOVA,g" $TEST_CONF + sed -i "s,%glance_path%,$PATH_GLANCE,g" $TEST_CONF + sed -i "s,%trove_path%,$PATH_TROVE,g" $TEST_CONF + sed -i "s,%service_host%,$SERVICE_HOST,g" $TEST_CONF + sed -i "s,%swifth_path%,$PATH_SWIFT,g" $TEST_CONF + + # Add the region name into test.conf + sed -i "s/%region_name%/${REGION_NAME}/g" $TEST_CONF + + # Add the tenant id's into test.conf + sed -i "s/%admin_tenant_id%/$(get_attribute_id project admin 1)/g" $TEST_CONF + sed -i "s/%alt_demo_tenant_id%/$(get_attribute_id project alt_demo 1)/g" $TEST_CONF + sed -i "s/%demo_tenant_id%/$(get_attribute_id project demo 1)/g" $TEST_CONF + sed -i "s/%admin_password%/$ADMIN_PASSWORD/g" $TEST_CONF + + # Enable neutron tests if needed + sed -i "s/%neutron_enabled%/$ENABLE_NEUTRON/g" $TEST_CONF + + # If neutron is enabled, we create a shared network and write this info to the + # confs so that the integration tests can use it. + if [[ $ENABLE_NEUTRON = true ]]; then + management_network_id=$(neutron --os-username=admin --os-password=$ADMIN_PASSWORD --os-tenant-name=admin --os-auth-url=$TROVE_AUTH_ENDPOINT net-list | awk '/ alt-private / {print $2}') + management_subnet=$(neutron --os-username=admin --os-password=$ADMIN_PASSWORD --os-tenant-name=admin --os-auth-url=$TROVE_AUTH_ENDPOINT subnet-list | awk '/ alt-private-subnet / {print $2}') + echo "Using neutron network $management_network_id and subnet $management_subnet" + sed -i "s,%shared_network%,$management_network_id,g" $TEST_CONF + sed -i "s,%shared_network_subnet%,$management_subnet,g" $TEST_CONF + else + # do not leave invalid keys in the configuration when using Nova for networking + sed -i "/%shared_network%/d" $TEST_CONF + sed -i "/%shared_network_subnet%/d" $TEST_CONF + fi + + if [ "$DATASTORE_TYPE" = "vertica" ]; then + # Vertica needs more time than mysql for its boot/start/stop operations. + setup_cluster_configs cluster_member_count 3 + elif [ "$DATASTORE_TYPE" = "pxc" ]; then + setup_cluster_configs min_cluster_member_count 2 + elif [ "$DATASTORE_TYPE" = "cassandra" ]; then + setup_cluster_configs cluster_member_count 2 + elif [ "$DATASTORE_TYPE" = "mongodb" ]; then + setup_cluster_configs cluster_member_count 2 + # Decrease the number of required config servers per cluster to save resources. + iniset $TROVE_CONF $DATASTORE_TYPE num_config_servers_per_cluster 1 + fi + + set_bin_path + +} + +function setup_cluster_configs() { + # Setting cluster_member_count to 2 to decrease cluster spawn time. + iniset $TROVE_CONF $DATASTORE_TYPE $1 $2 +} + +# Add useful flavors for testing (with corresponding *.resize flavors) +function add_test_flavors() { + # name id ram root_vol vcpu + # the ram and vcpu for name.resize are automatically calculated + # eph and non-eph flavors are created for each entry + add_flavor 'tiny' 10 512 3 1 + + add_flavor 'small' 15 768 3 1 + add_flavor 'small' 16 768 4 1 + add_flavor 'small' 17 768 5 1 + + add_flavor 'medium' 20 1024 4 1 + add_flavor 'medium' 21 1024 5 1 + + add_flavor 'large' 25 2048 5 1 + add_flavor 'large' 26 2048 10 1 + add_flavor 'large' 27 2048 15 1 + + # This will allow Nova to create an instance, but not enough disk to boot the image + add_flavor 'fault_1' 30 512 1 1 + # This should be enough memory to cause Nova to fail entirely due to too much allocation + add_flavor 'fault_2' 31 131072 5 1 +} + +function cmd_test_init() { + exclaim 'Initializing Configuration for Running Tests...' + + exclaim "Installing python test packages." + install_test_packages + + exclaim "Modifying test.conf and guest.conf with appropriate values." + mod_confs $1 + + exclaim "Creating Test Flavors." + add_test_flavors + + if [[ -n $KEY_DIR ]]; then + exclaim "Installing the SSH key from $KEY_DIR to the test environment." + mkdir -m 700 -p $USERHOME/.ssh + install -b --mode 0400 $KEY_DIR/id_rsa $USERHOME/.ssh + cat $KEY_DIR/authorized_keys >> $USERHOME/.ssh/authorized_keys + chmod 600 $USERHOME/.ssh/authorized_keys + fi +} + +function cmd_build_image() { + IMAGE_DATASTORE_TYPE=${1:-'mysql'} + ESCAPED_PATH_TROVE=${2:-'\/opt\/stack\/trove'} + HOST_SCP_USERNAME=${3:-'ubuntu'} + GUEST_USERNAME=${4:-'ubuntu'} + + exclaim "Ensuring we have all packages needed to build image." + sudo $HTTP_PROXY $PKG_MGR $PKG_GET_ARGS update + sudo $HTTP_PROXY $PKG_MGR $PKG_GET_ARGS install qemu + sudo -H $HTTP_PROXY pip install --upgrade pip dib-utils + + install_devstack_code + + cmd_clone_projects do_not_force_update $REDSTACK_SCRIPTS/image-projects-list + + exclaim "Use tripleo-diskimagebuilder to actually build the Trove Guest Agent Image." + build_guest_image $IMAGE_DATASTORE_TYPE +} + +function cmd_build_and_upload_image() { + local IMAGE_URL="" + # Use /tmp as file_cache + FILES=/tmp + if [[ -n $IMAGE_DOWNLOAD_URL ]]; then + exclaim "Downloading and using cached image" + IMAGE_URL=$IMAGE_DOWNLOAD_URL + else + exclaim "Trying to build image" + build_guest_image $1 + QCOW_IMAGE=`find $VM_PATH -name '*.qcow2'` + IMAGE_URL="file://$QCOW_IMAGE" + fi + + # The devstack openrc has references to 'enable_plugin' which causes errors + # in the stable/juno and stable/kilo branches. These are safe to ignore. + set +e; source $PATH_DEVSTACK_SRC/openrc admin admin; set -e + TOKEN=$(openstack token issue | grep ' id ' | get_field 2) + GLANCE_IMAGEIDS=$(glance image-list | grep $(basename $IMAGE_URL .qcow2) | get_field 1) + if [[ -n $GLANCE_IMAGEIDS ]]; then + glance image-delete $GLANCE_IMAGEIDS + fi + GLANCE_IMAGEID=`get_glance_id upload_image $IMAGE_URL $TOKEN` + set +e; source $PATH_DEVSTACK_SRC/openrc demo demo; set -e + [[ -z "$GLANCE_IMAGEID" ]] && echo "Glance upload failed!" && exit 1 + echo "IMAGE ID: $GLANCE_IMAGEID" + + exclaim "Updating Datastores" + cmd_set_datastore $GLANCE_IMAGEID $1 +} + + +function cmd_initialize() { + exclaim '(Re)Initializing Trove...' + pushd $PATH_DEVSTACK_SRC + ./unstack.sh + ./stack.sh + popd +} + + +############################################################################### +# Start Trove specific daemons interactively in a screen session +############################################################################### + +function tr_screen_it { + if screen -ls | grep -q stack; then + echo "Starting $@..." + screen -S stack -p $1 -X stuff "$2"$'\015' + fi +} + +function init_fake_mode() { + # Create a test conf which, unlike the conf which runs on a user's machine, + # takes advantage of the running keystone service we have in our VM. + # You could think of this fake mode, which runs in the VM as being + # slightly less fake than the default one which runs outside of it. + CONF_FILE=/tmp/trove.conf.test + cp $PATH_TROVE/etc/trove/trove.conf.test $CONF_FILE + # Switch keystone from the fake class to the real one. + sed -i \ + "s/trove.tests.fakes.keystone/keystone.middleware.auth_token/g" \ + $CONF_FILE + sed -i "s/log_file = rdtest.log//g" $CONF_FILE + sed -i "s/use_stderr = False/use_stderr = True/g" $CONF_FILE + cd $PATH_TROVE + set -e + rm -f trove_test.sqlite + set +e + $TROVE_BIN_DIR/trove-manage --config-file=$CONF_FILE db_sync + sqlite3 trove_test.sqlite \ + "INSERT INTO datastores VALUES ('a00000a0-00a0-0a00-00a0-000a000000aa', \ + 'mysql', 'b00000b0-00b0-0b00-00b0-000b000000bb'); \ + INSERT INTO datastores values ('e00000e0-00e0-0e00-00e0-000e000000ee', \ + 'Test_Datastore_1', ''); \ + INSERT INTO datastore_versions VALUES ('b00000b0-00b0-0b00-00b0-000b000000bb', \ + 'a00000a0-00a0-0a00-00a0-000a000000aa', '5.6', \ + 'c00000c0-00c0-0c00-00c0-000c000000cc', $MYSQL_PKG, 1, 'mysql'); \ + INSERT INTO datastore_versions VALUES ('d00000d0-00d0-0d00-00d0-000d000000dd', \ + 'a00000a0-00a0-0a00-00a0-000a000000aa', 'inactive_version', \ + '', '', 0, 'manager1'); \ + INSERT INTO datastore_configuration_parameters VALUES \ + ('00000000-0000-0000-0000-000000000001', \ + 'key_buffer_size', 'b00000b0-00b0-0b00-00b0-000b000000bb', \ + 0, 4294967296, 0, 'integer', 0, NULL); \ + INSERT INTO datastore_configuration_parameters VALUES \ + ('00000000-0000-0000-0000-000000000002', \ + 'connect_timeout', 'b00000b0-00b0-0b00-00b0-000b000000bb', \ + 0, 65535, 1, 'integer', 0, NULL); \ + INSERT INTO datastore_configuration_parameters VALUES \ + ('00000000-0000-0000-0000-000000000003', \ + 'join_buffer_size', 'b00000b0-00b0-0b00-00b0-000b000000bb', \ + 0, 4294967296, 0, 'integer', 0, NULL); \ + INSERT INTO datastore_configuration_parameters VALUES \ + ('00000000-0000-0000-0000-000000000004', \ + 'local_infile', 'b00000b0-00b0-0b00-00b0-000b000000bb', \ + 0, 1, 0, 'integer', 0, NULL); \ + INSERT INTO datastore_configuration_parameters VALUES \ + ('00000000-0000-0000-0000-000000000005', \ + 'collation_server', 'b00000b0-00b0-0b00-00b0-000b000000bb', \ + 0, NULL, NULL, 'string', 0, NULL); \ + " +} + +function cmd_start() { + if screen -ls | grep -q stack; then + set_trove_plugin_vars + USE_SCREEN=True + TOP_DIR=$PATH_DEVSTACK_SRC + LOGDIR=$TROVE_LOGDIR + RUNNING=$(screen -S stack -Q windows) + if [[ "$RUNNING" =~ " tr-" ]]; then + exclaim "${COLOR_RED}WARNING: Trove services appear to be running. Please run 'stop' or 'restart'${COLOR_NONE}" + else + source /dev/stdin < <(sed -n '/^function start_trove\(\)/,/^}/p' "$TROVE_SETUP_CMD_FILE") + start_trove + fi + else + echo "WARNING: Could not start Trove services because there is no stack screen session running" + fi +} + +function cmd_start_fake() { + init_fake_mode + CONF_FILE=/tmp/trove.conf.test + tr_screen_it tr-fake "cd $PATH_TROVE; $TROVE_BIN_DIR/trove-fake-mode --config-file=$CONF_FILE $@" +} + +function cmd_run() { + cd $PATH_TROVE; $TROVE_BIN_DIR/trove-api \ + --config-file=$TROVE_CONF $@ +} + +function cmd_run_fake() { + init_fake_mode + CONF_FILE=/tmp/trove.conf.test + $TROVE_BIN_DIR/trove-fake-mode --config-file=$CONF_FILE $@ +} + +############################################################################### +# Stop any active Trove screen session +############################################################################### + +function cmd_stop() { + if screen -ls | grep -q stack; then + set_trove_plugin_vars + rm -f $DEST/status/stack/tr-* + USE_SCREEN=True + source /dev/stdin < <(sed -n '/^function stop_trove\(\)/,/^}/p' "$TROVE_SETUP_CMD_FILE") + MAX_RETRY=5 + COUNT=1 + while true; do + RUNNING=$(screen -S stack -Q windows) + if [[ "$RUNNING" =~ " tr-" ]]; then + stop_trove + else + break + fi + ((COUNT++)) + if [ "$COUNT" -gt "$MAX_RETRY" ]; then + exclaim "${COLOR_RED}WARNING: Could not stop Trove services after ${MAX_RETRY} attempts${COLOR_NONE}" + break + fi + done + else + echo "WARNING: Could not stop Trove services because there is no stack screen session running" + fi +} + + +############################################################################### +# Run Integration Tests +############################################################################### + +function cmd_int_tests() { + exclaim "Running Trove Integration Tests..." + if [ ! $USAGE_ENDPOINT ]; then + export USAGE_ENDPOINT=trove.tests.util.usage.FakeVerifier + fi + cd $REDSTACK_SCRIPTS + if [ $# -lt 1 ]; then + args="--group=blackbox" + else + args="$@" + fi + + # -- verbose makes it prettier. + # -- logging-clear-handlers keeps the novaclient and other things from + # spewing logs to stdout. + args="$INT_TEST_OPTIONS -B $REDSTACK_TESTS/integration/int_tests.py --verbose --logging-clear-handlers $args" + echo "python $args" + python $args +} + +function cmd_int_tests_simple() { + exclaim "Running Trove Simple Integration Tests..." + cd $REDSTACK_SCRIPTS + if [ $# -lt 1 ]; then + args="--group=simple_blackbox" + else + args="$@" + fi + + # -- verbose makes it prettier. + # -- logging-clear-handlers keeps the novaclient and other things from + # spewing logs to stdout. + args="$INT_TEST_OPTIONS -B $REDSTACK_TESTS/integration/int_tests.py --verbose --logging-clear-handlers $args" + echo "python $args" + python $args +} + +function cmd_int_tests_white_box() { + export PYTHONPATH=$PYTHONPATH:$PATH_TROVE + export PYTHONPATH=$PYTHONPATH:$PATH_NOVA + cmd_int_tests --test-config white_box=True \ + --config-file=$TROVE_CONF \ + --nova-flags=/etc/nova/nova.conf $@ +} + +function cmd_example_tests() { + set +e + cmd_stop + set -e + cmd_start_fake + sleep 3 + echo " +{ + \"directory\": \"$REDSTACK_TESTS/../apidocs/src/resources/samples/\", + \"auth_url\":\"http://$KEYSTONE_AUTH_HOST:35357/v2.0/tokens\", + \"api_url\":\"http://$SERVICE_HOST:8779\", + \"replace_host\":\"https://ord.databases.api.rackspacecloud.com\", + \"replace_dns_hostname\": \"e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com\", + \"username\":\"examples\", + \"password\":\"examples\", + \"tenant\":\"trove\" +}" > /tmp/example-tests.conf + python $REDSTACK_TESTS/examples/examples/example_generation.py /tmp/example-tests.conf + pushd $REDSTACK_TESTS/../apidocs + mvn clean + mvn generate-sources + popd + cmd_stop +} + + +############################################################################### +# Misc. tools +############################################################################### + +function mysql_nova() { + echo mysql nova --execute "$@" + mysql -u root -p$MYSQL_PASSWORD nova --execute "$@" +} + +function mysql_trove() { + echo mysql trove --execute "$@" + mysql -u root -p$MYSQL_PASSWORD trove --execute "$@" +} + +function cmd_wipe_logs() { + for file in `ls $TROVE_LOGDIR/*.log` + do + echo "Reseting log file $file..." + echo "Reset at `date`" > $file + done +} + +function cmd_rd_sql() { + mysql -u root -p$MYSQL_PASSWORD trove +} + +function cmd_fake_sql() { + pushd $PATH_TROVE + sqlite3 trove_test.sqlite $@ + popd +} + +function cmd_vagrant_ssh() { + # Runs a command on a vagrant VM from the host machine. + VHOST=`vagrant ssh_config host | awk '/HostName/{print $2}'` + VUSER=`vagrant ssh_config host | awk '/User /{print $2}'` + VPORT=`vagrant ssh_config host | awk '/Port/{print $2}'` + VIDFILE=`vagrant ssh_config host | awk '/IdentityFile/{print $2}'` + echo ssh ${VUSER}@${VHOST} -p ${VPORT} -i ${VIDFILE} -o NoHostAuthenticationForLocalhost=yes "$@" + ssh ${VUSER}@${VHOST} -p ${VPORT} -i ${VIDFILE} -o NoHostAuthenticationForLocalhost=yes "$@" +} + + +function cmd_run_ci() { + exclaim "Running CI suite..." + set +e + cmd_stop_deps + cmd_stop + set -e + cmd_install + cmd_test_init $1 + # The arg will be the image type + cmd_build_and_upload_image $1 + + # Test in fake mode. + exclaim "Testing in fake mode." + cmd_start_fake + FAKE_MODE=True cmd_int_tests + cmd_stop + + # Test in real mode. + exclaim "Testing in real mode." + cmd_start + FAKE_MODE=False cmd_int_tests +} + +function cmd_wipe_queues() { + # Obliterate rabbit. + for i in stop_app reset start_app "change_password guest $RABBIT_PASSWORD"; \ + do sudo rabbitmqctl $i; done +} + +function cmd_clear() { + cmd_int_tests --group=dbaas.api.instances.delete + clean_instances + mysql_nova "DELETE FROM instance_info_caches;" + mysql_nova "DELETE FROM instances;" + mysql_trove "DELETE FROM instances;" + mysql_trove "DELETE FROM service_statuses;" + cmd_wipe_queues +} + +function exec_cmd_on_output() { + local output_cmd=$1 + local exec_cmd=$2 + local delete_sleep_time=${3:-0} + local skip_pattern=${4:-""} + + echo "Cleaning up objects from '${output_cmd}'" + local skip_cmd="cat" + if [[ -n "${skip_pattern}" ]]; then + local temp_skip_cmd=(grep -v "${skip_pattern}") + skip_cmd=${temp_skip_cmd[*]} + fi + local max_retry=10 + local count=1 + local again= + while true; do + ids=$($output_cmd | ${skip_cmd} | grep -v -e'---' | grep -iv ' id ' | cut -d'|' -f2) + if [[ -n $ids ]]; then + for id in $ids; do + echo -e "Executing: ${exec_cmd} ${id} ${again}" + # don't stop if we get an error executing the delete, and don't print + # out anything from stderr + set +e + ${exec_cmd} "${id}" &> /dev/null + set -e + done + sleep "${delete_sleep_time}" + else + break + fi + ((count++)) + if [[ "$count" -gt "$max_retry" ]]; then + exclaim "${COLOR_RED}WARNING: '$output_cmd' still returning output after ${max_retry} delete attempts${COLOR_NONE}" + break + fi + again="${COLOR_BLUE}(again)${COLOR_NONE}" + done +} + +function cmd_clean() { + echo "Cleaning up project '${OS_PROJECT_NAME}'" + # reset any stuck backups + mysql_trove "update backups set state='COMPLETED'" + # clear out any DS version metadata + mysql_trove "delete from datastore_version_metadata" + # reset any stuck instances, and clear all replicas + mysql_trove "update instances set task_id=2, slave_of_id=null" + # reset any stuck clusters + mysql_trove "update clusters set task_id=1" + # get rid of any extraneous quota usage + mysql_trove "delete from quota_usages" + # mark all instance modules as deleted + mysql_trove "update instance_modules set deleted=1" + + source "${PATH_DEVSTACK_SRC}"/openrc admin "${OS_PROJECT_NAME}" + # delete any trove clusters + exec_cmd_on_output "trove cluster-list" "trove cluster-delete" 20 + # delete any trove instances + exec_cmd_on_output "trove list" "trove delete" 10 + # delete any backups + exec_cmd_on_output "trove backup-list" "trove backup-delete" + # clean up any remaining nova instances or cinder volumes + exec_cmd_on_output "nova list" "nova delete" 5 + exec_cmd_on_output "cinder list" "cinder delete" 1 + # delete any config groups since all instances should be gone now + exec_cmd_on_output "trove configuration-list" "trove configuration-delete" + # delete any modules too + exec_cmd_on_output "trove module-list" "trove module-delete" + # make sure that security groups are also gone, except the default + exec_cmd_on_output "openstack security group list" "nova security group delete" 0 "default" + # delete server groups + exec_cmd_on_output "nova server-group-list" "nova server-group-delete" +} + +function cmd_kick_start() { + cmd_test_init $1 + cmd_build_and_upload_image $1 +} + +function cmd_dsvm_gate_tests() { + DATASTORE_TYPE=${1:-'mysql'} + TEST_GROUP=${2:-${DATASTORE_TYPE}} + HOST_SCP_USERNAME=${3:-'jenkins'} + GUEST_USERNAME=${4:-'ubuntu'} + CONTROLLER_IP=${5:-'10.1.0.1'} + ESCAPED_PATH_TROVE=${6:-'\/opt\/stack\/new\/trove'} + + if [[ $BRANCH_OVERRIDE == "stable/liberty" ]]; then + # Devstack in liberty doesn't copy the clouds.yaml file to /etc so we need to + # ensure we have access to the clouds.yaml file set up by devstack-gate + sudo mkdir -p ~/.config/openstack + sudo ln -s $DEST/.config/openstack/clouds.yaml ~/.config/openstack/clouds.yaml + sudo chown -R $(whoami) ~/.config + fi + + # Devstack vm-gate runs as the jenkins user, but needs to connect to the guest image as ubuntu + echo "User=ubuntu" >> /home/jenkins/.ssh/config + + # Fix iptables rules that prevent amqp connections from the devstack box to the guests + sudo iptables -D openstack-INPUT -j REJECT --reject-with icmp-host-prohibited || true + + sudo chown -R $(whoami) /etc/trove + sudo chown -R $(whoami) $DEST/trove-integration + iniset $TROVE_GUESTAGENT_CONF DEFAULT rabbit_host $CONTROLLER_IP + iniset $TROVE_GUESTAGENT_CONF oslo_messaging_rabbit rabbit_hosts $CONTROLLER_IP + cd $DEST/trove-integration/scripts + sudo -H $HTTP_PROXY pip install --upgrade pip dib-utils + + cmd_kick_start $DATASTORE_TYPE + + # Update the local swift endpoint in the catalog to use the CONTROLLER_IP instead of 127.0.0.1 + source $DEST/devstack/accrc/admin/admin + # NOTE(mriedem): We have to treat stable branches before liberty special + # due to constraints with older versions of python-openstackclient. + if [[ $BRANCH_OVERRIDE == "stable/juno" || $BRANCH_OVERRIDE == "stable/kilo" ]]; then + SWIFT_ENDPOINT=$(openstack endpoint list | grep 'swift' | get_field 1) + openstack endpoint create swift --region RegionOne --publicurl 'http://'$CONTROLLER_IP':8080/v1/AUTH_$(tenant_id)s' \ + --internalurl 'http://'$CONTROLLER_IP':8080/v1/AUTH_$(tenant_id)s' --adminurl 'http://'$CONTROLLER_IP':8080' + openstack endpoint delete $SWIFT_ENDPOINT + else + OS_CLIENT_ARGS="--os-auth-type v3password --os-auth-url $KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:5000/v3 --os-identity-api-version=3" + SWIFT_ENDPOINTS=$(openstack endpoint list $OS_CLIENT_ARGS --service swift -c ID -f value) + openstack endpoint create $OS_CLIENT_ARGS swift public 'http://'$CONTROLLER_IP':8080/v1/AUTH_$(tenant_id)s' --region RegionOne + openstack endpoint create $OS_CLIENT_ARGS swift internal 'http://'$CONTROLLER_IP':8080/v1/AUTH_$(tenant_id)s' --region RegionOne + openstack endpoint create $OS_CLIENT_ARGS swift admin 'http://'$CONTROLLER_IP':8080' --region RegionOne + echo $SWIFT_ENDPOINTS | xargs -n 1 openstack endpoint delete $OS_CLIENT_ARGS + fi + + cmd_int_tests --group=$TEST_GROUP +} + +function cmd_reset_task() { + mysql_trove "UPDATE instances SET task_id=1 WHERE id='$1'" +} + +function cmd_clone_projects() { + UPDATE_PROJECTS=$1 + PROJECT_LIST_FILES=${@:2} + for project in $(cat $PROJECT_LIST_FILES); do + if [ ! -d $PATH_DEVSTACK_OUTPUT/$project ]; then + echo "Creating a new clone of $project..." + git_clone $GIT_OPENSTACK/"$project".git ${PATH_DEVSTACK_OUTPUT}/$project master + else + if [ $UPDATE_PROJECTS != "force_update" ]; then + echo "$project was already cloned or exists in a shared folder. Ignoring..." + else + echo "$project was already cloned. Pulling changes to update." + cd $PATH_DEVSTACK_OUTPUT/$project + git pull + fi + fi + # Switch to a branch if specified. The order the variables are checked is: + # _BRANCH then PROJECT_CLIENT_BRANCH (if a client) then PROJECT_BRANCH + # Note: For the Trove project, only TROVE_BRANCH and PYTHON_TROVECLIENT_BRANCH are used + PROJECT_BRANCH_NAME=$(eval echo "${project}_BRANCH" | tr '[:lower:]-' '[:upper:]_') + PROJECT_BRANCH_VALUE=${!PROJECT_BRANCH_NAME} + # TROVE_BRANCH is defaulted to master if not set, so use the original value here + if [[ "$project" = "trove" ]]; then + PROJECT_BRANCH_VALUE=${TROVE_BRANCH_ORIG} + fi + BRANCH_SPECIFIED=$(test -z "${PROJECT_BRANCH_VALUE}${PROJECT_CLIENT_BRANCH}${PROJECT_BRANCH}" || echo 'True') + if [[ "${BRANCH_SPECIFIED}" = "True" ]]; then + # Set up the default branch and env var names for the project + DEFAULT_BRANCH="$PROJECT_BRANCH" + ENV_VARS="$PROJECT_BRANCH_NAME' or 'PROJECT_BRANCH" + # Don't use 'PROJECT_BRANCH' or 'PROJECT_CLIENT_BRANCH' for the Trove project + if [[ "$project" =~ "trove" ]]; then + DEFAULT_BRANCH=master + ENV_VARS="$PROJECT_BRANCH_NAME" + # Use 'PROJECT_CLIENT_BRANCH' first for clients + elif [[ "$project" =~ "client" ]]; then + DEFAULT_BRANCH="${PROJECT_CLIENT_BRANCH:-$PROJECT_BRANCH}" + ENV_VARS="$PROJECT_BRANCH_NAME' or 'PROJECT_CLIENT_BRANCH' or 'PROJECT_BRANCH" + fi + PROJ_BRANCH=$(get_project_branch $PROJECT_BRANCH_NAME $DEFAULT_BRANCH) + git_checkout "$project" "$PATH_DEVSTACK_OUTPUT/$project" "$PROJ_BRANCH" "$ENV_VARS" + fi + done +} + +function cmd_repl() { + INT_TEST_OPTIONS=-i cmd_int_tests_white_box --repl --group=_does_not_exist_ $@ +} + + +############################################################################### +# Process the user provided command and run the appropriate command +############################################################################### + +# Let's not run this as the root user +if [ $EUID -eq 0 ]; then + echo "You are running this script as root. You need to run as a regular user" + exit 1 +fi + +# Set this to exit immediately on error +set -o errexit + +# set_home_dir +set_http_proxy +set_trove_plugin_vars false + +function print_usage() { + echo "Usage: $0 [command]" + echo " + Commands : + --setup environment-- + install - Install all the required dependencies and bring up tr-api and tr-tmgr + - devstack config can be altered by using a USER_LOCAL_CONF file + which will be copied into devstack/local.conf on each 'install' run + (defaults to \$HOME/$USER_LOCAL_CONF_NAME) + - Set DEVSTACK_BRANCH to switch the branch/commit of devstack + (i.e. 'stable/kilo' or '7ef2462') + test-init - Configure the test configuration files and add keystone test users + build-image - Builds the vm image for the trove guest + initialize - Reinitialize the trove database, users, services, and test config + + --helper for environment-- + kick-start - kick start the setup of trove. + (redstack test-init/build-image in one step) + [mysql no-clean] no clean avoids rebuilding packages from scratch + - Set REBUILD_IMAGE=True to force rebuild (won't use cached image) + + --trove dependency services-- + start-deps - Start or resume daemons Trove depends on. + stop-deps - Kill daemons Trove depends on. + + --trove services-- + start - Start or resume daemons Trove depends on. + stop - Kill daemons Trove depends on. + restart - Runs stop then start for Trove services. + + --tests-- + unit-tests - Run the unit tests.dependencies + int-tests - Runs the integration tests (requires all daemons). + simple-tests - Runs the simple integration tests (requires all daemons). + dsvm-gate-tests - Configures and runs the int-tests in a devstack vm-gate environment. + + --tools-- + debug - Debug this script (shows all commands). + wipe-logs - Resets all log files. + rd-sql - Opens the Trove MySQL database. + vagrant-ssh - Runs a command from the host on the server. + clear - Destroy instances and rabbit queues. + clean - Clean up resources created by a failed test run. + run - Starts RD but not in a screen. + run-fake - Runs the server in fake mode. + update-projects - Git pull on all the daemons trove dependencies. + reset-task - Sets an instance task to NONE. + wipe-queues - Resets RabbitMQ queues. + " + exit 1 +} + +function run_command() { + # Print the available commands + if [ $# -lt 1 ]; then + print_usage + fi + + case "$1" in + "install" ) cmd_install;; + "test-init" ) cmd_test_init $@;; + "build-image" ) shift; cmd_build_image $@;; + "initialize" ) cmd_initialize;; + "unit-tests" ) cmd_unit_tests;; + "start-deps" ) cmd_start_deps;; + "stop-deps" ) cmd_stop_deps;; + "start" ) cmd_start;; + "int-tests" ) shift; cmd_int_tests $@;; + "int-tests-wb" ) shift; cmd_int_tests_white_box $@;; + "simple-tests") shift; cmd_int_tests_simple $@;; + "stop" ) cmd_stop;; + "restart" ) cmd_stop; cmd_start;; + "wipe-logs" ) cmd_wipe_logs;; + "rd-sql" ) shift; cmd_rd_sql $@;; + "fake-sql" ) shift; cmd_fake_sql $@;; + "run-ci" ) shift; cmd_run_ci $@;; + "vagrant-ssh" ) shift; cmd_vagrant_ssh $@;; + "debug" ) shift; echo "Enabling debugging."; \ + set -o xtrace; run_command $@;; + "clear" ) shift; cmd_clear $@;; + "clean" ) shift; cmd_clean $@;; + "run" ) shift; cmd_run $@;; + "kick-start" ) shift; cmd_kick_start $@;; + "dsvm-gate-tests" ) shift; cmd_dsvm_gate_tests $@;; + "run-fake" ) shift; cmd_run_fake $@;; + "start-fake" ) shift; cmd_start_fake $@;; + "update-projects" ) cmd_clone_projects force_update \ + $REDSTACK_SCRIPTS/projects-list \ + $REDSTACK_SCRIPTS/image-projects-list;; + "reset-task" ) shift; cmd_reset_task $@;; + "wipe-queues" ) shift; cmd_wipe_queues $@;; + "example-tests" ) shift; cmd_example_tests $@;; + "repl" ) shift; cmd_repl $@;; + "help" ) print_usage;; + * ) + echo "'$1' not a valid command" + exit 1 + esac +} + +run_command $@ diff --git a/integration/scripts/redstack.rc b/integration/scripts/redstack.rc new file mode 100644 index 0000000000..b4036ba18a --- /dev/null +++ b/integration/scripts/redstack.rc @@ -0,0 +1,120 @@ +# Paths inside the VM. +[ -z $SERVICE_HOST ] && SERVICE_HOST=`get_default_host_ip` +[ -z $DEST ] && DEST=/opt/stack +[ -z $BRIDGE_IP ] && BRIDGE_IP=172.24.4.1 +[ -z $PATH_DEVSTACK_SRC ] && PATH_DEVSTACK_SRC=~/devstack +[ -z $TROVE_CONF_DIR ] && TROVE_CONF_DIR=/etc/trove +[ -z $MYSQL_HOST ] && MYSQL_HOST=$SERVICE_HOST + +# Set up the region name +# Try REGION_NAME then OS_REGION_NAME then RegionOne (the devstack default) +REGION_NAME=${REGION_NAME:-${OS_REGION_NAME:-RegionOne}} + +# Enable neutron instead of nova-network +# Note: Until a few key changesets land, we can't enable Neutron properly. +# See: https://review.openstack.org/#/c/356026 +# https://review.openstack.org/#/c/356763 +# https://review.openstack.org/#/c/356701 +# NEUTRON_DEFAULT=true +NEUTRON_DEFAULT=false +if [[ $BRANCH_OVERRIDE == "stable/liberty" || $BRANCH_OVERRIDE == "stable/mitaka" ]]; then + NEUTRON_DEFAULT=false +fi +ENABLE_NEUTRON=$(get_bool ENABLE_NEUTRON $NEUTRON_DEFAULT) + +# Enable osprofiler - note: Enables Ceilometer as well +ENABLE_PROFILER=$(get_bool ENABLE_PROFILER false) +PROFILER_TRACE_SQL=$(get_bool PROFILER_TRACE_SQL false) +[ -z $PROFILER_HMAC_KEYS ] && PROFILER_HMAC_KEYS=SECRET_KEY + +# Enable ceilometer +ENABLE_CEILOMETER=$(get_bool ENABLE_CEILOMETER $ENABLE_PROFILER) + +# Enable Mistral +ENABLE_MISTRAL=$(get_bool ENABLE_MISTRAL false) + +# Enable LIBS_FROM_GIT +LIBS_FROM_GIT_ALL_CLIENTS=$(get_bool LIBS_FROM_GIT_ALL_CLIENTS false) +LIBS_FROM_GIT_ALL_OSLO=$(get_bool LIBS_FROM_GIT_ALL_OSLO false) + +# Don't include certain .rc files in local.conf.d by default +USING_VAGRANT=$(get_bool USING_VAGRANT false) +USE_KVM=$(get_bool USE_KVM false) +USE_UUID_TOKEN=$(get_bool USE_UUID_TOKEN false) + +# Specify configuration for Ceilometer +CEILOMETER_SERVICES_CONF=$(get_bool CEILOMETER_SERVICES_CONF $ENABLE_CEILOMETER) +CEILOMETER_CINDER_CONF=$(get_bool CEILOMETER_CINDER_CONF false) +CEILOMETER_NOVA_CONF=$(get_bool CEILOMETER_NOVA_CONF false) + +# Paths for various OpenStack components +PATH_DEVSTACK_OUTPUT=$DEST +PATH_NOVA=$DEST/nova +PATH_KEYSTONE=$DEST/keystone +PATH_GLANCE=$DEST/glance +PATH_SWIFT=$DEST/swift +PATH_TROVE=$DEST/trove +PATH_PYTHON_NOVACLIENT=$DEST/python-novaclient +PATH_KEYSTONECLIENT=$DEST/python-keystoneclient +PATH_OPENSTACKCLIENT=$DEST/python-openstackclient +PATH_PYTHON_SWIFTCLIENT=$DEST/python-swiftclient +PATH_PYTHON_TROVECLIENT=$DEST/python-troveclient +PATH_TROVE_DASHBOARD=$DEST/trove-dashboard +PATH_DISKIMAGEBUILDER=$DEST/diskimage-builder +PATH_TRIPLEO_ELEMENTS=$DEST/tripleo-image-elements + +# Save the state of TROVE_BRANCH first, since it's used in redstack +TROVE_BRANCH_ORIG=${TROVE_BRANCH} +# Devstack and OpenStack git repo source paths, etc. +GIT_BASE=${GIT_BASE:-git://git.openstack.org} +GIT_OPENSTACK=${GIT_OPENSTACK:-${GIT_BASE}/openstack} +DEVSTACK_REPO=${DEVSTACK_REPO:-${GIT_BASE}/openstack-dev/devstack.git} +TROVE_REPO=${TROVE_REPO:-${GIT_OPENSTACK}/trove.git} +TROVE_DIR=${TROVE_DIR:-${PATH_TROVE}} +TROVE_BRANCH=${TROVE_BRANCH:-master} +TROVE_CLIENT_REPO=${TROVE_CLIENT_REPO:-${TROVECLIENT_REPO:-${GIT_OPENSTACK}/python-troveclient.git}} +TROVE_CLIENT_DIR=${TROVE_CLIENT_DIR:-${TROVECLIENT_DIR:-${PATH_PYTHON_TROVECLIENT}}} +TROVE_CLIENT_BRANCH=${TROVE_CLIENT_BRANCH:-${TROVECLIENT_BRANCH:-master}} +TROVE_DASHBOARD_REPO=${TROVE_DASHBOARD_REPO:-${TROVEDASHBOARD_REPO:-${GIT_OPENSTACK}/trove-dashboard.git}} +TROVE_DASHBOARD_DIR=${TROVE_DASHBOARD_DIR:-${TROVEDASHBOARD_DIR:-${PATH_TROVE_DASHBOARD}}} +TROVE_DASHBOARD_BRANCH=${TROVE_DASHBOARD_BRANCH:-${TROVEDASHBOARD_BRANCH:-master}} + +# Destination for working data +DATA_DIR=${DEST}/data +# Destination for status files +SERVICE_DIR=${DEST}/status + +# Cinder Volume Group Name +VOLUME_GROUP=${VOLUME_GROUP:-stack-volumes} +VOLUME_BACKING_FILE=${VOLUME_BACKING_FILE:-${DATA_DIR}/${VOLUME_GROUP}-backing-file} +VOLUME_BACKING_FILE_SIZE=${VOLUME_BACKING_FILE_SIZE:-51200M} + +# Passwords used by devstack. +MYSQL_PASSWORD=e1a2c042c828d3566d0a +RABBIT_PASSWORD=f7999d1955c5014aa32c +SERVICE_TOKEN=be19c524ddc92109a224 +ADMIN_PASSWORD=3de4922d8b6ac5a1aad9 +SERVICE_PASSWORD=7de4162d826bc5a11ad9 + +# Swift hash used by devstack. +SWIFT_HASH=12go358snjw24501 + +# Swift Disk Image +SWIFT_DATA_DIR=${DATA_DIR}/swift +SWIFT_DISK_IMAGE=${SWIFT_DATA_DIR}/drives/images/swift.img + +DISTRO=${DISTRO:-ubuntu} +#DISTRO=fedora + +# The following values can be used to tweak how devstack sets +# up Trove. If not explicitly set, the defaults in the code are used. +# To make changes without modifying the repo, add these variables +# to options.rc or ~/redstack.options.rc +#export TROVE_MAX_ACCEPTED_VOLUME_SIZE=10 +#export TROVE_MAX_INSTANCES_PER_TENANT=10 +#export TROVE_MAX_VOLUMES_PER_TENANT=40 +#export TROVE_AGENT_CALL_LOW_TIMEOUT=15 +#export TROVE_AGENT_CALL_HIGH_TIMEOUT=300 +#export TROVE_RESIZE_TIME_OUT=900 +#export TROVE_USAGE_TIMEOUT=1500 +#export TROVE_STATE_CHANGE_WAIT_TIME=180 diff --git a/integration/scripts/reviews.rc b/integration/scripts/reviews.rc new file mode 100644 index 0000000000..accccdcdc3 --- /dev/null +++ b/integration/scripts/reviews.rc @@ -0,0 +1,5 @@ +# This file will contain variables such as below, uncommented. +# There will be a : separator between multiple inflight reviews +# The path comes from the gerrit review system, and is the only +# unique portion of each gerrit review. +#REVIEW_PYTHON_NOVACLIENT=18/5018/2 diff --git a/integration/tests/examples/README b/integration/tests/examples/README new file mode 100644 index 0000000000..f7ebb0b87c --- /dev/null +++ b/integration/tests/examples/README @@ -0,0 +1,13 @@ +Example Generator + +Settings for the example generator are in examples/local.conf + +After customizing examples/local.conf, run: +./example_gen.sh + +2012-06-12 + - Updated to work with trove + - All XML calls are commented out + - Management calls are also commented out +2012-06-14 + - Renabled XML calls diff --git a/integration/tests/examples/example_gen.sh b/integration/tests/examples/example_gen.sh new file mode 100755 index 0000000000..cbbe153fa3 --- /dev/null +++ b/integration/tests/examples/example_gen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +python ./examples/example_generation.py ./examples/local.conf + diff --git a/integration/tests/examples/examples/client.py b/integration/tests/examples/examples/client.py new file mode 100644 index 0000000000..e52d1833d6 --- /dev/null +++ b/integration/tests/examples/examples/client.py @@ -0,0 +1,235 @@ +import httplib2 +import json +import os +import re +import sys +import time +from urlparse import urlparse +import xml.dom.minidom + +from proboscis.asserts import * +from troveclient.compat.client import TroveHTTPClient +from troveclient.compat.xml import TroveXmlClient + + +print_req = True + + +class ConfigFile(object): + + def __init__(self, config_file): + if not os.path.exists(config_file): + raise RuntimeError("Could not find Example CONF at %s." % + config_file) + file_contents = open(config_file, "r").read() + try: + config = json.loads(file_contents) + except Exception as exception: + msg = 'Error loading config file "%s".' % config_file + raise RuntimeError(msg, exception) + + self.directory = config.get("directory", None) + if not self.directory.endswith('/'): + self.directory += '/' + self.api_url = config.get("api_url", None) + self.auth_url = config.get("auth_url", None) + self.username = config.get("username", None) + self.password = config.get("password", None) + self.tenant = config.get("tenant", None) + self.replace_host = config.get("replace_host", None) + self.replace_dns_hostname = config.get("replace_dns_hostname", None) + if self.auth_url: + auth_id, tenant_id = self.get_auth_token_id_tenant_id(self.auth_url, + self.username, + self.password) + else: + auth_id = self.tenant + tenant_id = self.tenant + + print "id = %s" % auth_id + self.headers = { + 'X-Auth-Token': str(auth_id) + } + print "tenantID = %s" % tenant_id + self.tenantID = tenant_id + self.dbaas_url = "%s/v1.0/%s" % (self.api_url, self.tenantID) + + +def shorten_url(url): + parsed = urlparse(url) + if parsed.query: + method_url = parsed.path + '?' + parsed.query + else: + method_url = parsed.path + return method_url + + +class SnippetWriter(object): + + def __init__(self, conf): + self.conf = conf + + def _indent_xml(self, my_string): + my_string = my_string.encode("utf-8") + # convert to plain string without indents and spaces + my_re = re.compile('>\s+([^\s])', re.DOTALL) + my_string = myre.sub('>\g<1>', my_string) + my_string = xml.dom.minidom.parseString(my_string).toprettyxml() + # remove line breaks + my_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1>\s+([^\s])', re.DOTALL) + my_string = myre.sub('>\g<1>', my_string) + my_string = xml.dom.minidom.parseString(my_string).toprettyxml() + # remove line breaks + my_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1>= len(sys.argv) - 1: + print('Expected an argument to follow "--test-conf".') + sys.exit() + conf_line = sys.argv[index + 1] + extra_test_conf_lines.append(conf_line) + elif arg[:11] == "--flagfile=": + pass + elif arg[:14] == "--config-file=": + rdl_config_file = arg[14:] + elif arg[:13] == "--nova-flags=": + nova_flag_file = arg[13:] + elif arg.startswith('--hide-elapsed'): + show_elapsed = False + else: + nose_args.append(arg) + index += 1 + + # Many of the test decorators depend on configuration values, so before + # start importing modules we have to load the test config followed by the + # flag files. + from trove.tests.config import CONFIG + + # Find config file. + if not "TEST_CONF" in os.environ: + raise RuntimeError("Please define an environment variable named " + + "TEST_CONF with the location to a conf file.") + file_path = os.path.expanduser(os.environ["TEST_CONF"]) + if not os.path.exists(file_path): + raise RuntimeError("Could not find TEST_CONF at " + file_path + ".") + # Load config file and then any lines we read from the arguments. + CONFIG.load_from_file(file_path) + for line in extra_test_conf_lines: + CONFIG.load_from_line(line) + + if CONFIG.white_box: # If white-box testing, set up the flags. + # Handle loading up RDL's config file madness. + initialize_rdl_config(rdl_config_file) + + # Set up the report, and print out how we're running the tests. + from tests.util import report + from datetime import datetime + report.log("Trove Integration Tests, %s" % datetime.now()) + report.log("Invoked via command: " + str(sys.argv)) + report.log("Groups = " + str(groups)) + report.log("Test conf file = %s" % os.environ["TEST_CONF"]) + if CONFIG.white_box: + report.log("") + report.log("Test config file = %s" % rdl_config_file) + report.log("") + report.log("sys.path:") + for path in sys.path: + report.log("\t%s" % path) + + # Now that all configurations are loaded its time to import everything + test_importer() + + atexit.register(_clean_up) + + c = config.Config(stream=sys.stdout, + env=os.environ, + verbosity=3, + plugins=core.DefaultPluginManager()) + runner = NovaTestRunner(stream=c.stream, + verbosity=c.verbosity, + config=c, + show_elapsed=show_elapsed, + known_bugs=CONFIG.known_bugs) + MAIN_RUNNER = runner + + if repl: + # Turn off the following "feature" of the unittest module in case + # we want to start a REPL. + sys.exit = lambda x: None + + proboscis.TestProgram(argv=nose_args, groups=groups, config=c, + testRunner=MAIN_RUNNER).run_and_exit() + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + + +if __name__ == "__main__": + run_main(import_tests) diff --git a/integration/tests/integration/localhost.test.conf b/integration/tests/integration/localhost.test.conf new file mode 100644 index 0000000000..6459935cae --- /dev/null +++ b/integration/tests/integration/localhost.test.conf @@ -0,0 +1,95 @@ +{ + "include-files":["core.test.conf"], + + "fake_mode": true, + "dbaas_url":"http://localhost:8779/v1.0", + "version_url":"http://localhost:8779", + "nova_auth_url":"http://localhost:8779/v1.0/auth", + "trove_auth_url":"http://localhost:8779/v1.0/auth", + "trove_client_insecure":false, + "auth_strategy":"fake", + + "trove_version":"v1.0", + "trove_api_updated":"2012-08-01T00:00:00Z", + + "trove_dns_support":false, + "trove_ip_support":false, + + "nova_client": null, + + + "users": [ + { + "auth_user":"admin", + "auth_key":"password", + "tenant":"admin-1000", + "requirements": { + "is_admin":true, + "services": ["trove"] + } + }, + { + "auth_user":"jsmith", + "auth_key":"password", + "tenant":"2500", + "requirements": { + "is_admin":false, + "services": ["trove"] + } + }, + { + "auth_user":"hub_cap", + "auth_key":"password", + "tenant":"3000", + "requirements": { + "is_admin":false, + "services": ["trove"] + } + } + ], + + "flavors": [ + { + "id": 1, + "name": "m1.tiny", + "ram": 512 + }, + { + "id": 2, + "name": "m1.small", + "ram": 2048 + }, + { + "id": 3, + "name": "m1.medium", + "ram": 4096 + }, + { + "id": 4, + "name": "m1.large", + "ram": 8192 + }, + { + "id": 5, + "name": "m1.xlarge", + "ram": 16384 + }, + { + "id": 6, + "name": "tinier", + "ram": 506 + }, + { + "id": 7, + "name": "m1.rd-tiny", + "ram": 512 + }, + { + "id": 8, + "name": "m1.rd-smaller", + "ram": 768 + } + + ], + "sentinel": null +} diff --git a/integration/tests/integration/run_local.sh b/integration/tests/integration/run_local.sh new file mode 100755 index 0000000000..83d313dc88 --- /dev/null +++ b/integration/tests/integration/run_local.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Specify the path to the Trove repo as argument one. +# This script will create a .pid file and report in the current directory. + +set -e +if [ $# -lt 1 ]; then + echo "Please give the path to the Trove repo as argument one." + exit 5 +else + TROVE_PATH=$1 +fi +if [ $# -lt 2 ]; then + echo "Please give the path to the Trove Client as argument two." + exit 5 +else + TROVECLIENT_PATH=$2 +fi +shift; +shift; + + +PID_FILE="`pwd`.pid" + +function start_server() { + pushd $TROVE_PATH + bin/start_server.sh --pid_file=$PID_FILE + popd +} + +function stop_server() { + if [ -f $PID_FILE ]; + then + pushd $TROVE_PATH + bin/stop_server.sh $PID_FILE + popd + else + echo "The pid file did not exist, so not stopping server." + fi +} +function on_error() { + echo "Something went wrong!" + stop_server +} + +trap on_error EXIT # Proceed to trap - END in event of failure. + +TROVE_CLIENT_PATH=$TROVECLIENT_PATH tox -e py26 +start_server +.tox/py26/bin/pip install -U $TROVECLIENT_PATH +PYTHONPATH=$PYTHONPATH:$TROVECLIENT_PATH .tox/py26/bin/python int_tests.py \ + --conf=localhost.test.conf -- $@ +stop_server + + +trap - EXIT +echo "Ran tests successfully. :)" +exit 0 diff --git a/integration/tests/integration/setup.py b/integration/tests/integration/setup.py new file mode 100644 index 0000000000..52216f6157 --- /dev/null +++ b/integration/tests/integration/setup.py @@ -0,0 +1,30 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from setuptools import setup + + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + + +setup( + name="Trove Integration Tests", + version="0.0.9.9", + author='OpenStack', + description="Runs integration tests on Ridley.", + license='Apache', + py_modules=[], + packages=['tests'], + scripts=[] +) diff --git a/integration/tests/integration/tests/README b/integration/tests/integration/tests/README new file mode 100644 index 0000000000..05e1db677b --- /dev/null +++ b/integration/tests/integration/tests/README @@ -0,0 +1 @@ +Integration tests. diff --git a/integration/tests/integration/tests/__init__.py b/integration/tests/integration/tests/__init__.py new file mode 100644 index 0000000000..65f633d2a2 --- /dev/null +++ b/integration/tests/integration/tests/__init__.py @@ -0,0 +1,27 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2011 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +:mod:`tests` -- Integration / Functional Tests for Nova +=================================== + +.. automodule:: tests + :platform: Unix + :synopsis: Tests for Nova. +.. moduleauthor:: Nirmal Ranganathan +.. moduleauthor:: Tim Simpson +""" diff --git a/integration/tests/integration/tests/api/__init__.py b/integration/tests/integration/tests/api/__init__.py new file mode 100644 index 0000000000..40d014dd8b --- /dev/null +++ b/integration/tests/integration/tests/api/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/integration/tests/integration/tests/api/delete_all.py b/integration/tests/integration/tests/api/delete_all.py new file mode 100644 index 0000000000..98c67aba52 --- /dev/null +++ b/integration/tests/integration/tests/api/delete_all.py @@ -0,0 +1,32 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from proboscis import test + +from trove.tests.config import CONFIG +from trove.tests.util import create_dbaas_client +from trove.tests.util.users import Requirements + +GROUP = "dbaas.api.instances.delete" + + +@test(groups=[GROUP]) +def delete_all(): + """Delete every single one.""" + user = CONFIG.users.find_user(Requirements(is_admin=False)) + dbaas = create_dbaas_client(user) + instances = dbaas.instances.list() + for instance in instances: + instance.delete() diff --git a/integration/tests/integration/tests/api/instances_pagination.py b/integration/tests/integration/tests/api/instances_pagination.py new file mode 100644 index 0000000000..a21aadd77c --- /dev/null +++ b/integration/tests/integration/tests/api/instances_pagination.py @@ -0,0 +1,219 @@ + +from proboscis import after_class +from proboscis import before_class +from proboscis import test +from proboscis.asserts import assert_equal +from proboscis.asserts import assert_is_not +from proboscis.asserts import assert_is_none +from proboscis.asserts import assert_true + + +from troveclient.compat import exceptions +from trove.tests.config import CONFIG +from trove.tests.util import create_dbaas_client +from trove.tests.util.users import Requirements + + +class TestBase(object): + + def set_up(self): + """Create a ton of instances.""" + reqs = Requirements(is_admin=False) + self.user = CONFIG.users.find_user(reqs) + self.dbaas = create_dbaas_client(self.user) + + def delete_instances(self): + chunk = 0 + while True: + chunk += 1 + attempts = 0 + instances = self.dbaas.instances.list() + if len(instances) == 0: + break + # Sit around and try to delete this chunk. + while True: + instance_results = [] + attempts += 1 + deleted_count = 0 + for instance in instances: + try: + instance.delete() + result = "[w]" + except exceptions.UnprocessableEntity: + result = "[W]" + except exceptions.NotFound: + result = "[O]" + deleted_count += 1 + except Exception: + result = "[X]" + instance_results.append(result) + print("Chunk %d, attempt %d : %s" + % (chunk, attempts, ",".join(instance_results))) + if deleted_count == len(instances): + break + + def create_instances(self): + self.ids = [] + for index in range(self.max): + name = "multi-%03d" % index + result = self.dbaas.instances.create(name, 1, + {'size': 1}, [], []) + self.ids.append(result.id) + # Sort the list of IDs in order, so we can confirm the lists pagination + # returns is also sorted correctly. + self.ids.sort() + + @staticmethod + def assert_instances_sorted_by_ids(instances): + # Assert that the strings are always increasing. + last_id = "" + for instance in instances: + assert_true(last_id < instance.id) + + def print_list(self, instances): + print("Length = %d" % len(instances)) + print(",".join([instance.id for instance in instances])) + + def test_pagination(self, requested_limit, requested_marker, + expected_length, expected_marker, expected_last_item): + instances = self.dbaas.instances.list(limit=requested_limit, + marker=requested_marker) + marker = instances.next + + self.print_list(instances) + + # Better get as many as we asked for. + assert_equal(len(instances), expected_length) + # The last one should be roughly this one in the list. + assert_equal(instances[-1].id, expected_last_item) + # Because limit < count, the marker must be something. + if expected_marker: + assert_is_not(marker, None) + assert_equal(marker, expected_marker) + else: + assert_is_none(marker) + self.assert_instances_sorted_by_ids(instances) + + +@test(runs_after_groups=["dbaas.guest.shutdown"], + groups=['dbaas.api.instances.pagination']) +class SimpleCreateAndDestroy(TestBase): + """ + It turns out a big part of guaranteeing pagination works is to make sure + we can create a big batch of instances and delete them without problems. + Even in fake mode though its worth it to check this is the case. + """ + + max = 5 + + @before_class + def set_up(self): + """Create a ton of instances.""" + super(SimpleCreateAndDestroy, self).set_up() + self.delete_instances() + + @test + def spin_up(self): + self.create_instances() + + @after_class(always_run=True) + def tear_down(self): + self.delete_instances() + + +@test(runs_after_groups=["dbaas.guest.shutdown"], + groups=['dbaas.api.instances.pagination']) +class InstancePagination50(TestBase): + + max = 50 + + @before_class + def set_up(self): + """Create a ton of instances.""" + super(InstancePagination50, self).set_up() + self.delete_instances() + self.create_instances() + + @after_class(always_run=True) + def tear_down(self): + """Tear down all instances.""" + self.delete_instances() + + @test + def pagination_short(self): + self.test_pagination(requested_limit=10, requested_marker=None, + expected_length=10, expected_marker=self.ids[9], + expected_last_item=self.ids[9]) + + @test + def pagination_default(self): + self.test_pagination(requested_limit=None, requested_marker=None, + expected_length=20, expected_marker=self.ids[19], + expected_last_item=self.ids[19]) + + @test + def pagination_full(self): + self.test_pagination(requested_limit=50, requested_marker=None, + expected_length=20, expected_marker=self.ids[19], + expected_last_item=self.ids[19]) + + +@test(runs_after_groups=["dbaas.guest.shutdown"], + groups=['dbaas.api.instances.pagination']) +class InstancePagination20(TestBase): + + max = 20 + + @before_class + def set_up(self): + """Create a ton of instances.""" + super(InstancePagination20, self).set_up() + self.delete_instances() + self.create_instances() + + @after_class(always_run=True) + def tear_down(self): + """Tear down all instances.""" + self.delete_instances() + + @test + def pagination_short(self): + self.test_pagination(requested_limit=10, requested_marker=None, + expected_length=10, expected_marker=self.ids[9], + expected_last_item=self.ids[9]) + + @test + def pagination_default(self): + self.test_pagination(requested_limit=None, requested_marker=None, + expected_length=20, expected_marker=None, + expected_last_item=self.ids[19]) + + @test + def pagination_full(self): + self.test_pagination(requested_limit=20, requested_marker=None, + expected_length=20, expected_marker=None, + expected_last_item=self.ids[19]) + + @test + def pagination_overkill(self): + self.test_pagination(requested_limit=30, requested_marker=None, + expected_length=20, expected_marker=None, + expected_last_item=self.ids[19]) + + @test + def pagination_last_half(self): + self.test_pagination(requested_limit=10, requested_marker=self.ids[9], + expected_length=10, expected_marker=None, + expected_last_item=self.ids[19]) + + @test + def pagination_third_quarter(self): + self.test_pagination(requested_limit=5, requested_marker=self.ids[9], + expected_length=5, expected_marker=self.ids[14], + expected_last_item=self.ids[14]) + + @test + def pagination_fourth_quarter(self): + self.test_pagination(requested_limit=20, requested_marker=self.ids[14], + expected_length=5, expected_marker=None, + expected_last_item=self.ids[19]) diff --git a/integration/tests/integration/tests/api/instances_quotas.py b/integration/tests/integration/tests/api/instances_quotas.py new file mode 100644 index 0000000000..3a1c2de637 --- /dev/null +++ b/integration/tests/integration/tests/api/instances_quotas.py @@ -0,0 +1,47 @@ +from proboscis import before_class +from proboscis import test +from proboscis.asserts import assert_raises + +from troveclient.compat import exceptions +from trove.tests.config import CONFIG +from trove.tests.util import create_client + + +@test(groups=['dbaas.api.instances.quotas']) +class InstanceQuotas(object): + + created_instances = [] + + @before_class + def setup(self): + self.client = create_client(is_admin=False) + + @test + def test_too_many_instances(self): + self.created_instances = [] + if 'trove_max_instances_per_user' in CONFIG.values: + too_many = CONFIG.values['trove_max_instances_per_user'] + already_there = len(self.client.instances.list()) + flavor = 1 + for i in range(too_many - already_there): + response = self.client.instances.create('too_many_%d' % i, + flavor, + {'size': 1}) + self.created_instances.append(response) + # This one better fail, because we just reached our quota. + assert_raises(exceptions.OverLimit, + self.client.instances.create, + "too_many", flavor, + {'size': 1}) + + @test(runs_after=[test_too_many_instances]) + def delete_excessive_entries(self): + # Delete all the instances called too_many*. + for id in self.created_instances: + while True: + try: + self.client.instances.delete(id) + except exceptions.UnprocessableEntity: + continue + except exceptions.NotFound: + break diff --git a/integration/tests/integration/tests/api/instances_states.py b/integration/tests/integration/tests/api/instances_states.py new file mode 100644 index 0000000000..965d8e7703 --- /dev/null +++ b/integration/tests/integration/tests/api/instances_states.py @@ -0,0 +1,76 @@ +# Copyright 2012 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +GROUP = "dbaas.api.instances.status" + +from proboscis import before_class +from proboscis import test +from proboscis.asserts import assert_equal + +from trove.tests.config import CONFIG +from trove.tests.util import create_dbaas_client +from trove.tests.util.users import Requirements +from trove.common.utils import poll_until + + +@test(groups=[GROUP]) +class InstanceStatusTests(object): + + @before_class + def set_up(self): + reqs = Requirements(is_admin=False) + self.user = CONFIG.users.find_user(reqs) + self.dbaas = create_dbaas_client(self.user) + + @test + def test_create_failure_on_volume_prov_failure(self): + # Fake nova will fail a volume of size 9. + response = self.dbaas.instances.create('volume_fail', 1, + {'size': 9}, []) + poll_until(lambda: self.dbaas.instances.get(response.id), + lambda instance: instance.status == 'ERROR', + time_out=10) + instance = self.dbaas.instances.get(response.id) + print "Status: %s" % instance.status + assert_equal(instance.status, "ERROR", + "Instance did not drop to error after volume prov failure.") + + @test + def test_create_failure_on_server_failure(self): + # Fake nova will fail a server ending with 'SERVER_ERROR'." + response = self.dbaas.instances.create('test_SERVER_ERROR', 1, + {'size': 1}, []) + poll_until(lambda: self.dbaas.instances.get(response.id), + lambda instance: instance.status == 'ERROR', + time_out=10) + instance = self.dbaas.instances.get(response.id) + print "Status: %s" % instance.status + assert_equal(instance.status, "ERROR", + "Instance did not drop to error after server prov failure.") + + ###TODO(ed-): We don't at present have a way to test DNS in FAKE_MODE. + @test(enabled=False) + def test_create_failure_on_dns_failure(self): + #TODO(ed-): Throw DNS-specific monkeywrench into works + response = self.dbaas.instances.create('test_DNS_ERROR', 1, + {'size': 1}, []) + poll_until(lambda: self.dbaas.instances.get(response.id), + lambda instance: instance.status == 'ERROR', + time_out=10) + instance = self.dbaas.instances.get(response.id) + print "Status: %s" % instance.status + assert_equal(instance.status, "ERROR", + "Instance did not drop to error after DNS prov failure.") diff --git a/integration/tests/integration/tests/colorizer.py b/integration/tests/integration/tests/colorizer.py new file mode 100644 index 0000000000..4dd797dc18 --- /dev/null +++ b/integration/tests/integration/tests/colorizer.py @@ -0,0 +1,446 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Colorizer Code is borrowed from Twisted: +# Copyright (c) 2001-2010 Twisted Matrix Laboratories. +# +# 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 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. +"""Unittest runner for Nova. + +To run all tests + python run_tests.py + +To run a single test: + python run_tests.py test_compute:ComputeTestCase.test_run_terminate + +To run a single test module: + python run_tests.py test_compute + + or + + python run_tests.py api.test_wsgi + +""" + +import gettext +import heapq +import logging +import os +import unittest +import sys +import time + +gettext.install('nova', unicode=1) + +from nose import config +from nose import core +from nose import result +from proboscis import case +from proboscis import SkipTest + +class _AnsiColorizer(object): + """ + A colorizer is an object that loosely wraps around a stream, allowing + callers to write text to the stream in a particular color. + + Colorizer classes must implement C{supported()} and C{write(text, color)}. + """ + _colors = dict(black=30, red=31, green=32, yellow=33, + blue=34, magenta=35, cyan=36, white=37) + + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + """ + A class method that returns True if the current platform supports + coloring terminal output using this method. Returns False otherwise. + """ + if not stream.isatty(): + return False # auto color only on TTYs + try: + import curses + except ImportError: + return False + else: + try: + try: + return curses.tigetnum("colors") > 2 + except curses.error: + curses.setupterm() + return curses.tigetnum("colors") > 2 + except: + raise + # guess false in case of error + return False + supported = classmethod(supported) + + def write(self, text, color): + """ + Write the given text to the stream in the given color. + + @param text: Text to be written to the stream. + + @param color: A string label for a color. e.g. 'red', 'white'. + """ + color = self._colors[color] + self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) + + +class _Win32Colorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + from win32console import GetStdHandle, STD_OUT_HANDLE, \ + FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ + FOREGROUND_INTENSITY + red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, + FOREGROUND_BLUE, FOREGROUND_INTENSITY) + self.stream = stream + self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) + self._colors = { + 'normal': red | green | blue, + 'red': red | bold, + 'green': green | bold, + 'blue': blue | bold, + 'yellow': red | green | bold, + 'magenta': red | blue | bold, + 'cyan': green | blue | bold, + 'white': red | green | blue | bold + } + + def supported(cls, stream=sys.stdout): + try: + import win32console + screenBuffer = win32console.GetStdHandle( + win32console.STD_OUT_HANDLE) + except ImportError: + return False + import pywintypes + try: + screenBuffer.SetConsoleTextAttribute( + win32console.FOREGROUND_RED | + win32console.FOREGROUND_GREEN | + win32console.FOREGROUND_BLUE) + except pywintypes.error: + return False + else: + return True + supported = classmethod(supported) + + def write(self, text, color): + color = self._colors[color] + self.screenBuffer.SetConsoleTextAttribute(color) + self.stream.write(text) + self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) + + +class _NullColorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + return True + supported = classmethod(supported) + + def write(self, text, color): + self.stream.write(text) + + +def get_elapsed_time_color(elapsed_time): + if elapsed_time > 1.0: + return 'yellow' + elif elapsed_time > 0.25: + return 'cyan' + else: + return 'green' + + +class NovaTestResult(case.TestResult): + def __init__(self, *args, **kw): + self.show_elapsed = kw.pop('show_elapsed') + self.known_bugs = kw.pop('known_bugs', {}) + super(NovaTestResult, self).__init__(*args, **kw) + self.num_slow_tests = 5 + self.slow_tests = [] # this is a fixed-sized heap + self._last_case = None + self.colorizer = None + # NOTE(vish): reset stdout for the terminal check + stdout = sys.stdout + sys.stdout = sys.__stdout__ + for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: + if colorizer.supported(): + self.colorizer = colorizer(self.stream) + break + sys.stdout = stdout + + # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate + # error results in it failing to be initialized later. Otherwise, + # _handleElapsedTime will fail, causing the wrong error message to + # be outputted. + self.start_time = time.time() + + def _intercept_known_bugs(self, test, err): + name = str(test) + excuse = self.known_bugs.get(name, None) + if excuse: + tracker_id, error_string = excuse + if error_string in str(err[1]): + skip = SkipTest("KNOWN BUG: %s\n%s" + % (tracker_id, str(err[1]))) + self.onError(test) + super(NovaTestResult, self).addSkip(test, skip) + else: + result = (RuntimeError, RuntimeError( + 'Test "%s" contains known bug %s.\n' + 'Expected the following error string:\n%s\n' + 'What was seen was the following:\n%s\n' + 'If the bug is no longer happening, please change ' + 'the test config.' + % (name, tracker_id, error_string, str(err))), None) + self.onError(test) + super(NovaTestResult, self).addError(test, result) + return True + return False + + def getDescription(self, test): + return str(test) + + def _handleElapsedTime(self, test): + self.elapsed_time = time.time() - self.start_time + item = (self.elapsed_time, test) + # Record only the n-slowest tests using heap + if len(self.slow_tests) >= self.num_slow_tests: + heapq.heappushpop(self.slow_tests, item) + else: + heapq.heappush(self.slow_tests, item) + + def _writeElapsedTime(self, test): + color = get_elapsed_time_color(self.elapsed_time) + self.colorizer.write(" %.2f" % self.elapsed_time, color) + + def _writeResult(self, test, long_result, color, short_result, success): + if self.showAll: + self.colorizer.write(long_result, color) + if self.show_elapsed and success: + self._writeElapsedTime(test) + self.stream.writeln() + elif self.dots: + self.stream.write(short_result) + self.stream.flush() + + # NOTE(vish): copied from unittest with edit to add color + def addSuccess(self, test): + if self._intercept_known_bugs(test, None): + return + unittest.TestResult.addSuccess(self, test) + self._handleElapsedTime(test) + self._writeResult(test, 'OK', 'green', '.', True) + + # NOTE(vish): copied from unittest with edit to add color + def addFailure(self, test, err): + if self._intercept_known_bugs(test, err): + return + self.onError(test) + unittest.TestResult.addFailure(self, test, err) + self._handleElapsedTime(test) + self._writeResult(test, 'FAIL', 'red', 'F', False) + + # NOTE(vish): copied from nose with edit to add color + def addError(self, test, err): + """Overrides normal addError to add support for + errorClasses. If the exception is a registered class, the + error will be added to the list for that class, not errors. + """ + if self._intercept_known_bugs(test, err): + return + self.onError(test) + self._handleElapsedTime(test) + stream = getattr(self, 'stream', None) + ec, ev, tb = err + try: + exc_info = self._exc_info_to_string(err, test) + except TypeError: + # 2.3 compat + exc_info = self._exc_info_to_string(err) + for cls, (storage, label, isfail) in self.errorClasses.items(): + if result.isclass(ec) and issubclass(ec, cls): + if isfail: + test.passed = False + storage.append((test, exc_info)) + # Might get patched into a streamless result + if stream is not None: + if self.showAll: + message = [label] + detail = result._exception_detail(err[1]) + if detail: + message.append(detail) + stream.writeln(": ".join(message)) + elif self.dots: + stream.write(label[:1]) + return + self.errors.append((test, exc_info)) + test.passed = False + if stream is not None: + self._writeResult(test, 'ERROR', 'red', 'E', False) + + @staticmethod + def get_doc(cls_or_func): + """Grabs the doc abbreviated doc string.""" + try: + return cls_or_func.__doc__.split("\n")[0].strip() + except (AttributeError, IndexError): + return None + + def startTest(self, test): + unittest.TestResult.startTest(self, test) + self.start_time = time.time() + test_name = None + try: + entry = test.test.__proboscis_case__.entry + if entry.method: + current_class = entry.method.im_class + test_name = self.get_doc(entry.home) or entry.home.__name__ + else: + current_class = entry.home + except AttributeError: + current_class = test.test.__class__ + + if self.showAll: + if current_class.__name__ != self._last_case: + self.stream.writeln(current_class.__name__) + self._last_case = current_class.__name__ + try: + doc = self.get_doc(current_class) + except (AttributeError, IndexError): + doc = None + if doc: + self.stream.writeln(' ' + doc) + + if not test_name: + if hasattr(test.test, 'shortDescription'): + test_name = test.test.shortDescription() + if not test_name: + test_name = test.test._testMethodName + self.stream.write('\t%s' % str(test_name).ljust(60)) + self.stream.flush() + + +class NovaTestRunner(core.TextTestRunner): + def __init__(self, *args, **kwargs): + self.show_elapsed = kwargs.pop('show_elapsed') + self.known_bugs = kwargs.pop('known_bugs', {}) + self.__result = None + self.__finished = False + self.__start_time = None + super(NovaTestRunner, self).__init__(*args, **kwargs) + + def _makeResult(self): + self.__result = NovaTestResult( + self.stream, + self.descriptions, + self.verbosity, + self.config, + show_elapsed=self.show_elapsed, + known_bugs=self.known_bugs) + self.__start_time = time.time() + return self.__result + + def _writeSlowTests(self, result_): + # Pare out 'fast' tests + slow_tests = [item for item in result_.slow_tests + if get_elapsed_time_color(item[0]) != 'green'] + if slow_tests: + slow_total_time = sum(item[0] for item in slow_tests) + self.stream.writeln("Slowest %i tests took %.2f secs:" + % (len(slow_tests), slow_total_time)) + for elapsed_time, test in sorted(slow_tests, reverse=True): + time_str = "%.2f" % elapsed_time + self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) + + def on_exit(self): + if self.__result is None: + print("Exiting before tests even started.") + else: + if not self.__finished: + msg = "Tests aborted, trying to print available results..." + print(msg) + stop_time = time.time() + self.__result.printErrors() + self.__result.printSummary(self.__start_time, stop_time) + self.config.plugins.finalize(self.__result) + if self.show_elapsed: + self._writeSlowTests(self.__result) + + def run(self, test): + result_ = super(NovaTestRunner, self).run(test) + if self.show_elapsed: + self._writeSlowTests(result_) + self.__finished = True + return result_ + + +if __name__ == '__main__': + logging.setup() + # If any argument looks like a test name but doesn't have "nova.tests" in + # front of it, automatically add that so we don't have to type as much + show_elapsed = True + argv = [] + test_fixture = os.getenv("UNITTEST_FIXTURE", "trove") + for x in sys.argv: + if x.startswith('test_'): + argv.append('%s.tests.%s' % (test_fixture, x)) + elif x.startswith('--hide-elapsed'): + show_elapsed = False + else: + argv.append(x) + + testdir = os.path.abspath(os.path.join(test_fixture, "tests")) + c = config.Config(stream=sys.stdout, + env=os.environ, + verbosity=3, + workingDir=testdir, + plugins=core.DefaultPluginManager()) + + runner = NovaTestRunner(stream=c.stream, + verbosity=c.verbosity, + config=c, + show_elapsed=show_elapsed) + sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) diff --git a/integration/tests/integration/tests/dns/__init__.py b/integration/tests/integration/tests/dns/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration/tests/integration/tests/dns/check_domain.py b/integration/tests/integration/tests/dns/check_domain.py new file mode 100644 index 0000000000..61e5f6384a --- /dev/null +++ b/integration/tests/integration/tests/dns/check_domain.py @@ -0,0 +1,174 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +"""Checks that the domain specified in the flag file exists and is valid. + +If you define the environment variable ADD_DOMAINS=True when running the tests, +they will create the domain if its not found (see below for details). + +""" +import os +import time +import unittest +from proboscis import test +from proboscis import before_class +from proboscis.asserts import assert_equal +from proboscis.asserts import assert_not_equal +from proboscis.decorators import expect_exception +from proboscis.decorators import time_out + +from trove.tests.config import CONFIG + +WHITE_BOX = CONFIG.white_box +RUN_DNS = CONFIG.values.get("trove_dns_support", False) + +if WHITE_BOX: + from nova import utils + from nova import flags + import rsdns + from trove.dns.rsdns.driver import create_client_with_flag_values + from trove.dns.driver import DnsEntry + from trove.dns.rsdns.driver import RsDnsInstanceEntryFactory + from trove.dns.rsdns.driver import RsDnsDriver + from trove.dns.rsdns.driver import RsDnsZone + from trove.utils import poll_until + FLAGS = flags.FLAGS + TEST_CONTENT = "126.1.1.1" + TEST_NAME = "hiwassup.%s" % FLAGS.dns_domain_name + DNS_DOMAIN_ID = None + + +@test(groups=["rsdns.domains", "rsdns.show_entries"], + enabled=WHITE_BOX and RUN_DNS) +class ClientTests(object): + + @before_class + def increase_logging(self): + import httplib2 + httplib2.debuglevel = 1 + + @test + def can_auth(self): + self.client = create_client_with_flag_values() + self.client.authenticate() + + @test(depends_on=[can_auth]) + def list_domains(self): + domains = self.client.domains.list() + print(domains) + + +@test(groups=["rsdns.domains"], depends_on=[ClientTests], + enabled=WHITE_BOX and RUN_DNS) +class RsDnsDriverTests(object): + """Tests the RS DNS Driver.""" + + def create_domain_if_needed(self): + """Adds the domain specified in the flags.""" + print("Creating domain %s" % self.driver.default_dns_zone.name) + future = self.driver.dns_client.domains.create( + self.driver.default_dns_zone.name) + while not future.ready: + time.sleep(2) + print("Got something: %s" % future.resource) + with open('/home/vagrant/dns_resource.txt', 'w') as f: + f.write('%r\n' % future.result[0].id) + global DNS_DOMAIN_ID + DNS_DOMAIN_ID = future.result[0].id + print("The domain should have been created with id=%s" % DNS_DOMAIN_ID) + + @test + @time_out(2 * 60) + def ensure_domain_specified_in_flags_exists(self): + """Make sure the domain in the FLAGS exists.""" + self.driver = RsDnsDriver(raise_if_zone_missing=False) + assert_not_equal(None, self.driver.default_dns_zone) + + def zone_found(): + zones = self.driver.get_dns_zones() + print("Retrieving zones.") + for zone in zones: + print("zone %s" % zone) + if zone.name == self.driver.default_dns_zone.name: + self.driver.default_dns_zone.id = zone.id + global DNS_DOMAIN_ID + DNS_DOMAIN_ID = zone.id + return True + return False + if zone_found(): + return + self.create_domain_if_needed() + for i in range(5): + if zone_found(): + return + self.fail("""Could not find default dns zone. + This happens when they clear the staging DNS service of data. + To fix it, manually run the tests as follows: + $ ADD_DOMAINS=True python int_tests.py + and if all goes well the tests will create a new domain + record.""") + + @test(depends_on=[ensure_domain_specified_in_flags_exists], + enabled=WHITE_BOX and FLAGS.dns_domain_name != "dbaas.rackspace.com") + def delete_all_entries(self): + """Deletes all entries under the default domain.""" + list = self.driver.get_entries() + for entry in list: + if entry.type == "A": + self.driver.delete_entry(name=entry.name, type=entry.type, + dns_zone=entry.dns_zone) + # It takes awhile for them to be deleted. + poll_until(lambda: self.driver.get_entries_by_name(TEST_NAME), + lambda list: len(list) == 0, + sleep_time=4, time_out=60) + + @test(depends_on=[delete_all_entries]) + def create_test_entry(self): + fullname = TEST_NAME + entry = DnsEntry(name=fullname, content=TEST_CONTENT, type="A", + ttl=3600) + self.driver.create_entry(entry) + list = None + for i in range(500): + list = self.driver.get_entries_by_name(name=fullname) + if len(list) > 0: + break + time.sleep(1) + print("This is the list: %r" % list) + assert_equal(1, len(list)) + list2 = self.driver.get_entries_by_content(content=TEST_CONTENT) + assert_equal(1, len(list2)) + + @test(depends_on=[delete_all_entries]) + def create_test_rsdns_entry(self): + """Create an entry using the RsDnsInstanceEntryFactory.""" + instance = {'uuid': '000136c0-effa-4711-a747-a5b9fbfcb3bd', 'id': '10'} + ip = "10.100.2.7" + factory = RsDnsInstanceEntryFactory(dns_domain_id=DNS_DOMAIN_ID) + entry = factory.create_entry(instance) + entry.content = ip + self.driver.create_entry(entry) + entries = self.driver.get_entries_by_name(name=entry.name) + assert_equal(1, len(entries)) + assert_equal(ip, entries[0].content) + assert_equal(FLAGS.dns_ttl, entries[0].ttl) + + @test(depends_on=[create_test_entry]) + def delete_test_entry(self): + fullname = TEST_NAME + self.driver.delete_entry(fullname, "A") + # It takes awhile for them to be deleted. + poll_until(lambda: self.driver.get_entries_by_name(TEST_NAME), + lambda list: len(list) == 0, + sleep_time=2, time_out=60) diff --git a/integration/tests/integration/tests/dns/concurrency.py b/integration/tests/integration/tests/dns/concurrency.py new file mode 100644 index 0000000000..4fe460b049 --- /dev/null +++ b/integration/tests/integration/tests/dns/concurrency.py @@ -0,0 +1,111 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +This test recreates an issue we had with eventlet. In the logs, we'd see that +the JSON response was malformed; instead of JSON, it contained the following +string: +Second simultaneous read on fileno 5 detected. Unless you really know what +you're doing, make sure that only one greenthread can read any particular +socket. Consider using a pools.Pool. If you do know what you're doing and want +to disable this error, call +eventlet.debug.hub_multiple_reader_prevention(False) + +It is perhaps the most helpful error message ever created. + +The root issue was that a subclass of httplib2.Http was created at program +started and used in all threads. + +Using the old (broken) RsDNS client code this test recreates the greatest error +message ever. +""" + +try: + import eventlet + CAN_USE_EVENTLET = True +except ImportError: + CAN_USE_EVENTLET = False +import uuid + +from proboscis import before_class +from proboscis import test +from proboscis.asserts import assert_true + +from trove.tests.config import CONFIG + +WHITE_BOX = CONFIG.white_box +RUN_DNS = CONFIG.values.get("trove_dns_support", False) + + +if CONFIG.white_box: + from trove.dns.rsdns.driver import RsDnsInstanceEntryFactory + from nova import flags + from nova import utils + FLAGS = flags.FLAGS + + +@test(groups=["rsdns.eventlet"], enabled=CAN_USE_EVENTLET) +class RsdnsEventletTests(object): + """Makes sure the RSDNS client can be used from multiple green threads.""" + + def assert_record_created(self, index): + msg = "Record %d wasn't created!" % index + assert_true(index in self.new_records, msg) + + @before_class(enabled=WHITE_BOX and RUN_DNS) + def create_driver(self): + """Creates the DNS Driver used in subsequent tests.""" + self.driver = utils.import_object(FLAGS.dns_driver) + self.entry_factory = RsDnsInstanceEntryFactory() + self.test_uuid = uuid.uuid4().hex + self.new_records = {} + + def make_record(self, index): + """Creates a record with the form 'eventlet-%s-%d'.""" + uuid = "eventlet-%s-%d" % (self.test_uuid, index) + instance = {'uuid': uuid} + entry = self.entry_factory.create_entry(instance) + entry.name = uuid + "." + self.entry_factory.default_dns_zone.name + entry.content = "123.123.123.123" + self.driver.create_entry(entry) + self.new_records[index] = True + + @test(enabled=WHITE_BOX and RUN_DNS) + def use_dns_from_a_single_thread(self): + """Add DNS records one at a time.""" + self.new_records = {} + for index in range(-1, -5, -1): + self.make_record(index) + self.assert_record_created(index) + + @test(enabled=WHITE_BOX and RUN_DNS) + def use_dns_from_multiple_greenthreads(self): + """Add multiple DNS records at once.""" + self.new_records = {} + + def make_record(index): + def __cb(): + self.make_record(index) + self.assert_record_created(index) + return index + return __cb + + pile = eventlet.GreenPile() + indices = range(1, 4) + for index in indices: + pile.spawn(make_record(index)) + + list(pile) # Wait for them to finish + for index in indices: + self.assert_record_created(index) diff --git a/integration/tests/integration/tests/dns/conversion.py b/integration/tests/integration/tests/dns/conversion.py new file mode 100644 index 0000000000..2af3b959ad --- /dev/null +++ b/integration/tests/integration/tests/dns/conversion.py @@ -0,0 +1,105 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +"""Tests classes which convert RS style-entries to Nova DNS entries.""" + +import hashlib +import re +import unittest +from proboscis import test +from proboscis.decorators import expect_exception + +from trove.tests.config import CONFIG + + +if CONFIG.white_box: + from nova import flags + from rsdns.client.records import Record + from trove.dns.rsdns.driver import EntryToRecordConverter + from trove.dns.rsdns.driver import RsDnsInstanceEntryFactory + from trove.dns.rsdns.driver import RsDnsZone + FLAGS = flags.FLAGS + driver = None + DEFAULT_ZONE = RsDnsZone(1, "dbaas.rackspace.org") + TEST_CONTENT = "126.1.1.1" + TEST_NAME = "hiwassup.dbaas.rackspace.org" + + +@test(groups=["unit", "rsdns.conversion"], + enabled=CONFIG.white_box) +class ConvertingNovaEntryNamesToRecordNames(unittest.TestCase): + + def setUp(self): + self.converter = EntryToRecordConverter(DEFAULT_ZONE) + self.fake_zone = RsDnsZone(id=5, name="blah.org") + + def test_normal_name(self): + long_name = self.converter.name_to_long_name("hi", self.fake_zone) + self.assertEqual("hi.blah.org", long_name) + + def test_short_name(self): + long_name = self.converter.name_to_long_name("", self.fake_zone) + self.assertEqual("", long_name) + + def test_long_name(self): + long_name = self.converter.name_to_long_name("blah.org.", + self.fake_zone) + self.assertEqual("blah.org..blah.org", long_name) + + +@test(groups=["unit", "rsdns.conversion"], + enabled=CONFIG.white_box) +class ConvertingRecordsToEntries(unittest.TestCase): + + def setUp(self): + self.converter = EntryToRecordConverter(DEFAULT_ZONE) + self.fake_zone = RsDnsZone(id=5, name="blah.org") + + def test_normal_name(self): + record = Record(None, {"id": 5, "name": "hi.blah.org", + "data": "stacker.com blah@blah 13452378", + "ttl": 5, + "type": "SOA"}) + entry = self.converter.record_to_entry(record=record, + dns_zone=self.fake_zone) + self.assertEqual("stacker.com blah@blah 13452378", entry.content) + self.assertEqual("hi.blah.org", entry.name) + self.assertEqual("5", str(entry.ttl)) + self.assertEqual("SOA", entry.type) + + +@test(groups=["rsdns.conversion"], + enabled=CONFIG.white_box) +class WhenCreatingAnEntryForAnInstance(unittest.TestCase): + # This isn't a unit test because RsDnsInstanceEntryFactory connects to the + # service. + + def setUp(self): + self.creator = RsDnsInstanceEntryFactory() + + def test_should_concatanate_strings(self): + instance = {'id': '56', + 'uuid': '000136c0-effa-4711-a747-a5b9fbfcb3bd'} + entry = self.creator.create_entry(instance) + expected_name = "%s.%s" % (hashlib.sha1(instance['uuid']).hexdigest(), + FLAGS.dns_domain_name) + self.assertEqual(expected_name, entry.name, + msg="Entry name should match - %s" % entry.name) + self.assertIsNone(entry.content) + self.assertEqual("A", entry.type) + self.assertEqual(FLAGS.dns_ttl, entry.ttl) + self.assertIsNone(entry.priority) + self.assertEqual(FLAGS.dns_domain_name, entry.dns_zone.name) + if not entry.dns_zone.id: + self.fail(msg="DNS Zone Id should not be empty") diff --git a/integration/tests/integration/tests/dns/dns.py b/integration/tests/integration/tests/dns/dns.py new file mode 100644 index 0000000000..3734ad5475 --- /dev/null +++ b/integration/tests/integration/tests/dns/dns.py @@ -0,0 +1,104 @@ + +import unittest + +from proboscis import test + +from trove.tests.api.instances import instance_info +from trove.tests.api.instances import GROUP_START as INSTANCE_START +from trove.tests.api.instances import GROUP_TEST +from trove.tests.api.instances import GROUP_STOP as INSTANCE_STOP +from trove.tests.config import CONFIG +from trove.common.utils import import_object +from trove.common.utils import poll_until + +WHITE_BOX = CONFIG.white_box + +if WHITE_BOX: + # TODO(tim.simpson): Restore this once white box functionality can be + # added back to this test module. + pass + # import rsdns + # from nova import flags + # from nova import utils + + # from trove import exception + # from trove.utils import poll_until + + # FLAGS = flags.FLAGS + +dns_driver = None + +GROUP = "dbaas.guest.dns" + + +@test(groups=[GROUP, GROUP_TEST]) +class Setup(unittest.TestCase): + """Creates the DNS Driver and entry factory used in subsequent tests.""" + + def test_create_rs_dns_driver(self): + global dns_driver + dns_driver = import_object(FLAGS.dns_driver) + + +def expected_dns_entry(): + """Returns expected DNS entry for this instance. + + :rtype: Instance of :class:`DnsEntry`. + + """ + return create_dns_entry(instance_info.local_id, instance_info.id) + + +@test(depends_on_classes=[Setup], + depends_on_groups=[INSTANCE_START], + groups=[GROUP, GROUP_TEST]) +class WhenInstanceIsCreated(unittest.TestCase): + """Make sure the DNS name was provisioned. + + This class actually calls the DNS driver to confirm the entry that should + exist for the given instance does exist. + + """ + + def test_dns_entry_should_exist(self): + entry = expected_dns_entry() + if entry: + def get_entries(): + return dns_driver.get_entries_by_name(entry.name) + try: + poll_until(get_entries, lambda entries: len(entries) > 0, + sleep_time=2, time_out=60) + except exception.PollTimeOut: + self.fail("Did not find name " + entry.name + \ + " in the entries, which were as follows:" + + str(dns_driver.get_entries())) + + +@test(depends_on_classes=[Setup, WhenInstanceIsCreated], + depends_on_groups=[INSTANCE_STOP], + groups=[GROUP]) +class AfterInstanceIsDestroyed(unittest.TestCase): + """Make sure the DNS name is removed along with an instance. + + Because the compute manager calls the DNS manager with RPC cast, it can + take awhile. So we wait for 30 seconds for it to disappear. + + """ + + def test_dns_entry_exist_should_be_removed_shortly_thereafter(self): + entry = expected_dns_entry() + + if not entry: + return + + def get_entries(): + return dns_driver.get_entries_by_name(entry.name) + + try: + poll_until(get_entries, lambda entries: len(entries) == 0, + sleep_time=2, time_out=60) + except exception.PollTimeOut: + # Manually delete the rogue item + dns_driver.delete_entry(entry.name, entry.type, entry.dns_zone) + self.fail("The DNS entry was never deleted when the instance " + "was destroyed.") diff --git a/integration/tests/integration/tests/initialize.py b/integration/tests/integration/tests/initialize.py new file mode 100644 index 0000000000..ddd5aa86d1 --- /dev/null +++ b/integration/tests/integration/tests/initialize.py @@ -0,0 +1,176 @@ +# Copyright (c) 2011 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import unittest +import os +import time +import socket + +from nose.plugins.skip import SkipTest + +from proboscis import test +from proboscis.asserts import fail +from proboscis.decorators import time_out +from tests.util.services import Service +from tests.util.services import start_proc +from tests.util.services import WebService +from trove.tests.config import CONFIG + + +FAKE = CONFIG.fake_mode +START_SERVICES = (not FAKE) and CONFIG.values.get('start_services', False) +START_NOVA_NETWORK = (START_SERVICES and + not CONFIG.values.get('neutron_enabled', + False)) +KEYSTONE_ALL = CONFIG.values.get('keystone_use_combined', True) +USE_NOVA_VOLUME = CONFIG.values.get('use_nova_volume', False) + +dbaas_image = None +instance_name = None +success_statuses = ["build", "active"] + + +def dbaas_url(): + return str(CONFIG.values.get("dbaas_url")) + +def nova_url(): + return str(CONFIG.values.get("nova_client")['url']) + + + +class Daemon(object): + """Starts a daemon.""" + + def __init__(self, alternate_path=None, conf_file_name=None, + extra_cmds=None, service_path_root=None, service_path=None): + # The path to the daemon bin if the other one doesn't work. + self.alternate_path = alternate_path + self.extra_cmds = extra_cmds or [] + # The name of a test config value which points to a conf file. + self.conf_file_name = conf_file_name + # The name of a test config value, which is inserted into the service_path. + self.service_path_root = service_path_root + # The first path to the daemon bin we try. + self.service_path = service_path or "%s" + + def run(self): + # Print out everything to make it + print("Looking for config value %s..." % self.service_path_root) + print(CONFIG.values[self.service_path_root]) + path = self.service_path % CONFIG.values[self.service_path_root] + print("Path = %s" % path) + if not os.path.exists(path): + path = self.alternate_path + if path is None: + fail("Could not find path to %s" % self.service_path_root) + conf_path = str(CONFIG.values[self.conf_file_name]) + cmds = CONFIG.python_cmd_list() + [path] + self.extra_cmds + \ + [conf_path] + print("Running cmds: %s" % cmds) + self.service = Service(cmds) + if not self.service.is_service_alive(): + self.service.start() + +@test(groups=["services.initialize"], + enabled=START_SERVICES and (not KEYSTONE_ALL)) +def start_keystone_all(): + """Starts the Keystone API.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/keystone-all", + extra_cmds=['--config-file'], + conf_file_name="keystone_conf").run() + + +@test(groups=["services.initialize", "services.initialize.glance"], + enabled=START_SERVICES) +def start_glance_registry(): + """Starts the Glance Registry.""" + Daemon(alternate_path="/usr/bin/glance-registry", + conf_file_name="glance_reg_conf", + service_path_root="usr_bin_dir", + service_path="%s/glance-registry").run() + + +@test(groups=["services.initialize", "services.initialize.glance"], + depends_on=[start_glance_registry], enabled=START_SERVICES) +def start_glance_api(): + """Starts the Glance API.""" + Daemon(alternate_path="/usr/bin/glance-api", + conf_file_name="glance_reg_conf", + service_path_root="usr_bin_dir", + service_path="%s/glance-api").run() + + +@test(groups=["services.initialize"], depends_on_classes=[start_glance_api], + enabled=START_NOVA_NETWORK) +def start_nova_network(): + """Starts the Nova Network Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/nova-network", + extra_cmds=['--config-file='], + conf_file_name="nova_conf").run() + + +@test(groups=["services.initialize"], enabled=START_SERVICES) +def start_scheduler(): + """Starts the Scheduler Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/nova-scheduler", + extra_cmds=['--config-file='], + conf_file_name="nova_conf").run() + + +@test(groups=["services.initialize"], + depends_on_classes=[start_glance_api], + enabled=START_SERVICES) +def start_compute(): + """Starts the Nova Compute Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/nova-compute", + extra_cmds=['--config-file='], + conf_file_name="nova_conf").run() + + +@test(groups=["services.initialize"], depends_on_classes=[start_scheduler], + enabled=START_SERVICES and USE_NOVA_VOLUME) +def start_volume(): + """Starts the Nova Compute Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/nova-volume", + extra_cmds=['--config-file='], + conf_file_name="nova_conf").run() + + +@test(groups=["services.initialize"], + depends_on_classes=[start_glance_api, start_nova_network, start_compute, + start_volume], + enabled=START_SERVICES) +def start_nova_api(): + """Starts the Nova Compute Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/nova-api", + extra_cmds=['--config-file='], + conf_file_name="nova_conf").run() + + +@test(groups=["services.initialize"], + depends_on_classes=[start_nova_api], + enabled=START_SERVICES) +def start_trove_api(): + """Starts the Trove Service.""" + Daemon(service_path_root="usr_bin_dir", + service_path="%s/trove-api", + extra_cmds=['--config-file='], + conf_file_name="trove_conf").run() diff --git a/integration/tests/integration/tests/smoke/__init__.py b/integration/tests/integration/tests/smoke/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration/tests/integration/tests/smoke/instance.py b/integration/tests/integration/tests/smoke/instance.py new file mode 100644 index 0000000000..3488e5f2b9 --- /dev/null +++ b/integration/tests/integration/tests/smoke/instance.py @@ -0,0 +1,103 @@ + +from proboscis.asserts import assert_equal +from proboscis import test +from proboscis import before_class + +from trove.common.utils import poll_until +from trove.tests.util import create_client + + +class InstanceGenerator(object): + + def __init__(self, client, status=None, name=None, flavor=None, + account_id=None, created_at=None, databases=None, users=None, + volume_size=None): + self.client = client + self.status = status + self.name = name + self.flavor = flavor + self.account_id = account_id + self.databases = databases + self.users = users + self.volume_size = volume_size + self.id = None + + def create_instance(self): + #make the call to create the instance + instance = self.client.instances.create(self.name, self.flavor, + self.volume_size, self.databases, self.users) + self.client.assert_http_code(200) + + #verify we are in a build state + assert_equal(instance.status, "BUILD") + #pull out the ID + self.id = instance.id + + return instance + + def wait_for_build_to_finish(self): + poll_until(lambda: self.client.instance.get(self.id), + lambda instance: instance.status != "BUILD", + time_out=600) + + def get_active_instance(self): + instance = self.client.instance.get(self.id) + self.client.assert_http_code(200) + + #check the container name + assert_equal(instance.name, self.name) + + #pull out volume info and verify + assert_equal(str(instance.volume_size), str(self.volume_size)) + + #pull out the flavor and verify + assert_equal(str(instance.flavor), str(self.flavor)) + + return instance + + +@test(groups=['smoke', 'positive']) +class CreateInstance(object): + + @before_class + def set_up(self): + client = create_client(is_admin=False) + name = 'test_createInstance_container' + flavor = 1 + volume_size = 1 + db_name = 'test_db' + databases = [ + { + "name": db_name + } + ] + users = [ + { + "name": "lite", + "password": "litepass", + "databases": [{"name": db_name}] + } + ] + + #create the Instance + instance = InstanceGenerator(client, name=self.name, + flavor=flavor, + volume_size=self.volume_size, + databases=databases, users=users) + instance.create_instance() + + #wait for the instance + instance.wait_for_build_to_finish() + + #get the active instance + inst = instance.get_active_instance() + + #list out the databases for our instance and verify the db name + dbs = client.databases.list(inst.id) + client.assert_http_code(200) + + assert_equal(len(dbs), 1) + assert_equal(dbs[0].name, instance.db_name) + + client.instance.delete(inst.id) + client.assert_http_code(202) diff --git a/integration/tests/integration/tests/util/__init__.py b/integration/tests/integration/tests/util/__init__.py new file mode 100644 index 0000000000..671d3c173e --- /dev/null +++ b/integration/tests/integration/tests/util/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/integration/tests/integration/tests/util/report.py b/integration/tests/integration/tests/util/report.py new file mode 100644 index 0000000000..c3c1007cf3 --- /dev/null +++ b/integration/tests/integration/tests/util/report.py @@ -0,0 +1,76 @@ +"""Creates a report for the test. +""" + +import os +import shutil +from os import path +from trove.tests.config import CONFIG + +USE_LOCAL_OVZ = CONFIG.use_local_ovz + + +class Reporter(object): + """Saves the logs from a test run.""" + + def __init__(self, root_path): + self.root_path = root_path + if not path.exists(self.root_path): + os.mkdir(self.root_path) + for file in os.listdir(self.root_path): + if file.endswith(".log"): + os.remove(path.join(self.root_path, file)) + + def _find_all_instance_ids(self): + instances = [] + if USE_LOCAL_OVZ: + for dir in os.listdir("/var/lib/vz/private"): + instances.append(dir) + return instances + + def log(self, msg): + with open("%s/report.log" % self.root_path, 'a') as file: + file.write(str(msg) + "\n") + + def _save_syslog(self): + try: + shutil.copyfile("/var/log/syslog", "host-syslog.log") + except (shutil.Error, IOError) as err: + self.log("ERROR logging syslog : %s" % (err)) + + def _update_instance(self, id): + root = "%s/%s" % (self.root_path, id) + + def save_file(path, short_name): + if USE_LOCAL_OVZ: + try: + shutil.copyfile("/var/lib/vz/private/%s/%s" % (id, path), + "%s-%s.log" % (root, short_name)) + except (shutil.Error, IOError) as err: + self.log("ERROR logging %s for instance id %s! : %s" + % (path, id, err)) + else: + #TODO: Can we somehow capture these (maybe SSH to the VM)? + pass + + save_file("/var/log/firstboot", "firstboot") + save_file("/var/log/syslog", "syslog") + save_file("/var/log/nova/guest.log", "nova-guest") + + def _update_instances(self): + for id in self._find_all_instance_ids(): + self._update_instance(id) + + def update(self): + self._update_instances() + self._save_syslog() + + +REPORTER = Reporter(CONFIG.report_directory) + + +def log(msg): + REPORTER.log(msg) + + +def update(): + REPORTER.update() diff --git a/integration/tests/integration/tests/util/rpc.py b/integration/tests/integration/tests/util/rpc.py new file mode 100644 index 0000000000..d534ff31a9 --- /dev/null +++ b/integration/tests/integration/tests/util/rpc.py @@ -0,0 +1,110 @@ +# Copyright (c) 2012 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Test utility for RPC checks. + +Functionality to check for rabbit here depends on having rabbit running on +the same machine as the tests, so that the rabbitmqctl commands will function. +The functionality is turned on or off by the test config "rabbit_runs_locally". + +""" + +import re + +from trove.tests.config import CONFIG +from services import start_proc + + +if CONFIG.values.get('rabbit_runs_locally', False) == True: + + DIRECT_ACCESS = True + + class Rabbit(object): + + def declare_queue(self, topic): + """Call this to declare a queue from Python.""" + #from trove.rpc.impl_kombu import Connection + from trove.openstack.common.rpc import create_connection + with create_connection() as conn: + consumer = conn.declare_topic_consumer(topic=topic) + + def get_queue_items(self, queue_name): + """Determines if the queue exists and if so the message count. + + If the queue exists the return value is an integer, otherwise + its None. + + Be careful because queue_name is used in a regex and can't have + any unescaped characters. + + """ + proc = start_proc(["/usr/bin/sudo", "rabbitmqctl", "list_queues"], + shell=False) + for line in iter(proc.stdout.readline, ""): + print("LIST QUEUES:" + line) + m = re.search("""%s\s+([0-9]+)""" % queue_name, line) + if m: + return int(m.group(1)) + return None + + @property + def is_alive(self): + """Calls list_queues, should fail.""" + try: + stdout, stderr = self.run(0, "rabbitmqctl", "list_queues") + for lines in stdout, stderr: + for line in lines: + if "no_exists" in line: + return False + return True + except Exception: + return False + + def reset(self): + out, err = self.run(0, "rabbitmqctl", "reset") + print(out) + print(err) + + def run(self, check_exit_code, *cmd): + cmds = ["/usr/bin/sudo"] + list(cmd) + proc = start_proc(cmds) + lines = proc.stdout.readlines() + err_lines = proc.stderr.readlines() + return lines, err_lines + + def start(self): + print("Calling rabbitmqctl start_app") + out = self.run(0, "rabbitmqctl", "start_app") + print(out) + out, err = self.run(0, "rabbitmqctl", "change_password", "guest", + CONFIG.values['rabbit_password']) + print(out) + print(err) + + def stop(self): + print("Calling rabbitmqctl stop_app") + out = self.run(0, "rabbitmqctl", "stop_app") + print(out) + +else: + + DIRECT_ACCESS = False + + class Rabbit(object): + + def __init__(self): + raise RuntimeError("rabbit_runs_locally is set to False in the " + "test config, so this test cannot be run.") + diff --git a/integration/tests/integration/tests/util/services.py b/integration/tests/integration/tests/util/services.py new file mode 100644 index 0000000000..b54f92d6b8 --- /dev/null +++ b/integration/tests/integration/tests/util/services.py @@ -0,0 +1,280 @@ +# Copyright (c) 2011 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Functions to initiate and shut down services needed by the tests.""" + +import os +import re +import subprocess +import time + +from collections import namedtuple +from httplib2 import Http +from nose.plugins.skip import SkipTest + +from proboscis import decorators + + +def _is_web_service_alive(url): + """Does a HTTP GET request to see if the web service is up.""" + client = Http() + try: + resp = client.request(url, 'GET') + return resp != None + except Exception: + return False + + +_running_services = [] + + +def get_running_services(): + """ Returns the list of services which this program has started.""" + return _running_services + + +def start_proc(cmd, shell=False): + """Given a command, starts and returns a process.""" + env = os.environ.copy() + proc = subprocess.Popen( + cmd, + shell=shell, + stdin=subprocess.PIPE, + bufsize=0, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env + ) + return proc + + +MemoryInfo = namedtuple("MemoryInfo", ['mapped', 'writeable', 'shared']) + + +class Service(object): + """Starts and stops a service under test. + + The methods to start and stop the service will not actually do anything + if they detect the service is already running on this machine. This is + because it may be useful for developers to start the services themselves + some other way. + + """ + + # TODO(tim.simpson): Hard to follow, consider renaming certain attributes. + + def __init__(self, cmd): + """Defines a service to run.""" + if not isinstance(cmd, list): + raise TypeError() + self.cmd = cmd + self.do_not_manage_proc = False + self.proc = None + + def __del__(self): + if self.is_running: + self.stop() + + def ensure_started(self): + """Starts the service if it is not running.""" + if not self.is_running: + self.start() + + def find_proc_id(self): + """Finds and returns the process id.""" + if not self.cmd: + return False + # The cmd[1] signifies the executable python script. It gets invoked + # as python /path/to/executable args, so the entry is + # /path/to/executable + actual_command = self.cmd[1].split("/")[-1] + proc_command = ["/usr/bin/pgrep", "-f", actual_command] + proc = start_proc(proc_command, shell=False) + # this is to make sure there is only one pid returned from the pgrep + has_two_lines = False + pid = None + for line in iter(proc.stdout.readline, ""): + if has_two_lines: + raise RuntimeError("Found PID twice.") + pid = int(line) + has_two_lines = True + return pid + + def get_memory_info(self): + """Returns how much memory the process is using according to pmap.""" + pid = self.find_proc_id() + if not pid: + raise RuntimeError("Can't find PID, so can't get memory.") + proc = start_proc(["/usr/bin/pmap", "-d", str(pid)], + shell=False) + for line in iter(proc.stdout.readline, ""): + m = re.search("""mapped\:\s([0-9]+)K\s+""" + """writeable/private:\s([0-9]+)K\s+""" + """shared:\s+([0-9]+)K""", line) + if m: + return MemoryInfo(int(m.group(1)), int(m.group(2)), + int(m.group(3))) + raise RuntimeError("Memory info not found.") + + def get_fd_count_from_proc_file(self): + """Returns file descriptors according to /proc//status.""" + pid = self.find_proc_id() + with open("/proc/%d/status" % pid) as status: + for line in status.readlines(): + index = line.find(":") + name = line[:index] + value = line[index + 1:] + if name == "FDSize": + return int(value) + raise RuntimeError("FDSize not found!") + + def get_fd_count(self): + """Returns file descriptors according to /proc//status.""" + pid = self.find_proc_id() + cmd = "Finding file descriptors..." + print("CMD" + cmd) + proc = start_proc(['ls', '-la', '/proc/%d/fd' % pid], shell=False) + count = -3 + has_two_lines = False + for line in iter(proc.stdout.readline, ""): + print("\t" + line) + count += 1 + if not count: + raise RuntimeError("Could not get file descriptors!") + return count + + + with open("/proc/%d/fd" % pid) as status: + for line in status.readlines(): + index = line.find(":") + name = line[:index] + value = line[index + 1:] + if name == "FDSize": + return int(value) + raise RuntimeError("FDSize not found!") + + def kill_proc(self): + """Kills the process, wherever it may be.""" + pid = self.find_proc_id() + if pid: + start_proc("sudo kill -9 " + str(pid), shell=True) + time.sleep(1) + if self.is_service_alive(): + raise RuntimeError('Cannot kill process, PID=' + + str(self.proc.pid)) + + def is_service_alive(self, proc_name_index=1): + """Searches for the process to see if its alive. + + This function will return true even if this class has not started + the service (searches using ps). + + """ + if not self.cmd: + return False + time.sleep(1) + # The cmd[1] signifies the executable python script. It gets invoked + # as python /path/to/executable args, so the entry is + # /path/to/executable + actual_command = self.cmd[proc_name_index].split("/")[-1] + print actual_command + proc_command = ["/usr/bin/pgrep", "-f", actual_command] + print proc_command + proc = start_proc(proc_command, shell=False) + line = proc.stdout.readline() + print line + # pgrep only returns a pid. if there is no pid, it'll return nothing + return len(line) != 0 + + @property + def is_running(self): + """Returns true if the service has already been started. + + Returns true if this program has started the service or if it + previously detected it had started. The main use of this property + is to know if the service was already begun by this program- + use is_service_alive for a more definitive answer. + + """ + return self.proc or self.do_not_manage_proc + + def restart(self, extra_args): + if self.do_not_manage_proc: + raise RuntimeError("Can't restart proc as the tests don't own it.") + self.stop() + time.sleep(2) + self.start(extra_args=extra_args) + + def start(self, time_out=30, extra_args=None): + """Starts the service if necessary.""" + extra_args = extra_args or [] + if self.is_running: + raise RuntimeError("Process is already running.") + if self.is_service_alive(): + self.do_not_manage_proc = True + return + self.proc = start_proc(self.cmd + extra_args, shell=False) + if not self._wait_for_start(time_out=time_out): + self.stop() + raise RuntimeError("Issued the command successfully but the " + "service (" + str(self.cmd + extra_args) + + ") never seemed to start.") + _running_services.append(self) + + def stop(self): + """Stops the service, but only if this program started it.""" + if self.do_not_manage_proc: + return + if not self.proc: + raise RuntimeError("Process was not started.") + self.proc.terminate() + self.proc.kill() + self.proc.wait() + self.proc.stdin.close() + self.kill_proc() + self.proc = None + global _running_services + _running_services = [svc for svc in _running_services if svc != self] + + def _wait_for_start(self, time_out): + """Waits until time_out (in seconds) for service to appear.""" + give_up_time = time.time() + time_out + while time.time() < give_up_time: + if self.is_service_alive(): + return True + return False + + +class NativeService(Service): + + def is_service_alive(self): + return super(NativeService, self).is_service_alive(proc_name_index=0) + + + +class WebService(Service): + """Starts and stops a web service under test.""" + + def __init__(self, cmd, url): + """Defines a service to run.""" + Service.__init__(self, cmd) + if not isinstance(url, (str, unicode)): + raise TypeError() + self.url = url + self.do_not_manage_proc = self.is_service_alive() + + def is_service_alive(self): + """Searches for the process to see if its alive.""" + return _is_web_service_alive(self.url) diff --git a/integration/tests/integration/tests/volumes/__init__.py b/integration/tests/integration/tests/volumes/__init__.py new file mode 100644 index 0000000000..09c4cfed1c --- /dev/null +++ b/integration/tests/integration/tests/volumes/__init__.py @@ -0,0 +1,25 @@ +# Copyright (c) 2011 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +:mod:`volumes` -- Tests for volumes. +=================================== +""" + +""""Tests for Volumes.""" + +# Is a set of tests written directly against the VolumeManager and VolumeClient +# classes which doesn't require standing up Nova daemons or anything. +VOLUMES_DRIVER = "trove.volumes.driver" diff --git a/integration/tests/integration/tests/volumes/driver.py b/integration/tests/integration/tests/volumes/driver.py new file mode 100644 index 0000000000..221b2a41f4 --- /dev/null +++ b/integration/tests/integration/tests/volumes/driver.py @@ -0,0 +1,546 @@ +# Copyright (c) 2012 OpenStack, LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from numbers import Number +import os +import re +import shutil +import socket +import time +import unittest + +import pexpect + +from proboscis import test +from proboscis.asserts import assert_raises +from proboscis.decorators import expect_exception +from proboscis.decorators import time_out + +from trove.tests.config import CONFIG +from trove.common.utils import poll_until +from trove.tests.util import process +from trove.common.utils import import_class +from tests import initialize + + +WHITE_BOX = CONFIG.white_box +VOLUMES_DRIVER = "trove.volumes.driver" + +if WHITE_BOX: + # TODO(tim.simpson): Restore this once white box functionality can be + # added back to this test module. + pass + # from nova import context + # from nova import exception + # from nova import flags + # from nova import utils + # from trove import exception as trove_exception + # from trove.utils import poll_until + # from trove import volume + # from trove.tests.volume import driver as test_driver + + # FLAGS = flags.FLAGS + + +UUID_PATTERN = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-' + '[0-9a-f]{4}-[0-9a-f]{12}$') + +HUGE_VOLUME = 5000 + + +def is_uuid(text): + return UUID_PATTERN.search(text) is not None + + +class StoryDetails(object): + + def __init__(self): + self.api = volume.API() + self.client = volume.Client() + self.context = context.get_admin_context() + self.device_path = None + self.volume_desc = None + self.volume_id = None + self.volume_name = None + self.volume = None + self.host = socket.gethostname() + self.original_uuid = None + self.original_device_info = None + self.resize_volume_size = 2 + + def get_volume(self): + return self.api.get(self.context, self.volume_id) + + @property + def mount_point(self): + return "%s/%s" % (LOCAL_MOUNT_PATH, self.volume_id) + + @property + def test_mount_file_path(self): + return "%s/test.txt" % self.mount_point + + +story = None +storyFail = None + +LOCAL_MOUNT_PATH = "/testsmnt" + + +class VolumeTest(unittest.TestCase): + """This test tells the story of a volume, from cradle to grave.""" + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + + def setUp(self): + global story, storyFail + self.story = story + self.storyFail = storyFail + + def assert_volume_as_expected(self, volume): + self.assertIsInstance(volume["id"], Number) + self.assertEqual(self.story.volume_name, volume["display_name"]) + self.assertEqual(self.story.volume_desc, volume["display_description"]) + self.assertEqual(1, volume["size"]) + self.assertEqual(self.story.context.user_id, volume["user_id"]) + self.assertEqual(self.story.context.project_id, volume["project_id"]) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[initialize.start_volume]) +class SetUp(VolumeTest): + + def test_05_create_story(self): + """Creating 'story' vars used by the rest of these tests.""" + global story, storyFail + story = StoryDetails() + storyFail = StoryDetails() + + @time_out(60) + def test_10_wait_for_topics(self): + """Wait until the volume topic is up before proceeding.""" + topics = ["volume"] + from tests.util.topics import hosts_up + while not all(hosts_up(topic) for topic in topics): + pass + + def test_20_refresh_local_folders(self): + """Delete the local folders used as mount locations if they exist.""" + if os.path.exists(LOCAL_MOUNT_PATH): + #TODO(rnirmal): Also need to remove any existing mounts. + shutil.rmtree(LOCAL_MOUNT_PATH) + os.mkdir(LOCAL_MOUNT_PATH) + # Give some time for the services to startup + time.sleep(10) + + @time_out(60) + def test_30_mgmt_volume_check(self): + """Get the volume information from the mgmt API""" + story_context = self.story.context + device_info = self.story.api.get_storage_device_info(story_context) + print("device_info : %r" % device_info) + self.assertNotEqual(device_info, None, + "the storage device information should exist") + self.story.original_device_info = device_info + + @time_out(60) + def test_31_mgmt_volume_info(self): + """Check the available space against the mgmt API info.""" + story_context = self.story.context + device_info = self.story.api.get_storage_device_info(story_context) + print("device_info : %r" % device_info) + info = {'spaceTotal': device_info['raw_total'], + 'spaceAvail': device_info['raw_avail']} + self._assert_available_space(info) + + def _assert_available_space(self, device_info, fail=False): + """ + Give the SAN device_info(fake or not) and get the asserts for free + """ + print("DEVICE_INFO on SAN : %r" % device_info) + # Calculate the GBs; Divide by 2 for the FLAGS.san_network_raid_factor + gbs = 1.0 / 1024 / 1024 / 1024 / 2 + total = int(device_info['spaceTotal']) * gbs + free = int(device_info['spaceAvail']) * gbs + used = total - free + usable = total * (FLAGS.san_max_provision_percent * 0.01) + real_free = float(int(usable - used)) + + print("total : %r" % total) + print("free : %r" % free) + print("used : %r" % used) + print("usable : %r" % usable) + print("real_free : %r" % real_free) + + check_space = self.story.api.check_for_available_space + self.assertFalse(check_space(self.story.context, HUGE_VOLUME)) + self.assertFalse(check_space(self.story.context, real_free + 1)) + + if fail: + self.assertFalse(check_space(self.story.context, real_free)) + self.assertFalse(check_space(self.story.context, real_free - 1)) + self.assertFalse(check_space(self.story.context, 1)) + else: + self.assertTrue(check_space(self.story.context, real_free)) + self.assertTrue(check_space(self.story.context, real_free - 1)) + self.assertTrue(check_space(self.story.context, 1)) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[SetUp]) +class AddVolumeFailure(VolumeTest): + + @time_out(60) + def test_add(self): + """ + Make call to FAIL a prov. volume and assert the return value is a + FAILURE. + """ + self.assertIsNone(self.storyFail.volume_id) + name = "TestVolume" + desc = "A volume that was created for testing." + self.storyFail.volume_name = name + self.storyFail.volume_desc = desc + volume = self.storyFail.api.create(self.storyFail.context, + size=HUGE_VOLUME, + snapshot_id=None, name=name, + description=desc) + self.assertEqual(HUGE_VOLUME, volume["size"]) + self.assertTrue("creating", volume["status"]) + self.assertTrue("detached", volume["attach_status"]) + self.storyFail.volume = volume + self.storyFail.volume_id = volume["id"] + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[AddVolumeFailure]) +class AfterVolumeFailureIsAdded(VolumeTest): + """Check that the volume can be retrieved via the API, and setup. + + All we want to see returned is a list-like with an initial string. + + """ + + @time_out(120) + def test_api_get(self): + """Wait until the volume is a FAILURE.""" + volume = poll_until(lambda: self.storyFail.get_volume(), + lambda volume: volume["status"] != "creating") + self.assertEqual(volume["status"], "error") + self.assertTrue(volume["attach_status"], "detached") + + @time_out(60) + def test_mgmt_volume_check(self): + """Get the volume information from the mgmt API""" + info = self.story.api.get_storage_device_info(self.story.context) + print("device_info : %r" % info) + self.assertNotEqual(info, None, + "the storage device information should exist") + self.assertEqual(self.story.original_device_info['raw_total'], + info['raw_total']) + self.assertEqual(self.story.original_device_info['raw_avail'], + info['raw_avail']) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[SetUp]) +class AddVolume(VolumeTest): + + @time_out(60) + def test_add(self): + """Make call to prov. a volume and assert the return value is OK.""" + self.assertIsNone(self.story.volume_id) + name = "TestVolume" + desc = "A volume that was created for testing." + self.story.volume_name = name + self.story.volume_desc = desc + volume = self.story.api.create(self.story.context, size=1, + snapshot_id=None, name=name, + description=desc) + self.assert_volume_as_expected(volume) + self.assertTrue("creating", volume["status"]) + self.assertTrue("detached", volume["attach_status"]) + self.story.volume = volume + self.story.volume_id = volume["id"] + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[AddVolume]) +class AfterVolumeIsAdded(VolumeTest): + """Check that the volume can be retrieved via the API, and setup. + + All we want to see returned is a list-like with an initial string. + + """ + + @time_out(120) + def test_api_get(self): + """Wait until the volume is finished provisioning.""" + volume = poll_until(lambda: self.story.get_volume(), + lambda volume: volume["status"] != "creating") + self.assertEqual(volume["status"], "available") + self.assert_volume_as_expected(volume) + self.assertTrue(volume["attach_status"], "detached") + + @time_out(60) + def test_mgmt_volume_check(self): + """Get the volume information from the mgmt API""" + print("self.story.original_device_info : %r" % + self.story.original_device_info) + info = self.story.api.get_storage_device_info(self.story.context) + print("device_info : %r" % info) + self.assertNotEqual(info, None, + "the storage device information should exist") + self.assertEqual(self.story.original_device_info['raw_total'], + info['raw_total']) + volume_size = int(self.story.volume['size']) * (1024 ** 3) * 2 + print("volume_size: %r" % volume_size) + print("self.story.volume['size']: %r" % self.story.volume['size']) + avail = int(self.story.original_device_info['raw_avail']) - volume_size + print("avail space: %r" % avail) + self.assertEqual(int(info['raw_avail']), avail) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[AfterVolumeIsAdded]) +class SetupVolume(VolumeTest): + + @time_out(60) + def test_assign_volume(self): + """Tell the volume it belongs to this host node.""" + #TODO(tim.simpson) If this is important, could we add a test to + # make sure some kind of exception is thrown if it + # isn't added to certain drivers? + self.assertNotEqual(None, self.story.volume_id) + self.story.api.assign_to_compute(self.story.context, + self.story.volume_id, + self.story.host) + + @time_out(60) + def test_setup_volume(self): + """Set up the volume on this host. AKA discovery.""" + self.assertNotEqual(None, self.story.volume_id) + device = self.story.client._setup_volume(self.story.context, + self.story.volume_id, + self.story.host) + if not isinstance(device, basestring): + self.fail("Expected device to be a string, but instead it was " + + str(type(device)) + ".") + self.story.device_path = device + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[SetupVolume]) +class FormatVolume(VolumeTest): + + @expect_exception(IOError) + @time_out(60) + def test_10_should_raise_IOError_if_format_fails(self): + """ + + Tests that if the driver's _format method fails, its + public format method will perform an assertion properly, discover + it failed, and raise an exception. + + """ + + volume_driver_cls = import_class(FLAGS.volume_driver) + + class BadFormatter(volume_driver_cls): + + def _format(self, device_path): + pass + + bad_client = volume.Client(volume_driver=BadFormatter()) + bad_client._format(self.story.device_path) + + @time_out(60) + def test_20_format(self): + self.assertNotEqual(None, self.story.device_path) + self.story.client._format(self.story.device_path) + + def test_30_check_options(self): + cmd = ("sudo dumpe2fs -h %s 2> /dev/null | " + "awk -F ':' '{ if($1 == \"Reserved block count\") " + "{ rescnt=$2 } } { if($1 == \"Block count\") " + "{ blkcnt=$2 } } END { print (rescnt/blkcnt)*100 }'") + cmd = cmd % self.story.device_path + out, err = process(cmd) + self.assertEqual(float(5), round(float(out)), msg=out) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[FormatVolume]) +class MountVolume(VolumeTest): + + @time_out(60) + def test_mount(self): + self.story.client._mount(self.story.device_path, + self.story.mount_point) + with open(self.story.test_mount_file_path, 'w') as file: + file.write("Yep, it's mounted alright.") + self.assertTrue(os.path.exists(self.story.test_mount_file_path)) + + def test_mount_options(self): + cmd = "mount -l | awk '/%s.*noatime/ { print $1 }'" + cmd %= LOCAL_MOUNT_PATH.replace('/', '') + out, err = process(cmd) + self.assertEqual(os.path.realpath(self.story.device_path), out.strip(), + msg=out) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[MountVolume]) +class ResizeVolume(VolumeTest): + + @time_out(300) + def test_resize(self): + self.story.api.resize(self.story.context, self.story.volume_id, + self.story.resize_volume_size) + + volume = poll_until(lambda: self.story.get_volume(), + lambda volume: volume["status"] == "resized") + self.assertEqual(volume["status"], "resized") + self.assertTrue(volume["attach_status"], "attached") + self.assertTrue(volume['size'], self.story.resize_volume_size) + + @time_out(300) + def test_resizefs_rescan(self): + self.story.client.resize_fs(self.story.context, + self.story.volume_id) + expected = "trove.tests.volume.driver.ISCSITestDriver" + if FLAGS.volume_driver is expected: + size = self.story.resize_volume_size * \ + test_driver.TESTS_VOLUME_SIZE_MULTIPLIER * 1024 * 1024 + else: + size = self.story.resize_volume_size * 1024 * 1024 + out, err = process('sudo blockdev --getsize64 %s' % + os.path.realpath(self.story.device_path)) + if int(out) < (size * 0.8): + self.fail("Size %s is not more or less %s" % (out, size)) + + # Reset the volume status to available + self.story.api.update(self.story.context, self.story.volume_id, + {'status': 'available'}) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[MountVolume]) +class UnmountVolume(VolumeTest): + + @time_out(60) + def test_unmount(self): + self.story.client._unmount(self.story.mount_point) + child = pexpect.spawn("sudo mount %s" % self.story.mount_point) + child.expect("mount: can't find %s in" % self.story.mount_point) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[UnmountVolume]) +class GrabUuid(VolumeTest): + + @time_out(60) + def test_uuid_must_match_pattern(self): + """UUID must be hex chars in the form 8-4-4-4-12.""" + client = self.story.client # volume.Client() + device_path = self.story.device_path # '/dev/sda5' + uuid = client.get_uuid(device_path) + self.story.original_uuid = uuid + self.assertTrue(is_uuid(uuid), "uuid must match regex") + + @time_out(60) + def test_get_invalid_uuid(self): + """DevicePathInvalidForUuid is raised if device_path is wrong.""" + client = self.story.client + device_path = "gdfjghsfjkhggrsyiyerreygghdsghsdfjhf" + self.assertRaises(trove_exception.DevicePathInvalidForUuid, + client.get_uuid, device_path) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[GrabUuid]) +class RemoveVolume(VolumeTest): + + @time_out(60) + def test_remove(self): + self.story.client.remove_volume(self.story.context, + self.story.volume_id, + self.story.host) + self.assertRaises(Exception, + self.story.client._format, self.story.device_path) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[GrabUuid]) +class Initialize(VolumeTest): + + @time_out(300) + def test_10_initialize_will_format(self): + """initialize will setup, format, and store the UUID of a volume""" + self.assertTrue(self.story.get_volume()['uuid'] is None) + self.story.client.initialize(self.story.context, self.story.volume_id, + self.story.host) + volume = self.story.get_volume() + self.assertTrue(is_uuid(volume['uuid']), "uuid must match regex") + self.assertNotEqual(self.story.original_uuid, volume['uuid'], + "Validate our assumption that the volume UUID " + "will change when the volume is formatted.") + self.story.client.remove_volume(self.story.context, + self.story.volume_id, + self.story.host) + + @time_out(60) + def test_20_initialize_the_second_time_will_not_format(self): + """If initialize is called but a UUID exists, it should not format.""" + old_uuid = self.story.get_volume()['uuid'] + self.assertTrue(old_uuid is not None) + + class VolumeClientNoFmt(volume.Client): + + def _format(self, device_path): + raise RuntimeError("_format should not be called!") + + no_fmt_client = VolumeClientNoFmt() + no_fmt_client.initialize(self.story.context, self.story.volume_id, + self.story.host) + self.assertEqual(old_uuid, self.story.get_volume()['uuid'], + "UUID should be the same as no formatting occurred.") + self.story.client.remove_volume(self.story.context, + self.story.volume_id, + self.story.host) + + def test_30_check_device_exists(self): + assert_raises(exception.InvalidDevicePath, self.story.client._format, + self.story.device_path) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[Initialize]) +class DeleteVolume(VolumeTest): + + @time_out(60) + def test_delete(self): + self.story.api.delete(self.story.context, self.story.volume_id) + + +@test(groups=[VOLUMES_DRIVER], depends_on_classes=[DeleteVolume]) +class ConfirmMissing(VolumeTest): + + @time_out(60) + def test_discover_should_fail(self): + try: + self.story.client.driver.discover_volume(self.story.context, + self.story.volume) + self.fail("Expecting an error but did not get one.") + except exception.Error: + pass + except trove_exception.ISCSITargetNotDiscoverable: + pass + + @time_out(60) + def test_get_missing_volume(self): + try: + volume = poll_until(lambda: self.story.api.get(self.story.context, + self.story.volume_id), + lambda volume: volume["status"] != "deleted") + self.assertEqual(volume["deleted"], False) + except exception.VolumeNotFound: + pass diff --git a/integration/tests/integration/tox.ini b/integration/tests/integration/tox.ini new file mode 100644 index 0000000000..8105149305 --- /dev/null +++ b/integration/tests/integration/tox.ini @@ -0,0 +1,28 @@ +# Examples: +# Run tests against Trove running locally in fake mode: +# TROVE_CLIENT_PATH=../some_path tox -e local -- --group=blackbox +[tox] +envlist = py26 + +[testenv] +deps = + coverage + nose + pexpect + proboscis + sqlalchemy + {env:TROVE_PATH} + {env:TROVE_CLIENT_PATH} + +[testenv:py26] + +[testenv:local] +deps = + nose + pexpect + proboscis + sqlalchemy + {env:TROVE_PATH} + {env:TROVE_CLIENT_PATH} +commands = + {envpython} int_tests.py --conf=localhost.test.conf {posargs:DEFAULTS} diff --git a/integration/xsd/common.ent b/integration/xsd/common.ent new file mode 100644 index 0000000000..b8972274d6 --- /dev/null +++ b/integration/xsd/common.ent @@ -0,0 +1,72 @@ + + + + +GET'> +PUT'> +POST'> +DELETE'> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '> + + + + + + '> + + + + + + '> diff --git a/integration/xsd/dbaas.wadl b/integration/xsd/dbaas.wadl new file mode 100644 index 0000000000..91953198a9 --- /dev/null +++ b/integration/xsd/dbaas.wadl @@ -0,0 +1,1177 @@ + + + %common; + + +]> + + + + + + + + + + + + + + + The account ID of the owner of the specified instance. + + + + + + + The instance ID for the specified database instance. + + + + + + + + + + + + + The name for the specified database. + + + + + + + + + The name for the specified user. + + + + + + + + + + + + + + + The flavor ID for the specified flavor. + + + + + + + + + + + + + Returns detailed information about the specified version of the API. + + Reviewer: in the DNS project, we have been requested by the customer to provide a table of parameters (should be pulled automatically if parms defined in wadl) and a table of attributes (for calls that allow detailed info about the object created to be specified. No doubt our DB customers will want this too. + Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description. + This operation returns detailed information about the specified version of the API. + + + + + The following examples show the List Version Details requests: + + + + + + + + + + + + + The following examples show the List Version Details responses: + + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists information about all versions of the API. + + This operation lists information about all versions of the API. + + + + + The following examples show the List Versions requests: + + + + + + + + + + + + + The following examples show the List Versions responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Creates a new database instance. + This operation asynchronously provisions a new database instance. This call requires the user to specify a flavor and a volume size. The service then provisions the instance with the requested flavor and sets up a volume of the specified size, which is the storage for the database instance. + Notes + + You can create only one database instance per POST request. + + + You can create a database instance with one or more databases, and users associated to those databases. + + + The default binding for the MySQL instance is port 3306. + + + Database instances are directly accessible only on the internal ServiceNet network and using a Cloud resource within the same regional datacenter. For example, a database instance in DFW can only be accessed by a Cloud Server in DFW. For details and information about using a public Cloud Load Balancer to allow access to your database instance, refer to + for details. + + + The following table lists the required and optional attributes for Create Instance: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Required and Optional Attributes for Create Instance
Applies To Name DescriptionRequired
InstanceflavorRefReference (href) to a flavor as specified in the response from the List Flavors API call. + This is the actual URI as specified by the href field in the link. For example, in the following List Flavors response, the link to flavor id 1 is specified as + "https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1". + Rather than the flavor URI, you can also pass the flavor id (integer) as the value for flavorRef. For example, the flavor id for the flavor URI shown above is "1". + Refer to for details.Yes
(volume) sizeSpecifies the volume size in gigabytes (GB). The value specified must be between 1 and 50.Yes
nameName of the instance to create. The length of the name is limited to 255 characters and any characters are permitted.No
DatabasenameSpecifies database names for creating databases on instance creation. Refer to for the required xml/json format.No
character_setSet of symbols and encodings. The default character set is utf8.No
collateSet of rules for comparing characters in a character set. The default value for collate is utf8_general_ci.No
UsernameSpecifies user name for the database on instance creation. Refer to for the required xml/json format.No
passwordSpecifies password for those users on instance creation. Refer to for the required xml/json format.No
(database) nameSpecifies names of databases that those users can access on instance creation. Refer to for the required xml/json format.No
+ + Refer to for a list of possible database instance statuses that may be returned. +
+ + + + The following examples show the Create Database Instance requests and responses: + + + + + + + + + + + + + + + + + + + For convenience, notice in the response examples above that resources contain links to themselves. This allows a client to easily obtain + resource URIs rather than to construct them. There are two kinds of link relations + associated with resources. A self link contains a versioned link to the resource. These + links should be used in cases where the link will be followed immediately. A bookmark + link provides a permanent link to a resource that is appropriate for long term storage. + + + + &commonFaults; + &getFaults; +
+ + + + Deletes the specified database instance. + This operation deletes the specified database instance, including any associated data. + Refer to for a list of possible database instance statuses that may be returned. + This operation does not delete any read slaves. + Reviewer: please provide a description of read slaves that I can add to the previous note. + This operation is not allowed when the instance state is either REBUILDING or BUILDING. + + + + + The following examples show the Delete Database Instance requests: + + + + + + + + + + + &commonFaults; + + + + The following examples show the Delete Database Instance responses: + + + + + + + + + + &getFaults; + + + + + Lists the status and information for all database instances. + This operation lists the status and information for all database instances. + Refer to for a list of possible database instance statuses that may be returned. + + + + + The following examples show the List All Database Instances Detail requests: + + + + + + + + + + + + + + The following examples show the List All Database Instances responses: + + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists status and details for a specified database instance. + This operation lists the status and details of the specified database instance. + This operation lists the volume size in gigabytes (GB) and the approximate GB used. + After instance creation, the used size of your volume will be + greater than 0. This is expected and due to the automatic creation of + non-empty transaction logs for mysql optimization. The used attribute is not returned + in the response when the status for the instance is BUILD, REBOOT, or RESIZE. + Refer to for a list of possible database instance statuses that may be returned. + The list operations return a DNS-resolvable hostname associated with the database instance instead of an IP address. Since the hostname always resolves to the correct IP address of the database instance, this relieves the user from the task of maintaining the mapping. Note that although the IP address may likely change on resizing, migrating, and so forth, the hostname always resolves to the correct database instance. + + + + + The following examples show the List Database Instance Status and Details requests: + + + + + + + + + + + + + The following examples show the List Database Instance Status and Details responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Restart the database service on the instance. + The restart operation will restart only the MySQL Instance. Restarting MySQL will erase any dynamic configuration settings that you have made within MySQL. + The MySQL service will be unavailable until the instance restarts. + This operation returns a 202 Accepted response. + + + + + The following examples show the Restart Instance requests: + + + + + + + + + + + + + The following examples show the Restart Instance responses: + + + + + + + + + + &commonFaults; + &getFaults; + &postPutFaults; + + + + + Resize the memory of the instance. + This operation changes the memory size of the instance, assuming a valid flavorRef is provided. Restarts MySQL in the process. + + + + + The following examples show the Resize Instance requests: + + + + + + + + + + + + + The following examples show the Resize Instance responses: + + + + + + + + + + &commonFaults; + &getFaults; + &postPutFaults; + + + + + Resize the volume attached to the Instance. + This operation supports resizing the attached volume for an instance. It supports only increasing the volume size and does not support decreasing the size. + The volume size is in gigabytes (GB) and must be an integer. + You cannot increase the volume to a size larger than the API volume size limit specifies. + This operation returns a 202 Accepted response. + + + + + The following examples show the Resize Instance Volume requests: + + + + + + + + + + + + + The following examples show the Resize Instance Volume responses: + + + + + + + + + + &commonFaults; + &getFaults; + &postPutFaults; + + + + + + Creates a new database within the specified instance. + This operation creates a new database within the specified instance. + The name of the database is a required attribute. + The following additional attributes can be specified for each database: collate and character_set. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Required and Optional Attributes for Create Database
Name DescriptionRequired
nameSpecifies the database name for creating the database. Refer to the request examples for the required xml/json format.Yes
character_setSet of symbols and encodings. The default character set is utf8.No
collateSet of rules for comparing characters in a character set. The default value for collate is utf8_general_ci.No
+ + See the MySQL documentation for information about supported character sets and collations at http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html. + + + The following database names are reserved and cannot be used for creating databases: lost+found, information_schema, and mysql. + Refer to the following tables for information about characters that are valid/invalid for creating database names. + + + + + + + + + + + + + + + + + + + + + + + +
Valid Characters That Can Be Used in a Database Name
Character
Letters (upper and lower cases allowed)
Numbers
'@', '?', '#', and spaces are allowed, but not at the beginning and end of the database name
'_' is allowed anywhere in the database name
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Characters That Cannot Be Used in a Database Name
Character
Single quotes
Double quotes
Back quotes
Semicolons
Commas
Backslashes
Forwardslashes
+ + + + + + + + + + + + + + + +
Length Restrictions for Database Name
RestrictionValue
Database-name maximum length64
+
+ + + + The following examples show the Create Database requests: + + + + + + + + + + + + + The following examples show the Create Database responses: + + + + + + + + + + &commonFaults; + &getFaults; +
+ + + + + Lists databases for the specified instance. + This operation lists the databases for the specified instance. + This operation returns only the user-defined databases, not the system databases. The system databases (mysql, information_schema, lost+found) can only be viewed by a database administrator. + + + + + The following examples show the List Databases for Instance requests: + + + + + + + + + + + + + The following examples show the List Databases for Instance responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + Deletes the specified database. + This operation deletes the requested database within the specified database instance. Note that all data associated with the database is also deleted. + + + + + The following examples show the Delete Database requests: + + + + + + + + + + + + + The following examples show the Delete Database responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Creates a user for the specified database instance. + This operation asynchronously provisions a new user for the specified database instance based on the configuration defined in the request object. Once the request is validated and progress has started on the provisioning process, a 202 Accepted response object is returned. + Writer: please add the following note back into the doc once the List User Details call is added back into the API: Using the identifier, the caller can check on the progress of the operation by performing a GET on users/name (for more details on this operation see the "List User Details" section of this document). + If the corresponding request cannot be fulfilled due to insufficient or invalid data, an HTTP 400 "Bad Request" error response is returned with information regarding the nature of the failure. Failures in the validation process are non-recoverable and require the caller to correct the cause of the failure and POST the request again. + + The following table lists the required attributes for Create User. Refer to the request examples for the required xml/json format: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Required Attributes for Create User
Applies ToName DescriptionRequired
UsernameName of the user for the + database.Yes
passwordUser password for database + access.Yes
(database) nameName of the database that the user can access. One or more database names must be specified.No
+ + Notes + + A user is granted all privileges on the specified databases. + + + The following user name is reserved and cannot be used for creating users: root. + + + + Refer to the following tables for information about characters that are valid/invalid for creating database names, user names, and passwords. + + + + + + + + + + + + + + + + + + + + + + + +
Valid Characters That Can Be Used in a Database Name, User Name, and Password
Character
Letters (upper and lower cases allowed)
Numbers
'@', '?', '#', and spaces are allowed, but not at the beginning and end of the database name, user name, and password
"_" is allowed anywhere in the database name, user name, and password
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Characters That Cannot Be Used in a Database Name, User Name, and Password
Character
Single quotes
Double quotes
Back quotes
Semicolons
Commas
Backslashes
Forwardslashes
Spaces at the front or end of the user name or password
+ + + + + + + + + + + + + + + + + + + + + + + +
Length Restrictions for Database Name, User Name, and Password
RestrictionValue
Database name maximum length64
User name maximum length16
Password maximum lengthunlimited (no restrictions)
+
+ + + + The following examples show the Create User requests: + + + + + + + + + + + + + The following examples show the Create User responses: + + + + + + + + + + &commonFaults; + &getFaults; +
+ + + + + Lists the users in the specified database instance. + This operation lists the users in the specified database instance, along with the associated databases for that user. + This operation does not return the system users (database administrators that administer the health of the database). Also, this operation returns the "root" user only if "root" user has been enabled. + + The following notes apply to MySQL users: + + + User names can be up to 16 characters long. + + + When you create accounts with INSERT, you must use FLUSH PRIVILEGES to tell the server to reload the grant tables. + + + For additional information, refer to: http://dev.mysql.com/doc/refman/5.1/en/user-account-management.html + + + + + + + The following examples show the List Users in Database Instance requests: + + + + + + + + + + + + + + The following examples show the List Users in Database Instance responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + Deletes the user identified by {name} for the specified database instance. + This operation deletes the specified user for the specified database instance. + There is a bug in a python library that Rackspace is using that may cause incorrect user deletions to occur + if a period (.) is used in the user name. In this case, the user name is truncated to remove the portion of the + name from the period to the end, leaving only the portion from the beginning up to the period. For example, for a + user named "my.userA", the bug would truncate the user name to "my", and if the user "my" exists, that user will + be incorrectly deleted. To avoid the problem, do not use periods in user names. + + + + + The following examples show the Delete User requests: + + + + + + + + + + + + + The following examples show the Delete User responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Enables the root user for the specified database instance and returns the root password. + This operation enables login from any host for the root user and provides the user with a generated root password. + Changes you make as a root user may cause detrimental effects to the database instance and unpredictable behavior for API operations. When you enable the root user, you accept the possibility that we will not be able to support your database instance. While enabling root does not prevent us from a “best effort” approach to helping you if something goes wrong with your instance, we cannot ensure that we will be able to assist you if you change core MySQL settings. These changes can be (but are not limited to) turning off binlogs, removing users that we use to access your instance, and so forth. + + + + + The following examples show the Enable Root User requests: + + + + + + + + + + + + + The following examples show the Enable Root User responses: + + + + + + + + + + + &commonFaults; + &getFaults; + + + + + Returns true if root user is enabled for the specified database instance or false otherwise. + This operation checks an active specified database instance to see if root access is enabled. It returns True if root user is enabled for the specified database instance or False otherwise. + + + + + The following examples show the Check Root User Access requests: + + + + + + + + + + + + + The following examples show the Check Root User Access responses: + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + Lists information for all available flavors. + This operation lists information for all available flavors. + This resource is identical to the flavors found in the OpenStack Nova API, but without the disk property. + Reviewer: please check that the xml example below is now correct. Previously it was reported to be incorrect. + + + + + The following examples show the List Flavors requests: + + + + + + + + + + + + + The following examples show the List Flavors responses: + + + + + + + + + + + &commonFaults; + &getFaults; + + + + + + + Lists all flavor information about the specified flavor ID. + This operation lists all information for the specified flavor ID with details of the RAM. + This resource is identical to the flavors found in the OpenStack Nova API, but without the disk property. + The flavorId parameter should be an integer. If a floating point value is used for the flavorId parameter, the decimal portion is truncated and the integer portion is used as the value of the flavorId. + Writer: need to confirm that this behavior is not changed in subsequent releases, and if it is prevented, remove the Note above. + + + + + The following examples show the List Flavor By ID requests: + + + + + + + + + + + + + The following examples show the List Flavor By ID responses: + + + + + + + + + + &commonFaults; + &getFaults; + + +
diff --git a/integration/xsd/dbaas.xsd b/integration/xsd/dbaas.xsd new file mode 100644 index 0000000000..3e1876a538 --- /dev/null +++ b/integration/xsd/dbaas.xsd @@ -0,0 +1,613 @@ + + + + + + DBaaS + + +