diff options
Diffstat (limited to 'integration')
350 files changed, 24503 insertions, 0 deletions
diff --git a/integration/.gitignore b/integration/.gitignore new file mode 100644 index 00000000..75bff192 --- /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 00000000..725c7456 --- /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 00000000..6b1fe765 --- /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 00000000..93516194 --- /dev/null +++ b/integration/apidocs/pom.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.rackspace.cloud.dbaas</groupId>
+ <artifactId>dbaas-docs</artifactId>
+ <version>1.0.0</version>
+
+ <name>Database Public API Spec</name>
+ <packaging>jar</packaging>
+
+ <profiles>
+ <profile>
+ <id>Rackspace Research Repositories</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <repositories>
+ <repository>
+ <id>rackspace-research</id>
+ <name>Rackspace Research Repository</name>
+ <url>http://maven.research.rackspacecloud.com/content/groups/public/</url>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>rackspace-research</id>
+ <name>Rackspace Research Repository</name>
+ <url>http://maven.research.rackspacecloud.com/content/groups/public/</url>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+ </profiles>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <doctools.version>1.5.0</doctools.version>
+ </properties>
+
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <resources>
+ <resource>
+ <directory>../xsd</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>com.rackspace.cloud.api</groupId>
+ <artifactId>clouddocs-maven-plugin</artifactId>
+ <!-- this <version> relates only to the *plugin* used for document formatting -->
+ <!-- the version of the *API* that is the subject of the document is indicated within each document -->
+ <version>${doctools.version}</version>
+ <executions>
+ <execution>
+ <id>g1</id>
+ <goals>
+ <goal>generate-pdf</goal>
+ <goal>generate-webhelp</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ <sourceDirectory>src/resources</sourceDirectory>
+ <highlightSource>false</highlightSource>
+ <!-- this is the Rackspace-internal service developer guide formerly known as ManagementAPISpec.xml -->
+ <includes>cdb-mgmt-devguide.xml</includes>
+ <pdfUrl>../../cdb-mgmt-devguide-internal.pdf</pdfUrl>
+ <enableDisqus>intranet</enableDisqus>
+ <feedbackEmail>mike.asthalter@rackspace.com</feedbackEmail><postProcess>
+ <copy todir="target/docbkx/webhelp/cdb-mgmt-devguide/content/images">
+ <fileset dir="src/resources/images" includes="**/*.png"/>
+ </copy>
+ </postProcess>
+ </configuration>
+ </execution>
+ <execution>
+ <id>g2</id>
+ <goals>
+ <goal>generate-pdf</goal>
+ <goal>generate-webhelp</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ <sourceDirectory>src/resources</sourceDirectory>
+ <highlightSource>false</highlightSource>
+ <trimWadlUriCount>2</trimWadlUriCount>
+ <!-- this is the public-facing client developer guide formerly known as PublicAPISpec.xml -->
+ <!-- this document that will be published as cdb-devguide-latest.pdf at http://docs.rackspacecloud.com/api/ -->
+ <pdfUrl>../../../cdb-devguide-latest.pdf</pdfUrl>
+ <enableDisqus>1</enableDisqus>
+ <googleAnalyticsId>UA-23102455-4</googleAnalyticsId>
+ <includes>cdb-devguide.xml</includes>
+ <canonicalUrlBase>http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content</canonicalUrlBase>
+ <postProcess>
+ <copy todir="target/docbkx/webhelp/cdb-devguide/content/images">
+ <fileset dir="src/resources/images" includes="**/*.png"/>
+ </copy>
+ </postProcess>
+ </configuration>
+ </execution>
+ <execution>
+ <id>g3</id>
+ <goals>
+ <goal>generate-pdf</goal>
+ <goal>generate-webhelp</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ <sourceDirectory>src/resources</sourceDirectory>
+ <highlightSource>false</highlightSource>
+ <!-- this is the Rackspace Getting Started guide -->
+ <includes>cdb-getting-started.xml</includes>
+ <pdfUrl>../../../cdb-getting-started.pdf</pdfUrl>
+ <enableDisqus>1</enableDisqus>
+ <googleAnalyticsId>UA-23102455-4</googleAnalyticsId>
+ <canonicalUrlBase>http://docs.rackspace.com/cdb/api/v1.0/cdb-getting-started/content</canonicalUrlBase>
+ <postProcess>
+ <copy todir="target/docbkx/webhelp/cdb-getting-started/content/images">
+ <fileset dir="src/resources/images" includes="**/*.png"/>
+ </copy>
+ </postProcess>
+ </configuration>
+ </execution>
+ <!-- MA: added this to generate public release notes -->
+ <execution>
+ <id>g4</id>
+ <goals>
+ <goal>generate-webhelp</goal>
+ <goal>generate-pdf</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ <sourceDirectory>src/resources</sourceDirectory>
+ <highlightSource>false</highlightSource>
+ <pdfUrl>../../../cdb-releasenotes-latest.pdf</pdfUrl>
+ <includes>cdb-releasenotes.xml</includes>
+ <enableDisqus>1</enableDisqus>
+ <feedbackEmail>mike.asthalter@rackspace.com</feedbackEmail>
+ <enableGoogleAnalytics>1</enableGoogleAnalytics>
+ <chapterAutolabel>0</chapterAutolabel>
+ <security>external</security>
+ <canonicalUrlBase>http://docs.rackspace.com/cdb/api/v1.0/cdb-releasenotes/content</canonicalUrlBase>
+ <postProcess>
+ <move todir="target/docbkx/webhelp/cdb-releasenotes-external" failonerror="false">
+ <fileset dir="target/docbkx/webhelp/cdb-releasenotes"/>
+ </move>
+ <move file="target/docbkx/pdf/cdb-releasenotes.pdf" tofile="target/docbkx/pdf/cdb-releasenotes-YYYYMMDD.pdf" failonerror="false"/>
+ <delete file="target/docbkx/pdf/cdb-releasenotes.fo" failonerror="false"/>
+ </postProcess>
+ </configuration>
+ </execution>
+ <execution>
+ <id>g5</id>
+ <goals>
+ <goal>generate-pdf</goal>
+ <goal>generate-webhelp</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <xincludeSupported>true</xincludeSupported>
+ <sourceDirectory>src/resources</sourceDirectory>
+ <highlightSource>false</highlightSource>
+ <pdfUrl>../../../cdb-releasenotes-latest.pdf</pdfUrl>
+ <includes>cdb-releasenotes.xml</includes>
+ <enableDisqus>intranet</enableDisqus>
+ <feedbackEmail>mike.asthalter@rackspace.com</feedbackEmail>
+ <enableGoogleAnalytics>1</enableGoogleAnalytics>
+ <chapterAutolabel>0</chapterAutolabel>
+ <security>internal</security>
+ <canonicalUrlBase>http://docs-internal.rackspace.com/cdb/api/v1.0/cdb-releasenotes/content</canonicalUrlBase>
+ <postProcess>
+ <move todir="target/docbkx/webhelp/cdb-releasenotes-internal" failonerror="false">
+ <fileset dir="target/docbkx/webhelp/cdb-releasenotes"/>
+ </move>
+ <move file="target/docbkx/pdf/cdb-releasenotes.pdf" tofile="target/docbkx/pdf/cdb-releasenotes-internal.pdf" failonerror="false"/>
+ <delete file="target/docbkx/pdf/cdb-releasenotes.fo" failonerror="false"/>
+ </postProcess>
+ </configuration>
+ </execution>
+ </executions>
+ <configuration>
+ <socialIcons>1</socialIcons>
+ <feedbackEmail>mike.asthalter@rackspace.com</feedbackEmail>
+ <branding>rackspace</branding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/integration/apidocs/src/resources/cdb-devguide.xml b/integration/apidocs/src/resources/cdb-devguide.xml new file mode 100644 index 00000000..5c2ae69b --- /dev/null +++ b/integration/apidocs/src/resources/cdb-devguide.xml @@ -0,0 +1,1675 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book [ + <!-- Some useful entities borrowed from HTML --> + <!ENTITY ndash "–"> + <!ENTITY mdash "—"> + <!ENTITY hellip "…"> + + <!-- Useful for describing APIs --> + <!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'> + <!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'> + <!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'> + <!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'> + + <!-- changing authentication endpoints; define entities for US & UK rather than maintaining in text --> + <!ENTITY ENDPOINT-US "https://identity.api.rackspacecloud.com/v1.1/"> + <!ENTITY ENDPOINT-UK "https://lon.identity.api.rackspacecloud.com/v1.1/"> + <!ENTITY ENDPOINT-US-20 "https://identity.api.rackspacecloud.com/v2.0/"> + <!ENTITY ENDPOINT-UK-20 "https://lon.identity.api.rackspacecloud.com/v2.0/"> + + <!ENTITY CHECK '<inlinemediaobject xmlns="http://docbook.org/ns/docbook"> + <imageobject> + <imagedata fileref="img/Check_mark_23x20_02.svg" + format="SVG" scale="60"/> + </imageobject> + </inlinemediaobject>'> + + <!ENTITY ARROW '<inlinemediaobject xmlns="http://docbook.org/ns/docbook"> + <imageobject> + <imagedata fileref="img/Arrow_east.svg" + format="SVG" scale="60"/> + </imageobject> + </inlinemediaobject>'> +]> +<book xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + xml:id="cdb-devguide" + version="5.0"> + + <?rax title.font.size="35px" subtitle.font.size="20px"?> + <title>Rackspace Cloud Databases Developer Guide</title> + <titleabbrev>Rackspace Cloud Databases Developer + Guide</titleabbrev> + <info> + <author> + <personname> + <firstname/> + <surname/> + </personname> + <affiliation> + <orgname>Rackspace Cloud</orgname> + </affiliation> + </author> + <copyright> + <year>2010</year> + <year>2011</year> + <year>2012</year> + <holder>Rackspace US, Inc.</holder> + </copyright> + <releaseinfo>API v1.0</releaseinfo> + <productname>Rackspace Cloud Databases</productname> + <pubdate>2012-09-04</pubdate> + <legalnotice role="rs-api"> + <annotation> + <remark>Copyright details are filled in by the template.</remark> + </annotation> + </legalnotice> + <abstract> + <para>This document is intended for software developers + interested in developing applications using the + Rackspace Cloud Databases Application Programming + Interface (<abbrev>API</abbrev>). </para> + </abstract> + + <revhistory> + <revision> + <date>2012-09-04</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Added information for pricing and + service level (refer to <xref + linkend="Pricing_SLA-d1e1362" + />).</para> + </listitem> + <listitem> + <para>Updated maximum volume size for a + database instance (refer to <xref + linkend="Absolute_Limits-d1e1397" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-21</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Changed FAILED status for database + instance to ERROR instead (see <xref + linkend="database_instance_status" + />).</para> + </listitem> + <listitem> + <para>List reserved names that cannot be + used for creating databases (see <xref + linkend="POST_createDatabase__version___accountId__instances__instanceId__databases_" + />) and users (see <xref + linkend="POST_createUser__version___accountId__instances__instanceId__users_" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-01</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases.</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + </revhistory> + + <cover> + <para>this is a placeholder for the front cover</para> + </cover> + <cover> + <para>this is a placeholder for the back cover</para> + </cover> + <raxm:metadata xmlns:raxm="http://docs.rackspace.com/api/metadata"> + <raxm:product version="v1.0">cdb</raxm:product> + <raxm:priority>2</raxm:priority> + </raxm:metadata> + </info> + <chapter xml:id="overview"> + <title>Overview</title> + <para>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.</para> + <note> + <para>Rackspace recommends that Cloud Databases users back + up their data using <emphasis role="bold" + >mysqldump</emphasis> until backups are supported + in Cloud Databases.</para> + </note> + <para>The following figure shows an + overview of Cloud Databases Infrastructure: <informalfigure> + <mediaobject> + <imageobject role="fo"> + <imagedata + fileref="images/Cloud_DB_Infographic-1.svg" + contentwidth="6in"/> + </imageobject> + <imageobject role="html"> + <imagedata + fileref="images/Cloud_DB_Infographic-1.png"/> + </imageobject> + </mediaobject> + </informalfigure> + </para> + <remark>Writer: need to get architecture diagram for DBaaS. + Emailed Daniel 5/1/12.</remark> + <para security="writeronly">We welcome feedback, comments, and bug reports at <link + xlink:href="http://feedback.rackspacecloud.com" + >http://feedback.rackspacecloud.com</link>.</para> + <remark>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.</remark> + <?hard-pagebreak?> + <section xml:id="Intended_Audience-d1e122"> + <title>Intended Audience</title> + <para> 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: </para> + <itemizedlist spacing="compact"> + <listitem> + <para>ReSTful web services</para> + </listitem> + <listitem> + <para>HTTP/1.1 conventions</para> + </listitem> + <listitem> + <para>JSON and/or XML data serialization + formats</para> + </listitem> + <listitem> + <para>ATOM Syndication Format</para> + </listitem> + </itemizedlist> + </section> + <?hard-pagebreak?> + <section xml:id="Document_Change_History-d1e166"> + <title>Document Change History</title> + <para>This version of the Developer Guide replaces and + obsoletes all previous versions. The most recent + changes are described in the table below:</para> + <?rax revhistory?> + </section> + <section xml:id="Additional_Resources-d1e532"> + <title>Additional Resources</title> + <para>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:</para> + <itemizedlist> + <listitem> + <para>The WADL is <link + xlink:href="http://docs.rackspace.com/cdb/api/v1.0/cdb.wadl" + />.</para> + </listitem> + <listitem> + <para>The XSD is <link + xlink:href="http://docs.rackspace.com/cdb/api/v1.0/xsd/cdb.xsd"/>. </para> + </listitem> + </itemizedlist> + <para>You can download the most current versions of other + API-related documents from <link + xlink:href="http://docs.rackspace.com/" + >http://docs.rackspace.com/</link>. </para> + <para>For more details about Rackspace Cloud Databases, + refer to <link + xlink:href="http://www.rackspace.com/cloud/cloud_hosting_products/databases/" + >http://www.rackspace.com/cloud/cloud_hosting_products/databases/</link>. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. </para> + <para>Using this API document, your Rackspace Cloud + account, and at least one Cloud Server, you can get + started whenever you'd like. See the + <citetitle>Getting Started with Rackspace Cloud + Databases and Servers</citetitle> at <link + xlink:href="http://docs.rackspace.com/" + >http://docs.rackspace.com/</link> for information + about getting started using the API.</para> + <para>Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases!</para> + <para>You can also follow Rackspace updates and + announcements via twitter at: <link + xlink:href="http://www.twitter.com/rackspace" + >http://www.twitter.com/rackspace</link>. </para> + <para>This API uses standard HTTP 1.1 response codes as + documented at: <link + xlink:href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" + >http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</link>. </para> + </section> + <?hard-pagebreak?> + <section xml:id="Pricing_SLA-d1e1362"> + <title>Pricing and Service Level</title> + <para>Cloud Databases is part of the Rackspace Cloud and + your use through the API will be billed as per the + pricing schedule at <link + xlink:href="http://www.rackspace.com/cloud/public/databases/pricing" + >http://www.rackspace.com/cloud/public/databases/pricing</link>.</para> + <para>The Service Level Agreement (SLA) for Cloud + Databases is available at <link + xlink:href="http://www.rackspace.com/cloud/legal/sla/#cloud_databases" + >http://www.rackspace.com/cloud/legal/sla/#cloud_databases</link>.</para> + </section> + </chapter> + + <chapter xml:id="Concepts-d1e563"> + <title>Concepts</title> + <?dbhtml stop-chunking?> + <para> To use the Cloud Databases API effectively, you should + understand several key concepts: </para> + <remark>Reviewer: Daniel Morris is asking Daniel Salinas to + do an initial write-up of this chapter.</remark> + <section xml:id="DatabaseInstance-d1e588"> + <title>Database Instance</title> + <para>A database instance is an isolated MySQL instance in + a single tenant environment on a shared physical host + machine.</para> + <remark security="writeronly">Writer: once we support + MSSQL, we need to describe here what is used for MSSQL + in place of database instance.</remark> + </section> + <section xml:id="Database"> + <title>Database</title> + <para>A MySQL database within a database instance.</para> + <remark security="writeronly">Writer: once we support + MSSQL, we need to modify the wording here, such as: + The actual database, whether it is in MySQL or + MSSQL.</remark> + </section> + <section xml:id="Flavor"> + <title>Flavor</title> + <para>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.</para> + </section> + <section xml:id="Volume"> + <title>Volume</title> + <para>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.</para> + </section> + </chapter> + <chapter xml:id="General_API_Information-d1e633"> + <title>General API Information</title> + <para> 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. </para> + <note> + <para> All requests to authenticate against and operate the service are performed using + SSL over HTTP (HTTPS) on TCP port 443. </para> + </note> + <section xml:id="Authentication-d1e647"> + <title>Authentication</title> + <para> Every ReST request against the Database Service + requires the inclusion of a specific authorization + token, supplied by the <code>X-Auth-Token</code> HTTP + header. Customers obtain this token by first using the + Rackspace Cloud Authentication Service and supplying a + valid username and API access key. </para> + <section xml:id="Geographic_Endpoints"> + <title>Geographic Endpoints</title> + <para> The Rackspace Cloud Authentication Service + serves as the entry point to all Rackspace Cloud + APIs and is itself a ReSTful web service. </para> + <para> To access the Authentication Service, you must + know whether your account is US-based or UK-based: </para> + <itemizedlist spacing="compact"> + <listitem> + <para> US-based accounts authenticate through + <link xlink:href="&ENDPOINT-US-20;" + >&ENDPOINT-US-20;</link>. </para> + </listitem> + <listitem> + <para> UK-based accounts authenticate through + <link xlink:href="&ENDPOINT-UK-20;" + >&ENDPOINT-UK-20;</link>. </para> + </listitem> + </itemizedlist> + <para> 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: </para> + <itemizedlist spacing="compact"> + <listitem> + <para> If your account was created via <link + xlink:href="http://www.rackspacecloud.com" + >http://www.rackspacecloud.com</link>, + it is a US-based account. </para> + </listitem> + <listitem> + <para> If your account was created via <link + xlink:href="http://www.rackspace.co.uk" + >http://www.rackspace.co.uk</link>, it + is a UK-based account. </para> + </listitem> + </itemizedlist> + <para> If you are unsure how your account was created, + use the Rackspace contact information at either + site to ask for help. </para> + </section> + <section xml:id="Retrieving_Auth_Token"> + <title>Retrieving the Authentication Token</title> + <informaltable rules="all"> + <tbody> + <tr> + <td colspan="1">&POST; </td> + <td colspan="1"> v2.0/tokens </td> + <td colspan="4"> Authenticate to receive a + token and a service catalog. </td> + </tr> + </tbody> + </informaltable> + <simpara> Normal Response Code(s): + <returnvalue>200</returnvalue>, + <returnvalue>203</returnvalue> + </simpara> + <simpara> Error Response Code(s): unauthorized + (<errorcode>401</errorcode>), userDisabled + (<errorcode>403</errorcode>), badRequest + (<errorcode>400</errorcode>), authFault + (<errorcode>500</errorcode>), + serviceUnavailable (<errorcode>503</errorcode>) </simpara> + <para> 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 <code>username</code> and + <code>apiKey</code>. 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.</para> + <example> + <title>Auth Request: XML</title> + <programlistingco> + <areaspec> + <area xml:id="credentials.xml.user" + units="linecolumn" coords="13 17"/> + <area xml:id="credentials.xml.key" + units="linecolumn" coords="14 15"/> + </areaspec> + <programlisting language="xml"> +<xi:include href="samples/db-credentials-20.xml" parse="text"/> + </programlisting> + </programlistingco> + </example> + <example> + <title>Auth Request: JSON</title> + <programlistingco> + <areaspec> + <area xml:id="credentials.json.user" + units="linecolumn" coords="14 22"/> + <area xml:id="credentials.json.key" + units="linecolumn" coords="15 20"/> + </areaspec> + <programlisting language="json"> +<xi:include href="samples/db-credentials-20.json" parse="text"/> + </programlisting> + </programlistingco> + </example> + <calloutlist> + <callout arearefs="credentials.xml.user"> + <para> The username supplied here is your + common Rackspace Cloud username. </para> + </callout> + <callout arearefs="credentials.xml.key"> + <para> The key is your API access key. The key + can be obtained from the Rackspace Cloud + Control Panel in the <guimenu><Your + Account></guimenu>/<guimenuitem>API + Access</guimenuitem> section (login + here: <link + xlink:href="http://mycloud.rackspace.com/" + >Control Panel Login</link>). </para> + </callout> + </calloutlist> + <example> + <title>Auth Response: XML</title> + + <programlistingco> + <areaspec> + <area xml:id="response.xml.namespaces" units="linecolumn" coords="7 9"/> + <area xml:id="response.xml.token" units="linecolumn" coords="12 10"/> + <area xml:id="response.xml.role" units="linecolumn" coords="14 12"/> + <area xml:id="response.xml.catalog" units="linecolumn" coords="19 21"/> + <area xml:id="response.xml.servicetype" units="linecolumn" coords="43 34"/> + <area xml:id="response.xml.servicename" units="linecolumn" coords="43 54"/> + <area xml:id="response.xml.region" units="linecolumn" coords="44 30"/> + <area xml:id="response.xml.tenant" units="linecolumn" coords="45 66"/> + <area xml:id="response.xml.url" units="linecolumn" coords="46 9"/> + </areaspec> + <programlisting language="xml"><xi:include href="samples/db-auth-20.xml" parse="text"/></programlisting> + </programlistingco> + </example> + + + <example xml:id="auth-response-example-json"> + <title>Auth Response: JSON</title> + + <programlistingco> + <areaspec> + <area xml:id="response.json.namespaces" units="linecolumn" coords="200 1"/> + <area xml:id="response.json.token" units="linecolumn" coords="9 8"/> + <area xml:id="response.json.role" units="linecolumn" coords="16 12"/> + <area xml:id="response.json.catalog" units="linecolumn" coords="29 8"/> + <area xml:id="response.json.servicetype" units="linecolumn" coords="102 16"/> + <area xml:id="response.json.servicename" units="linecolumn" coords="101 16"/> + <area xml:id="response.json.region" units="linecolumn" coords="94 24"/> + <area xml:id="response.json.tenant" units="linecolumn" coords="91 24"/> + <area xml:id="response.json.url" units="linecolumn" coords="92 24"/> + </areaspec> + <programlisting language="json"><xi:include href="samples/db-auth-20.json" parse="text"/> + </programlisting> + </programlistingco> + </example> + <note> + <para>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.</para> + </note> + <calloutlist> + <callout arearefs="response.xml.namespaces"> + <para> + In XML responses only, + a list of namespaces identifies API extensions that add functionality to the core API. + </para> + <para> </para> + </callout> + <callout arearefs="response.xml.token"> + <para> 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. + </para> + <para> The token's <code>expires</code> attribute denotes the time + after which the token will automatically become + invalid. A token may be manually revoked before + the time identified by the <code>expires</code> + attribute; <code>expires</code> 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. </para> + <para> </para> + </callout> + <callout arearefs="response.xml.role"> + <para> Users can be assigned multiple roles, + with each role providing specific + privileges. In this example, + <code>jsmith</code> is the + administrative user for the account, + holding the fully-privileged + <code>identity:admin</code> 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. </para> + <para> </para> + </callout> + <callout arearefs="response.xml.catalog"> + <para> 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. </para> + <para> </para> + </callout> + <callout arearefs="response.xml.servicetype"> + <para> + 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 <code>type="compute"</code>, + identifying them as compute services even though the word "compute" does not appear in their names. + </para> + <important> + <para>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.</para> + </important> + <para> </para> + </callout> + <callout arearefs="response.xml.servicename"> + <para> + 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. + </para> + <important> + <para> 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, + <code>type="compute"</code> 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 + <code>type="compute"</code> in the authentication + response's service catalog. </para> + </important> + <para> </para> + </callout> + <callout arearefs="response.xml.region"> + <para> A service may expose endpoints in different regions. + Regional endpoints allow clients to provision + resources in a manner that provides high + availability. </para> + <para> Some services are not region-specific. These services supply a single + non-regional endpoint and do not provide access to internal URLs. </para> + <para> </para> + </callout> + <callout arearefs="response.xml.tenant"> + <para> 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.</para> + <para> </para> + </callout> + <callout arearefs="response.xml.url"> + <para> + 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. + </para> + <para> </para> + </callout> + </calloutlist> + <para>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. </para> + <important> + <para>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.</para> + </important> + <tip> + <para>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, + <code>type="compute"</code> 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 + <citetitle>Cloud Identity Client Developer + Guide</citetitle> at <link + xlink:href="http://docs.rackspace.com" + >http://docs.rackspace.com/</link>.</para> + </tip> + <para>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. </para> + </section> + </section> + <section xml:id="Service_Access_Endpoints-d1e753"> + <title>Service Access/Endpoints</title> + <para>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.</para> + <para>You can find the available service access/endpoints + for Cloud Databases summarized in the table + below.</para> + <?rax-fo keep-with-next?> + <para> + <table rules="all"> + <caption>Regionalized Service Endpoints</caption> + <thead> + <tr align="center"> + <td colspan="2">Region</td> + <td colspan="5">Endpoint</td> + </tr> + </thead> + <tbody> + <tr align="left"> + <td colspan="2">Chicago (ORD)</td> + <td colspan="5" + ><code>https://ord.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ </td> + </tr> + <tr align="left"> + <td colspan="2">Dallas/Ft. Worth (DFW)</td> + <td colspan="5" + ><code>https://dfw.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ </td> + </tr> + <tr align="left" > + <td colspan="2">London (LON)</td> + <td colspan="5"> + <code>https://lon.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ </td> + </tr> + </tbody> + </table> + </para> + + <para>Replace the sample account ID number, + <parameter>1234</parameter>, with your actual + account number returned as part of the authentication + service response.</para> + <para>You will find the actual account number after the + final '/' in the <code>publicURL</code> field returned + by the authentication response. For example, in <xref + linkend="auth-response-example-json"/> you can see + from the <code>publicURL</code> field for + <code>cloudServers</code> + ("https://servers.api.rackspacecloud.com/v1.0/<emphasis + role="bold">1100111</emphasis>") that the account + number is 1100111.</para> + </section> + <section xml:id="DB_service_versions"> + <title>Cloud Databases Service Versions</title> + <para> The Cloud Databases version defines the contract + and build information for the API. </para> + <section xml:id="Contract_Version-d1e825"> + <title>Contract Version</title> + <para> 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. + </para> + <example> + <title>Example Request URL (contract version + in <emphasis role="strong" + >bold</emphasis>)</title> + <programlisting>https://ord.databases.api.rackspacecloud.com/<emphasis role="strong">v1.0</emphasis>/1234</programlisting> + </example> + <note> + <para>This document pertains to contract + version 1.0.</para> + </note> + + </section> + <section xml:id="API_Version_Headers-d1e855"> + <title>API Version</title> + <para>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 <xref + linkend="API_Versions"/> for details.</para> + </section> + </section> + <section xml:id="Request_Response_Types-d1e903"> + <title>Request/Response Types</title> + <para> The Cloud Databases API supports both the JSON and + XML data serialization formats. The request format is + specified using the <code>Content-Type</code> header + and is <emphasis>required</emphasis> for calls that + have a request body. The response format can be + specified in requests either by using the + <code>Accept</code> header or by adding an + <code>.xml</code> or <code>.json</code> 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 <code>Accept</code> header and + a query extension, the query extension takes + precedence.</para> + <para security="writeronly">Some operations support an Atom representation that + can be used to efficiently determine when the state of + services has changed. </para> + <remark>Reviewer: the previous sentence will be hidden + for the Private Beta, since it does not appear that + Atom will be supported yet. Correct?</remark> + <table rules="all"> + <caption>Response Formats</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td>Format</td> + <td>Accept Header</td> + <td>Query Extension</td> + <td>Default</td> + </tr> + </thead> + <tbody> + <tr> + <td>JSON</td> + <td>application/json</td> + <td>.json</td> + <td>Yes</td> + </tr> + <tr> + <td>XML</td> + <td>application/xml</td> + <td>.xml</td> + <td>No</td> + </tr> + <tr security="writeronly"> + <td>ATOM</td> + <td>application/atom+xml</td> + <td>.atom</td> + <td>No</td> + </tr> + </tbody> + </table> + <remark>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?</remark> + <para>In the request example below, notice that + <parameter>Content-Type</parameter> is set to + <parameter>application/json</parameter>, but + <parameter>application/xml</parameter> is + requested via the <parameter>Accept</parameter> + header:</para> + <example xml:id="request_with_headers_json"> + <title>Request with Headers: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"><xi:include href="samples/db-request-types.json" parse="text"><xi:fallback>Missing code sample!<?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <para><?rax-fo keep-with-next?>Therefore an XML response format is returned:</para> + <example> + <title>Response with Headers: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"><xi:include href="samples/db-response-types.xml" parse="text"><xi:fallback>Missing code sample!<?rax fail?></xi:fallback></xi:include></programlisting> + </example> + </section> + <section xml:id="sync_asynch_responses" security="writeronly"> + <title>Synchronous and Asynchronous Responses</title> + <remark>Reviewer: please give me the updated info for this + section. Need to replace info about callback URL, + etc.</remark> + <para> All successful &GET; requests are + <emphasis>synchronous</emphasis> 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.</para> + <para>&PUT;, &POST;, and &DELETE; calls are <emphasis>asynchronous</emphasis>, 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:</para> + <example> + <title>202 ACCEPTED Response: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: need code example</programlisting> + </example> + <example> + <title>202 ACCEPTED Response: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json">Reviewer: need code example</programlisting> + </example> + <para>The following table shows the attributes for asynchronous responses:</para> + <table rules="all"> + <caption>Attributes for Asynchronous Responses</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td colspan="1">Attribute</td> + <td colspan="4">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">jobId</td> + <td colspan="4">An identifier for the specific request.</td> + </tr> + <tr> + <td colspan="1">callbackUrl</td> + <td colspan="4">Resource locator for querying the status of the request.</td> + </tr> + </tbody> + </table> + <note> + <para>The status for asynchronous calls is retained for up to 24 hours.</para> + </note> + <note> + <para>If a request body does not pass initial validation or an error condition + arises, you may receive an immediate error response from the request.</para> + </note> + <para>When a request is made to the callback URL provided + and the job is still running, another + <returnvalue>202</returnvalue> 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 + <returnvalue>200</returnvalue> OK and contain the + information for the created database. See XXXX for a + specific example.</para> + <para>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 <returnvalue>400</returnvalue> BAD + REQUEST and contain details regarding the specific + validation error.</para> + <note> + <para>If the response from querying a callback URL is a + <returnvalue>404</returnvalue> 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 <returnvalue>404</returnvalue> NOT FOUND. </para> + </note> + <para>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.</para> + </section> + <section xml:id="Content_Compression-d1e1120" security="writeronly"> + <title>Content Compression</title> + <remark>Reviewer: I am hiding this entire section for the + Private Beta, since I'm not sure that it applies. Is + that correct?</remark> + <para> 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 + <code>Accept-Encoding</code> header on the request from the client and indicated + by the <code>Content-Encoding</code> header in the server response. Unless the + header is explicitly set, encoding defaults to disabled. </para> + <table rules="all"> + <caption>Encoding Headers</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td>Header Type</td> + <td>Name</td> + <td>Value</td> + </tr> + </thead> + <tbody> + <tr> + <td>HTTP/1.1 Request</td> + <td><code>Accept-Encoding</code></td> + <td>gzip</td> + </tr> + <tr> + <td>HTTP/1.1 Response</td> + <td><code>Content-Encoding</code></td> + <td>gzip</td> + </tr> + </tbody> + </table> + </section> + <section xml:id="Persistent_Connections-d1e1187" security="writeronly"> + <title>Persistent Connections</title> + <remark>Reviewer: I am hiding this entire section for the + Private Beta, since I'm not sure that it applies. Is + that correct?</remark> + <para> + 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. + </para> + <para> + To prevent abuse, HTTP sessions have a timeout of 20 + seconds before being closed. + </para> + <note> + <para> + The server may close the connection at any time + and clients should not rely on this behavior. + </para> + </note> + </section> + <?hard-pagebreak?> + <section xml:id="Limits-d1e1208"> + <title>Limits</title> + <para> + 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: <firstterm>rate limits</firstterm> and + <firstterm>absolute limits</firstterm>. Rate limits + are thresholds that are reset after a certain amount + of time passes. Absolute limits are fixed. + </para> + <section xml:id="Rate_Limits-d1e1222"> + <title>Rate Limits</title> + <para> 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<emphasis + role="bold">/v1.0/instances</emphasis>. </para> + <para>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: </para> + <table rules="all"> + <caption>Default Rate Limits</caption> + <thead> + <tr align="center"> + <td colspan="1">Verb</td> + <td colspan="2">URI</td> + <td colspan="2">RegEx</td> + <td colspan="1">Default</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">&GET; changes-since</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">3/minute</td> + </tr> + <tr> + <td colspan="1">&POST;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">10/minute</td> + </tr> + <tr> + <td colspan="1">&POST; instances</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">50/day</td> + </tr> + <tr> + <td colspan="1">&PUT;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">10/minute</td> + </tr> + <tr> + <td colspan="1">&DELETE;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">100/minute</td> + </tr> + </tbody> + </table> + <para> 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. </para> + <para> If you exceed the thresholds established for + your account, a <errorcode>413 (Rate + Control)</errorcode> HTTP response will be + returned with a <code>Retry-After</code> header to + notify the client when it can attempt to try + again. </para> + </section> + <section xml:id="Absolute_Limits-d1e1397"> + <title>Absolute Limits</title> + <remark>Reviewer: Need to update this entire section. + Please give me your updates.</remark> + <para>Refer to the following table for the absolute + limits that are set.</para> + <table rules="all"> + <caption>Absolute Limits</caption> + <thead> + <tr> + <td colspan="1">Name</td> + <td colspan="3">Description</td> + <td colspan="1">Limit</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">Instances</td> + <td colspan="3">Maximum number of instances allowed + for your account</td> + <td colspan="1">5</td> + </tr> + <tr> + <td colspan="1">Volume Size</td> + <td colspan="3">Maximum volume size per + instance in gigabytes (GB) for your + account</td> + <td colspan="1">50</td> + </tr> + </tbody> + </table> + </section> + </section> + <section xml:id="datetimeformat"> + <title>Date/Time Format</title> + <para> The Database Service uses an ISO-8601 compliant + date format for the display and consumption of + date/time values. </para> + <para>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. + </para> + <example> + <title>DB Service Date/Time Format</title> + <programlisting>yyyy-MM-dd'T'HH:mm:ss.SSSZ</programlisting> + <para>See the table below for a description of the date/time format codes.</para> + <para>May 19th, 2011 at 8:07:08 AM, GMT-5 would have the following + format:</para> + <programlisting>2011-05-19T08:07:08-05:00</programlisting> + </example> + <table rules="all"> + <caption>Explanation of Date/Time Format Codes</caption> + <thead> + <tr> + <td>Code</td> + <td>Description</td> + </tr> + </thead> + <tbody> + <tr> + <td>yyyy</td> + <td>Four digit year</td> + </tr> + <tr> + <td>MM</td> + <td>Two digit month</td> + </tr> + <tr> + <td>dd</td> + <td>Two digit day of month</td> + </tr> + <tr> + <td>T</td> + <td>Separator for date/time</td> + </tr> + <tr> + <td>HH</td> + <td>Two digit hour of day (00-23)</td> + </tr> + <tr> + <td>mm</td> + <td>Two digit minutes of hour</td> + </tr> + <tr> + <td>ss</td> + <td>Two digit seconds of the minute</td> + </tr> + <tr> + <td>SSS</td> + <td>Three digit milliseconds of the second</td> + </tr> + <tr> + <td>Z</td> + <td>RFC-822 timezone</td> + </tr> + </tbody> + </table> + + </section> + <section xml:id="pagination"> + <title>Pagination</title> + <para>To reduce load on the service, list operations + return a maximum of 20 items at a time. This is + referred to as <emphasis>pagination</emphasis>. 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.</para> + <para> 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. <emphasis>Limit</emphasis> + is the restriction on the maximum number of items for + that type that can be returned. + <emphasis>Marker</emphasis> 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: + <code>?limit=10&marker=1234</code>. Items are + displayed sorted by ID. </para> + <para>Pagination applies only to the calls listed in the + following table: </para> + <informaltable rules="all"> + <thead> + <tr align="center"> + <td colspan="1">Verb</td> + <td colspan="2">URI</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">&GET;</td> + <td colspan="2">/instances/</td> + <td colspan="3">Lists the status and + information for all database + instances.</td> + </tr> + <tr> + <td colspan="1">&GET;</td> + <td colspan="2"> + /instances/{instanceId}/databases </td> + <td colspan="3">Lists databases for the + specified instance.</td> + </tr> + <tr> + <td colspan="1">&GET;</td> + <td colspan="2"> /instances/{instanceId}/users </td> + <td colspan="3">Lists the users in the + specified database instance.</td> + </tr> + </tbody> + </informaltable> + <para>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 + <code>{"href": "<url>", "rel": "next"}</code>. + Any response that is truncated by pagination will have + a <emphasis>next</emphasis> link, which points to the + next item in the collection. If there are no more + items, no <emphasis>next</emphasis> link is + returned.</para> + <para>See the examples of paged List Instances calls that + follow.</para> + <example> + <title>List Instances Paged Request: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-instances-index-pagination-request.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>List Instances Paged Request: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-instances-index-pagination-request.json" parse="text"/> + </programlisting> + </example> + <para>Notice that the paged request examples above set the + limit to 2 (<code>?limit=2</code>), so the responses + that follow each show 2 instances and return a + <emphasis>marker</emphasis> set to the UUID of the + last item in the returned list + (<code>?marker=4137d6a4-03b7-4b66-b0ef-8c7c35c470d3</code>). + Also a link is provided to retrieve the next 2 results + (<code>limit=2</code>) in the link element + identified by the attribute <code>rel="next"</code> + (XML) or <code>"rel":"next"</code> (JSON):</para> + <example> + <title>List Instances Paged Response: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-instances-index-pagination-response.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>List Instances Paged Response: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-instances-index-pagination-response.json" parse="text"/> + </programlisting> + </example> + </section> + <section xml:id="efficient_polling_changes_since_parm" security="writeronly"> + <title>Efficient Polling with the + <parameter>Changes-Since</parameter> + Parameter</title> + <remark>Reviewer: I have hidden this section, since it + does not appear that it will be supported for Private + Beta. Correct?</remark> + <para> 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 <parameter>changes-since</parameter> + parameter to check for changes since a previous + request. The <parameter>changes-since</parameter> time + is specified as <link + xlink:href="http://en.wikipedia.org/wiki/Unix_time" + >Unix time</link> (the number of seconds since + January 1, 1970, 00:00:00 UTC, not counting leap + seconds). If nothing has changed since the + <parameter>changes-since</parameter> time, a + <returnvalue>304 (Not Modified)</returnvalue> + response will be returned. If data has changed, only + the items changed since the specified time will be + returned in the response. </para> + <remark>Reviewer: does the following sentence apply, and + should it be included?</remark> + <para>For example, performing a &GET; against + https://api.servers.rackspacecloud.com/v1.0/224532/servers?<parameter>changes-since</parameter>=1244012982 + would list all servers that have changed since Wed, 03 + Jun 2009 07:09:42 UTC. </para> + </section> + <section xml:id="DB_faults"> + <title>Faults</title> + <para> 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. </para> + <para>The following table lists possible fault types with their associated error codes + and descriptions.</para> + <informaltable rules="all"> + <thead> + <tr align="center"> + <td colspan="2">Fault Type</td> + <td colspan="1">Associated Error Code</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="2"><code>badRequest</code></td> + <td colspan="1">400</td> + <td colspan="3">There was one or more errors in the user request.</td> + </tr> + <tr> + <td colspan="2"><code>unauthorized</code></td> + <td colspan="1">401</td> + <td colspan="3">The supplied token is not authorized to access the resources, either it's expired or invalid.</td> + </tr> + <tr> + <td colspan="2"><code>forbidden</code></td> + <td colspan="1">403</td> + <td colspan="3">Access to the requested + resource was denied.</td> + </tr> + <tr> + <td colspan="2"><code>itemNotFound</code></td> + <td colspan="1">404</td> + <td colspan="3">The back-end services did not + find anything matching the + Request-URI.</td> + </tr> + <tr> + <td colspan="2"><code>badMethod</code></td> + <td colspan="1">405</td> + <td colspan="3">The request method is not allowed for this resource.</td> + </tr> + <tr> + <td colspan="2"><code>overLimit</code></td> + <td colspan="1">413</td> + <td colspan="3">Either the number of entities in the request is larger than + allowed limits, or the user has exceeded allowable request rate limits. + See the <code>details</code> element for more specifics. Contact support + if you think you need higher request rate limits.</td> + </tr> + <tr> + <td colspan="2"><code>badMediaType</code></td> + <td colspan="1">415</td> + <td colspan="3">The requested content type is not supported by this service.</td> + </tr> + <tr> + <td colspan="2" + ><code>unprocessableEntity</code></td> + <td colspan="1">422</td> + <td colspan="3">The requested resource could + not be processed on at the moment.</td> + </tr> + <tr> + <td colspan="2" + ><code>instanceFault</code></td> + <td colspan="1">500</td> + <td colspan="3">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.</td> + </tr> + <tr> + <td colspan="2" + ><code>notImplemented</code></td> + <td colspan="1">501</td> + <td colspan="3">The requested method or resource is not implemented.</td> + </tr> + <tr> + <td colspan="2" + ><code>serviceUnavailable</code></td> + <td colspan="1">503</td> + <td colspan="3">The Database Service is not + available.</td> + </tr> + </tbody> + </informaltable> + <para>The following two <code>instanceFault</code> + examples show errors when the server has erred or + cannot perform the requested operation:</para> + + <example> + <title>Example instanceFault Response: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-instanceFault.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example Fault Response: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-instanceFault.json" parse="text"/> + </programlisting> + </example> + <para> The error code (<code>code</code>) is returned in the body of the response for + convenience. The <code>message</code> element returns a human-readable message that + is appropriate for display to the end user. The <code>details</code> element is + optional and may contain information that is useful for tracking down an error, such + as a stack trace. The <code>details</code> element may or may not be appropriate for + display to an end user, depending on the role and experience of the end user.</para> + <para>The fault's root element (for example, + <code>instanceFault</code>) may change depending + on the type of error. </para> + <para><?rax-fo keep-with-next?>The following two <code>badRequest</code> examples + show errors when the volume size is invalid:</para> + <example> + <title>Example badRequest Fault on Volume Size Errors: + XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-badRequest.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example badRequest Fault on Volume Size Errors: + JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-badRequest.json" parse="text"/> + </programlisting> + </example> + <para> The next two examples show + <code>itemNotFound</code> errors:</para> + <example> + <title>Example itemNotFound Fault: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-itemNotFound.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example itemNotFound Fault: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-itemNotFound.json" parse="text"/> + </programlisting> + </example> + </section> + <section xml:id="database_instance_status"> + <title>Database Instance Status</title> + <para><?rax-fo keep-with-next?>When making an API call to create, list, or delete + database instance(s), the following database instance + status values are possible:</para> + <itemizedlist spacing="compact"> + <listitem> + <para>BUILD – The database instance is being provisioned.</para> + </listitem> + <listitem> + <para>REBOOT – The database instance is + rebooting.</para> + </listitem> + <listitem> + <para>ACTIVE – The database instance is + online and available to take requests.</para> + </listitem> + <listitem> + <para>BLOCKED – The database instance is + unresponsive at the moment.</para> + </listitem> + <listitem> + <para>RESIZE – The database instance is being + resized at the moment.</para> + </listitem> + <listitem> + <para>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. </para> + <para> + <note> + <para>If MySQL has crashed (causing the + SHUTDOWN status), please call support + for assistance.</para> + </note> + </para> + </listitem> + <listitem> + <para>ERROR – The last operation for the + database instance failed due to an + error.</para> + </listitem> + </itemizedlist> + </section> + <section xml:id="Accessibility"> + <title>Database Instance Accessibility</title> + <para>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.</para> + <para>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:<orderedlist> + <listitem> + <para>Using the Cloud Databases API, create a + database instance. For details see <xref + linkend="POST_createInstance__version___accountId__instances_" + />.</para> + </listitem> + <listitem> + <para>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 + <citetitle>Cloud Load Balancers + Developer Guide</citetitle> at <link + xlink:href="http://docs.rackspace.com/api/" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + </orderedlist></para> + </section> + </chapter> + + <chapter xml:id="API_Operations-d1e2264" + xmlns="http://docbook.org/ns/docbook" + role="api-reference"> + <title>API Operations</title> + <note> + <para>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.</para> + </note> + <?hard-pagebreak?> + <section xml:id="API_Versions"> + <title>API Versions</title> + <para>This section describes the versions that are + supported for the Cloud Databases API.</para> + <wadl:resources xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#versions"> + <wadl:method href="getVersions"/> + </wadl:resource> + <wadl:resource href="../../../xsd/dbaas.wadl#version"> + <wadl:method href="getVersionInfo" /> + </wadl:resource> + </wadl:resources> + + </section> + + <section xml:id="Database_Instances"> + <title>Database Instances</title> + <para>This section describes the operations that are supported for database instances.</para> + <wadl:resources xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#instances" > + <wadl:method href="createInstance"/> + <wadl:method href="getInstance"/> + </wadl:resource> + + <wadl:resource href="../../../xsd/dbaas.wadl#instanceId"> + <wadl:method href="getInstanceById"/> + <wadl:method href="deleteInstance"/> + </wadl:resource> + <wadl:resource href="../../../xsd/dbaas.wadl#root"> + <wadl:method href="createRoot"/> + <wadl:method href="isRootEnabled"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="Database_Instances_Actions"> + <title>Database Instance Actions</title> + <para>This section describes the actions that are supported for database instances.</para> + <wadl:resources xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#instanceAction" > + <wadl:method href="restartInstance"/> + <wadl:method href="resizeInstance"/> + <wadl:method href="resizeVolume"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="databases"> + <title>Databases</title> + <para>This section describes the operations that are + supported for databases.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#databases"> + <wadl:method href="createDatabase"/> + <wadl:method href="getDatabases"/> + </wadl:resource> + <wadl:resource href="../../../xsd/dbaas.wadl#databaseName"> + <wadl:method href="deleteDatabase"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="user_management"> + <title>Users</title> + <para>This section describes the operations that are + supported for managing database users.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#users"> + <wadl:method href="createUser"/> + <wadl:method href="getUsers"/> + </wadl:resource> + <wadl:resource href="../../../xsd/dbaas.wadl#userId"> + <wadl:method href="deleteUser"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="flavors"> + <title>Flavors</title> + <para>This section describes the operations that are + supported for flavors.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/dbaas.wadl#flavors"> + <wadl:method href="getFlavors"/> + </wadl:resource> + + <wadl:resource href="../../../xsd/dbaas.wadl#flavorId"> + <wadl:method href="getFlavorById"/> + </wadl:resource> + </wadl:resources> + </section> + </chapter> + <glossary> + <title>Glossary</title> + <glossentry xml:id="Database-d1e017"> + <glossterm>database</glossterm> + <glossdef> + <para>A MySQL database within a database instance.</para> + </glossdef> + </glossentry> + <glossentry xml:id="Database-Instance-d1e016"> + <glossterm>database instance</glossterm> + <glossdef> + <para>A database instance is an isolated MySQL instance in a single tenant environment on a + shared physical host machine. Also referred to as instance.</para> + </glossdef> + </glossentry> + <glossentry xml:id="Flavor-d1e018"> + <glossterm>flavor</glossterm> + <glossdef> + <para>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.</para> + </glossdef> + </glossentry> + <glossentry xml:id="Volume-d1e019"> + <glossterm>volume</glossterm> + <glossdef> + <para>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.</para> + </glossdef> + </glossentry> + </glossary> +</book> 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 00000000..ba3275b3 --- /dev/null +++ b/integration/apidocs/src/resources/cdb-getting-started.xml @@ -0,0 +1,1131 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE book [ + <!-- Some useful entities borrowed from HTML --> + <!ENTITY ndash "–"> + <!ENTITY mdash "—"> + <!ENTITY hellip "…"> + + <!-- Useful for describing APIs --> + <!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'> + <!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'> + <!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'> + <!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'> + + <!-- changing authentication endpoints; define entities for US & UK rather than maintaining in text --> + <!ENTITY ENDPOINT-US "https://identity.api.rackspacecloud.com/v1.1/"> + <!ENTITY ENDPOINT-UK "https://lon.identity.api.rackspacecloud.com/v1.1/"> + <!ENTITY ENDPOINT-US-20 "https://identity.api.rackspacecloud.com/v2.0/"> + <!ENTITY ENDPOINT-UK-20 "https://lon.identity.api.rackspacecloud.com/v2.0/"> + + <!-- Useful for specs --> + <!ENTITY MAY '<emphasis xmlns="http://docbook.org/ns/docbook" role="strong">MAY</emphasis>'> + <!ENTITY SHOULD '<emphasis xmlns="http://docbook.org/ns/docbook" role="strong">SHOULD</emphasis>'> + <!ENTITY MUST '<emphasis xmlns="http://docbook.org/ns/docbook" role="strong">MUST</emphasis>'> + <!ENTITY MUST_NOT '<emphasis xmlns="http://docbook.org/ns/docbook" role="strong">MUST NOT</emphasis>'> +]> + +<book version="5.0" + xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + xml:id="cdb-getting-started"> + + <?rax title.font.size="35px" subtitle.font.size="20px"?> + <title>Rackspace Cloud Databases Getting Started Guide</title> + <titleabbrev>Rackspace Cloud Databases and Servers Getting Started Guide</titleabbrev> + + <info> + + <copyright> + <year>2011</year> + <year>2012</year> + <holder>Rackspace US, Inc.</holder> + </copyright> + <releaseinfo>API v1.0</releaseinfo> + <productname>Rackspace Cloud Databases</productname> + <pubdate>2012-09-04</pubdate> + <legalnotice role="rs-api"> + <annotation> + <remark>Copyright details are filled in by the template.</remark> + </annotation> + </legalnotice> + <revhistory> + <revision> + <date>2012-09-04</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Added information for pricing and + service level (refer to <xref + linkend="Pricing_SLA-d1e1362" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-02</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Corrected request examples for + authentication (refer to <xref + linkend="Generating_Auth_Token" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-01</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases.</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + </revhistory> + <raxm:metadata xmlns:raxm="http://docs.rackspace.com/api/metadata"> + <raxm:product version="v1.0">cdb</raxm:product> + <raxm:priority>1</raxm:priority> + </raxm:metadata> + </info> + <chapter xml:id="DB_Doc_Change_History"> + <title>Document Change History</title> + <para>This version of the Getting Started replaces and + obsoletes all previous versions. The most recent changes + are described in the table below:</para> + <?rax revhistory?> + </chapter> + <chapter xml:id="DB_Overview"> + <title>Overview</title> + <para>Follow the steps described in this guide to use the + Rackspace Cloud Databases API and the Cloud + Servers<trademark/> section of the Rackspace Cloud Control + Panel to create and access your database instances via + Rackspace Cloud Servers.</para> + <para>For details about using the Cloud Databases API, refer + to the <citetitle>Cloud Databases Developer + Guide</citetitle> at <link + xlink:href="http://docs.rackspace.com/" + >http://docs.rackspace.com/</link>.</para> + <para>For more details about Rackspace Cloud Databases, refer + to <link + xlink:href="http://www.rackspace.com/cloud/cloud_hosting_products/databases/" + >http://www.rackspace.com/cloud/cloud_hosting_products/databases/</link>. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. </para> + <para>Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know what + you think about Cloud Databases!</para> + <section xml:id="Prerequisites_Examples"> + <title>Prerequisites for Running Examples</title> + <para>In order to run the examples in this guide, you must + have the following prerequisites: <itemizedlist + spacing="compact"> + <listitem> + <para>Rackspace Cloud account</para> + </listitem> + <listitem> + <para>Rackspace Cloud username and password, + as specified during registration</para> + </listitem> + </itemizedlist></para> + </section> + <section xml:id="Pricing_SLA-d1e1362"> + <title>Pricing and Service Level</title> + <para>Cloud Databases is part of the Rackspace Cloud and + your use through the API will be billed as per the + pricing schedule at <link + xlink:href="http://www.rackspace.com/cloud/public/databases/pricing" + >http://www.rackspace.com/cloud/public/databases/pricing</link>. + 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 <link + xlink:href="http://www.rackspace.com/cloud/public/servers/pricing" + >http://www.rackspace.com/cloud/public/servers/pricing</link>.</para> + <para>The Service Level Agreements (SLAs) for Cloud + Databases and Cloud Servers are available at <link + xlink:href="http://www.rackspace.com/cloud/legal/sla/#cloud_databases" + >http://www.rackspace.com/cloud/legal/sla/#cloud_databases</link> + and <link + xlink:href="http://www.rackspace.com/cloud/legal/sla" + >http://www.rackspace.com/cloud/legal/sla</link> + respectively.</para> + </section> + </chapter> + <chapter xml:id="DB_Sending_API_Requests"> + <title>Send Requests to the API</title> + <para>You have several options for sending requests through an + API: </para> + <itemizedlist spacing="compact"> + <listitem> + <para>Developers and testers may prefer to use cURL, + the command-line tool from <link + xlink:href="http://curl.haxx.se/" + >http://curl.haxx.se/</link>. </para> + <para>With cURL you can send HTTP requests and receive + responses back from the command line. </para> + </listitem> + <listitem> + <para>If you like to use a more graphical interface, + the ReST client for Firefox also works well for + testing and trying out commands, see <link + xlink:href="https://addons.mozilla.org/en-US/firefox/addon/restclient/" + >https://addons.mozilla.org/en-US/firefox/addon/restclient/</link>. </para> + </listitem> + <listitem> + <para>You can also download and install rest-client, a + Java application to test ReSTful web services, + from <link + xlink:href="http://code.google.com/p/rest-client/" + >http://code.google.com/p/rest-client/</link>. </para> + </listitem> + </itemizedlist> + <section xml:id="DB_using-curl-cli"> + <title>Send API Requests Using cURL</title> + <para>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 + <link xlink:href="http://curl.haxx.se/" + >http://curl.haxx.se/</link>. </para> + <para>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. </para> + <para>The following cURL command-line options will be used + in this guide to run the examples:</para> + <variablelist> + <title>cURL Command-Line Options</title> + <varlistentry> + <term><option>-d</option></term> + <listitem> + <para>Sends the specified data in a post + request to the HTTP server.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>-i</option></term> + <listitem> + <para>Includes the HTTP header in the + output.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>-H HEADER</option></term> + <listitem> + <para>Specify an HTTP header in the request.</para> + </listitem> + </varlistentry> + </variablelist> + <note> + <para>If you have the tools, you can run the cURL JSON + request examples with the following options to + format the output from cURL: <command><curl + JSON request example> | python + -mjson.tool</command>.</para> + </note> + </section> + <section xml:id="DB_copy_paste_examples"> + <title>Copying and Pasting cURL Request Examples into a + Terminal Window</title> + <para>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 <emphasis role="bold" + >vi</emphasis> or <emphasis role="bold" + >TextEdit</emphasis>). Then modify each example + with your required account information and so forth, + as detailed in this guide.</para> + <note> + <para>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.</para> + </note> + <para>Consider the following cURL Authenticate Request: + XML example that is described in detail in <xref + linkend="Generating_Auth_Token"/>:</para> + <example> + <title>cURL Authenticate Request: XML</title> + <?dbfo keep-together="always"?> + <screen language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ +'<?xml version="1.0" encoding="UTF-8"?> + <auth> + <apiKeyCredentials + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + username="<emphasis role="bold">your_username</emphasis>" + apiKey="<emphasis role="bold">your_api_key</emphasis>"/> + </auth><option>'</option> \ +<uri>'https://identity.api.rackspacecloud.com/v2.0/tokens'</uri></screen> + </example> + <para>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:</para> + <screen language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ + + +(... 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><option>'</option> \ +<uri>'https://identity.api.rackspacecloud.com/v2.0/tokens'</uri></screen> + <para>However the lines <emphasis>within</emphasis> the + xml portion of the message are + <emphasis>not</emphasis> escaped with a backslash + ('\') in order to avoid issues with the xml + processing:</para> + <screen language="bash">'<?xml version="1.0" encoding="UTF-8"?> + <auth> + <apiKeyCredentials + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + username="<emphasis role="bold">your_username</emphasis>" + apiKey="<emphasis role="bold">your_api_key</emphasis>"/> + </auth><option>'</option> \ +</screen> + <note> + <para>The final line of the xml message is escaped + since the backslash lies + <emphasis>outside</emphasis> the xml message + and continues the cURL command to the next + line.</para> + </note> + <para>After you are finished modifying the text for the + cURL request example with your information (for + example <emphasis role="bold" + ><code>your_username</code></emphasis> and + <emphasis role="bold" + ><code>your_api_key</code></emphasis>), paste it + into your terminal window. Then execute the cURL + command by pressing <guilabel>Enter</guilabel>.</para> + <para>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.</para> + </section> + </chapter> + <chapter xml:id="Generating_Auth_Token"> + <title>Generate an Authentication Token</title> + <para>You need to generate a token whether you use cURL or a + ReST client.</para> + <para>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 + <code>X-Auth-Token</code> header. </para> + <para>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.</para> + <para>Remember to replace the names in the Authenticate + Request examples below with their respective + values:<itemizedlist spacing="compact"> + <listitem> + <para><emphasis role="bold" + >your_username</emphasis> — The + username is your common Rackspace Cloud + username, as supplied during + registration.</para> + </listitem> + <listitem> + <para><emphasis role="bold" + >your_api_key</emphasis> — The key + is your API access key. The key can be + obtained from the Rackspace Cloud Control + Panel in the <guimenu><Your + Account></guimenu> / <guimenuitem>API + Keys</guimenuitem> section (login here: + <link + xlink:href="http://mycloud.rackspace.com/" + >Control Panel Login</link>).</para> + </listitem> + </itemizedlist></para> + <para> To access the Authentication Service, you must know + whether your account is US-based or UK-based: </para> + <itemizedlist spacing="compact"> + <listitem> + <para> US-based accounts authenticate through <link + xlink:href="&ENDPOINT-US-20;" + >&ENDPOINT-US-20;</link>. </para> + </listitem> + <listitem> + <para> UK-based accounts authenticate through <link + xlink:href="&ENDPOINT-UK-20;" + >&ENDPOINT-UK-20;</link>. </para> + </listitem> + </itemizedlist> + <para> 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: </para> + <itemizedlist spacing="compact"> + <listitem> + <para> If your account was created via <link + xlink:href="http://www.rackspacecloud.com" + >http://www.rackspacecloud.com</link>, it is a + US-based account. </para> + </listitem> + <listitem> + <para> If your account was created via <link + xlink:href="http:/www.rackspace.co.uk" + >http:/www.rackspace.co.uk</link>, it is a + UK-based account. </para> + </listitem> + </itemizedlist> + <para> If you are unsure how your account was created, use the + Rackspace contact information at either site to ask for + help. </para> + <para>Notice that you authenticate using a special URL for + Cloud authentication services. For example, for US-based + accounts, you use + <code>https://identity.api.rackspacecloud.com/v2.0/tokens</code>, + as shown in the following Authenticate Request examples. + Note that the <code>v2.0</code> component in the URL + indicates that you are using version 2.0 of the Cloud Auth + API.</para> + <example> + <title>cURL Authenticate Request: XML</title> + <?dbfo keep-together="always"?> + <screen language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ +'<?xml version="1.0" encoding="UTF-8"?> +<auth> + <apiKeyCredentials + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + username="<emphasis role="bold">your_username</emphasis>" + apiKey="<emphasis role="bold">your_api_key</emphasis>"/> +</auth>' \ +<option>-H 'Content-Type: application/xml'</option> \ +<option>-H 'Accept: application/xml'</option> \ +<uri>'https://identity.api.rackspacecloud.com/v2.0/tokens'</uri></screen> + </example> + <example> + <title>cURL Authenticate Request: JSON</title> + <?dbfo keep-together="always"?> + <screen language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ +'{ + "auth": + { + "RAX-KSKEY:apiKeyCredentials": + { + "username": "<emphasis role="bold">your_username</emphasis>", + "apiKey": "<emphasis role="bold">your_api_key</emphasis>"} + } +}' \ +<option>-H 'Content-Type: application/json'</option> \ +<uri>'https://identity.api.rackspacecloud.com/v2.0/tokens'</uri></screen> + </example> + <note> + <para>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 + <code>'https://lon.identity.api.rackspacecloud.com/v2.0/tokens'</code> + instead.</para> + </note> + <example> + <title>Authenticate Response: XML</title> + <programlisting language="xml"><xi:include href="../../src/resources/samples/db-auth-20.xml" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <example> + <title>Authenticate Response: JSON</title> + <programlisting language="json"><xi:include href="../../src/resources/samples/db-auth-20.json" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <para>The authentication token <code>id</code> is returned + along with an <code>expires</code> attribute that + specifies when the token expires. </para> + <note> + <title>Notes</title> + <itemizedlist spacing="compact"> + <listitem> + <para>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.</para> + </listitem> + <listitem> + <para>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.</para> + </listitem> + <listitem> + <para>The <code>id</code> attribute in the + Authenticate Response specifies the + authentication token. Tokens are valid for a + finite duration.</para> + <para>Remember to supply your authentication token + wherever you see the field <emphasis + role="bold">your_auth_token</emphasis> in + the examples in this guide.</para> + </listitem> + <listitem> + <para> The <code>expires</code> 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; <code>expires</code> + 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.</para> + </listitem> + <listitem> + <para>Applications should be designed to + re-authenticate after receiving a 401 + (Unauthorized) response from a service + endpoint.</para> + </listitem> + </itemizedlist> + </note> + <para>The <code>publicURL</code> endpoints for + <code>cloudDatabases</code> (for example + <code>https://ord.databases.api.rackspacecloud.com/v1.0/1100111</code>) + are also returned in the response. </para> + <para>You will find the actual account number after the final + '/' in the <code>publicURL</code> 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 <emphasis + role="bold">your_acct_id</emphasis> specified in the + examples in this guide.</para> + <para>After authentication, you can use cURL to perform &GET;, + &DELETE;, and &POST; requests for the Cloud Databases + API.</para> + </chapter> + <chapter xml:id="Service_Access_Endpoints-d1e753"> + <title>Service Access/Endpoints</title> + <para>The endpoints to use for your Cloud Databases API calls + are summarized in the table below.</para> + + <para> + <table rules="all"> + <caption>Regionalized Service Endpoints</caption> + <thead> + <tr align="center"> + <td colspan="2">Region</td> + <td colspan="5">Endpoint</td> + </tr> + </thead> + <tbody> + <tr align="left"> + <td colspan="2">Chicago (ORD)</td> + <td colspan="5" + ><code>https://ord.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ + </td> + </tr> + <tr align="left"> + <td colspan="2">Dallas/Ft. Worth (DFW)</td> + <td colspan="5" + ><code>https://dfw.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ + </td> + </tr> + <tr align="left"> + <td colspan="2">London (LON)</td> + <td colspan="5"> + <code>https://lon.databases.api.rackspacecloud.com/v1.0/</code><parameter>1234</parameter>/ + </td> + </tr> + </tbody> + </table> + </para> + <note> + <title>Notes:</title> + <itemizedlist> + <listitem> + <para>Choose the endpoint from the table for the + datacenter where your Cloud resources are + located.</para> + </listitem> + <listitem> + <para>The Cloud Server that you use in <xref + linkend="Create_Cloud_Server"/> must be + located in the same datacenter where your + database resides.</para> + </listitem> + <listitem> + <para>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.</para> + </listitem> + </itemizedlist> + </note> + <para>Replace the sample account ID number, + <parameter>1234</parameter>, with your actual account + number returned as part of the authentication response. + Use your actual account number wherever you see the field + <emphasis role="bold">your_acct_id</emphasis> + specified in this guide. Refer to <xref + linkend="Generating_Auth_Token"/>.</para> + <para>When making a Cloud Databases API call, place the + endpoint at the beginning of the request URL, for example: + (<code>https://ord.databases.api.rackspacecloud.com/v1.0/your_acct_id/</code>), + as you can see in the cURL List Flavors Details Request + examples in <xref linkend="List_Flavors"/>.</para> + </chapter> + <chapter xml:id="List_Flavors"> + <title>List Flavors</title> + <para> 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.</para> + <para>You need to use the List Flavors API call + (<code>/flavors</code>) to find the available + configurations for your database instance, and then decide + which size you need.</para> + <para>This operation does not require a request body.</para> + <para>The following examples show the cURL requests for List + Flavors:</para> + <example> + <title>cURL List Flavors Request: XML</title> + <programlisting language="bash"><command>curl -i</command> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Accept: application/xml'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/flavors'</uri></programlisting> + </example> + <example> + <title>cURL List Flavors Request: JSON</title> + <programlisting language="bash"><command>curl -i</command> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Accept: application/json'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/flavors'</uri></programlisting> + </example> + <para>Remember to replace the names in the examples above with + their actual respective values for all the cURL examples + that follow:<itemizedlist spacing="compact"> + <listitem> + <para><emphasis role="bold" + >your_auth_token</emphasis> — as + returned in your authentication response (see + the response examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold" + >your_acct_id</emphasis> — as + returned in your authentication response (must + be replaced in the request URL)</para> + </listitem> + </itemizedlist></para> + <para>The following examples show the responses for List + Flavors:</para> + <example> + <title>List Flavors Response: XML</title> + <programlisting language="xml"><xi:include href="../../src/resources/samples/db-flavors-response.xml" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <example> + <title>List Flavors Response: JSON</title> + <programlisting language="json"><xi:include href="../../src/resources/samples/db-flavors-response.json" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <para>In the previous examples, you can see from the flavor + <code>name</code> that there are multiple flavors + available, including <code>medium</code> (with 1 virtual + CPU and 2 gigabytes of memory) and <code>tiny</code> (with + 1 virtual CPU and 0.5 gigabytes of memory). </para> + <para>In this example, assume that you decide to use the tiny + flavor (id 1) to provide the needed capacity for your + database instance.</para> + <para>Notice that there are two kinds of link relations + associated with flavor resources. A <code>self</code> link + contains a <emphasis>versioned</emphasis> 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 <code>bookmark</code> link provides a + permanent link to a flavor resource that is appropriate + for long term storage and works across API + versions.</para> + </chapter> + + <chapter xml:id="Create_DB_Instance"> + <title>Create a Database Instance with a Database and a + User</title> + <para>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). </para> + <para>Refer to <link + xlink:href="http://docs-beta.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createDatabase__version___accountID__instances__instanceId__databases_databases.html" + >Create Database</link> in the <citetitle>Cloud + Databases Developer Guide</citetitle> for the + restrictions for choosing the database name.</para> + <para>The example creates a database instance + <code>myrackinstance</code>, with the following: <itemizedlist> + <listitem> + <para>the tiny flavor</para> + </listitem> + <listitem> + <para>volume size of 2 gigabytes (GB)</para> + </listitem> + <listitem> + <para>a database named <code>sampledb</code> with:<itemizedlist> + <listitem> + <para><code>utf8</code> character + set</para> + </listitem> + <listitem> + <para><code>utf8_general_ci</code> + collation</para> + </listitem> + </itemizedlist></para> + </listitem> + <listitem> + <para>a user <code>simplestUser</code> with + password <code>password</code></para> + </listitem> + </itemizedlist> + </para> + <para>Notice that the request specifies the flavor reference + (<code>flavorRef</code>) to the tiny flavor (id 1) + that was returned by the List Flavors Details call: + <code>"https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1"</code>. + This is the <code>self</code> link that is the versioned + link to the flavor resource. Refer to the response + examples in <xref linkend="List_Flavors"/>.</para> + <para>The following examples show the cURL requests for Create + Instance:</para> + <example> + <title>cURL Create Instance Request: XML</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ +'<?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/<emphasis role="bold">your_acct_id</emphasis>/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: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/xml'</option> \ +<option>-H 'Accept: application/xml'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances'</uri></programlisting> + </example> + <example> + <title>cURL Create Instance Request: JSON</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> <option>-d</option> \ +'{ + "instance": { + "databases": [ + { + "character_set": "utf8", + "collate": "utf8_general_ci", + "name": "sampledb" + } + ], + "flavorRef": "https://ord.databases.api.rackspacecloud.com/v1.0/<emphasis role="bold">your_acct_id</emphasis>/flavors/1", + "name": "myrackinstance", + "users": [ + { + "databases": [ + { + "name": "sampledb" + } + ], + "name": "simplestUser", + "password": "password" + } + ], + "volume": + { + "size": 2 + } + } +}' \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/json'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances'</uri></programlisting> + </example> + <para>Remember to replace the names in the examples above with + their actual respective values for all the cURL examples + that follow:<itemizedlist spacing="compact"> + <listitem> + <para><emphasis role="bold" + >your_auth_token</emphasis> — as + returned in your authentication response (see + the response examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold" + >your_acct_id</emphasis> — as + returned in your authentication response (must + be replaced in the request URL)</para> + </listitem> + </itemizedlist></para> + <para>Remember to replace the account id shown in the + <code>flavorRef</code> property for the examples with + your actual account id: + <code>"https://ord.databases.api.rackspacecloud.com/v1.0/</code><emphasis + role="bold" + >your_acct_id</emphasis><code>/flavors/1"</code>. + 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". </para> + <para>The following examples show the Create Instance + responses:</para> + + <example> + <title>Create Instance Response: XML</title> + <programlisting language="xml"><xi:include href="../../src/resources/samples/db-gs-create-database-instance-response.xml" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + + <example> + <title>Create Instance Response: JSON</title> + <programlisting language="json"><xi:include href="../../src/resources/samples/db-gs-create-database-instance-response.json" parse="text"><xi:fallback>Missing code sample! <?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <para>You will need to specify the instance id returned (in + the response examples above: + <code>id="d379ba5c-9a1f-4aa9-9a17-afe237d04c65"</code>) + on subsequent API calls that require it, for example List + Databases for Instance.</para> + <para>Note that the database and user are not listed in the + Create Instance responses. Next you will verify that both + were successfully created.<note> + <para> 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. </para> + </note></para> + </chapter> + <chapter xml:id="List_DBS_For_Instance"> + <title>List Databases for Instance</title> + <para>In this section you will list the databases in the + specified database instance.</para> + <para>This operation does not require a request body.</para> + <para xmlns="http://docbook.org/ns/docbook">The following + examples show the cURL requests for List Databases for + Instance:</para> + <example> + <title>cURL List Databases for Instance Request: + XML</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/xml'</option> \ +<option>-H 'Accept: application/xml'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances/</uri><emphasis role="bold">instance_id</emphasis><uri>/databases'</uri></programlisting> + </example> + <example> + <?dbfo keep-together="always"?> + <title>cURL List Databases for Instance Request: + JSON</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/json'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances/</uri><emphasis role="bold">instance_id</emphasis><uri>/databases'</uri></programlisting> + </example> + <para>Remember to replace the names in the examples above with + their actual respective values:<itemizedlist spacing="compact"> + <listitem> + <para><emphasis role="bold" + >your_auth_token</emphasis> — as + returned in your authentication response (see + the examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold" + >your_acct_id</emphasis> — as + returned in your authentication response (see + the examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold">instance_id</emphasis> + — as returned in your create instance + response (see the examples in <xref + linkend="Create_DB_Instance"/>)</para> + </listitem> + </itemizedlist></para> + <para>The following examples show the responses for Create + User:</para> + <example> + <title>List Databases for Instance Response: XML</title> + <programlisting language="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> +</programlisting> + </example> + <example> + <title>List Databases for Instance Response: JSON</title> + <programlisting language="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" + } + ] +}</programlisting> + </example> + <para>You can see that the database <code>sampledb</code> was + successfully created. Next you will list the users.</para> + </chapter> + <chapter xml:id="List_Users_DB_Instance"> + <title>List Users in Database Instance</title> + <para>In this section you will list the users in the specified + database instance.</para> + <para>This operation does not require a request body.</para> + <para>The following examples show the cURL requests for List + Users in Database Instance:</para> + <example> + <title>cURL List Users in Database Instance Request: + XML</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/xml'</option> \ +<option>-H 'Accept: application/xml'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances/</uri><emphasis role="bold">instance_id</emphasis><uri>/users'</uri></programlisting> + </example> + <example> + <?dbfo keep-together="always"?> + <title>cURL List Users in Database Instance Request: + JSON</title> + <programlisting language="bash"><command>curl</command> <option>-i</option> \ +-H 'X-Auth-Token: <emphasis role="bold">your_auth_token</emphasis>' \ +<option>-H 'Content-Type: application/json'</option> \ +<uri>'https://ord.databases.api.rackspacecloud.com/v1.0/</uri><emphasis role="bold">your_acct_id</emphasis><uri>/instances/</uri><emphasis role="bold">instance_id</emphasis><uri>/users'</uri></programlisting> + </example> + <para>Remember to replace the names in the examples above with + their actual respective values:<itemizedlist + spacing="compact"> + <listitem> + <para><emphasis role="bold" + >your_auth_token</emphasis> — as + returned in your authentication response (see + the examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold" + >your_acct_id</emphasis> — as + returned in your authentication response (see + the examples in <xref + linkend="Generating_Auth_Token"/>)</para> + </listitem> + <listitem> + <para><emphasis role="bold">instance_id</emphasis> + — as returned in your create instance + response (see the examples in <xref + linkend="Create_DB_Instance"/>)</para> + </listitem> + </itemizedlist></para> + <para>The following examples show the responses for List + Users in Database Instance:</para> + <example> + <title>List Users in Database Instance Response: + XML</title> + <programlisting language="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> +</programlisting> + </example> + <example> + <title>List Users in Database Instance Response: + JSON</title> + <programlisting language="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" + } + ] +}</programlisting> + </example> + <para>You can see that the user <code>simplestUser</code> was + successfully created.</para> + </chapter> + <chapter xml:id="Create_Cloud_Server"> + <title>Create a New Cloud Server</title> + <para>Create a Cloud Server (or use an existing Cloud Server) + to access your database.</para> + <note> + <para>If you are using an existing Cloud Server on your + account, skip this step and go directly to <xref + linkend="Configure_Cloud_Server_DB"/>.</para> + </note> + <para>Create a Cloud Server using the Cloud Servers section of + the Cloud Control Panel (login here: <link + xlink:href="http://mycloud.rackspace.com/" + >Control Panel Login</link>).</para> + <procedure> + <title>To create a Cloud Server using the Cloud Control + Panel:</title> + <step> + <para>Click <guimenuitem>Servers</guimenuitem> to view + the Cloud Servers page.</para> + </step> + <step> + <para> Using the <guimenu>Region</guimenu> 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.)</para> + </step> + <step> + <para> Select an image from a list of different + operating systems, including Linux Distributions + and Windows Images:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata + fileref="images/Choose_CS_Image_CCP.png" + contentwidth="6in"/> + </imageobject> + </inlinemediaobject></para> + </step> + <step> + <para> Specify the <guilabel>Server Name</guilabel> + and select a <guilabel>Size</guilabel> for your + Cloud Server, then click <guibutton>Create + Server</guibutton>. </para> + </step> + </procedure> + <note> + <para>You can also create a Cloud Server using the Cloud + Servers API. Refer to the <link + xlink:href="http://docs.rackspace.com/">Next Generation Cloud + Servers Developer Guide</link> for + details.</para> + </note> + </chapter> + <chapter xml:id="Configure_Cloud_Server_DB"> + <title>Configuring an Application to Use Your Cloud + Database</title> + <para>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 <xref linkend="Create_DB_Instance"/> + for the <code>hostname</code> returned and the request + examples for the user name and password you + specified.</para> + </chapter> + <chapter xml:id="MySQL_GUI_Admin"> + <title>MySQL GUI Administration</title> + <para> If you want to access your database using the command + line MySQL client, then you have now completed the + <citetitle>Getting Started</citetitle>.</para> + <para>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. </para> + <para>For more detailed installation configuration + instructions see the phpMyAdmin documentation at: <link + xlink:href="http://www.phpmyadmin.net/documentation/" + >http://www.phpmyadmin.net/documentation/</link>. <note> + <para>Rackspace does not provide phpMyAdmin support, + and the user is responsible for any security + related configuration.</para> + </note></para> + <procedure> + <title>To install and configure phpMyAdmin on an Ubuntu + 11.04 Cloud Server:</title> + <step> + <para> Install phpMyAdmin:</para> + <para> + <command>sudo apt-get install + phpmyadmin</command></para> + </step> + <step> + <para> Set up a symbolic link to the phpmyadmin config + file:</para> + <para><command>sudo ln -s /etc/phpmyadmin/apache.conf + /etc/apache2/conf.d/phpmyadmin.conf</command> + </para> + </step> + <step> + <para>Edit the + <code>/etc/phpmyadmin/config-db.php</code> + config file to point to your database + instance:</para> + <para><command>$dbserver='<cloud database + hostname>';</command> + </para> + </step> + <step> + <para>Restart apache:</para> + <para><command>sudo apachectl restart</command></para> + </step> + <step> + <para>Access phpMyAdmin at + http://<your_ipaddress>/phpMyAdmin:</para> + <para><inlinemediaobject> + <imageobject> + <imagedata fileref="images/phpMyAdmin.png" + contentwidth="6in"/> + </imageobject> + </inlinemediaobject></para> + </step> + </procedure> + <para>This concludes the <citetitle>Getting + Started</citetitle>. Thank you for using Rackspace Cloud + products.</para> + </chapter> +</book> 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 00000000..ea7d784c --- /dev/null +++ b/integration/apidocs/src/resources/cdb-mgmt-devguide.xml @@ -0,0 +1,1267 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book [ + <!-- Some useful entities borrowed from HTML --> + <!ENTITY ndash "–"> + <!ENTITY mdash "—"> + <!ENTITY hellip "…"> + + <!-- Useful for describing APIs --> + <!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'> + <!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'> + <!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'> + <!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'> + + <!ENTITY CHECK '<inlinemediaobject xmlns="http://docbook.org/ns/docbook"> + <imageobject> + <imagedata fileref="img/Check_mark_23x20_02.svg" + format="SVG" scale="60"/> + </imageobject> + </inlinemediaobject>'> + + <!ENTITY ARROW '<inlinemediaobject xmlns="http://docbook.org/ns/docbook"> + <imageobject> + <imagedata fileref="img/Arrow_east.svg" + format="SVG" scale="60"/> + </imageobject> + </inlinemediaobject>'> +]> +<book xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + xml:id="cdb-devguide" + + version="5.0"> + + <?rax title.font.size="35px" subtitle.font.size="20px"?> + <title>Rackspace Cloud Databases Developer Guide for Service + Management</title> + <?rax status.bar.text="RAX INTERNAL"?> + <titleabbrev>Rackspace Cloud Databases Management Developer + Guide</titleabbrev> + <info> + <author> + <personname> + <firstname/> + <surname/> + </personname> + <affiliation> + <orgname>Rackspace Cloud</orgname> + </affiliation> + </author> + <copyright> + <year>2011</year> + <year>2012</year> + <holder>Rackspace US, Inc.</holder> + </copyright> + <releaseinfo>API v1.0</releaseinfo> + <productname>Rackspace Cloud Databases</productname> + <pubdate>2012-10-04</pubdate> + <legalnotice role="rs-api"> + <annotation> + <remark>Copyright details are filled in by the template.</remark> + </annotation> + </legalnotice> + <abstract> + <para>This document is intended for software developers + interested in developing service management + applications using the Rackspace Cloud Databases + Application Programming Interface + (<abbrev>API</abbrev>). </para> + </abstract> + + <revhistory> + <revision> + <date>2012-10-04</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Added new API call List All Active + Accounts (see <xref + linkend="GET_getaccounts_mgmt_accounts_" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-10-02</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Added new API calls Get Hardware + Info (see <xref + linkend="GET_getHwInfo_mgmt_instances__instanceId__hwinfo_" + />) and Update All Instances on Host + (see <xref + linkend="POST_updateHostInstances_mgmt_hosts__hostId__instances_action_" + />).</para> + </listitem> + <listitem> + <para>Added tenant_id field to sample + responses for List All Instances for a + Host API call (see <xref + linkend="GET_gethostbyid_mgmt_hosts__hostId__" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-21</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Changed FAILED status for database + instance to ERROR instead (see <xref + linkend="database_instance_status" + />).</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + <revision> + <date>2012-08-01</date> + <revdescription> + <itemizedlist spacing="compact"> + <listitem> + <para>Initial Unlimited Availability (UA) + release for Rackspace Cloud + Databases.</para> + </listitem> + </itemizedlist> + </revdescription> + </revision> + </revhistory> + + <cover> + <para>this is a placeholder for the front cover</para> + </cover> + <cover> + <para>this is a placeholder for the back cover</para> + </cover> + </info> + <chapter xml:id="overview"> + <title>Overview</title> + <para>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 <citetitle>Cloud Databases + Developer Guide for Service + Management</citetitle>.</para> + <remark>Writer: need to synch up Overview section with + marketing blurb for web. These should be + consistent.</remark> + <para>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.</para> + <para>The following figure shows an + overview of Cloud Databases Infrastructure: <informalfigure> + <mediaobject> + <imageobject role="fo"> + <imagedata + fileref="images/Cloud_DB_Infographic-1.svg" + contentwidth="6in"/> + </imageobject> + <imageobject role="html"> + <imagedata + fileref="images/Cloud_DB_Infographic-1.png" + /> + </imageobject> + </mediaobject> + </informalfigure> + </para> + <remark>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.</remark> + <para security="writeronly">We welcome feedback, comments, and bug reports at <link + xlink:href="http://feedback.rackspacecloud.com" + >http://feedback.rackspacecloud.com</link>.</para> + <remark>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.</remark> + <section xml:id="Intended_Audience-d1e122"> + <title>Intended Audience</title> + <para>Two APIs provide access to Rackspace Cloud + Databases: </para> + <itemizedlist spacing="compact"> + <listitem> + <para>The <emphasis>management</emphasis> 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.</para> + </listitem> + <listitem> + <para>The <emphasis>public</emphasis> API, + available to developers within Rackspace and + Rackspace customers, is the subject of a + companion document, the <citetitle>Cloud + Databases Developer + Guide</citetitle>.</para> + </listitem> + </itemizedlist> + <para>To use the information provided here, you should + first have a general understanding of the Database + service. You should also be familiar with: </para> + <itemizedlist spacing="compact"> + <listitem> + <para>Database terminology</para> + </listitem> + <listitem> + <para>ReSTful web services</para> + </listitem> + <listitem> + <para>HTTP/1.1 conventions</para> + </listitem> + <listitem> + <para>JSON and/or XML data serialization + formats</para> + </listitem> + <listitem security="writeronly"> + <para>ATOM Syndication Format</para> + </listitem> + </itemizedlist> + </section> + <?hard-pagebreak?> + <section xml:id="Document_Change_History-d1e166"> + <title>Document Change History</title> + <para>This version of the Developer Guide replaces and + obsoletes all previous versions. The most recent + changes are described in the table below:</para> + <?rax revhistory?> + </section> + <section xml:id="Additional_Resources-d1e532"> + <title>Additional Resources</title> + <para>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:</para> + <itemizedlist> + <listitem> + <para>The WADL is <link + xlink:href="http://docs-internal.rackspace.com/cdb/api/v1.0/management.wadl" + />.</para> + </listitem> + <listitem> + <para>The XSD is <link + xlink:href="http://docs-internal.rackspace.com/cdb/api/v1.0/xsd/management.xsd"/>. </para> + </listitem> + </itemizedlist> + <para>You can download the most current versions of other + API-related documents from <link + xlink:href="http://docs.rackspace.com/" + >http://docs.rackspace.com/</link>. </para> + <para>For information about getting started using Cloud + Databases and Cloud Servers, refer to + <citetitle>Getting Started with Rackspace Cloud + Databases and Servers</citetitle>.</para> + <para>For more details about Rackspace Cloud Databases, + refer to <link + xlink:href="http://www.rackspace.com/cloud/cloud_hosting_products/databases/" + >http://www.rackspace.com/cloud/cloud_hosting_products/databases/</link>. + This site also offers links to Rackspace's official + support channels, including knowledge center articles, + forums, phone, chat, and email. </para> + <para>Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> to find out what + customers think about Cloud Databases.</para> + <para>You can also follow Rackspace updates and + announcements via twitter at <link + xlink:href="http://www.twitter.com/rackspace" + >http://www.twitter.com/rackspace</link>. </para> + <para>This API uses standard HTTP 1.1 response codes as + documented at <link + xlink:href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" + >http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</link>. + </para> + </section> + </chapter> + <chapter xml:id="Concepts-d1e563"> + <title>Concepts</title> + <?dbhtml stop-chunking?> + <para> To use the Cloud Databases API effectively, you should + understand several key concepts: </para> + <remark>Reviewer: Daniel Morris is asking Daniel Salinas to + do an initial write-up of this chapter.</remark> + <section xml:id="DatabaseInstance-d1e588"> + <title>Database Instance</title> + <para>A database instance is an isolated MySQL instance in + a single tenant environment on a shared physical host + machine.</para> + <remark security="writeronly">Writer: once we support + MSSQL, we need to describe here what is used for MSSQL + in place of database instance.</remark> + </section> + <section xml:id="Database"> + <title>Database</title> + <para>A MySQL database within a database instance.</para> + <remark security="writeronly">Writer: once we support + MSSQL, we need to modify the wording here, such as: + The actual database, whether it is in MySQL or + MSSQL.</remark> + </section> + <section xml:id="Flavor"> + <title>Flavor</title> + <para>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.</para> + </section> + <section xml:id="Volume"> + <title>Volume</title> + <para>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.</para> + </section> + </chapter> + <chapter xml:id="General_API_Information-d1e633"> + <title>General API Information</title> + <para> 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 API<remark>as well as + additional data fields not available to + customers</remark>.</para> + <section xml:id="Authentication-d1e647"> + <title>Authentication</title> + <para>Every ReST request against the Cloud Databases + Management API requires that the caller be + authenticated with HTTP Basic Auth.</para> + <note> + <para>If you cannot access the Cloud Databases + Management API, please email the Cloud DB team at + <email>clouddb_all@rackspace.com</email> to + request access. Access will be granted on an + as-needed basis until access to the Management API + is integrated with Global Auth.</para> + </note> + </section> + <section xml:id="Service_Access_Endpoints-d1e753"> + <title>Service Access</title> + <para>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.</para> + <?rax-fo keep-with-next?> + <para>Replace the sample account ID number, + <parameter>1234</parameter>, as shown in the URLs + in this guide, with your actual account number + returned as part of the authentication service + response.</para> + <para>You will find the actual account number after the + final '/' in the <code>publicURL</code> field returned + by the authentication response. In the following + example, you can see from the <code>publicURL</code> + field for <code>cloudServers</code> + (publicURL="https://servers.api.rackspacecloud.com/v1.0/322781") + that the account number is 322781.</para> + </section> + <section xml:id="Request_Response_Types-d1e503"> + <title>Request/Response Types</title> + <para> The Cloud Databases API supports both the JSON and + XML data serialization formats. The request format is + specified using the <code>Content-Type</code> header + and is <emphasis>required</emphasis> for calls that + have a request body. The response format can be + specified in requests either by using the + <code>Accept</code> header or by adding an + <code>.xml</code> or <code>.json</code> 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 <code>Accept</code> header and + a query extension, the query extension takes + precedence.</para> + <para security="writeronly">Some operations support an Atom representation that + can be used to efficiently determine when the state of + services has changed. </para> + <remark>Reviewer: the previous sentence will be hidden + for the Private Beta, since it does not appear that + Atom will be supported yet. Correct?</remark> + <table rules="all"> + <caption>Response Formats</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td>Format</td> + <td>Accept Header</td> + <td>Query Extension</td> + <td>Default</td> + </tr> + </thead> + <tbody> + <tr> + <td>JSON</td> + <td>application/json</td> + <td>.json</td> + <td>Yes</td> + </tr> + <tr> + <td>XML</td> + <td>application/xml</td> + <td>.xml</td> + <td>No</td> + </tr> + <tr security="writeronly"> + <td>ATOM</td> + <td>application/atom+xml</td> + <td>.atom</td> + <td>No</td> + </tr> + </tbody> + </table> + <remark>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?</remark> + <para>In the request example below, notice that + <parameter>Content-Type</parameter> is set to + <parameter>application/json</parameter>, but + <parameter>application/xml</parameter> is + requested via the <parameter>Accept</parameter> + header:</para> + <example xml:id="request_with_headers_json"> + <title>Request with Headers: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"><xi:include href="samples/db-request-types.json" parse="text"><xi:fallback>Missing code sample!<?rax fail?></xi:fallback></xi:include></programlisting> + </example> + <para><?rax-fo keep-with-next?>Therefore an XML response format is returned:</para> + <example> + <title>Response with Headers: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"><xi:include href="samples/db-response-types.xml" parse="text"><xi:fallback>Missing code sample!<?rax fail?></xi:fallback></xi:include></programlisting> + </example> + </section> + <section xml:id="sync_asynch_responses" security="writeronly"> + <title>Synchronous and Asynchronous Responses</title> + <remark>Reviewer: please give me the updated info for this + section. Need to replace info about callback URL, + etc.</remark> + <para> All successful &GET; requests are + <emphasis>synchronous</emphasis> 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.</para> + <para>&PUT;, &POST;, and &DELETE; calls are <emphasis>asynchronous</emphasis>, 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:</para> + <example> + <title>202 ACCEPTED Response: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: need code example</programlisting> + </example> + <example> + <title>202 ACCEPTED Response: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json">Reviewer: need code example</programlisting> + </example> + <para>The following table shows the attributes for asynchronous responses:</para> + <table rules="all"> + <caption>Attributes for Asynchronous Responses</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td colspan="1">Attribute</td> + <td colspan="4">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">jobId</td> + <td colspan="4">An identifier for the specific request.</td> + </tr> + <tr> + <td colspan="1">callbackUrl</td> + <td colspan="4">Resource locator for querying the status of the request.</td> + </tr> + </tbody> + </table> + <note> + <para>The status for asynchronous calls is retained for up to 24 hours.</para> + </note> + <note> + <para>If a request body does not pass initial validation or an error condition + arises, you may receive an immediate error response from the request.</para> + </note> + <para>When a request is made to the callback URL provided + and the job is still running, another + <returnvalue>202</returnvalue> 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 + <returnvalue>200</returnvalue> OK and contain the + information for the created database. See XXXX for a + specific example.</para> + <para>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 <returnvalue>400</returnvalue> BAD + REQUEST and contain details regarding the specific + validation error.</para> + <note> + <para>If the response from querying a callback URL is a + <returnvalue>404</returnvalue> 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 <returnvalue>404</returnvalue> NOT FOUND. </para> + </note> + <para>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.</para> + </section> + <section xml:id="Content_Compression-d1e1120" security="writeronly"> + <title>Content Compression</title> + <para> 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 + <code>Accept-Encoding</code> header on the request from the client and indicated + by the <code>Content-Encoding</code> header in the server response. Unless the + header is explicitly set, encoding defaults to disabled. </para> + <table rules="all"> + <caption>Encoding Headers</caption> + <?dbfo keep-together="always"?> + <thead> + <tr align="center"> + <td>Header Type</td> + <td>Name</td> + <td>Value</td> + </tr> + </thead> + <tbody> + <tr> + <td>HTTP/1.1 Request</td> + <td><code>Accept-Encoding</code></td> + <td>gzip</td> + </tr> + <tr> + <td>HTTP/1.1 Response</td> + <td><code>Content-Encoding</code></td> + <td>gzip</td> + </tr> + </tbody> + </table> + </section> + <section xml:id="Persistent_Connections-d1e1187" security="writeronly"> + <title>Persistent Connections</title> + <para> + 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. + </para> + <para> + To prevent abuse, HTTP sessions have a timeout of 20 + seconds before being closed. + </para> + <note> + <para> + The server may close the connection at any time + and clients should not rely on this behavior. + </para> + </note> + </section> + <?hard-pagebreak?> + <section xml:id="Limits-d1e1208"> + <title>Limits</title> + <para> + 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: <firstterm>rate limits</firstterm> and + <firstterm>absolute limits</firstterm>. Rate limits + are thresholds that are reset after a certain amount + of time passes. Absolute limits are fixed. + </para> + <section xml:id="Rate_Limits-d1e1222"> + <title>Rate Limits</title> + <para> 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<emphasis + role="bold">/v1.0/instances</emphasis>. </para> + <para>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: </para> + <table rules="all"> + <caption>Default Rate Limits</caption> + <thead> + <tr align="center"> + <td colspan="1">Verb</td> + <td colspan="2">URI</td> + <td colspan="2">RegEx</td> + <td colspan="1">Default</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">&GET; changes-since</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">3/minute</td> + </tr> + <tr> + <td colspan="1">&POST;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">10/minute</td> + </tr> + <tr> + <td colspan="1">&POST; instances</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">50/day</td> + </tr> + <tr> + <td colspan="1">&PUT;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">10/minute</td> + </tr> + <tr> + <td colspan="1">&DELETE;</td> + <td colspan="2">*/instances/*</td> + <td colspan="2">^/vd+.d+/instances.*</td> + <td colspan="1">100/minute</td> + </tr> + </tbody> + </table> + <para> 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. </para> + <para> If you exceed the thresholds established for + your account, a <errorcode>413 (Rate + Control)</errorcode> HTTP response will be + returned with a <code>Retry-After</code> header to + notify the client when it can attempt to try + again. </para> + </section> + <section xml:id="Absolute_Limits-d1e1397"> + <title>Absolute Limits</title> + <remark>Reviewer: Need to update this entire section. + Please give me your updates.</remark> + <para>Refer to the following table for the absolute + limits that are set.</para> + <table rules="all"> + <caption>Absolute Limits</caption> + <thead> + <tr> + <td colspan="1">Name</td> + <td colspan="3">Description</td> + <td colspan="1">Limit</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">Instances</td> + <td colspan="3">Maximum number of instances allowed + for your account</td> + <td colspan="1">5</td> + </tr> + <tr> + <td colspan="1">Volume Size</td> + <td colspan="3">Maximum volume size per + instance in gigabytes (GB) for your + account</td> + <td colspan="1">25</td> + </tr> + </tbody> + </table> + </section> + </section> + <section xml:id="datetimeformat"> + <title>Date/Time Format</title> + <para> The Database Service uses an ISO-8601 compliant + date format for the display and consumption of + date/time values. </para> + <para>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. </para> + <example> + <title>DB Service Date/Time Format</title> + <programlisting>yyyy-MM-dd'T'HH:mm:ss.SSSZ</programlisting> + <para>See the table below for a description of the date/time format codes.</para> + <para>May 19th, 2011 at 8:07:08 AM, GMT-5 would have the following + format:</para> + <programlisting>2011-05-19T08:07:08-05:00</programlisting> + </example> + <table rules="all"> + <caption>Explanation of Date/Time Format Codes</caption> + <thead> + <tr> + <td>Code</td> + <td>Description</td> + </tr> + </thead> + <tbody> + <tr> + <td>yyyy</td> + <td>Four digit year</td> + </tr> + <tr> + <td>MM</td> + <td>Two digit month</td> + </tr> + <tr> + <td>dd</td> + <td>Two digit day of month</td> + </tr> + <tr> + <td>T</td> + <td>Separator for date/time</td> + </tr> + <tr> + <td>HH</td> + <td>Two digit hour of day (00-23)</td> + </tr> + <tr> + <td>mm</td> + <td>Two digit minutes of hour</td> + </tr> + <tr> + <td>ss</td> + <td>Two digit seconds of the minute</td> + </tr> + <tr> + <td>SSS</td> + <td>Three digit milliseconds of the second</td> + </tr> + <tr> + <td>Z</td> + <td>RFC-822 timezone</td> + </tr> + </tbody> + </table> + + </section> + <section xml:id="pagination" security="writeronly"> + <title>Pagination</title> + <remark>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?</remark> + <para>To reduce load on the service, list operations will + return a maximum of 100 items at a time. This is + referred to as <emphasis>pagination</emphasis>.</para> + <para> 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. <emphasis>Limit</emphasis> is the restriction on the maximum + number of items for that type that can be returned. <emphasis>Offset</emphasis> 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. </para> + <para>It is important to note that offset <emphasis>must</emphasis> 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 + <code>limit</code> and <code>offset</code> respectively, and both apply only to + &GET; calls. If unspecified, they default to <code>limit=100</code> and + <code>offset=0</code>. See the examples that follow.</para> + <example> + <title>Examples of Limits and Offsets for Paging Calls</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <para>Pagination applies only to the calls listed in the following table: </para> + <remark>Reviewer: need to update the table of paginated API + calls below:</remark> + <informaltable rules="all"> + <thead> + <tr align="center"> + <td colspan="1">Verb</td> + <td colspan="2">URI</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">&GET;</td> + <td colspan="2">/URI/</td> + <td colspan="3">List all databases manageable + by the account specified. </td> + </tr> + + <tr> + <td colspan="1">&GET;</td> + <td colspan="2" + >/URI/?name=<replaceable>Name</replaceable></td> + <td colspan="3">Filter databases.</td> + </tr> + + <tr> + <td colspan="1">&GET;</td> + <td colspan="2" + >/URI/<replaceable>ID</replaceable></td> + <td colspan="3">List details of the specified + database.</td> + </tr> + + <tr> + <td colspan="1">&GET;</td> + <td colspan="2" + >/URI/<replaceable>ID</replaceable>/xxx</td> + <td colspan="3">List details xxx.</td> + </tr> + + <tr> + <td colspan="1">&GET;</td> + <td colspan="2" + >/URI/<replaceable>ID</replaceable>/yyy</td> + <td colspan="3">List details yyy.</td> + </tr> + + + + </tbody> + </informaltable> + <para>See the following section for examples of paged List + Databases calls.</para> + <section xml:id="Pagination_Elements_and_Attributes-d1e1754"> + <title>Pagination Elements and Attributes</title> + <remark>Reviewer: please review and give me your + updates for this section.</remark> + <para>For any collection in a result, there is a <code>totalEntries</code> 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 <code>previous</code> + and/or <code>next</code>, specifying how to get to the previous and/or next set + of records. </para> + <note> + <para>The <code>previous</code> and/or <code>next</code> link elements are + displayed only if there are items available in the corresponding link. See + the following examples for details.</para> + </note> + <example> + <title>List Databases Request with limit: + XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Request with limit: + JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Response with totalEntries: + XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Response with totalEntries: + JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <para> In the previous two response examples, note that + <code>totalEntries=112</code> and that a link has been provided to retrieve + the next 3 results (<code>limit=3</code>) in the link element identified by the + attribute <code>rel="next"</code> (XML) or <code>"rel":"next"</code> (JSON). </para> + <para>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 (<code>offset=3</code>):</para> + <example> + <title>List Databases Request with limit and + offset: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Request with limit and + offset: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Response with Links to + previous and next Results: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <example> + <title>List Databases Response with Links to + previous and next Results: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="xml">Reviewer: Need code example.</programlisting> + </example> + <para> + <?rax-fo keep-with-next?> In the previous two response examples, note that + <code>totalEntries=112</code> and two links have been provided to:<itemizedlist> + <listitem> + <para>Retrieve the next 3 results (<code>limit=3</code>) via the link + element identified by the attribute <code>rel="next"</code> (XML) or + <code>"rel":"next"</code> (JSON)</para> + </listitem> + <listitem> + <para>Retrieve the previous 3 results via the link element identified by + the attribute <code>rel="previous"</code> (XML) or + <code>"rel":"previous"</code> (JSON) </para> + </listitem> + </itemizedlist></para> + </section> + </section> + <section xml:id="efficient_polling_changes_since_parm" security="writeronly"> + <title>Efficient Polling with the + <parameter>Changes-Since</parameter> + Parameter</title> + <remark>Reviewer: I have hidden this section, since it + does not appear that it will be supported for Private + Beta. Correct?</remark> + <para> 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 <parameter>changes-since</parameter> + parameter to check for changes since a previous + request. The <parameter>changes-since</parameter> time + is specified as <link + xlink:href="http://en.wikipedia.org/wiki/Unix_time" + >Unix time</link> (the number of seconds since + January 1, 1970, 00:00:00 UTC, not counting leap + seconds). If nothing has changed since the + <parameter>changes-since</parameter> time, a + <returnvalue>304 (Not Modified)</returnvalue> + response will be returned. If data has changed, only + the items changed since the specified time will be + returned in the response. </para> + <remark>Reviewer: does the following sentence apply, and + should it be included?</remark> + <para>For example, performing a &GET; against + https://api.servers.rackspacecloud.com/v1.0/224532/servers?<parameter>changes-since</parameter>=1244012982 + would list all servers that have changed since Wed, 03 + Jun 2009 07:09:42 UTC. </para> + </section> + <section xml:id="DB_faults"> + <title>Faults</title> + <remark>Reviewer: need to update this section as needed + for Cloud Databases.</remark> + <para> 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. </para> + <para>The following table lists possible fault types with their associated error codes + and descriptions.</para> + <informaltable rules="all"> + <thead> + <tr align="center"> + <td colspan="2">Fault Type</td> + <td colspan="1">Associated Error Code</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="2"><code>badRequest</code></td> + <td colspan="1">400</td> + <td colspan="3">There was one or more errors in the user request.</td> + </tr> + <tr> + <td colspan="2"><code>unauthorized</code></td> + <td colspan="1">401</td> + <td colspan="3">The supplied token is not authorized to access the resources, either it's expired or invalid.</td> + </tr> + <tr> + <td colspan="2"><code>forbidden</code></td> + <td colspan="1">403</td> + <td colspan="3">Access to the requested + resource was denied.</td> + </tr> + <tr> + <td colspan="2"><code>itemNotFound</code></td> + <td colspan="1">404</td> + <td colspan="3">The back-end services did not + find anything matching the + Request-URI.</td> + </tr> + <tr> + <td colspan="2"><code>badMethod</code></td> + <td colspan="1">405</td> + <td colspan="3">The request method is not allowed for this resource.</td> + </tr> + <tr> + <td colspan="2"><code>overLimit</code></td> + <td colspan="1">413</td> + <td colspan="3">Either the number of entities in the request is larger than + allowed limits, or the user has exceeded allowable request rate limits. + See the <code>details</code> element for more specifics. Contact support + if you think you need higher request rate limits.</td> + </tr> + <tr> + <td colspan="2"><code>badMediaType</code></td> + <td colspan="1">415</td> + <td colspan="3">The requested content type is not supported by this service.</td> + </tr> + <tr> + <td colspan="2" + ><code>unprocessableEntity</code></td> + <td colspan="1">422</td> + <td colspan="3">The requested resource could + not be processed on at the moment.</td> + </tr> + <tr> + <td colspan="2" + ><code>instanceFault</code></td> + <td colspan="1">500</td> + <td colspan="3">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.</td> + </tr> + <tr> + <td colspan="2" + ><code>notImplemented</code></td> + <td colspan="1">501</td> + <td colspan="3">The requested method or resource is not implemented.</td> + </tr> + <tr> + <td colspan="2" + ><code>serviceUnavailable</code></td> + <td colspan="1">503</td> + <td colspan="3">The Database Service is not + available.</td> + </tr> + </tbody> + </informaltable> + <para>The following two <code>instanceFault</code> + examples show errors when the server has erred or + cannot perform the requested operation:</para> + + <example> + <title>Example instanceFault Response: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-instanceFault.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example instanceFault Response: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-instanceFault.json" parse="text"/> + </programlisting> + </example> + <para> The error code (<code>code</code>) is returned in the body of the response for + convenience. The <code>message</code> element returns a human-readable message that + is appropriate for display to the end user. The <code>details</code> element is + optional and may contain information that is useful for tracking down an error, such + as a stack trace. The <code>details</code> element may or may not be appropriate for + display to an end user, depending on the role and experience of the end user.</para> + <para>The fault's root element (for example, + <code>instanceFault</code>) may change depending + on the type of error. </para> + <para><?rax-fo keep-with-next?>The following two <code>badRequest</code> examples + show errors when the volume size is invalid:</para> + <example> + <title>Example badRequest Fault on Volume Size Errors: + XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-badRequest.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example badRequest Fault on Volume Size Errors: + JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-badRequest.json" parse="text"/> + </programlisting> + </example> + <para> The next two examples show + <code>itemNotFound</code> errors:</para> + <example> + <title>Example itemNotFound Fault: XML</title> + <?dbfo keep-together="always"?> + <programlisting language="xml"> +<xi:include href="samples/db-faults-itemNotFound.xml" parse="text"/> + </programlisting> + </example> + <example> + <title>Example itemNotFound Fault: JSON</title> + <?dbfo keep-together="always"?> + <programlisting language="json"> +<xi:include href="samples/db-faults-itemNotFound.json" parse="text"/> + </programlisting> + </example> + </section> + <section xml:id="database_instance_status"> + <title>Database Instance Status</title> + <para><?rax-fo keep-with-next?>When making an API call to create, list, or delete + database instance(s), the following database instance + status values are possible:</para> + <itemizedlist spacing="compact"> + <listitem> + <para>BUILD – The database instance is being provisioned.</para> + </listitem> + <listitem> + <para>REBOOT – The database instance is + rebooting.</para> + </listitem> + <listitem> + <para>ACTIVE – The database instance is + online and available to take requests.</para> + </listitem> + <listitem> + <para>BLOCKED – The database instance is + unresponsive at the moment.</para> + </listitem> + <listitem> + <para>RESIZE – The database instance is being + resized at the moment.</para> + </listitem> + <listitem> + <para>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. </para> + <para> + <note> + <para>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.</para> + </note> + </para> + </listitem> + <listitem> + <para>ERROR – The last operation for the + database instance failed due to an + error.</para> + </listitem> + </itemizedlist> + </section> + </chapter> + + <chapter xml:id="API_Operations-d1e2264" + xmlns="http://docbook.org/ns/docbook" + role="api-reference"> + <title>API Operations</title> + + <section xml:id="Database_Instance_Management"> + <title>Database Instance Management</title> + <para>This section describes the operations that are supported for managing database instances.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/management.wadl#instances"> + <wadl:method href="getIndex"/> + </wadl:resource> + <wadl:resource href="../../../xsd/management.wadl#instanceId"> + <wadl:method href="showinstances"/> + </wadl:resource> + <wadl:resource href="../../../xsd/management.wadl#diagnostics"> + <wadl:method href="getdiagnosticdetails"/> + </wadl:resource> + <wadl:resource + href="../../../xsd/management.wadl#rootdetails"> + <wadl:method href="getrootdetails"/> + </wadl:resource> + <wadl:resource + href="../../../xsd/management.wadl#hwInfo"> + <wadl:method href="getHwInfo"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="Host_Information"> + <title>Host Information</title> + <para>This section describes the operations that are supported for getting host information.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/management.wadl#hosts"> + <wadl:method href="gethosts"/> + </wadl:resource> + <wadl:resource href="../../../xsd/management.wadl#hostid"> + <wadl:method href="gethostbyid"/> + </wadl:resource> + <wadl:resource href="../../../xsd/management.wadl#instanceAction-hostId"> + <wadl:method href="updateHostInstances"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="Management_Instances_Actions"> + <title>Management Instance Actions</title> + <para>This section describes the actions that are supported for database instances.</para> + <wadl:resources xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/management.wadl#instanceAction" > + <wadl:method href="rebootInstance"/> + + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="Storage_Management"> + <title>Storage Management</title> + <para>This section describes the operations that are supported for getting information about storage devices.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource href="../../../xsd/management.wadl#storage"> + <wadl:method href="indexstorage"/> + </wadl:resource> + </wadl:resources> + </section> + + <section xml:id="Account_Information"> + <title>Account Information</title> + <para>This section describes the operations that are + supported for accounts.</para> + <wadl:resources + xmlns:wadl="http://wadl.dev.java.net/2009/02"> + <wadl:resource + href="../../../xsd/management.wadl#accounts"> + <wadl:method href="getaccounts"/> + </wadl:resource> + <wadl:resource href="../../../xsd/management.wadl#accountid"> + <wadl:method href="getaccountbyid"/> + </wadl:resource> + </wadl:resources> + </section> + + </chapter> +</book> diff --git a/integration/apidocs/src/resources/cdb-releasenotes.xml b/integration/apidocs/src/resources/cdb-releasenotes.xml new file mode 100644 index 00000000..205ff653 --- /dev/null +++ b/integration/apidocs/src/resources/cdb-releasenotes.xml @@ -0,0 +1,711 @@ +<?xml version="1.0" encoding="UTF-8"?> +<book xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + xml:id="cdb-releasenotes" + version="5.0"> + + <?rax title.font.size="35px" subtitle.font.size="20px"?> + <title>Rackspace Cloud Databases Release Notes</title> + <titleabbrev>Rackspace Cloud DBs Rel Notes</titleabbrev> + <info> + <author> + <personname> + <firstname/> + <surname/> + </personname> + <affiliation> + <orgname>Rackspace Cloud</orgname> + </affiliation> + </author> + <copyright> + <year>2010</year> + <year>2011</year> + <year>2012</year> + <holder>Rackspace US, Inc.</holder> + </copyright> + <releaseinfo>API v1.0</releaseinfo> + <productname>Rackspace Cloud Databases</productname> + <pubdate>2012-10-02</pubdate> + <legalnotice role="rs-api"> + <annotation> + <remark>Copyright details are filled in by the template.</remark> + </annotation> + </legalnotice> + <abstract> + <para>This document is intended for software developers + interested in developing applications using the + Rackspace Cloud Databases Application Program + Interface (<abbrev>API</abbrev>). </para> + </abstract> + <revhistory> + <revision security="internal"> + <date>2012-10-02</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Updated for v1.0.13.</para> + </revdescription> + </revision> + <revision security="internal"> + <date>2012-09-17</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Updated for v1.0.12.</para> + </revdescription> + </revision> + <revision> + <date>2012-09-13</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Updated for v1.0.11.</para> + </revdescription> + </revision> + <revision> + <date>2012-08-21</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Updated for v1.0.8.</para> + </revdescription> + </revision> + <revision security="internal"> + <date>2012-08-02</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Updated for v1.0.7.</para> + </revdescription> + </revision> + <revision> + <date>2012-08-01</date> + <revdescription> + <para xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >Initial Unlimited Availability (UA) release + for Rackspace Cloud Databases.</para> + </revdescription> + </revision> + </revhistory> + <raxm:metadata xmlns:raxm="http://docs.rackspace.com/api/metadata"> + <raxm:product version="v1.0">cdb</raxm:product> + <raxm:priority>10</raxm:priority> + </raxm:metadata> + </info> + <chapter xml:id="doc_change_history"> + <title>Document Change History</title> + <para>This version of the Release Notes replaces and obsoletes all + previous versions. The most recent changes are described + in the table below:</para> + <?rax revhistory?> + </chapter> + <chapter xml:id="cdbv1.0.13" security="internal"> + <title>v1.0.13, October 2, 2012 </title> + <itemizedlist spacing="compact"> + <title>Bug Fixes</title> + <listitem security="internal"> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/Search.mvc/advanced?q=D-08245" + >D-08245: </link>Formatted code to comply with + pep8 code style.</para> + </listitem> + <listitem security="internal"> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/Search.mvc/Advanced?q=D-08357" + >D-08357: </link>Initialized and updated usage + data for instances that existed prior to billing + enablement.</para> + </listitem> + <listitem security="internal"> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/Search.mvc/Advanced?q=D-08648" + >D-08648: </link>Fixed guest agent to properly + address lost connections with message bus.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to + create databases in the <emphasis role="italic" + >Getting Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in + the <emphasis role="italic">Cloud Databases + Developer Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml">File + a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + <chapter xml:id="cdbv1.0.12" security="internal"> + <title>v1.0.12 (Hotfix), September 17, 2012 </title> + <itemizedlist spacing="compact" security="internal"> + <title>Bug Fixes</title> + <listitem security="internal"> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A378774" + >D-08422: </link>Fixed billing code to + properly send events to Yagi and fixed billing + timestamp format.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Known Issues / Limitations</title> + <listitem> + <para>List Versions API call does not work and returns + a "403 Forbidden" response.</para> + </listitem> + <listitem> + <para>Users' database privileges are not modifiable. + Users must be deleted and recreated to add + additional database privileges.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to + create databases in the <emphasis role="italic" + >Getting Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in + the <emphasis role="italic">Cloud Databases + Developer Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml">File + a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + <chapter xml:id="cdbv1.0.11"> + <title>v1.0.11, September 13, 2012 </title> + <itemizedlist spacing="compact"> + <title>Bug Fixes</title> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A355524" + >D-08051: </link>Fixed issue preventing users + from increasing/decreasing their instance flavor + size.</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A271288" + >D-06654: </link>Resizing instance using same + volume size no longer generates misleading error + message.</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A348396" + >D-07897: </link>Fixed issue where instance in + ERROR fails to delete.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A344970" + >D-07819: </link>Fixed issue causing usage + information to be applied when instance goes into + error status after failed provisioning.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A303374" + >D-07213: </link>Fixed guest update Mgmt API + call.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact" security="internal"> + <title>Maintenance</title> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A367019" + >D-08217: </link>Corrected yagi logrotate + configuration to prevent log file handle from + disappearing.</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/story.mvc/Summary?oidToken=Story%3A273769" + >B-27788: </link>Updated usage modify events + (resize volume/flavor).</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/story.mvc/Summary?oidToken=Story%3A304905" + >B-29438: </link>Added Environment Variable to + usage event.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Known Issues / Limitations</title> + <listitem> + <para>List Versions API call does not work and returns + a "403 Forbidden" response.</para> + </listitem> + <listitem> + <para>Users' database privileges are not modifiable. Users must be + deleted and recreated to add additional database + privileges.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to + create databases in the <emphasis role="italic" + >Getting Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in + the <emphasis role="italic">Cloud Databases + Developer Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml">File + a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + <chapter xml:id="cdbv1.0.8"> + <title>v1.0.8, August 21, 2012 </title> + <itemizedlist spacing="compact"> + <title>What's New</title> + <listitem> + <para>Added reserved database names that cannot be used for + creating databases (see <link + xlink:href="http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createDatabase__version___accountId__instances__instanceId__databases_.html" + >http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createDatabase__version___accountId__instances__instanceId__databases_.html</link> + for details).</para> + </listitem> + <listitem> + <para>Added reserved user names that cannot be used for creating + users (see <link + xlink:href="http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createUser__version___accountId__instances__instanceId__users_.html" + >http://docs.rackspace.com/cdb/api/v1.0/cdb-devguide/content/POST_createUser__version___accountId__instances__instanceId__users_.html</link> + for details).</para> + </listitem> + <listitem> + <para>Added support for the InnoDB plugin.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Bug Fixes</title> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:315404" + >D-07436: </link>Added <code>updated</code> + attribute to the response from the List Versions + API call.</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:317427" + >D-07488: </link>Increased + <code>max_allowed_packet</code> default to 16 + MB.</para> + </listitem> + <listitem> + <para><link security="internal" + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:306892" + >D-07278: </link>Fixed + <code>open_files_limit</code> based on flavor + size.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:270656" + >D-06636: </link>Fixed issue where rabbit + restart causes delete_queue to fail.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:306364" + >D-07273: </link>Added check for account id in + Mgmt API calls.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A331826#!/sKH/assetstream/baseAssetStream/Defect:331826" + >D-07644: </link>Fixed the issue where the + Usage events for "Exists" do not have a "T" in the + date/time format.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect%3A276083#!/sKD/assetstream/baseAssetStream/Defect:276083" + >D-06783: </link>Split usage events of over 24 + hours into "exists" events.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/defect.mvc/Summary?oidToken=Defect:312544" + >D-07366: </link>Simplified ioprio + calculation.</para> + </listitem> + <listitem security="internal"> + <para><link + xlink:href="https://www15.v1host.com/RACKSPCE/story.mvc/Summary?oidToken=Story:317533#!/sAXI/assetstream/baseAssetStream/Story:317533" + >Story:317533: </link>Added cpulimit + overcommit multiplier.</para> + </listitem> + <listitem security="internal"> + <para>Fixed the trove-api issues with rabbit + connections. Using openstack-common rpc.</para> + </listitem> + <listitem security="internal"> + <para>Updated the usage event with + the environment set to STAGE instead of + STAGING.</para> + </listitem> + <listitem security="internal"> + <para>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.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Known Issues / Limitations</title> + <listitem> + <para>List Versions API call does not work and returns + a "403 Forbidden" response.</para> + </listitem> + <listitem> + <para>Users' database privileges are not modifiable. + Users must be deleted and recreated to add + additional database privileges.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to + create databases in the <emphasis role="italic" + >Getting Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in + the <emphasis role="italic">Cloud Databases + Developer Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml">File + a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + <chapter xml:id="cdbv1.0.7" security="internal"> + <title>v1.0.7, August 2, 2012 </title> + <itemizedlist spacing="compact"> + <title>What's New</title> + <listitem> + <para>Multi-cab scheduler.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Bug Fixes</title> + <listitem> + <para>Fixed usage bug with httplib2.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to + create databases in the <emphasis role="italic" + >Getting Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in + the <emphasis role="italic">Cloud Databases + Developer Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US + and UK customers 24x7x365 via phone, chat, or you + may also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml">File + a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + <chapter xml:id="cdbv1.0.6"> + <title>v1.0.6, August 1, 2012 </title> + <para>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.</para> + <itemizedlist spacing="compact"> + <title>Product Features</title> + <listitem> + <para> + <emphasis role="bold">Performance Optimized MySQL + Instances</emphasis> + </para> + <para>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.</para> + <para> </para> + </listitem> + <listitem> + <para> + <emphasis role="bold">High-performance block level + storage</emphasis> + </para> + <para>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.</para> + <para> </para> + </listitem> + <listitem> + <para> + <emphasis role="bold">Storage + Management</emphasis> + </para> + <para>Increase storage allocation seamlessly without + downtime as your dataset size increases.</para> + <para> </para> + </listitem> + <listitem> + <para> + <emphasis role="bold">Database Instance + Management</emphasis> + </para> + <para>Increase or decrease flavor (RAM) size as + database input/output (I/O) demands + increase.</para> + <para> </para> + </listitem> + <listitem> + <para> + <emphasis role="bold">Database + Management</emphasis> + </para> + <para>Create, list, and delete databases on your MySQL + database instance.</para> + <para> </para> + </listitem> + <listitem> + <para> + <emphasis role="bold">User Management</emphasis> + </para> + <para>Control database access by adding and removing + MySQL users. Optionally enable root user access + for additional control.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>What's New</title> + <listitem> + <para>Performance improvements.</para> + </listitem> + <listitem> + <para>Documentation updates to the <link + xlink:href="http://docs.rackspace.com/api/" + >CDB Developer Guide</link>.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Bug Fixes</title> + <listitem> + <para>Removed root user from the users list.</para> + </listitem> + <listitem security="internal"> + <para>List users no longer shows root (or any other + specified users we define).</para> + </listitem> + <listitem security="internal"> + <para>XML Pagination works for list instances.</para> + </listitem> + <listitem security="internal"> + <para>Kernel openvz updates.</para> + </listitem> + <listitem security="internal"> + <para>Added Environment field to usage payload + xml.</para> + </listitem> + <listitem security="internal"> + <para>Separate the modify event into modify + volume/flavor types.</para> + </listitem> + <listitem security="internal"> + <para>Added categories searching for atom hopper + billing.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Known Issues / Limitations</title> + <listitem> + <para>Users' database privileges are not modifiable. + Users must be deleted and recreated to add + additional database privileges.</para> + </listitem> + </itemizedlist> + <itemizedlist spacing="compact"> + <title>Resources</title> + <listitem> + <para> Get started using the Cloud Databases API to create + databases in the <emphasis role="italic">Getting + Started Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Get reference information and examples in the <emphasis + role="italic">Cloud Databases Developer + Guide</emphasis> at: <link + xlink:href="http://docs.rackspace.com/api/" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >http://docs.rackspace.com/api/</link>.</para> + </listitem> + <listitem> + <para> Support for Cloud Databases is available for US and UK + customers 24x7x365 via phone, chat, or you may + also <link + xlink:href="https://manage.rackspacecloud.com/Tickets/YourTickets.do" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:html="http://www.w3.org/1999/xhtml" + >File a Ticket</link>.</para> + </listitem> + <listitem> + <para> Please visit our <link + xlink:href="http://feedback.rackspacecloud.com/forums/71021-product-feedback/category/42449-cloud-databases" + >Product Feedback Forum</link> and let us know + what you think about Cloud Databases! </para> + </listitem> + </itemizedlist> + </chapter> + +</book> diff --git a/integration/apidocs/src/resources/images/Choose_CS_Image.png b/integration/apidocs/src/resources/images/Choose_CS_Image.png Binary files differnew file mode 100644 index 00000000..b60a9df8 --- /dev/null +++ b/integration/apidocs/src/resources/images/Choose_CS_Image.png diff --git a/integration/apidocs/src/resources/images/Choose_CS_Image_CCP.png b/integration/apidocs/src/resources/images/Choose_CS_Image_CCP.png Binary files differnew file mode 100644 index 00000000..2e6f23eb --- /dev/null +++ b/integration/apidocs/src/resources/images/Choose_CS_Image_CCP.png diff --git a/integration/apidocs/src/resources/images/Choose_Image_CCP.png b/integration/apidocs/src/resources/images/Choose_Image_CCP.png Binary files differnew file mode 100644 index 00000000..14a55572 --- /dev/null +++ b/integration/apidocs/src/resources/images/Choose_Image_CCP.png diff --git a/integration/apidocs/src/resources/images/Choose_Image_CCP.tiff b/integration/apidocs/src/resources/images/Choose_Image_CCP.tiff Binary files differnew file mode 100644 index 00000000..79d5a45f --- /dev/null +++ b/integration/apidocs/src/resources/images/Choose_Image_CCP.tiff diff --git a/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png b/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png Binary files differnew file mode 100644 index 00000000..7a00ae00 --- /dev/null +++ b/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.png diff --git a/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.svg b/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.svg new file mode 100644 index 00000000..4a3f125a --- /dev/null +++ b/integration/apidocs/src/resources/images/Cloud_DB_Infographic-1.svg @@ -0,0 +1,4533 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY ns_svg "http://www.w3.org/2000/svg">
+ <!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg version="1.1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="468" height="600" viewBox="0 0 468 600"
+ overflow="visible" enable-background="new 0 0 468 600" xml:space="preserve">
+<g id="Background">
+</g>
+<g id="Guides">
+</g>
+<g id="Foreground">
+ <g>
+ <rect fill-rule="evenodd" clip-rule="evenodd" fill="none" width="468" height="600"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M142.025,299.98c-0.021-0.322-0.048-0.649-0.048-0.98c0-4.907,2.362-8.891,5.956-9.836c1.219-0.321,2.16,0.147,2.16,1.013
+ s-0.941,1.335-2.16,1.014c-3.594-0.945-5.956-4.929-5.956-9.836s2.362-8.891,5.956-9.836c1.219-0.321,2.16,0.147,2.16,1.014
+ c0,0.865-0.947,1.353-2.16,1.013c-2.472-0.69-4.605-3.97-4.548-7.322c0.041-2.415,1.104-4.752,2.901-6.55
+ c1.797-1.798,4.135-2.86,6.549-2.901"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.291,300.284c0.036-0.42,0.039-0.849,0.039-1.284c0-4.907-2.362-8.891-5.955-9.836c-1.219-0.321-2.161,0.147-2.161,1.013
+ s0.942,1.335,2.161,1.014c3.593-0.945,5.955-4.929,5.955-9.836s-2.362-8.891-5.955-9.836c-1.219-0.321-2.161,0.147-2.161,1.014
+ c0,0.865,0.947,1.353,2.161,1.013c2.471-0.69,4.604-3.97,4.548-7.322c-0.041-2.415-1.104-4.752-2.901-6.55s-4.135-2.86-6.55-2.901
+ "/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C310.343,283.358,310.343,293.752,310.343,295.062z"/>
+ </g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 240.458,269.445 240.458,275.668 300.633,275.668 300.633,281.731 "/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C204.343,283.358,204.343,293.752,204.343,295.062z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C177.843,308.858,177.843,319.252,177.843,320.562z"/>
+ </g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 234.512,269.991 234.512,301.305 168.268,301.305 168.268,307.12 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 228.087,269.445 228.087,275.668 194.723,275.668 194.723,281.731 "/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C177.843,283.358,177.843,293.752,177.843,295.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C230.843,283.358,230.843,293.752,230.843,295.062z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C257.343,283.358,257.343,293.752,257.343,295.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,290.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,286.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,282.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,295.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C283.843,283.358,283.843,293.752,283.843,295.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C204.343,308.858,204.343,319.252,204.343,320.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C230.843,308.858,230.843,319.252,230.843,320.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C257.343,308.858,257.343,319.252,257.343,320.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C283.843,308.858,283.843,319.252,283.843,320.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,316.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,311.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,307.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,320.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C310.343,308.858,310.343,319.252,310.343,320.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C177.843,334.358,177.843,344.752,177.843,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C204.343,334.358,204.343,344.752,204.343,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C230.843,334.358,230.843,344.752,230.843,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C257.343,334.358,257.343,344.752,257.343,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C283.843,334.358,283.843,344.752,283.843,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,341.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,337.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,333.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,346.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C310.343,334.358,310.343,344.752,310.343,346.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C177.843,359.858,177.843,370.252,177.843,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C204.343,359.858,204.343,370.252,204.343,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C230.843,359.858,230.843,370.252,230.843,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C257.343,359.858,257.343,370.252,257.343,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C283.843,359.858,283.843,370.252,283.843,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,367.224c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,362.887c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,358.549c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,371.562c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C310.343,359.858,310.343,370.252,310.343,371.562z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.843,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C177.843,385.358,177.843,395.752,177.843,397.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.343,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C204.343,385.358,204.343,395.752,204.343,397.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.843,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C230.843,385.358,230.843,395.752,230.843,397.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.343,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C257.343,385.358,257.343,395.752,257.343,397.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.843,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C283.843,385.358,283.843,395.752,283.843,397.062z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,392.724c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,388.387c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,384.049c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M310.343,397.062c0,1.31-4.322,2.371-9.653,2.371c-5.332,0-9.654-1.062-9.654-2.371v-13.013c0-1.309,4.322-2.37,9.654-2.37
+ c5.331,0,9.653,1.062,9.653,2.37C310.343,385.358,310.343,395.752,310.343,397.062z"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.978,387.165c0-4.907,2.362-8.875,5.956-9.82c1.219-0.321,2.16,0.147,2.16,1.013c0,0.866-0.941,1.335-2.16,1.014
+ c-3.594-0.945-5.956-4.929-5.956-9.836"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.33,369.535c0,4.907-2.362,8.875-5.955,9.82c-1.219,0.321-2.161-0.147-2.161-1.013s0.942-1.335,2.161-1.014
+ c3.593,0.945,5.955,4.929,5.955,9.836"/>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M170.007,409.653c0.01-0.271-0.347-1.6-3.229-1.319c-2.454,0.238-3.472,2.875-3.438,4.299c0.035,1.424,0.834,3.577,3.89,3.577
+ s3.639-1.667,3.639-1.667"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M174.265,408.336c-0.032,0.356-0.103,6.538,0.156,7.704c0.121,0.546,5.987,0.065,5.987,0.065"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.461,408.146c-0.663,0.083-3.936,1.306-3.175,4.99c0.745,3.605,4.509,3.467,6.547,2.403
+ c1.905-0.994,2.241-4.325,0.958-5.775c-1.084-1.225-2.954-1.617-4.308-1.452"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M193.64,408.236c-0.17-0.028-0.255,3.116,0,5.184s1.983,2.832,3.739,2.804s3.285-0.991,3.427-2.832s-0.113-5.24-0.113-5.24"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.314,408.299c0.034,0.243,0.139,7.39,0.174,7.668"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M204.314,408.272c2.291-0.312,4.485-0.245,5.833,0.964c1.311,1.176,2.285,4.481-0.173,6.105
+ c-1.871,1.235-5.105,0.697-5.105,0.697"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M220.889,408.352c0.034,0.243,0.139,7.39,0.174,7.668"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M220.889,408.325c2.292-0.312,4.486-0.245,5.833,0.964c1.311,1.176,2.286,4.481-0.173,6.105
+ c-1.871,1.235-5.104,0.696-5.104,0.696"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M229.965,416.18c0,0,2.796-6.752,2.94-7.168c0.278-0.799,1.067-0.644,1.449,0.181c0.889,1.917,3.321,6.987,3.321,6.987"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M231.321,413.797c0.14,0,4.634-0.243,4.634-0.243"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M241.945,408.628c-0.047,0.355,0.057,7.368,0.118,7.563"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M238.311,408.431c0.142,0,7.166-0.17,7.364-0.17"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M246.729,416.18c0,0,2.796-6.752,2.94-7.168c0.277-0.799,1.067-0.644,1.449,0.181c0.888,1.917,3.32,6.987,3.32,6.987"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M248.086,413.797c0.139,0,4.633-0.243,4.633-0.243"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.23,408.525c0.035,0.174,0.382,7.216,0.382,7.494"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.975,412.13c2.59-0.134,4.763,0.591,4.681,2.188c-0.072,1.4-1.111,1.806-2.327,1.841c-1.666,0.047-4.576,0.069-4.576,0.069
+ "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.384,408.49c0.59-0.139,3.451-0.313,5.049,0c1.805,0.354,1.726,2.603,0.069,3.223c-1.006,0.378-3.316,0.494-4.961,0.528"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M269.119,416.18c0,0,2.796-6.752,2.94-7.168c0.277-0.799,1.067-0.644,1.449,0.181c0.888,1.917,3.32,6.987,3.32,6.987"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M270.476,413.797c0.139,0,4.633-0.243,4.633-0.243"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M285.576,409.28c-0.057-0.368-0.906-1.246-3.172-1.218s-3.823,2.011-2.492,3.201c0.363,0.324,1.19,0.725,4.418,0.821
+ c2.861,0.085,2.691,2.181,1.983,2.945s-2.352,1.331-4.504,1.104c-1.537-0.162-2.267-0.995-2.21-1.335"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.426,408.389c-0.032,0.356,0,6.537,0.26,7.704c0.121,0.546,5.986,0.064,5.986,0.064"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.814,408.518c0.162-0.032,5.663-0.129,5.663-0.129"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.945,412.335c0.162-0.002,4.01-0.063,4.204-0.099"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M306.012,409.28c-0.057-0.368-0.906-1.246-3.173-1.218c-2.266,0.028-3.823,2.011-2.492,3.201
+ c0.363,0.324,1.19,0.725,4.418,0.821c2.861,0.085,2.691,2.181,1.983,2.945c-0.709,0.765-2.352,1.331-4.504,1.104
+ c-1.537-0.162-2.267-0.995-2.21-1.335"/>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.993,369.535c0-2.729,0.716-5.169,1.983-6.959"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M144.152,341.463c-1.366-1.812-2.175-4.345-2.175-7.188"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.346,334.275c0,2.71-0.736,5.134-1.986,6.92"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M323.437,362.696c1.213,1.781,1.894,4.174,1.894,6.839"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#ABABAB" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M343.184,332.184c-0.003,21.906-0.03,53.119-0.03,54.105c0,2.048,1.4,3.186,4.587,3.413c3.186,0.228,91.406,0,93.227,0
+ c1.821,0,3.642-0.875,3.642-3.868s0.037-102.671,0-104.089c-0.042-1.625,0.035-3.449-4.552-3.449c-4.586,0-93,0-93,0
+ c-1.855,0-3.903,1.4-3.903,4.359c0,1.33,0-1.273,0,19.042"/>
+ <g>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="313.162" r="1.97"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="313.162" r="5.767"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="313.162" r="1.971"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="313.162" r="5.767"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="400.922" y1="307.396" x2="386.266" y2="307.396"/>
+ </g>
+ <g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 388.557,295.794 382.446,295.794 393.631,303.865 404.814,295.794 398.704,295.794 "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M376.691,295.105c0-3.287,2.664-5.952,5.951-5.952s5.951,2.665,5.951,5.952v0.688"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M382.643,289.153c0,0,6.823,0,10.11,0s5.951,2.665,5.951,5.952v0.688"/>
+ </g>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="328.662" r="1.97"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="328.662" r="5.767"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="328.662" r="1.971"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="328.662" r="5.767"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="400.922" y1="322.896" x2="386.266" y2="322.896"/>
+ </g>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="344.162" r="1.97"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="386.265" cy="344.162" r="5.767"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="344.162" r="1.971"/>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="400.922" cy="344.162" r="5.767"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="400.922" y1="338.396" x2="386.266" y2="338.396"/>
+ </g>
+ <path fill="#BCBCBC" d="M369.979,323.727c1.075,1.224,1.369,4.556-1.678,5.002c-3.048,0.446-4.112-2.081-3.628-3.764
+ C365.156,323.28,367.817,321.265,369.979,323.727z M369.826,340.785c0.799-0.06,1.399,0.604,1.475,1.044
+ c0.164,0.995-1.466,11.697-1.466,12.006c0,0.307-0.439,0.891-0.93,0.891s-1.026-0.358-1.026-0.358l0.352-9.221
+ c0.032-0.993-0.473-0.914-0.473-0.914l-6.467,0.237c0,0,1.901,6.755,2.209,8.167c0.307,1.412-0.553,2.027-1.044,2.089
+ c-0.49,0.06-9.886,0.06-10.744,0c-0.862-0.062-0.922-1.29,0-1.412c0.92-0.123,7.184-1.045,7.552-1.229
+ c0.369-0.185,0.245-0.613,0.123-0.922c-0.123-0.307-3.685-7.86-4.177-8.596c-0.491-0.738-0.736-2.026-0.307-2.764
+ c0.429-0.736,5.834-11.116,6.326-12.038c0.489-0.92,1.657-0.613,2.024-0.307c0.369,0.307,5.406,3.87,5.774,4.178
+ c0.367,0.307,3.561,3.069,3.561,3.069s3.502-5.588,3.868-5.957c0.37-0.368,1.659-0.432,1.353,0.858
+ c-0.307,1.292-3.072,7.371-3.317,7.862c-0.246,0.49-1.352,0.553-1.842,0.244c-0.492-0.307-5.896-3.376-5.896-3.376
+ s-3.255,3.991-3.99,5.834C362.765,340.172,369.028,340.849,369.826,340.785z"/>
+ <path fill="#BCBCBC" d="M425.401,305.398c1.444,0.362,3.83,4.625-0.071,5.927c-3.902,1.3-4.842-2.963-3.976-4.265
+ C422.221,305.761,423.957,305.037,425.401,305.398z M428.452,313.741c0,0,4.249,8.085,4.176,9.097
+ c-0.071,1.012-1.661,8.768-1.879,9.78c-0.216,1.011-0.94,1.06-0.94-0.675c0-1.735,0.362-8.237,0.218-8.673
+ c-0.144-0.433-1.806-3.323-1.806-3.323s-0.289,9.924,0,13.176s0.65,10.622,0.65,10.622s1.444,9.853,1.589,10.647
+ s-0.939,1.807-1.589,0.216c-0.65-1.589-3.398-12.235-3.758-14.259c-0.362-2.022-1.014-6.865-1.302-6.938
+ c-0.289-0.071-2.457,9.541-2.457,9.541l-1.88,11.874c-0.07,0.434-1.395,0.577-1.395,0l0.07-13.609
+ c0-0.579,1.254-11.009,1.254-11.009s0.433-10.766,0.433-11.127c0-0.362-2.697,1.517-3.492,2.601
+ c-0.794,1.084-1.59,1.735-3.034,1.735c-1.446,0-6.721,0.143-7.01,0.071c-0.29-0.071-0.867-1.155,0.361-1.373
+ s7.515-1.806,7.515-1.806l5.298-6.167c0,0,0.651-0.722,1.373-0.722C421.572,313.421,428.452,313.741,428.452,313.741z"/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 385.155,300.988 375.418,300.988 375.418,327.483 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 411.785,318.953 411.77,300.988 401.595,300.988 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 375.474,338.801 375.418,354.646 411.77,354.646 411.77,325.422 "/>
+ </g>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#1478CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="333.633" cy="316.906" r="16.5"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M324.947,318.829c0,0-2.481-3.294-3.294-3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M342.318,314.984c0,0,2.481,3.294,3.294,3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M341.333,311.096c-1.485-1.821-3.747-3.198-6.28-3.414c-5.002-0.427-10.887,2.959-9.949,11.354c0,0,2.629-3.794,3.812-3.698"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M326.336,323.132c1.474,1.612,3.539,2.8,5.877,3c5.002,0.427,10.887-2.959,9.949-11.354c0,0-2.629,3.793-3.812,3.698"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M367.844,364.486c0.027,0.134,0.295,5.577,0.295,5.791"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M369.965,367.271c2.002-0.103,3.681,0.457,3.617,1.691c-0.056,1.082-0.859,1.396-1.799,1.422
+ c-1.287,0.037-3.537,0.054-3.537,0.054"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M367.963,364.459c0.455-0.106,2.667-0.241,3.901,0c1.395,0.273,1.333,2.012,0.054,2.491c-0.777,0.291-2.563,0.382-3.834,0.408"
+ />
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M375.68,370.402c0,0,2.161-5.219,2.273-5.54c0.215-0.617,0.824-0.497,1.119,0.14c0.687,1.481,2.566,5.4,2.566,5.4"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M376.729,368.561c0.107,0,3.58-0.188,3.58-0.188"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M388.701,365.398c0.008-0.209-0.268-1.235-2.495-1.02c-1.897,0.185-2.684,2.223-2.657,3.322c0.027,1.101,0.645,2.765,3.006,2.765
+ s2.813-1.288,2.813-1.288"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M391.543,364.18c-0.031,0.276,0.15,6.08,0.2,6.23"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M391.627,368.282c0.064-0.224,3.458-2.018,4.675-4.066"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M393.516,366.97c1.409,1.217,3.33,3.106,3.362,3.49"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M399.175,364.304c-0.132-0.021-0.197,2.408,0,4.006c0.196,1.598,1.532,2.188,2.89,2.167c1.356-0.021,2.539-0.766,2.648-2.188
+ s-0.088-4.05-0.088-4.05"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M407.383,364.566c-0.031,0.275,0.144,5.693,0.193,5.844"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M407.446,364.459c0.456-0.106,2.436-0.251,3.68-0.067c1.927,0.284,2.167,2.407,0.284,3.021
+ c-0.754,0.245-3.508,0.361-3.508,0.361"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M419.463,365.185c-0.044-0.276-0.701-0.936-2.454-0.914s-2.958,1.51-1.929,2.403c0.281,0.244,0.922,0.545,3.419,0.617
+ c2.213,0.063,2.081,1.638,1.533,2.212s-1.818,0.999-3.483,0.829c-1.189-0.121-1.754-0.747-1.71-1.002"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M397.85,376.665c-0.021-0.278-0.9-0.966-2.422-0.838c-1.797,0.15-2.299,1.903-2.276,2.847c0.023,0.942,0.553,2.419,2.575,2.368
+ c1.888-0.048,2.165-0.616,2.316-1.1c0.208-0.66,0.064-1.244,0.064-1.244l-2.273,0.043"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M382.02,375.764c0,0.107,0.064,5.059,0.107,5.188"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M381.399,380.993c0.192,0,1.588-0.043,1.716-0.064"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M381.162,375.722c0.149,0,1.802-0.064,1.887-0.043"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M372.279,375.88c0.616,0.576,3.127,4.252,3.127,4.252s1.785-3.21,2.222-4.032c0.437-0.823,0.741-0.165,0.739,0.384
+ c-0.001,0.235,0.194,4.382,0.16,4.511"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M372.07,375.657c-0.026,0.235,0.129,5.208,0.172,5.337"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M408.021,376.419c-0.038-0.243-0.601-0.825-2.101-0.806c-1.5,0.019-2.531,1.331-1.65,2.118c0.241,0.216,0.789,0.48,2.926,0.544
+ c1.894,0.057,1.781,1.444,1.313,1.95c-0.469,0.507-1.557,0.882-2.981,0.731c-1.019-0.107-1.501-0.658-1.464-0.884"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M361.864,376.701c0.007-0.18-0.229-1.059-2.138-0.874c-1.625,0.158-2.299,1.903-2.276,2.847
+ c0.022,0.942,0.552,2.368,2.575,2.368c2.022,0,2.409-1.104,2.409-1.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M366.604,375.702c-0.438,0.055-2.605,0.865-2.102,3.305c0.493,2.387,2.985,2.295,4.334,1.591
+ c1.262-0.658,1.484-2.863,0.635-3.823c-0.718-0.812-1.955-1.071-2.852-0.962"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M385.648,375.813c-0.026,0.235,0.051,5.006,0.094,5.135"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M385.813,375.977c0.617,0.576,3.448,4.267,3.448,4.267c0.686,0.796,1.152,1.015,1.427,0.384
+ c0.273-0.631,0.077-4.507-0.043-4.982"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M412.808,375.702c-0.439,0.055-2.606,0.865-2.103,3.305c0.494,2.387,2.986,2.295,4.334,1.591
+ c1.262-0.658,1.484-2.863,0.635-3.823c-0.717-0.812-1.955-1.071-2.852-0.962"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M419.932,375.702c-0.439,0.055-2.605,0.865-2.102,3.305c0.493,2.387,2.985,2.295,4.334,1.591
+ c1.261-0.658,1.483-2.863,0.634-3.823c-0.718-0.812-1.955-1.071-2.852-0.962"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M425.25,375.813c-0.026,0.235,0.051,5.006,0.094,5.135"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M425.414,375.977c0.617,0.576,3.449,4.267,3.449,4.267c0.685,0.796,1.151,1.015,1.426,0.384
+ c0.273-0.631,0.077-4.507-0.043-4.982"/>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M355.58,373.234c-4.879,2.162-3.471,8.961-0.095,9.914"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M433.576,383.148c4.879-2.162,3.471-8.96,0.095-9.914"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#ABABAB" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M124.816,332.184c0.003,21.906,0.03,53.119,0.03,54.105c0,2.048-1.4,3.186-4.587,3.413c-3.186,0.228-91.406,0-93.227,0
+ c-1.821,0-3.642-0.875-3.642-3.868s-0.037-102.671,0-104.089c0.042-1.625-0.035-3.449,4.552-3.449c4.586,0,93,0,93,0
+ c1.855,0,3.903,1.4,3.903,4.359c0,1.33,0-1.273,0,19.042"/>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#1478CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="134.361" cy="316.906" r="16.5"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M125.676,318.829c0,0-2.482-3.294-3.295-3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M143.046,314.984c0,0,2.482,3.294,3.295,3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M142.062,311.096c-1.485-1.821-3.747-3.198-6.28-3.414c-5.002-0.427-10.888,2.959-9.95,11.354c0,0,2.629-3.794,3.813-3.698"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M127.064,323.132c1.474,1.612,3.539,2.8,5.876,3c5.002,0.427,10.888-2.959,9.95-11.354c0,0-2.629,3.793-3.813,3.698"/>
+ </g>
+ <g>
+ <g>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#BCBCBC" d="M40.854,314.653c0,0-4.173,7.942-4.102,8.937
+ s1.633,8.613,1.846,9.607s0.923,1.042,0.923-0.662s-0.354-8.094-0.213-8.52c0.143-0.426,1.775-3.266,1.775-3.266
+ s0.283,9.749,0,12.944c-0.284,3.194-0.64,10.437-0.64,10.437s-1.42,9.679-1.562,10.459c-0.143,0.781,0.923,1.775,1.562,0.213
+ c0.64-1.562,3.337-12.021,3.692-14.009c0.354-1.988,0.994-6.745,1.277-6.815c0.284-0.071,2.414,9.371,2.414,9.371
+ s1.775,11.24,1.846,11.667c0.071,0.426,1.372,0.567,1.372,0c0-0.568-0.071-12.803-0.071-13.371
+ c0-0.567-1.229-10.814-1.229-10.814s-0.336-11.974-0.336-12.329c0-0.354,4.687-1.604,5.977-1.885
+ c1.291-0.28,1.671-1.231,2.567-2.333c0.902-1.109,4.064-5.311,4.164-5.586c0.101-0.275-0.465-1.342-1.316-0.46
+ s-5.845,5.268-5.845,5.268s-5.921,0.831-6.631,0.831S40.854,314.653,40.854,314.653z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#BCBCBC" d="M43.853,306.456c-1.42,0.355-3.763,4.544,0.071,5.822
+ c3.833,1.277,4.756-2.911,3.904-4.189C46.977,306.812,45.272,306.102,43.853,306.456z"/>
+ </g>
+ <g>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#BCBCBC" d="M109.421,306.456c1.42,0.355,3.763,4.544-0.071,5.822
+ c-3.833,1.277-4.756-2.911-3.904-4.189C106.297,306.812,108.001,306.102,109.421,306.456z"/>
+ <path fill="#BCBCBC" d="M104.132,315.38l-1.844,6.639c0,0-2.806-4.706-2.923-5.115s-1.402-0.615-1.286,0.467
+ c0.117,1.083,2.807,7.835,2.981,8.361c0.158,0.474,1.663,1.538,2.561,0.026c-0.067,2.831-0.093,5.073-0.093,5.073
+ s-1.229,10.247-1.229,10.814l-0.071,13.371c0,0.567,1.301,0.426,1.372,0c0.07-0.427,1.846-11.667,1.846-11.667
+ s2.13-9.442,2.414-9.371c0.284,0.07,0.923,4.827,1.277,6.815c0.355,1.987,3.053,12.447,3.692,14.009
+ c0.639,1.563,1.704,0.568,1.562-0.213c-0.142-0.78-1.562-10.459-1.562-10.459s-0.355-7.242-0.64-10.437
+ c-0.199-2.253,0.215-7.765,0.477-10.819c2.307-4.247,1.387-7.111,0.422-8.222c0,0-7.955-0.322-8.665-0.322
+ C104.313,314.331,104.217,314.721,104.132,315.38z"/>
+ <path fill="#BCBCBC" d="M100.078,314.979l-1.004-0.389c0.036-0.082,0.056-0.174,0.055-0.271c-0.007-0.355-0.3-0.638-0.654-0.632
+ c-0.355,0.006-0.639,0.299-0.632,0.654c0.002,0.112,0.032,0.217,0.084,0.308l-1.032,0.44l-1.71,0.06l0.818,9.413l3.834-0.165
+ c-0.266-0.69-0.591-1.539-0.915-2.403c-0.687-1.83-1.438-3.901-1.508-4.552c-0.1-0.93,0.482-1.462,1.152-1.53
+ c0.417-0.042,1.244,0.113,1.442,0.807c0.055,0.194,1.23,2.207,2.056,3.607l0.292-1.051l-0.36-4.185l-0.117-0.176
+ L100.078,314.979z"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M58.77,304.716c0,0,2.985,3.029,5.541,1.264c2.557-1.766,4.303-6.596,13.62-6.596h14.165"/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 53.59,310.603 53.649,291.138 97.733,291.138 97.655,311.774 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 97.586,326.356 97.516,343.074 53.59,343.074 53.59,319.269 "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M69.322,330.139c0.117,1.693,2.837,0.149,0.778-0.635"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M76.544,330.539c0.498-1.622-2.595-1.156-0.954,0.313"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M82.32,329.712c-1.26-1.138-2.144,1.862-0.119,0.997"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M86.947,330.139c0.117,1.693,2.837,0.149,0.778-0.635"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M64.57,329.712c-1.26-1.138-2.144,1.862-0.119,0.997"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="59.252" y1="335.916" x2="91.7" y2="335.916"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="89.548" y1="337.979" x2="89.548" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="87.198" y1="337.979" x2="87.198" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="84.849" y1="337.979" x2="84.849" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="82.498" y1="337.979" x2="82.498" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="80.148" y1="337.979" x2="80.148" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="77.799" y1="337.979" x2="77.799" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="75.449" y1="337.979" x2="75.449" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="73.1" y1="337.979" x2="73.1" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="70.75" y1="337.979" x2="70.75" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="68.4" y1="337.979" x2="68.4" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="66.051" y1="337.979" x2="66.051" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="63.701" y1="337.979" x2="63.701" y2="333.656"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="61.352" y1="337.979" x2="61.352" y2="333.656"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M58.501,316.968c-0.005,3.965-0.024,6.708-0.024,7.171c0,1.313,0.656,2.217,2.053,2.217h29.606c1.64,0,2.274-1.117,2.274-2.22
+ s0-24.247,0-25.562c0-1.313-0.656-2.217-2.053-2.217c-1.396,0-27.967,0-29.606,0c-1.639,0-2.274,1.118-2.274,2.221
+ c0,0.397,0,3.672,0,7.754"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="57.404" y1="343.074" x2="57.404" y2="352.574"/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 92.904,343.324 92.904,352.574 58.904,352.574 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="92.904" y1="345.408" x2="58.904" y2="345.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M92.548,303.066h-2.856c-2.51,0.018-2.172,5.182-4.682,5.182c-2.511,0-2.172-5.182-4.683-5.182s-2.172,5.182-4.683,5.182
+ c-2.51,0-2.172-5.182-4.682-5.182c-2.511,0-2.172,5.182-4.683,5.182s-2.172-5.163-4.683-5.182h-2.627"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="89.212" y1="322.966" x2="61.23" y2="322.966"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="64.11" y1="321.75" x2="64.11" y2="311.876"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="67.307" y1="321.75" x2="67.307" y2="313.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="70.502" y1="321.75" x2="70.502" y2="314.854"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="73.698" y1="321.75" x2="73.698" y2="312.384"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="76.894" y1="321.75" x2="76.894" y2="313.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="80.09" y1="321.75" x2="80.09" y2="312.688"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="83.285" y1="321.75" x2="83.285" y2="313.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#9A9A9A" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="86.481" y1="321.75" x2="86.481" y2="311.571"/>
+ </g>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M113.783,365.537c-0.026-0.338-1.094-1.171-2.939-1.017c-2.18,0.183-2.789,2.31-2.762,3.453c0.028,1.144,0.671,2.936,3.125,2.874
+ c2.289-0.059,2.626-0.748,2.811-1.334c0.251-0.802,0.078-1.51,0.078-1.51l-2.76,0.052"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M65.833,364.444c0,0.13,0.078,6.137,0.13,6.293"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M65.08,370.788c0.234,0,1.926-0.052,2.082-0.078"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M64.791,364.392c0.183,0,2.187-0.078,2.291-0.052"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M36.429,364.584c0.748,0.699,3.795,5.159,3.795,5.159s2.166-3.895,2.695-4.894c0.529-0.997,0.899-0.199,0.897,0.467
+ c-0.002,0.286,0.234,5.316,0.194,5.473"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M36.176,364.313c-0.032,0.287,0.156,6.319,0.208,6.476"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M72.5,364.714c-0.037,0.286,0.047,5.919,0.096,6.076"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M69.58,364.557c0.114,0,5.757-0.137,5.916-0.137"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M49.148,364.369c-0.533,0.067-3.162,1.05-2.55,4.009c0.599,2.896,3.622,2.785,5.259,1.931c1.53-0.799,1.801-3.475,0.77-4.64
+ c-0.871-0.983-2.373-1.299-3.461-1.167"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M55.813,364.502c-0.032,0.287,0.062,6.074,0.113,6.23"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M56.014,364.701c0.747,0.7,4.183,5.178,4.183,5.178c0.832,0.965,1.397,1.231,1.73,0.465c0.333-0.765,0.094-5.469-0.051-6.045"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M86.512,364.714c-0.037,0.286,0.046,5.919,0.096,6.076"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M86.645,364.557c0.477-0.103,2.536-0.217,3.825-0.004c1.997,0.331,2.209,2.542,0.241,3.145
+ c-0.788,0.241-3.652,0.313-3.652,0.313"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="89.416" y1="367.924" x2="91.896" y2="370.808"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M79.64,364.369c-0.532,0.067-3.161,1.05-2.55,4.009c0.599,2.896,3.622,2.785,5.259,1.931c1.53-0.799,1.801-3.475,0.77-4.64
+ c-0.871-0.983-2.373-1.299-3.46-1.167"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M95.329,364.444c0,0.13,0.078,6.137,0.13,6.293"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M94.576,370.788c0.234,0,1.926-0.052,2.082-0.078"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M94.287,364.392c0.183,0,2.187-0.078,2.291-0.052"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M99.322,364.502c-0.032,0.287,0.062,6.074,0.113,6.23"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M99.522,364.701c0.747,0.7,4.183,5.178,4.183,5.178c0.832,0.965,1.397,1.231,1.73,0.465c0.333-0.765,0.094-5.469-0.051-6.045"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M78.35,376.842c-0.021-0.279-0.9-0.966-2.422-0.838c-1.797,0.15-2.299,1.903-2.276,2.846c0.023,0.942,0.553,2.419,2.575,2.368
+ c1.888-0.048,2.165-0.616,2.316-1.099c0.208-0.661,0.064-1.245,0.064-1.245l-2.274,0.043"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M62.52,375.94c0,0.107,0.064,5.058,0.107,5.187"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M61.899,381.169c0.193,0,1.588-0.043,1.716-0.064"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M61.662,375.897c0.149,0,1.802-0.064,1.888-0.043"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M52.779,376.056c0.616,0.576,3.127,4.252,3.127,4.252s1.785-3.21,2.222-4.032s0.741-0.164,0.739,0.384
+ c-0.001,0.236,0.194,4.382,0.16,4.511"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M52.57,375.833c-0.026,0.236,0.129,5.208,0.172,5.337"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M88.521,376.596c-0.038-0.244-0.601-0.825-2.101-0.807s-2.531,1.331-1.65,2.119c0.241,0.215,0.789,0.48,2.926,0.544
+ c1.894,0.056,1.781,1.443,1.313,1.95c-0.469,0.506-1.557,0.881-2.981,0.731c-1.018-0.107-1.501-0.659-1.464-0.884"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M42.364,376.877c0.006-0.18-0.229-1.059-2.138-0.873c-1.625,0.157-2.299,1.903-2.276,2.846s0.552,2.368,2.575,2.368
+ c2.022,0,2.409-1.104,2.409-1.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M47.104,375.879c-0.438,0.055-2.605,0.864-2.102,3.304c0.493,2.387,2.985,2.295,4.334,1.592
+ c1.262-0.659,1.484-2.864,0.635-3.824c-0.718-0.811-1.955-1.07-2.853-0.962"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M66.148,375.988c-0.026,0.236,0.051,5.006,0.094,5.135"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M66.313,376.152c0.616,0.577,3.447,4.267,3.447,4.267c0.686,0.796,1.152,1.016,1.427,0.385
+ c0.273-0.632,0.077-4.508-0.043-4.983"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M93.308,375.879c-0.439,0.055-2.606,0.864-2.103,3.304c0.494,2.387,2.986,2.295,4.335,1.592
+ c1.261-0.659,1.483-2.864,0.634-3.824c-0.718-0.811-1.955-1.07-2.852-0.962"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M100.432,375.879c-0.439,0.055-2.605,0.864-2.102,3.304c0.493,2.387,2.985,2.295,4.334,1.592
+ c1.261-0.659,1.483-2.864,0.634-3.824c-0.718-0.811-1.955-1.07-2.852-0.962"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M105.75,375.988c-0.026,0.236,0.051,5.006,0.094,5.135"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M105.915,376.152c0.616,0.577,3.447,4.267,3.447,4.267c0.686,0.796,1.152,1.016,1.427,0.385
+ c0.273-0.632,0.077-4.508-0.043-4.983"/>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M36.08,373.411c-4.879,2.162-3.471,8.96-0.095,9.914"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M114.076,383.325c4.879-2.162,3.471-8.961,0.095-9.914"/>
+ </g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.584" y1="203.188" x2="233.588" y2="216.295"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.047,136.605c-0.134-0.022-0.201,2.449,0,4.075c0.2,1.626,1.559,2.228,2.939,2.205s2.583-0.779,2.694-2.228
+ c0.112-1.447-0.089-4.12-0.089-4.12"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.667,136.449c0.166,0.134,2.704,3.407,3.036,3.764c0.333,0.356,1.145-0.608,2.143-2.049c0.55-0.795,1.185-1.715,1.061-1.893
+ "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M261.895,140.404c0.022,0.179-0.022,2.517,0.045,2.628"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M268.834,136.534c-0.521,0.065-3.095,1.026-2.496,3.923c0.586,2.836,3.546,2.727,5.147,1.891
+ c1.498-0.782,1.763-3.401,0.753-4.542c-0.852-0.962-2.322-1.271-3.387-1.142"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.51,136.871c-0.036,0.28,0.045,5.794,0.094,5.947"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.64,136.717c0.466-0.101,2.482-0.212,3.745-0.004c1.954,0.324,2.161,2.488,0.235,3.078
+ c-0.771,0.236-3.575,0.306-3.575,0.306"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="286.353" y1="140.013" x2="288.781" y2="142.836"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M268.313,146.356c-0.031,0.279,0.146,5.791,0.197,5.944"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M268.377,146.247c0.464-0.109,2.478-0.257,3.744-0.069c1.959,0.29,2.204,2.45,0.289,3.073c-0.768,0.25-3.569,0.368-3.569,0.368"
+ />
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.165,152.292c0,0,2.198-5.309,2.313-5.636c0.218-0.627,0.839-0.506,1.139,0.143c0.698,1.507,2.611,5.493,2.611,5.493"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M261.231,150.419c0.109,0,3.643-0.19,3.643-0.19"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.797,146.356c-0.031,0.279,0.146,5.791,0.197,5.944"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.861,146.247c0.465-0.109,2.479-0.257,3.744-0.069c1.96,0.29,2.204,2.45,0.289,3.073c-0.767,0.25-3.568,0.368-3.568,0.368"
+ />
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M225.391,28.948c10.65-3.64,29.464-1.591,32.248,19.668c3.227,24.634-27.522,32.18-41.135,19.412
+ C204.971,57.212,208.075,34.866,225.391,28.948z"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="217.57" y1="33.729" x2="250.612" y2="33.729"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="211.572" y1="42.276" x2="256.006" y2="42.276"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="209.725" y1="50.823" x2="217.141" y2="50.823"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="211.157" y1="59.371" x2="256.177" y2="59.371"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="216.754" y1="67.918" x2="250.421" y2="67.918"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M232.215,74.12c-4.537-0.264-8.951-2.266-12.152-6.092c-9.049-10.816-6.613-33.162,6.974-39.08
+ c1.726-0.752,3.725-1.261,5.83-1.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M235.319,74.12c4.536-0.264,8.951-2.266,12.152-6.092c9.049-10.816,6.613-33.162-6.974-39.08
+ c-1.726-0.752-3.725-1.261-5.83-1.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M223.648,43.72c0.898-6.5,2.947-12.193,6.148-14.771c0.934-0.752,2.015-1.261,3.153-1.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M232.615,74.12c-2.455-0.264-4.859-2.266-6.591-6.092c-1.213-2.679-2.044-6.064-2.497-9.732"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.781" y1="58.561" x2="233.772" y2="74.101"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.789" y1="27.468" x2="233.776" y2="43.47"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M243.68,43.633c-0.884-6.464-2.899-12.118-6.017-14.685c-0.913-0.752-1.971-1.261-3.085-1.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M234.939,74.12c2.4-0.264,4.72-2.266,6.413-6.092c1.219-2.754,2.044-6.256,2.478-10.043"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="250.596" y1="50.823" x2="258.012" y2="50.823"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M221.356,49.048c0.13,1.239,1.2,4.155,1.389,4.504c0.133,0.247,0.387,0.534,0.626,0c0.281-0.628,0.947-2.451,1.129-3.044
+ c0.166-0.541,0.412-1.206,0.712-0.272c0.314,0.978,1.249,3.02,1.39,3.299c0.319,0.634,0.508,0.14,0.727-0.61
+ c0.219-0.751,1.217-3.963,1.087-4.033"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.17,49.048c0.13,1.239,1.201,4.155,1.389,4.504c0.133,0.247,0.387,0.534,0.627,0c0.281-0.628,0.947-2.451,1.129-3.044
+ c0.165-0.541,0.412-1.206,0.712-0.272c0.314,0.978,1.249,3.02,1.39,3.299c0.318,0.634,0.507,0.14,0.727-0.61
+ c0.219-0.751,1.216-3.963,1.086-4.033"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M238.984,49.048c0.13,1.239,1.201,4.155,1.389,4.504c0.133,0.247,0.387,0.534,0.626,0c0.281-0.628,0.948-2.451,1.13-3.044
+ c0.165-0.541,0.411-1.206,0.711-0.272c0.314,0.978,1.25,3.02,1.391,3.299c0.318,0.634,0.507,0.14,0.726-0.61
+ c0.219-0.751,1.217-3.963,1.087-4.033"/>
+ </g>
+ </g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.847" y1="92.705" x2="226.847" y2="98.359"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="240.42" y1="92.705" x2="240.42" y2="98.359"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M246.419,483.738c4.907,0,8.875-2.362,9.82-5.956c0.321-1.219-0.147-2.16-1.013-2.16c-0.866,0-1.335,0.941-1.014,2.16
+ c0.945,3.594,4.929,5.956,9.836,5.956s8.891-2.362,9.836-5.956c0.32-1.219-0.147-2.16-1.013-2.16c-0.866,0-1.335,0.941-1.015,2.16
+ c0.946,3.594,4.93,5.956,9.837,5.956s8.891-2.362,9.836-5.956c0.321-1.219-0.147-2.16-1.013-2.16c-0.866,0-1.335,0.941-1.014,2.16
+ c0.945,3.594,4.929,5.956,9.836,5.956s8.891-2.362,9.836-5.956c0.32-1.219-0.148-2.16-1.014-2.16s-1.353,0.947-1.013,2.16
+ c0.69,2.472,3.97,4.604,7.322,4.548c2.415-0.041,4.752-1.104,6.55-2.901c1.798-1.797,2.86-4.135,2.901-6.55
+ c0.057-3.353-2.077-6.632-4.548-7.322c-1.214-0.339-2.161,0.147-2.161,1.014c0,0.865,0.942,1.334,2.161,1.014
+ c3.593-0.946,5.955-4.93,5.955-9.837s-2.362-8.891-5.955-9.836c-1.219-0.32-2.161,0.147-2.161,1.013
+ c0,0.866,0.942,1.335,2.161,1.015c3.593-0.946,5.955-4.93,5.955-9.837"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M220.89,483.738c-4.907,0-8.875-2.362-9.821-5.956c-0.32-1.219,0.148-2.16,1.014-2.16s1.334,0.941,1.014,2.16
+ c-0.945,3.594-4.929,5.956-9.836,5.956s-8.891-2.362-9.837-5.956c-0.32-1.219,0.147-2.16,1.014-2.16
+ c0.865,0,1.334,0.941,1.014,2.16c-0.945,3.594-4.929,5.956-9.836,5.956s-8.891-2.362-9.837-5.956
+ c-0.32-1.219,0.148-2.16,1.014-2.16s1.334,0.941,1.014,2.16c-0.945,3.594-4.929,5.956-9.836,5.956s-8.891-2.362-9.837-5.956
+ c-0.32-1.219,0.148-2.16,1.014-2.16c0.866,0,1.353,0.947,1.014,2.16c-0.69,2.472-3.97,4.604-7.322,4.548
+ c-2.415-0.041-4.753-1.104-6.55-2.901c-1.798-1.797-2.86-4.135-2.901-6.55c-0.058-3.353,2.076-6.632,4.548-7.322
+ c1.213-0.339,2.16,0.147,2.16,1.014c0,0.865-0.941,1.334-2.16,1.014c-3.594-0.946-5.956-4.93-5.956-9.837s2.362-8.891,5.956-9.836
+ c1.219-0.32,2.16,0.147,2.16,1.013c0,0.866-0.941,1.335-2.16,1.015c-3.594-0.946-5.956-4.93-5.956-9.837"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M216.977,494.732c-21.186,0-41.063,0-41.063,0c-1.855,0-3.903,1.4-3.903,4.359c0,2.958,0,94.586,0,96.634s1.4,3.186,4.586,3.414
+ c3.186,0.228,112.407,0,114.228,0s3.642-0.876,3.642-3.869s-0.228-95.688,0-97.089s0.035-3.449-4.552-3.449
+ c-2.057,0-38.514,0.002-38.514,0.002"/>
+ <g>
+
+ <rect x="253.063" y="552.85" fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" width="4.32" height="3.547"/>
+
+ <rect x="242.411" y="552.85" fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" width="5.32" height="3.547"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="251.295" y1="531.87" x2="252.059" y2="527.115"/>
+
+ <rect x="241.364" y="531.828" fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" width="16.739" height="4.437"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="249.988" y1="556.908" x2="250.753" y2="552.151"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#C50022" d="M263.152,523.329c-0.282,0.708-3.269,7.087-3.693,7.087
+ c-0.424,0-8.075-0.282-8.642-0.282c-0.564,0,0,1.272,0.708,1.413c0.706,0.142,8.782,1.839,9.206,1.557
+ c0.425-0.284,3.693-5.674,3.693-5.674l0.707,5.39c0,0-5.793,2.097-6.501,2.114c-0.705,0.017-0.727,1.295,0.404,1.295
+ s5.954-0.849,5.954-0.849s-0.14,4.384-0.14,5.091s-1.697,13.375-1.133,22.724c0.044,0.706,1.415,0.99,1.415-0.141
+ s2.687-17.899,3.394-20.305c0.208-0.708,0.865-0.422,1.006,0.425c0.142,0.85,1.555,8.127,1.555,8.127s2.97,11.611,3.254,12.177
+ c0.282,0.566,1.555,0.425,1.414-0.283c-0.142-0.706-1.98-13.165-1.98-13.165s-0.284-11.962-0.141-12.669
+ c0.141-0.706-1.697-13.749-1.839-14.455c-0.142-0.707-2.563-1.272-4.259-1.133C265.84,521.916,263.152,523.329,263.152,523.329z"
+ />
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#C50022" d="M261.813,515.889c-1.514,1.094-2.095,5.469,2.248,5.469
+ c4.341,0,2.994-3.906,2.394-4.844C265.856,515.575,263.329,514.795,261.813,515.889z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M259.177,539.906l-18.684-0.143l-0.142-1.414c0,0,18.684,0,19.25,0c0.564,0-0.143,4.117,0,6.805c0.14,2.686,0,15.479,0,15.479"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.187,539.764c0,0.989-0.142,20.729-0.142,20.729"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M243.904,561.908c-0.566,0.847-0.283,2.119,0.423,1.413c0.708-0.709,0.426-0.849-0.566-0.709"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M259.751,563.842c0.87-0.528,1.152-1.803,0.213-1.46c-0.94,0.342-0.743,0.589,0.212,0.88"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.045,547.698c0.566,0,15.414,0,15.414,0"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.045,558.762c0.424,0,15.132,0,15.132,0"/>
+ </g>
+ <g>
+ <g>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#C50022" d="M200.174,534.396c-0.211-0.21-0.702-0.351-1.369,0.526
+ c-0.666,0.878-3.824,4.912-4.771,11.263c-0.318,2.135-0.315,4.842,1.193,5.403s11.141,0.526,11.597,0.631
+ c0.456,0.105,0.877,0.597,0.877,1.158s-0.265,6.736,0.753,9.613c0.325,0.917,2.07,0.562,2-0.351s0.271-10.27,0.48-11.217
+ c0.211-0.947,0.035-1.614-0.666-1.965c-0.702-0.351-6.18-1.369-9.478-2.211c-0.28-0.072-0.394-0.12-0.28-0.878
+ c0.211-1.403,1.954-5.438,1.954-5.438s2.751,2.043,3.032,2.358c0.28,0.316,0.596,0.562,1.402,0.351
+ c0.808-0.21,5.801-1.139,7.134-1.279s2.175-0.281,2.28-0.912c0.105-0.632-0.421-1.368-1.158-1.229
+ c-0.736,0.141-8.104,0.211-8.104,0.211s-2.993-2.653-3.719-3.403C202.278,535.94,200.174,534.396,200.174,534.396z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#C50022" d="M204.821,535.425c1.854,0.479,3.556-0.794,3.492-3.491
+ c-0.063-2.698-2.84-3.149-4.222-2.444C202.536,530.282,200.886,534.409,204.821,535.425z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M209.068,545.939c0.597,0.035,7.895-0.034,9.403-0.069s6.533,0.027,7.13,0.098"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M225.602,537.715c-0.07,0.526-0.035,24.646,0,25.382"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M216.237,543.982c0.351,0,2.594-0.07,2.979-0.035"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M221.396,542.386c0.105-0.526,1.056-9.997,1.091-10.278"/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 190.769,538.924 191.951,554.679 203.957,554.679 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.506" y1="554.679" x2="198.506" y2="563.097"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M191.031,562.785c0.623-0.935,9.033-4.359,13.704,0.312"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M211.917,525.435c0,0,1.472,1.698-0.184,4.008c0,0,4.029-1.064,3.751-4.116"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M215.559,525.247c8.857-0.064,6.295-0.114,6.483-0.114c0.565,0,0.918-0.283,0.918-0.989s-0.141-11.032-0.141-11.598
+ s-0.212-0.989-0.707-1.13c-0.494-0.142-17.394,0.242-17.394,0.242c-0.848,0-1.201,0.494-1.201,1.201
+ c0,0.706,0.142,11.244,0.212,11.88c0.071,0.636,0.565,0.706,0.989,0.777c0.149,0.024-2.622-0.016,7.061-0.079"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="1.453" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M210.884,519.477c0.8,0.688,1.423,2.106,1.423,2.106l4.464-5.813"/>
+ </g>
+ <g>
+
+ <circle fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#1478CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" cx="234.191" cy="492.447" r="16.5"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M225.507,494.37c0,0-2.482-3.294-3.295-3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M242.877,490.525c0,0,2.482,3.294,3.295,3.408"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M241.893,486.637c-1.486-1.821-3.747-3.198-6.28-3.414c-5.002-0.427-10.888,2.959-9.95,11.354c0,0,2.629-3.794,3.813-3.698"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M226.896,498.672c1.474,1.613,3.538,2.801,5.876,3c5.002,0.428,10.888-2.958,9.95-11.353c0,0-2.629,3.793-3.813,3.698"/>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.358,583.246c-0.026,0.289,0,5.299,0.21,6.244c0.099,0.442,4.852,0.053,4.852,0.053"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.674,583.352c0.131-0.026,4.589-0.105,4.589-0.105"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.779,586.445c0.131-0.003,3.25-0.051,3.406-0.08"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M211.049,583.168c0,0.131,0.079,6.19,0.132,6.349"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M210.29,589.568c0.236,0,1.943-0.053,2.101-0.079"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M209.999,583.115c0.184,0,2.206-0.079,2.311-0.053"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M190.643,583.309c0.754,0.706,3.828,5.205,3.828,5.205s2.185-3.93,2.719-4.936c0.534-1.007,0.907-0.202,0.905,0.47
+ c-0.001,0.289,0.237,5.363,0.196,5.521"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M190.387,583.036c-0.032,0.289,0.158,6.375,0.21,6.533"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M227.884,583.44c-0.038,0.288,0.046,5.972,0.096,6.13"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M224.938,583.281c0.115,0,5.808-0.138,5.968-0.138"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M272.655,584.314c0.008-0.22-0.281-1.296-2.616-1.069c-1.989,0.193-2.814,2.33-2.787,3.483c0.029,1.154,0.676,2.899,3.152,2.899
+ s2.949-1.352,2.949-1.352"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M201.035,589.561c0,0,2.265-5.473,2.383-5.809c0.225-0.647,0.864-0.521,1.174,0.146c0.72,1.553,2.691,5.662,2.691,5.662"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M202.134,587.63c0.112,0,3.755-0.197,3.755-0.197"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M215.963,583.227c-0.032,0.289,0.063,6.128,0.115,6.285"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M216.165,583.428c0.755,0.705,4.221,5.223,4.221,5.223c0.839,0.974,1.409,1.242,1.745,0.47s0.095-5.518-0.052-6.099"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M233.586,583.246c-0.026,0.289,0,5.299,0.21,6.244c0.099,0.442,4.852,0.053,4.852,0.053"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M233.9,583.352c0.132-0.026,4.59-0.105,4.59-0.105"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M234.006,586.445c0.132-0.003,3.25-0.051,3.407-0.08"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M241.391,583.227c-0.032,0.289,0.063,6.128,0.114,6.285"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M241.593,583.428c0.754,0.705,4.22,5.223,4.22,5.223c0.839,0.974,1.41,1.242,1.746,0.47s0.095-5.518-0.052-6.099"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M249.966,589.561c0,0,2.266-5.473,2.383-5.809c0.226-0.647,0.865-0.521,1.175,0.146c0.72,1.553,2.69,5.662,2.69,5.662"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M251.065,587.63c0.112,0,3.754-0.197,3.754-0.197"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.377,583.227c-0.032,0.289,0.063,6.128,0.115,6.285"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.579,583.428c0.755,0.705,4.221,5.223,4.221,5.223c0.839,0.974,1.409,1.242,1.745,0.47s0.095-5.518-0.052-6.099"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M183.025,573.058c0,0.132,0.079,6.191,0.131,6.349"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M182.267,579.458c0.236,0,1.943-0.053,2.101-0.079"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M181.976,573.006c0.184,0,2.206-0.079,2.311-0.053"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M208.621,573.33c-0.037,0.289,0.047,5.972,0.097,6.13"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M205.676,573.171c0.115,0,5.807-0.138,5.968-0.138"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M202.556,573.859c-0.046-0.298-0.734-1.01-2.571-0.986c-1.836,0.022-3.098,1.629-2.02,2.594
+ c0.295,0.263,0.965,0.588,3.581,0.665c2.318,0.069,2.181,1.768,1.606,2.388c-0.574,0.619-1.905,1.078-3.649,0.895
+ c-1.246-0.131-1.837-0.806-1.791-1.081"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.614,572.982c-0.538,0.067-3.189,1.059-2.572,4.044c0.604,2.922,3.654,2.81,5.305,1.948
+ c1.544-0.807,1.816-3.505,0.776-4.681c-0.878-0.992-2.394-1.311-3.49-1.178"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M212.756,579.451c0,0,2.265-5.473,2.383-5.81c0.225-0.647,0.864-0.521,1.174,0.146c0.72,1.554,2.691,5.663,2.691,5.663"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M213.854,577.52c0.112,0,3.755-0.197,3.755-0.197"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M187.939,573.116c-0.032,0.289,0.063,6.128,0.114,6.285"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M188.142,573.317c0.754,0.706,4.22,5.223,4.22,5.223c0.84,0.974,1.41,1.242,1.746,0.471c0.336-0.772,0.095-5.518-0.052-6.1"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M221.957,573.137c-0.025,0.289-0.083,5.298,0.127,6.243c0.098,0.443,4.852,0.053,4.852,0.053"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M229.437,573.137c-0.026,0.289-0.084,5.298,0.126,6.243c0.099,0.443,4.852,0.053,4.852,0.053"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M236.685,579.451c0,0,2.266-5.473,2.383-5.81c0.226-0.647,0.865-0.521,1.175,0.146c0.72,1.554,2.69,5.663,2.69,5.663"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M237.784,577.52c0.112,0,3.754-0.197,3.754-0.197"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M246.547,573.33c-0.037,0.289,0.046,5.972,0.096,6.13"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M243.602,573.171c0.114,0,5.807-0.138,5.968-0.138"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M253.857,573.058c0,0.132,0.079,6.191,0.131,6.349"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M253.099,579.458c0.236,0,1.943-0.053,2.101-0.079"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M252.808,573.006c0.184,0,2.205-0.079,2.311-0.053"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M267.533,573.116c-0.032,0.289,0.063,6.128,0.114,6.285"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M267.734,573.317c0.755,0.706,4.221,5.223,4.221,5.223c0.839,0.974,1.41,1.242,1.746,0.471c0.335-0.772,0.094-5.518-0.052-6.1"
+ />
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M287.092,579.426c-1.341-0.213-6.068-4.093-6.712-5.712c-0.884-2.223,2.733-3.604,3.218-1.164
+ c0.29,1.464-1.562,2.238-3.616,3.457c-2.398,1.422-1.419,4.546,2.679,3.645c3.166-0.697,1.754-4.472,1.754-4.472h1.853"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M170.76,519.86c-0.579,0.048-1.184,0.071-1.783,0.071c-7.215,0-13.071-3.474-14.462-8.757c-0.472-1.791,0.218-3.177,1.49-3.177
+ s1.962,1.386,1.49,3.177c-1.391,5.283-7.247,8.757-14.462,8.757c-7.216,0-13.072-3.474-14.463-8.757
+ c-0.471-1.791,0.219-3.177,1.49-3.177c1.273,0,1.989,1.394,1.49,3.177c-1.016,3.634-5.836,6.771-10.767,6.688
+ c-3.551-0.061-6.986-1.623-9.63-4.266c-2.644-2.644-4.205-6.079-4.266-9.631c-0.084-4.93,3.054-9.75,6.687-10.766
+ c1.784-0.499,3.177,0.217,3.177,1.489s-1.385,1.962-3.177,1.49c-5.282-1.39-8.756-7.247-8.756-14.462s3.474-13.071,8.756-14.462
+ c1.792-0.472,3.177,0.218,3.177,1.49s-1.385,1.961-3.177,1.49c-5.282-1.391-8.756-7.247-8.756-14.463"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M142.009,334.275c0-0.236-0.01-0.47,0.001-0.701"/>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M287.061,176.642c0.007-0.21-0.269-1.235-2.496-1.02c-1.896,0.184-2.684,2.223-2.656,3.322c0.026,1.101,0.644,2.765,3.005,2.765
+ c2.362,0,2.813-1.288,2.813-1.288"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.25,175.623c-0.025,0.276-0.08,5.053,0.12,5.955c0.094,0.422,4.627,0.05,4.627,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M299.021,175.477c-0.513,0.063-3.042,1.009-2.453,3.856c0.575,2.786,3.484,2.68,5.06,1.857c1.472-0.769,1.732-3.343,0.74-4.463
+ c-0.838-0.946-2.283-1.25-3.329-1.123"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M305.096,175.547c-0.132-0.021-0.197,2.408,0,4.006s1.532,2.188,2.889,2.167c1.357-0.021,2.539-0.767,2.648-2.189
+ c0.11-1.422-0.087-4.049-0.087-4.049"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M313.023,175.595c0.026,0.188,0.107,5.711,0.134,5.926"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M313.023,175.574c1.771-0.24,3.467-0.189,4.508,0.745c1.013,0.909,1.767,3.464-0.134,4.719
+ c-1.445,0.954-3.944,0.538-3.944,0.538"/>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M287.025,185.902c-0.044-0.285-0.7-0.964-2.451-0.941c-1.752,0.021-2.955,1.554-1.927,2.474c0.281,0.25,0.921,0.561,3.415,0.635
+ c2.211,0.065,2.079,1.685,1.532,2.276c-0.548,0.591-1.817,1.028-3.48,0.854c-1.188-0.125-1.752-0.77-1.708-1.032"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.434,185.212c-0.025,0.276,0,5.053,0.2,5.955c0.094,0.422,4.627,0.05,4.627,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.733,185.313c0.126-0.025,4.377-0.101,4.377-0.101"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.834,188.263c0.125-0.002,3.1-0.049,3.25-0.076"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M297.477,185.397c-0.036,0.274,0.044,5.694,0.092,5.846"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M297.604,185.246c0.458-0.1,2.439-0.21,3.68-0.004c1.921,0.317,2.125,2.445,0.232,3.025c-0.759,0.231-3.514,0.3-3.514,0.3"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="300.271" y1="188.484" x2="302.657" y2="191.259"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M304.586,185.18c0.284,1.335,2.232,5.406,2.407,5.778c0.154,0.327,0.534,0.671,0.832,0c0.351-0.788,2.43-5.538,2.298-5.757"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M311.894,185.212c-0.024,0.276,0,5.053,0.2,5.955c0.094,0.422,4.627,0.05,4.627,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M312.194,185.313c0.125-0.025,4.376-0.101,4.376-0.101"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M312.295,188.263c0.125-0.002,3.1-0.049,3.249-0.076"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M318.582,185.397c-0.035,0.274,0.045,5.694,0.092,5.846"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M318.711,185.246c0.457-0.1,2.438-0.21,3.68-0.004c1.921,0.317,2.125,2.445,0.231,3.025c-0.758,0.231-3.514,0.3-3.514,0.3"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="321.377" y1="188.484" x2="323.763" y2="191.259"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M330.578,185.902c-0.044-0.285-0.7-0.964-2.451-0.941c-1.752,0.021-2.955,1.554-1.927,2.474c0.281,0.25,0.921,0.561,3.415,0.635
+ c2.211,0.065,2.079,1.685,1.532,2.276c-0.548,0.591-1.817,1.028-3.48,0.854c-1.188-0.125-1.752-0.77-1.708-1.032"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M297.118,166.209c-0.134-0.021-0.201,2.45,0,4.076c0.2,1.625,1.559,2.227,2.939,2.204s2.583-0.779,2.694-2.227
+ c0.111-1.448-0.089-4.12-0.089-4.12"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M280.738,166.054c0.166,0.134,2.703,3.407,3.036,3.764s1.145-0.609,2.142-2.049c0.551-0.796,1.186-1.715,1.061-1.894"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.966,170.009c0.022,0.178-0.022,2.517,0.044,2.628"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.905,166.138c-0.521,0.065-3.095,1.027-2.496,3.924c0.586,2.835,3.545,2.726,5.147,1.89c1.498-0.782,1.763-3.4,0.753-4.541
+ c-0.853-0.963-2.322-1.271-3.387-1.143"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M305.581,166.476c-0.036,0.279,0.045,5.793,0.093,5.947"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M305.711,166.32c0.466-0.1,2.482-0.212,3.744-0.003c1.955,0.323,2.162,2.487,0.236,3.078c-0.771,0.235-3.575,0.306-3.575,0.306
+ "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="308.424" y1="169.616" x2="310.852" y2="172.439"/>
+ </g>
+ </g>
+ </g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="270.916" y1="20.499" x2="266.284" y2="23.742"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="277.156" y1="29.91" x2="272.525" y2="33.153"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="199.104" y1="18.975" x2="203.734" y2="22.218"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.592" y1="28.177" x2="197.224" y2="31.42"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M268.462,22.122c-2.813-2.027-6.186-3.048-9.408-2.704c-6.072,0.649,1.127,8.713,1.52,2.195
+ c0.496-8.252-14.525-18.188-25.846-10.369c-5.018,3.465,5.342,5.594,2.629,0.883c-4.212-7.318-23.923-6.011-27.688,6.089
+ c-2.3,7.39,7.587,1.138,1.728,0.328c-3.717-0.513-6.976,0.415-9.671,2.173"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M105.919,276.618c-0.702-1.979-1.1-4.222-1.1-6.613c0-7.215,3.474-13.071,8.756-14.462c1.792-0.472,3.177,0.218,3.177,1.49
+ s-1.385,1.961-3.177,1.49c-5.282-1.391-8.756-7.247-8.756-14.463c0-7.215,3.474-13.071,8.756-14.462
+ c1.792-0.472,3.177,0.218,3.177,1.49c0,1.273-1.385,1.962-3.177,1.49c-5.282-1.391-8.756-7.247-8.756-14.462
+ c0-7.216,3.474-13.072,8.756-14.463c1.792-0.471,3.177,0.219,3.177,1.491s-1.385,1.961-3.177,1.489
+ c-5.282-1.391-8.756-7.247-8.756-14.462s3.474-13.072,8.756-14.462c1.792-0.472,3.177,0.218,3.177,1.49s-1.385,1.961-3.177,1.489
+ c-5.282-1.39-8.756-7.247-8.756-14.462s3.474-13.071,8.756-14.462c1.792-0.472,3.177,0.218,3.177,1.49s-1.385,1.961-3.177,1.49
+ c-5.282-1.391-8.756-7.247-8.756-14.463c0-7.215,3.474-13.071,8.756-14.462c1.792-0.472,3.177,0.218,3.177,1.49
+ c0,1.273-1.385,1.962-3.177,1.49c-5.282-1.391-8.756-7.247-8.756-14.462c0-7.216,3.474-13.072,8.756-14.463
+ c1.792-0.471,3.177,0.218,3.177,1.491c0,1.272-1.393,1.988-3.177,1.489c-3.633-1.016-6.771-5.836-6.687-10.767
+ c0.061-3.551,1.623-6.986,4.266-9.63c2.644-2.644,6.079-4.205,9.63-4.266c4.931-0.084,9.751,3.054,10.767,6.687
+ c0.499,1.784-0.217,3.177-1.489,3.177s-1.962-1.385-1.491-3.177c1.391-5.282,7.247-8.756,14.463-8.756
+ c7.215,0,13.071,3.474,14.462,8.756c0.472,1.792-0.218,3.177-1.491,3.177c-1.272,0-1.961-1.385-1.489-3.177
+ c1.391-5.282,7.247-8.756,14.462-8.756s13.072,3.474,14.463,8.756c0.471,1.792-0.219,3.177-1.491,3.177s-1.961-1.385-1.489-3.177
+ c1.39-5.282,7.247-8.756,14.462-8.756"/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 260.856,275.668 260.856,377.859 274.189,377.859 274.189,383.82 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#328300" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 233.631,301.305 247.689,301.305 247.689,307.12 "/>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M194.72,114.128c-0.028,0.311,0,5.692,0.226,6.708c0.106,0.476,5.213,0.057,5.213,0.057"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M195.059,114.241c0.141-0.028,4.93-0.113,4.93-0.113"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M195.172,117.564c0.141-0.003,3.491-0.055,3.66-0.086"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M153.991,113.902c-0.035,0.311,0.169,6.849,0.226,7.018"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M154.086,118.523c0.072-0.252,3.896-2.272,5.267-4.581"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M156.214,117.045c1.587,1.37,3.752,3.499,3.788,3.932"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M170.306,114.338c-0.035,0.311,0.162,6.413,0.218,6.582"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M170.377,114.217c0.514-0.121,2.744-0.283,4.146-0.076c2.17,0.32,2.441,2.712,0.32,3.402c-0.849,0.277-3.952,0.407-3.952,0.407"
+ />
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M166.894,114.904c-0.05-0.32-0.789-1.085-2.762-1.06c-1.973,0.024-3.329,1.75-2.17,2.786c0.316,0.283,1.037,0.632,3.847,0.715
+ c2.49,0.074,2.343,1.899,1.727,2.564c-0.617,0.666-2.047,1.159-3.921,0.962c-1.339-0.141-1.974-0.866-1.925-1.162"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M150.964,115.275c0.009-0.236-0.302-1.393-2.812-1.149c-2.137,0.208-3.023,2.504-2.993,3.743s0.726,3.114,3.387,3.114
+ c2.66,0,3.168-1.451,3.168-1.451"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.831,120.911c0,0,2.435-5.879,2.56-6.24c0.242-0.696,0.93-0.561,1.263,0.157c0.772,1.669,2.891,6.083,2.891,6.083"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M138.012,118.837c0.121,0,4.034-0.212,4.034-0.212"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M128.016,114.336c-0.04,0.31,0.05,6.416,0.104,6.586"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M128.16,114.166c0.517-0.112,2.748-0.236,4.146-0.005c2.164,0.358,2.394,2.755,0.261,3.408
+ c-0.854,0.262-3.958,0.339-3.958,0.339"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="131.164" y1="117.814" x2="133.853" y2="120.94"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M191.528,115.275c0.009-0.236-0.302-1.393-2.812-1.149c-2.137,0.208-3.023,2.504-2.993,3.743s0.726,3.114,3.387,3.114
+ c2.66,0,3.168-1.451,3.168-1.451"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M177.289,120.911c0,0,2.435-5.879,2.561-6.24c0.242-0.696,0.929-0.561,1.262,0.157c0.773,1.669,2.891,6.083,2.891,6.083"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M178.471,118.837c0.121,0,4.033-0.212,4.033-0.212"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M146.102,102.063c-0.028,0.31,0,5.691,0.226,6.707c0.106,0.476,5.213,0.057,5.213,0.057"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M146.44,102.175c0.141-0.027,4.93-0.112,4.93-0.112"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M146.554,105.499c0.141-0.003,3.491-0.055,3.66-0.086"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.624,101.978c0,0.141,0.085,6.651,0.142,6.82"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M142.684,101.837c-0.035,0.311,0.169,6.849,0.226,7.018"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.844,105.64c0.507-0.058,5.332-0.029,5.867-0.171"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M130.077,102.271c-0.04,0.31,0.05,6.415,0.103,6.585"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M126.912,102.1c0.123,0,6.239-0.148,6.412-0.148"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M164.053,126.061c0.03,0.212,0.121,6.434,0.15,6.675"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M164.053,126.037c1.995-0.271,3.905-0.213,5.078,0.84c1.141,1.023,1.99,3.901-0.15,5.314c-1.629,1.076-4.444,0.607-4.444,0.607"
+ />
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M154.68,126.006c-0.148-0.024-0.223,2.713,0,4.513c0.222,1.801,1.726,2.466,3.255,2.441c1.528-0.024,2.86-0.863,2.983-2.466
+ s-0.099-4.562-0.099-4.562"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M133.737,127.239c0.009-0.236-0.302-1.392-2.812-1.148c-2.137,0.207-3.023,2.503-2.993,3.743c0.03,1.239,0.726,3.114,3.387,3.114
+ c2.66,0,3.168-1.451,3.168-1.451"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M147.163,125.927c-0.578,0.072-3.427,1.137-2.764,4.345c0.648,3.139,3.926,3.019,5.699,2.093
+ c1.658-0.866,1.951-3.766,0.834-5.028c-0.943-1.066-2.571-1.408-3.751-1.265"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="1.1227" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M137.388,126.093c-0.027,0.31-0.089,5.691,0.137,6.707c0.105,0.476,5.212,0.057,5.212,0.057"/>
+ </g>
+ <g>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M136.933,340.004c12.398,8.382,59.978,7.355,96.466,2.187
+ c38.499-5.454,74.381-2.59,96.465,2.187v18.229c-22.084-4.776-57.966-7.64-96.465-2.187c-36.488,5.169-84.067,6.196-96.466-2.187
+ V340.004z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.933,340.004c12.398,8.382,59.978,7.355,96.466,2.187c38.499-5.454,74.381-2.59,96.465,2.187"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.933,358.232c12.398,8.383,59.978,7.355,96.466,2.187c38.499-5.453,74.381-2.59,96.465,2.187"/>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M152.549,350.783c-0.075,0.295-0.715,6.227-0.684,6.396"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M152.709,350.635c0.514-0.047,2.692,0.098,4.021,0.49c2.055,0.604,1.993,2.955-0.152,3.336
+ c-0.859,0.15-3.879-0.145-3.879-0.145"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="155.188" y1="354.531" x2="157.422" y2="357.881"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M160.753,351.38c-0.043,0.301-0.291,5.553-0.123,6.555c0.079,0.47,5.082,0.322,5.082,0.322"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M161.077,351.507c0.139-0.02,4.816,0.143,4.816,0.143"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M161.018,354.755c0.138,0.005,3.41,0.126,3.575,0.104"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M168.298,351.941c0.026,0.206,0.01,6.205,0.034,6.438"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M168.299,351.92c1.928-0.227,3.769-0.14,4.883,0.896c1.082,1.006,1.853,3.793-0.235,5.12c-1.589,1.01-4.295,0.512-4.295,0.512"
+ />
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M176.797,351.999c-0.143-0.021-0.168,2.618,0.076,4.351c0.244,1.731,1.705,2.348,3.179,2.298s2.743-0.88,2.835-2.428
+ c0.092-1.547-0.172-4.396-0.172-4.396"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.275,352.055c-0.023,0.3,0.286,6.342,0.345,6.503"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.491,352.256c0.807,0.703,4.558,5.254,4.558,5.254c0.904,0.978,1.505,1.234,1.825,0.424
+ c0.319-0.813-0.102-5.716-0.274-6.313"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M194.354,351.762c0.04,0.203,0.44,6.188,0.482,6.42"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M194.353,351.739c1.908-0.36,3.75-0.402,4.932,0.553c1.15,0.928,2.113,3.655,0.123,5.126c-1.513,1.117-4.247,0.808-4.247,0.808
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M202.873,357.927c0,0,2.034-6.083,2.135-6.455c0.194-0.716,0.895-0.628,1.28,0.07c0.895,1.623,3.334,5.912,3.334,5.912"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M203.915,355.757c0.122-0.009,4.041-0.496,4.041-0.496"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M211.443,350.659c-0.008,0.3,0.614,6.288,0.682,6.444"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M211.668,350.848c0.837,0.657,4.803,4.984,4.803,4.984c0.949,0.926,1.559,1.149,1.834,0.326s-0.398-5.675-0.601-6.259"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M223.217,349.503c-0.002,0.305,0.813,6.219,0.885,6.378"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M220.126,349.714c0.12-0.015,6.036-0.887,6.204-0.907"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M240.944,353.486c-1.07-0.037-5.147-2.622-5.808-3.829c-0.906-1.656,1.794-3.086,2.408-1.221c0.369,1.117-1.007,1.903-2.5,3.056
+ c-1.742,1.344-0.674,3.697,2.449,2.597c2.413-0.851,0.944-3.671,0.944-3.671l1.451-0.178"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M252.883,346.862c-0.068-0.303-0.824-0.98-2.701-0.826c-1.876,0.155-3.053,1.889-1.88,2.798c0.32,0.248,1.03,0.531,3.71,0.425
+ c2.377-0.096,2.357,1.651,1.814,2.326s-1.871,1.24-3.669,1.177c-1.283-0.045-1.936-0.693-1.909-0.978"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M261.834,346.448c-0.004-0.232-0.368-1.346-2.811-0.98c-2.078,0.313-2.828,2.604-2.735,3.816c0.094,1.21,0.87,3.008,3.472,2.872
+ c2.603-0.137,3.025-1.582,3.025-1.582"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M264.914,351.854c0,0,2.349-5.966,2.47-6.333c0.231-0.705,0.927-0.58,1.273,0.137c0.808,1.668,3.02,6.079,3.02,6.079"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M266.066,349.743c0.123-0.002,4.061-0.283,4.061-0.283"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M274.275,344.873c-0.027,0.302-0.087,5.54,0.133,6.528c0.103,0.463,5.073,0.056,5.073,0.056"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M281.236,351.691c0,0,2.659-5.836,2.799-6.196c0.268-0.691,0.955-0.531,1.264,0.203c0.721,1.708,2.699,6.229,2.699,6.229"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M282.5,349.643c0.121,0.005,4.069-0.07,4.069-0.07"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.561,345.504c0.021,0.15,0.002,6.164-0.01,6.401"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M292.736,348.697c2.213,0.002,4.032,0.717,3.891,2.074c-0.123,1.188-1.027,1.488-2.064,1.463
+ c-1.422-0.033-3.902-0.146-3.902-0.146"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M290.692,345.481c0.509-0.092,2.954-0.112,4.302,0.226c1.521,0.381,1.354,2.294-0.086,2.748
+ c-0.873,0.276-2.848,0.272-4.25,0.229"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M299.572,345.818c-0.055,0.3-0.574,5.548-0.44,6.559c0.063,0.474,5.084,0.501,5.084,0.501"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M307.059,346.51c-0.064,0.299-0.68,5.536-0.582,6.551c0.047,0.476,5.064,0.678,5.064,0.678"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M307.375,346.66c0.141-0.01,4.809,0.479,4.809,0.479"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M307.088,349.905c0.137,0.016,3.402,0.364,3.57,0.354"/>
+ </g>
+ </g>
+ </g>
+ <polygon fill-rule="evenodd" clip-rule="evenodd" fill="#469714" points="233.052,76.718 237.572,101.516 235.052,98.435
+ 236.354,111.981 233.833,108.9 234.623,125.057 230.102,100.258 232.622,103.339 231.32,89.792 233.842,92.873 "/>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#469714" d="M312.368,19.321c-9.702-0.323-19.529,1.903-28.633,6.419
+ c0.937-1.196,1.885-2.358,2.842-3.483c-5.181,1.773-10.177,4.241-14.85,7.358c0.842-1.43,1.701-2.828,2.575-4.191
+ c-5.814,3.115-11.216,7.17-15.969,12.09c6.966-8.627,15.461-15.212,24.636-19.495c-0.946,1.169-1.881,2.372-2.804,3.61
+ c5.029-2.271,10.292-3.863,15.651-4.738c-0.998,0.922-1.991,1.88-2.979,2.874C299.245,18.584,305.834,18.412,312.368,19.321z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M240.508,94.985c4.947-1.606,9.045-5.067,10.646-9.945c1.515-4.615-8.781,0.842-3.892,1.774
+ c17.788,3.389,27.848-12.971,24.418-20.761c-2.954-6.714-6.994,2.939-2.269,1.692c11.463-3.026,11.984-24.989,3.231-27.759
+ c-5.169-1.636-1.185,7.724,0.763,3.339c1.832-4.127,2.142-8.02,1.419-11.419"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#469714" d="M158.148,17.019c9.701-0.322,19.529,1.903,28.633,6.419
+ c-0.938-1.196-1.885-2.358-2.842-3.483c5.18,1.774,10.176,4.241,14.85,7.359c-0.843-1.431-1.702-2.828-2.575-4.192
+ c5.814,3.115,11.216,7.17,15.968,12.091c-6.965-8.628-15.46-15.212-24.635-19.495c0.945,1.168,1.881,2.371,2.803,3.61
+ c-5.028-2.271-10.291-3.863-15.65-4.738c0.997,0.922,1.991,1.879,2.979,2.873C171.271,16.281,164.683,16.109,158.148,17.019z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M194.586,29.793c-1.68,4.149-1.955,8.468-0.434,11.115c2.767,4.819,7.796-4.782,0.8-2.016c-9.006,3.561-7.469,25.887,2.431,29.33
+ c5.897,2.051,0.289-7.707-0.934-3.233c-3.635,13.299,10.338,25.978,23.577,22.757c7.198-1.751-4.212-6.455-2.089-1.724
+ c1.919,4.28,5.129,7.082,8.83,8.596"/>
+ <g>
+ <g>
+ <path fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M197.976,188.389c0.605-0.573,1.422-0.925,2.321-0.925c1.077,0,2.036,0.505,2.654,1.289c0.605-0.573,1.424-0.925,2.323-0.925
+ c1.076,0,2.036,0.504,2.654,1.289c0.605-0.573,1.423-0.925,2.321-0.925c1.077,0,2.036,0.504,2.655,1.288
+ c0.604-0.572,1.423-0.924,2.321-0.924c1.077,0,2.036,0.504,2.655,1.288c0.604-0.573,1.422-0.925,2.321-0.925
+ c1.077,0,2.035,0.504,2.654,1.288c0.605-0.572,1.422-0.924,2.321-0.924c1.077,0,2.036,0.504,2.654,1.288
+ c0.605-0.572,1.423-0.924,2.322-0.924c1.077,0,2.036,0.504,2.654,1.288c0.605-0.573,1.423-0.924,2.322-0.924
+ c1.077,0,2.036,0.503,2.654,1.288c0.605-0.573,1.423-0.925,2.322-0.925c1.076,0,2.035,0.504,2.654,1.288
+ c0.604-0.572,1.422-0.924,2.321-0.924c1.076,0,2.035,0.504,2.654,1.288c0.605-0.573,1.423-0.924,2.322-0.924
+ c0.655,0,1.267,0.187,1.785,0.51c0.269-1.598,1.658-2.815,3.331-2.815c0.656,0,1.268,0.188,1.786,0.511
+ c0.268-1.598,1.657-2.815,3.331-2.815c0.655,0,1.268,0.187,1.785,0.51c0.269-1.598,1.658-2.814,3.332-2.814
+ c0.655,0,1.268,0.187,1.785,0.51"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.301" y1="194.256" x2="247.52" y2="194.997"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="261.29" y1="190.762" x2="268.824" y2="187.969"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="261.29" y1="193.582" x2="268.824" y2="190.553"/>
+
+ <polygon fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 194.771,190.678 194.959,196.426 232.438,199.129 232.438,193.122 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="212.907" y1="191.998" x2="212.907" y2="197.762"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.354" y1="191.651" x2="208.354" y2="197.414"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M212.609,193.103c-1.357-0.013-2.093,0.47-2.149,1.548s1.075,1.58,2.149,1.688"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="227.826" y1="193.041" x2="227.826" y2="198.805"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M232.081,194.493c-1.357-0.014-2.093,0.469-2.149,1.547c-0.057,1.079,1.075,1.58,2.149,1.689"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M242.409,197.31c-0.489,0.489,0.908,0.839,0.698,0.21C242.898,196.891,242.409,197.31,242.409,197.31z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M246.584,197.59c-0.488,0.488,0.908,0.838,0.699,0.209C247.073,197.17,246.584,197.59,246.584,197.59z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M250.426,194.437c1.352,2.028,1.352,4.156,0,6.384"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="205.943" y1="185.393" x2="218.52" y2="181.304"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="209.635" y1="185.672" x2="222.907" y2="181.195"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="242.857" y1="188.192" x2="258.547" y2="181.49"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="246.548" y1="188.473" x2="262.053" y2="181.727"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="250.239" y1="188.753" x2="266.552" y2="181.528"/>
+ <path fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M256.55,181.264c0.325,0.225,0.589,0.505,0.817,0.827c0.619-0.712,1.532-1.163,2.551-1.163c1.137,0,2.143,0.563,2.755,1.424
+ c0.619-0.712,1.532-1.162,2.55-1.162c1.138,0,2.144,0.562,2.756,1.422c0.619-0.712,1.531-1.161,2.55-1.161
+ c1.865,0,3.378,1.512,3.378,3.378c0,0.164-0.012,0.325-0.034,0.482c0.896,0.607,1.483,1.634,1.483,2.797
+ c0,1.153-0.578,2.171-1.459,2.78l0.01,0.263c0,1.865-1.513,3.378-3.378,3.378c-0.655,0-1.268-0.187-1.786-0.51
+ c-0.268,1.598-1.657,2.814-3.331,2.814c-0.655,0-1.268-0.187-1.785-0.51c-0.269,1.598-1.658,2.815-3.332,2.815
+ c-0.655,0-1.268-0.187-1.785-0.51c-0.269,1.598-1.658,2.814-3.332,2.814c-0.655,0-1.267-0.187-1.785-0.51
+ c-0.269,1.598-1.657,2.814-3.331,2.814c-1.077,0-2.036-0.504-2.654-1.288c-0.605,0.573-1.423,0.924-2.322,0.924
+ c-1.077,0-2.035-0.503-2.654-1.287c-0.605,0.572-1.422,0.924-2.321,0.924c-1.077,0-2.036-0.504-2.654-1.289
+ c-0.605,0.573-1.423,0.925-2.322,0.925c-1.077,0-2.036-0.504-2.654-1.288c-0.605,0.572-1.423,0.924-2.322,0.924
+ c-1.077,0-2.036-0.504-2.654-1.288c-0.605,0.573-1.423,0.925-2.322,0.925c-1.076,0-2.035-0.504-2.653-1.288
+ c-0.605,0.572-1.423,0.924-2.322,0.924c-1.076,0-2.035-0.504-2.654-1.288c-0.605,0.572-1.423,0.924-2.322,0.924
+ c-1.076,0-2.035-0.504-2.654-1.288c-0.605,0.573-1.422,0.924-2.322,0.924c-1.076,0-2.035-0.503-2.654-1.288
+ c-0.605,0.573-1.422,0.925-2.321,0.925c-1.077,0-2.036-0.504-2.655-1.289c-0.605,0.573-1.423,0.925-2.322,0.925
+ c-1.076,0-2.035-0.504-2.654-1.288c-0.605,0.573-1.422,0.924-2.321,0.924c-1.866,0-3.378-1.513-3.378-3.378
+ c0-0.173,0.013-0.343,0.037-0.508c-1.214-0.513-2.066-1.714-2.066-3.114c0-1.416,0.872-2.629,2.107-3.131
+ c-0.052-0.234-0.078-0.479-0.078-0.729c0-1.865,1.512-3.378,3.378-3.378c0.872,0,1.666,0.33,2.266,0.872
+ c0.373-1.46,1.697-2.538,3.272-2.538c0.872,0,1.666,0.33,2.266,0.872c0.373-1.46,1.697-2.539,3.273-2.539
+ c0.871,0,1.666,0.33,2.265,0.872c0.213-0.829,0.732-1.536,1.431-1.991"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="253.07" y1="182.098" x2="254.543" y2="181.472"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="239.207" y1="187.913" x2="249.105" y2="183.752"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.495" y1="187.633" x2="245.652" y2="183.458"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="231.804" y1="187.353" x2="243.035" y2="182.84"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.112" y1="187.072" x2="238.771" y2="182.89"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="224.421" y1="186.792" x2="234.93" y2="182.772"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="220.73" y1="186.513" x2="232.91" y2="181.98"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="217.039" y1="186.232" x2="228.607" y2="182.053"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="213.348" y1="185.952" x2="224.622" y2="182.006"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.4972" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="209.349" y1="182.896" x2="214.295" y2="181.277"/>
+ </g>
+ <g>
+ <path fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M253.392,181.045c-0.269,1.598-1.657,2.815-3.331,2.815c-1.077,0-2.036-0.504-2.654-1.289c-0.605,0.573-1.423,0.925-2.322,0.925
+ c-1.077,0-2.035-0.504-2.654-1.288c-0.605,0.573-1.422,0.924-2.321,0.924c-1.077,0-2.036-0.504-2.654-1.288
+ c-0.605,0.573-1.423,0.924-2.322,0.924c-1.077,0-2.036-0.503-2.654-1.288c-0.605,0.573-1.423,0.925-2.322,0.925
+ c-1.077,0-2.036-0.504-2.654-1.289c-0.605,0.573-1.423,0.925-2.322,0.925c-1.076,0-2.035-0.504-2.653-1.288
+ c-0.605,0.573-1.423,0.924-2.322,0.924c-1.076,0-2.035-0.504-2.654-1.288c-0.605,0.573-1.423,0.924-2.322,0.924
+ c-1.076,0-2.035-0.503-2.654-1.288c-0.605,0.573-1.422,0.925-2.322,0.925c-1.076,0-2.035-0.504-2.654-1.289
+ c-0.605,0.573-1.422,0.925-2.321,0.925c-1.077,0-2.036-0.504-2.655-1.289c-0.605,0.573-1.423,0.925-2.322,0.925
+ c-1.076,0-2.035-0.503-2.654-1.288c-0.605,0.573-1.422,0.925-2.321,0.925c-1.866,0-3.378-1.513-3.378-3.379
+ c0-0.173,0.013-0.342,0.037-0.508c-1.214-0.512-2.066-1.714-2.066-3.114c0-1.416,0.872-2.629,2.107-3.131
+ c-0.052-0.234-0.078-0.479-0.078-0.729c0-1.865,1.512-3.378,3.378-3.378c0.872,0,1.666,0.33,2.266,0.872
+ c0.373-1.459,1.697-2.538,3.272-2.538c0.872,0,1.666,0.33,2.266,0.872c0.373-1.459,1.697-2.538,3.273-2.538
+ c0.871,0,1.666,0.33,2.265,0.872c0.374-1.46,1.697-2.538,3.273-2.538c0.872,0,1.666,0.33,2.266,0.872
+ c0.373-1.46,1.697-2.539,3.272-2.539c1.138,0,2.144,0.563,2.756,1.424c0.619-0.712,1.532-1.163,2.55-1.163
+ c1.138,0,2.144,0.563,2.755,1.423c0.62-0.712,1.532-1.161,2.55-1.161c1.138,0,2.144,0.562,2.756,1.423
+ c0.619-0.712,1.532-1.162,2.55-1.162c1.138,0,2.143,0.562,2.756,1.422c0.619-0.712,1.531-1.161,2.549-1.161
+ c1.138,0,2.144,0.562,2.756,1.423c0.619-0.712,1.532-1.162,2.551-1.162c1.137,0,2.143,0.562,2.755,1.423
+ c0.619-0.712,1.532-1.162,2.55-1.162c1.138,0,2.144,0.562,2.756,1.423c0.619-0.712,1.531-1.162,2.55-1.162
+ c1.137,0,2.143,0.562,2.755,1.423c0.619-0.712,1.532-1.162,2.551-1.162c1.137,0,2.143,0.562,2.755,1.423
+ c0.619-0.712,1.532-1.162,2.55-1.162c1.138,0,2.144,0.562,2.756,1.423c0.619-0.712,1.531-1.162,2.55-1.162
+ c1.865,0,3.378,1.513,3.378,3.378c0,0.164-0.012,0.325-0.034,0.482c0.896,0.608,1.483,1.634,1.483,2.797
+ c0,1.153-0.578,2.171-1.459,2.781l0.01,0.262c0,1.866-1.513,3.379-3.378,3.379c-0.655,0-1.268-0.188-1.786-0.51
+ c-0.268,1.597-1.657,2.814-3.331,2.814c-0.655,0-1.268-0.187-1.785-0.51c-0.269,1.598-1.658,2.814-3.332,2.814
+ c-0.655,0-1.268-0.187-1.785-0.51c-0.269,1.598-1.658,2.814-3.332,2.814C254.521,181.555,253.91,181.368,253.392,181.045z"/>
+ <path fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M197.976,168.5c0.605-0.573,1.422-0.924,2.321-0.924c1.077,0,2.036,0.504,2.654,1.289c0.605-0.573,1.424-0.925,2.323-0.925
+ c1.076,0,2.036,0.503,2.654,1.288c0.605-0.573,1.423-0.925,2.321-0.925c1.077,0,2.036,0.504,2.655,1.289
+ c0.604-0.573,1.423-0.925,2.321-0.925c1.077,0,2.036,0.504,2.655,1.288c0.604-0.572,1.422-0.924,2.321-0.924
+ c1.077,0,2.035,0.503,2.654,1.288c0.605-0.573,1.422-0.925,2.321-0.925c1.077,0,2.036,0.504,2.654,1.289
+ c0.605-0.573,1.423-0.925,2.322-0.925c1.077,0,2.036,0.504,2.654,1.288c0.605-0.572,1.423-0.924,2.322-0.924
+ c1.077,0,2.036,0.504,2.654,1.288c0.605-0.573,1.423-0.924,2.322-0.924c1.076,0,2.035,0.503,2.654,1.288
+ c0.604-0.573,1.422-0.925,2.321-0.925c1.076,0,2.035,0.504,2.654,1.289c0.605-0.573,1.423-0.925,2.322-0.925
+ c0.655,0,1.267,0.187,1.785,0.51c0.269-1.598,1.658-2.814,3.331-2.814c0.656,0,1.268,0.187,1.786,0.51
+ c0.268-1.598,1.657-2.814,3.331-2.814c0.655,0,1.268,0.187,1.785,0.51c0.269-1.598,1.658-2.815,3.332-2.815
+ c0.655,0,1.268,0.188,1.785,0.51"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.301" y1="174.367" x2="247.52" y2="175.109"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="261.29" y1="170.873" x2="268.824" y2="168.081"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="261.29" y1="173.694" x2="268.824" y2="170.664"/>
+
+ <polygon fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 194.771,170.789 194.959,176.537 232.438,179.24 232.438,173.234 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="212.907" y1="172.109" x2="212.907" y2="177.873"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.354" y1="171.763" x2="208.354" y2="177.526"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M212.609,173.215c-1.357-0.014-2.093,0.469-2.149,1.548c-0.057,1.078,1.075,1.579,2.149,1.688"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="227.826" y1="173.152" x2="227.826" y2="178.916"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M232.081,174.604c-1.357-0.014-2.093,0.47-2.149,1.548s1.075,1.58,2.149,1.688"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M242.409,177.422c-0.489,0.488,0.908,0.838,0.698,0.209C242.898,177.003,242.409,177.422,242.409,177.422z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.7458" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M246.584,177.701c-0.488,0.489,0.908,0.838,0.699,0.209C247.073,177.282,246.584,177.701,246.584,177.701z"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M250.426,174.548c1.352,2.028,1.352,4.156,0,6.384"/>
+ </g>
+ </g>
+ <g>
+
+ <rect x="213.674" y="127.035" fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#555555" stroke-width="0.9155" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" width="39.592" height="38.122"/>
+ <g>
+ <path fill="none" stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M237.71,134.487l0.019,0.006l0.019,0.007c0.195,0.06,0.343,0.028,0.495-0.186c0.434-0.612,1.242-1.752,1.352-1.907
+ c0.15-0.214,0.833-0.063,1.57,0.361c0.737,0.426,1.209,0.942,1.099,1.18c-0.08,0.172-0.663,1.442-0.976,2.124
+ c-0.118,0.257-0.055,0.404,0.116,0.548c0.555,0.466,1.076,0.987,1.548,1.552c0.139,0.162,0.286,0.225,0.542,0.106
+ c0.683-0.313,1.952-0.896,2.124-0.975c0.237-0.11,0.754,0.36,1.18,1.098s0.575,1.42,0.361,1.571
+ c-0.155,0.108-1.295,0.917-1.906,1.351c-0.215,0.152-0.246,0.3-0.188,0.489c0.255,0.689,0.45,1.409,0.58,2.15
+ c0.038,0.221,0.135,0.349,0.416,0.375c0.746,0.07,2.138,0.2,2.326,0.217c0.261,0.023,0.473,0.69,0.473,1.541
+ c0,0.852-0.212,1.518-0.473,1.541c-0.188,0.017-1.58,0.147-2.326,0.217c-0.281,0.026-0.378,0.155-0.416,0.375
+ c-0.127,0.724-0.317,1.43-0.565,2.107l-0.006,0.02l-0.006,0.018c-0.061,0.195-0.029,0.344,0.186,0.495
+ c0.611,0.435,1.751,1.242,1.906,1.352c0.214,0.15,0.064,0.834-0.361,1.57c-0.426,0.737-0.942,1.209-1.18,1.099
+ c-0.172-0.08-1.441-0.663-2.124-0.976c-0.256-0.118-0.403-0.055-0.547,0.116c-0.467,0.555-0.988,1.076-1.552,1.548
+ c-0.162,0.139-0.226,0.286-0.107,0.543c0.313,0.682,0.896,1.951,0.976,2.123c0.11,0.237-0.361,0.754-1.099,1.18
+ s-1.42,0.575-1.57,0.361c-0.109-0.155-0.918-1.295-1.352-1.906c-0.152-0.214-0.3-0.246-0.488-0.188
+ c-0.69,0.256-1.409,0.451-2.151,0.58c-0.22,0.038-0.349,0.135-0.375,0.416c-0.069,0.747-0.2,2.138-0.217,2.326
+ c-0.023,0.262-0.69,0.473-1.541,0.473s-1.518-0.211-1.541-0.473c-0.017-0.188-0.147-1.579-0.217-2.326
+ c-0.026-0.281-0.154-0.378-0.375-0.416c-0.742-0.129-1.461-0.324-2.151-0.58c-0.188-0.058-0.337-0.025-0.488,0.188
+ c-0.434,0.611-1.242,1.751-1.352,1.906c-0.15,0.214-0.833,0.064-1.57-0.361c-0.737-0.425-1.209-0.941-1.099-1.18
+ c0.08-0.172,0.663-1.441,0.976-2.123c0.118-0.257,0.055-0.404-0.116-0.548c-0.555-0.467-1.076-0.988-1.548-1.552
+ c-0.139-0.162-0.286-0.226-0.542-0.107c-0.683,0.313-1.952,0.896-2.124,0.976c-0.237,0.11-0.754-0.361-1.18-1.099
+ c-0.426-0.736-0.575-1.42-0.361-1.57c0.155-0.109,1.295-0.918,1.906-1.352c0.215-0.151,0.246-0.3,0.188-0.488
+ c-0.255-0.69-0.45-1.409-0.58-2.151c-0.038-0.22-0.135-0.349-0.416-0.375c-0.746-0.069-2.138-0.2-2.326-0.217
+ c-0.261-0.023-0.473-0.689-0.473-1.541c0-0.851,0.212-1.518,0.473-1.541c0.188-0.017,1.58-0.146,2.326-0.217
+ c0.281-0.026,0.378-0.154,0.416-0.375c0.127-0.724,0.317-1.429,0.564-2.106l0.007-0.019l0.006-0.017
+ c0.061-0.197,0.029-0.346-0.186-0.498c-0.611-0.434-1.751-1.242-1.906-1.351c-0.214-0.151-0.064-0.834,0.361-1.571
+ s0.942-1.208,1.18-1.098c0.172,0.079,1.441,0.662,2.123,0.975c0.257,0.118,0.404,0.056,0.548-0.116
+ c0.467-0.555,0.988-1.076,1.552-1.548c0.162-0.138,0.226-0.285,0.107-0.542c-0.313-0.682-0.896-1.952-0.975-2.124
+ c-0.111-0.237,0.36-0.754,1.098-1.18c0.737-0.425,1.42-0.575,1.57-0.361c0.109,0.155,0.918,1.295,1.352,1.907
+ c0.152,0.214,0.3,0.245,0.488,0.188c0.69-0.255,1.41-0.45,2.151-0.579c0.221-0.039,0.349-0.136,0.375-0.416
+ c0.069-0.747,0.2-2.139,0.217-2.327c0.023-0.261,0.69-0.473,1.541-0.473s1.518,0.212,1.541,0.473
+ c0.017,0.188,0.147,1.58,0.217,2.327c0.026,0.28,0.155,0.377,0.375,0.416C236.326,134.049,237.031,134.239,237.71,134.487z
+ M226.001,146.096c0,4.125,3.344,7.469,7.469,7.469c4.124,0,7.468-3.344,7.468-7.469s-3.344-7.468-7.468-7.468
+ C229.345,138.628,226.001,141.971,226.001,146.096z"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="222.028" y1="137.696" x2="219.758" y2="139.778"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="227.459" y1="132.691" x2="225.102" y2="134.85"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.378" y1="133.866" x2="225.74" y2="136.287"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="223.549" y1="138.346" x2="220.906" y2="140.766"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="218.981" y1="144.569" x2="218.125" y2="145.346"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="230.207" y1="134.201" x2="221.976" y2="141.78"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.955" y1="130.754" x2="231.903" y2="132.627"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.062" y1="131.741" x2="218.254" y2="147.241"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.244" y1="133.593" x2="219.89" y2="147.753"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="236.831" y1="134.154" x2="231.747" y2="138.834"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.392" y1="143.811" x2="221.312" y2="148.476"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M221.785,150.063"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.004" y1="146.169" x2="221.771" y2="150.063"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="240.71" y1="132.614" x2="234.122" y2="138.669"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="241.954" y1="133.471" x2="235.924" y2="139.024"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.207" y1="148.024" x2="220.085" y2="153.646"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.77" y1="149.51" x2="221.023" y2="154.802"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="241.17" y1="136.233" x2="237.37" y2="139.72"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="242.155" y1="137.333" x2="238.586" y2="140.614"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="227.635" y1="150.752" x2="223.998" y2="154.087"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.641" y1="151.829" x2="225.059" y2="155.122"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="243.266" y1="138.346" x2="239.539" y2="141.763"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="246.221" y1="137.626" x2="240.309" y2="143.07"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="229.876" y1="152.73" x2="225.25" y2="156.976"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="231.414" y1="153.316" x2="225.203" y2="159.037"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="247.025" y1="138.926" x2="240.74" y2="144.701"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="245.25" y1="142.569" x2="240.955" y2="146.521"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.357" y1="153.565" x2="226.534" y2="159.834"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="236.008" y1="153.121" x2="230.594" y2="158.105"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="245.734" y1="144.157" x2="240.338" y2="149.115"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="247.493" y1="144.546" x2="231.676" y2="159.133"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="245.517" y1="148.391" x2="231.958" y2="160.894"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="248.71" y1="145.453" x2="246.128" y2="147.814"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="245.12" y1="150.786" x2="237.556" y2="157.754"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.078" y1="160.074" x2="233.551" y2="161.465"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="241.34" y1="156.291" x2="238.771" y2="158.648"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="246.344" y1="151.68" x2="243.753" y2="154.054"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="242" y1="157.705" x2="239.637" y2="159.873"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="247.035" y1="153.071" x2="245.253" y2="154.696"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M163.622,208.423c-0.021,0.239,0,4.401,0.175,5.187c0.082,0.368,4.031,0.044,4.031,0.044"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M163.885,208.51c0.108-0.021,3.813-0.087,3.813-0.087"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M163.972,211.08c0.109-0.002,2.7-0.043,2.831-0.066"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M184.327,208.357c0,0.108,0.065,5.144,0.108,5.274"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M183.696,213.674c0.196,0,1.614-0.043,1.745-0.064"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M183.454,208.313c0.153,0,1.833-0.065,1.92-0.044"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M178.925,208.583c-0.031,0.24,0.039,4.962,0.08,5.093"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M176.478,208.451c0.096,0,4.825-0.114,4.958-0.114"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M137.373,209.31c0.007-0.183-0.233-1.077-2.174-0.889c-1.652,0.161-2.338,1.937-2.314,2.895c0.023,0.959,0.561,2.408,2.618,2.408
+ s2.45-1.122,2.45-1.122"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.675,208.294c-0.446,0.057-2.649,0.88-2.137,3.36c0.501,2.428,3.036,2.334,4.407,1.618c1.283-0.67,1.51-2.912,0.645-3.889
+ c-0.729-0.824-1.988-1.089-2.899-0.978"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M147.157,208.405c-0.027,0.24,0.052,5.092,0.095,5.223"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M147.324,208.573c0.627,0.586,3.507,4.339,3.507,4.339c0.696,0.809,1.171,1.032,1.45,0.391s0.078-4.584-0.043-5.067"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M155.527,208.405c-0.027,0.24,0.051,5.092,0.095,5.223"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M155.694,208.573c0.627,0.586,3.506,4.339,3.506,4.339c0.697,0.809,1.172,1.032,1.45,0.391c0.279-0.642,0.079-4.584-0.042-5.067
+ "/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M173.988,209.31c0.006-0.183-0.234-1.077-2.174-0.889c-1.652,0.161-2.339,1.937-2.314,2.895c0.022,0.959,0.561,2.408,2.618,2.408
+ s2.45-1.122,2.45-1.122"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M190.145,208.294c-0.446,0.057-2.649,0.88-2.137,3.36c0.501,2.428,3.035,2.334,4.407,1.618c1.282-0.67,1.509-2.912,0.645-3.889
+ c-0.729-0.824-1.988-1.089-2.899-0.978"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M195.627,208.405c-0.027,0.24,0.051,5.092,0.095,5.223"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M195.794,208.573c0.627,0.586,3.507,4.339,3.507,4.339c0.696,0.809,1.171,1.032,1.45,0.391c0.278-0.642,0.078-4.584-0.043-5.067
+ "/>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M203.806,210.605c9.499-1.713,15.786,2.784,19.65,6.336c0,0-2.932-0.221-3.841,0.582"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M223.472,216.787c-0.745-1.351-0.516-3.583-0.516-3.583"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.978,387.165c0,4.907,2.362,8.891,5.956,9.836c1.219,0.321,2.16-0.147,2.16-1.013s-0.941-1.335-2.16-1.014
+ c-3.594,0.945-5.956,4.929-5.956,9.836s2.362,8.891,5.956,9.836c1.219,0.321,2.16-0.147,2.16-1.013s-0.941-1.335-2.16-1.014
+ c-3.594,0.945-5.956,4.929-5.956,9.836"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.978,422.456c0,4.907,2.362,8.891,5.956,9.837c1.219,0.32,2.16-0.148,2.16-1.014s-0.941-1.334-2.16-1.014
+ c-3.594,0.945-5.956,4.929-5.956,9.836"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.33,404.811c0-4.907-2.362-8.891-5.955-9.836c-1.219-0.321-2.161,0.147-2.161,1.013c0,0.866,0.942,1.335,2.161,1.014
+ c3.593-0.945,5.955-4.929,5.955-9.836"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.865" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.33,440.102c0-4.907-2.362-8.891-5.955-9.836c-1.219-0.32-2.161,0.147-2.161,1.013c0,0.866,0.942,1.335,2.161,1.015
+ c3.593-0.946,5.955-4.93,5.955-9.837s-2.362-8.891-5.955-9.836c-1.219-0.321-2.161,0.147-2.161,1.013
+ c0,0.866,0.942,1.335,2.161,1.014c3.593-0.945,5.955-4.929,5.955-9.836"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="144.818" y1="424.275" x2="142.723" y2="426.692"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="148.949" y1="424.275" x2="144.386" y2="429.562"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="153.081" y1="424.275" x2="147.979" y2="430.187"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="146.603" y1="431.842" x2="142.42" y2="436.682"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="157.213" y1="424.275" x2="142.017" y2="441.92"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="161.344" y1="424.275" x2="143.062" y2="445.507"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="165.476" y1="424.275" x2="144.938" y2="448.129"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="169.606" y1="424.275" x2="149.276" y2="447.888"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="147.58" y1="449.914" x2="142.088" y2="456.271"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="173.738" y1="424.275" x2="142.248" y2="460.86"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="177.869" y1="424.275" x2="143.54" y2="464.16"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="182.001" y1="424.275" x2="145.635" y2="466.526"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="148.395" y1="468.174" x2="143.409" y2="473.939"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="176.16" y1="479.087" x2="173.103" y2="482.621"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.868" y1="478.874" x2="189.579" y2="482.679"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="271.137" y1="479.14" x2="268.152" y2="482.589"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="323.178" y1="471.476" x2="314.319" y2="481.751"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="321.861" y1="468.205" x2="310.945" y2="480.871"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="317.864" y1="468.048" x2="309.695" y2="477.521"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="320.2" y1="465.334" x2="324.815" y2="459.781"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="324.83" y1="455.153" x2="300.841" y2="483.011"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="323.577" y1="451.809" x2="297.045" y2="482.621"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="321.321" y1="449.63" x2="293.868" y2="481.512"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="316.503" y1="450.428" x2="291.402" y2="479.576"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="324.702" y1="440.912" x2="319.21" y2="447.267"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="324.279" y1="436.592" x2="284.666" y2="482.604"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="322.693" y1="433.635" x2="280.361" y2="482.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="319.016" y1="433.106" x2="276.87" y2="482.061"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="324.623" y1="426.616" x2="321.48" y2="430.23"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="186.148" y1="424.275" x2="178.083" y2="433.627"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="173.947" y1="438.466" x2="150.09" y2="466.15"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="173.957" y1="443.24" x2="144.753" y2="477.156"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="173.986" y1="448.008" x2="146.773" y2="479.61"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="194.427" y1="424.275" x2="186.878" y2="433.021"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="190.295" y1="424.275" x2="182.432" y2="433.385"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="174.14" y1="452.63" x2="149.361" y2="481.403"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="174.045" y1="457.542" x2="152.845" y2="482.157"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="174.027" y1="462.361" x2="160.209" y2="478.399"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.149" y1="465.859" x2="161.299" y2="481.935"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="177.291" y1="468.171" x2="164.449" y2="483.075"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.918" y1="469.92" x2="168.181" y2="483.54"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="184.328" y1="469.602" x2="178.002" y2="476.93"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="223.378" y1="424.275" x2="209.711" y2="440.12"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="219.215" y1="424.275" x2="208.9" y2="436.238"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="215.084" y1="424.275" x2="206.804" y2="433.874"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="210.952" y1="424.275" x2="204.427" y2="431.836"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="206.82" y1="424.275" x2="200.292" y2="431.84"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.689" y1="424.275" x2="195.794" y2="432.265"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.558" y1="424.275" x2="191.496" y2="432.458"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="227.462" y1="424.275" x2="209.886" y2="444.681"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="210.05" y1="463.729" x2="206.325" y2="468.033"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="206.341" y1="468.033" x2="195.547" y2="480.544"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.17" y1="468.08" x2="195.718" y2="475.546"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="197.625" y1="468.556" x2="184.816" y2="483.412"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.974" y1="469.158" x2="181.331" y2="482.661"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="188.706" y1="469.315" x2="178.444" y2="481.214"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="197.307" y1="454.473" x2="196.219" y2="455.717"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="188.438" y1="445.591" x2="186.327" y2="448.02"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.854" y1="445.265" x2="186.559" y2="452.547"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="197.081" y1="445.153" x2="187.586" y2="456.152"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="197.329" y1="449.691" x2="192.078" y2="455.762"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="231.609" y1="424.275" x2="216.496" y2="441.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="216.512" y1="441.805" x2="210.022" y2="449.322"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="235.74" y1="424.275" x2="220.225" y2="442.275"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="239.903" y1="424.275" x2="224.101" y2="442.598"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="244.004" y1="424.275" x2="228.77" y2="441.95"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="248.119" y1="424.275" x2="232.752" y2="442.113"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="252.251" y1="424.275" x2="234.324" y2="445.087"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="256.382" y1="424.275" x2="238.939" y2="444.525"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="260.514" y1="424.275" x2="245.106" y2="442.16"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="264.645" y1="424.275" x2="249.254" y2="442.142"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="268.776" y1="424.275" x2="253.557" y2="441.942"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="272.907" y1="424.275" x2="257.382" y2="442.297"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="277.039" y1="424.275" x2="261.754" y2="442.018"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="281.17" y1="424.275" x2="265.806" y2="442.11"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="285.302" y1="424.275" x2="270.14" y2="441.875"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="289.434" y1="424.275" x2="273.409" y2="442.876"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="293.564" y1="424.275" x2="274.628" y2="446.261"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="270.891" y1="450.656" x2="268.961" y2="452.877"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="266.747" y1="450.667" x2="265.409" y2="452.202"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="262.344" y1="450.982" x2="261.208" y2="452.283"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="258.545" y1="450.597" x2="257.062" y2="452.299"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="258.123" y1="446.288" x2="256.852" y2="447.746"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="255.061" y1="449.877" x2="251.815" y2="453.62"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="250.266" y1="450.616" x2="246.749" y2="454.679"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="245.815" y1="450.987" x2="244.645" y2="452.329"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="242.547" y1="449.985" x2="240.371" y2="452.492"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="237.9" y1="450.583" x2="235.768" y2="453.041"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.496" y1="450.9" x2="232.113" y2="452.488"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="231.895" y1="447.964" x2="226.948" y2="453.688"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="225.448" y1="450.65" x2="223.899" y2="452.431"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="222.711" y1="449.017" x2="210.019" y2="463.729"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="219.402" y1="448.098" x2="218.192" y2="449.479"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="218.224" y1="449.479" x2="215.744" y2="452.325"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="215.76" y1="452.325" x2="210.244" y2="458.679"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="215.121" y1="448.229" x2="210.091" y2="454.044"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="266.031" y1="446.708" x2="264.499" y2="448.459"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="218.101" y1="459.193" x2="197.919" y2="482.589"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="220.559" y1="461.136" x2="201.649" y2="483.055"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="225.78" y1="459.871" x2="205.907" y2="482.908"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.699" y1="461.274" x2="212.761" y2="479.743"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="234.778" y1="459.017" x2="215.15" y2="481.768"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="236.838" y1="461.421" x2="218.285" y2="482.927"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="241.038" y1="461.337" x2="227.777" y2="476.696"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="246.504" y1="459.807" x2="233.078" y2="475.337"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="248.835" y1="461.875" x2="236.936" y2="475.655"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="252.851" y1="457.231" x2="251.61" y2="458.645"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="253.57" y1="461.176" x2="240.501" y2="476.313"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="257.811" y1="461.051" x2="243.334" y2="477.822"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="262.251" y1="455.917" x2="260.979" y2="457.365"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="268.992" y1="452.877" x2="265.08" y2="457.396"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="262.033" y1="460.963" x2="245.892" y2="479.651"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="266.254" y1="460.811" x2="248.24" y2="481.723"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="271.288" y1="459.761" x2="257.098" y2="476.23"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="274.073" y1="461.325" x2="257.118" y2="481.009"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="278.47" y1="461.018" x2="260.006" y2="482.455"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="283.026" y1="460.523" x2="263.569" y2="483.114"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="287.791" y1="459.8" x2="274.277" y2="475.474"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="290.971" y1="456.11" x2="290.133" y2="457.063"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="318.432" y1="424.275" x2="295.003" y2="451.417"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="314.237" y1="424.275" x2="294.883" y2="446.735"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="310.105" y1="424.275" x2="295.352" y2="441.393"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="291.293" y1="450.953" x2="290.23" y2="452.165"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="282.821" y1="455.993" x2="281.018" y2="458.058"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="290.414" y1="447.194" x2="285.726" y2="452.598"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="305.99" y1="424.275" x2="290.588" y2="442.133"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="301.843" y1="424.275" x2="286.724" y2="441.816"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="297.712" y1="424.275" x2="282.791" y2="441.587"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="280.404" y1="449.188" x2="277.769" y2="452.224"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="279.841" y1="445.059" x2="272.686" y2="453.33"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="322.484" y1="424.275" x2="296.674" y2="454.248"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="293.725" y1="457.709" x2="273.926" y2="480.681"/>
+ </g>
+ <g>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="461.902" x2="179.209" y2="465.402"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="460.526" x2="179.209" y2="464.026"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="459.15" x2="179.209" y2="462.65"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="457.774" x2="179.209" y2="461.275"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="456.398" x2="179.209" y2="459.899"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="176.476" y1="456.019" x2="179.209" y2="458.523"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="453.647" x2="179.209" y2="457.147"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="452.271" x2="179.209" y2="455.771"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="450.896" x2="179.209" y2="454.396"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="449.52" x2="179.209" y2="453.02"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="448.144" x2="179.209" y2="451.644"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="446.768" x2="179.209" y2="450.268"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="445.392" x2="179.209" y2="448.893"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="176.508" y1="445.016" x2="179.209" y2="447.517"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="442.641" x2="179.209" y2="446.141"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="441.265" x2="179.209" y2="444.765"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="439.889" x2="179.209" y2="443.389"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="438.513" x2="179.209" y2="442.013"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.412" y1="437.137" x2="179.209" y2="440.637"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="175.797" y1="436.102" x2="179.209" y2="439.261"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="444.919" x2="202.038" y2="448.419"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="446.35" x2="202.038" y2="449.85"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="447.78" x2="202.038" y2="451.28"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="449.211" x2="202.038" y2="452.712"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="450.643" x2="202.038" y2="454.143"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="452.073" x2="202.038" y2="455.573"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="454.935" x2="202.038" y2="458.436"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.24" y1="456.366" x2="202.038" y2="459.866"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="197.195" y1="456.861" x2="200.992" y2="460.361"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="195.654" y1="456.916" x2="199.451" y2="460.416"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="194.113" y1="456.972" x2="197.91" y2="460.472"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.572" y1="457.026" x2="196.369" y2="460.526"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="191.031" y1="457.081" x2="194.828" y2="460.582"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="189.49" y1="457.137" x2="193.287" y2="460.637"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="187.949" y1="457.191" x2="191.746" y2="460.691"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="186.573" y1="457.467" x2="190.37" y2="460.967"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.143" y1="457.577" x2="188.939" y2="461.077"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.557" y1="459.479" x2="187.453" y2="461.242"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.986" y1="454.177" x2="202.038" y2="457.004"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.659" y1="443.858" x2="202.038" y2="446.988"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="200.025" y1="443.687" x2="202.038" y2="445.557"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.22" y1="458.03" x2="208.239" y2="457.502"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.185" y1="447.064" x2="208.205" y2="446.535"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.273" y1="449.071" x2="185.298" y2="448.542"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M179.321,441.322c0.002-1.056,0.851-1.985,1.907-2.078"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.304" y1="447.63" x2="179.308" y2="441.322"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.342" y1="447.104" x2="179.29" y2="447.63"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.344" y1="445.904" x2="185.327" y2="447.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M186.349,444.819c-0.552,0.049-1.018,0.535-1.019,1.085"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="201.218" y1="443.527" x2="186.334" y2="444.819"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M202.217,444.439c0.001-0.554-0.46-0.962-1.013-0.912"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.215" y1="445.639" x2="202.203" y2="444.439"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.248" y1="445.112" x2="202.201" y2="445.639"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.269" y1="438.805" x2="208.234" y2="445.112"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M206.355,437.059c1.059-0.089,1.9,0.689,1.898,1.746"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.311" y1="458.635" x2="179.259" y2="449.071"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.349" y1="458.107" x2="179.297" y2="458.635"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.222" y1="456.629" x2="202.17" y2="447.064"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.256" y1="456.101" x2="202.208" y2="456.629"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.216" y1="459.421" x2="202.205" y2="458.03"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M201.211,460.509c0.552-0.049,0.989-0.534,0.991-1.088"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="186.341" y1="461.812" x2="201.197" y2="460.509"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.344,460.898c-0.002,0.554,0.431,0.962,0.983,0.913"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.347" y1="459.508" x2="185.329" y2="460.898"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.308" y1="460.039" x2="185.333" y2="459.508"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="179.296" y1="466.538" x2="179.294" y2="460.039"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M181.207,468.281c-1.057,0.093-1.928-0.69-1.925-1.743"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="206.32" y1="466.081" x2="181.193" y2="468.281"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M208.241,464.003c-0.002,1.053-0.875,1.984-1.935,2.078"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.253" y1="457.502" x2="208.227" y2="464.003"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="208.219" y1="446.535" x2="208.241" y2="456.101"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.312" y1="448.542" x2="185.335" y2="458.107"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="181.242" y1="439.244" x2="206.341" y2="437.059"/>
+
+ <path fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M179.805,467.791l-4.005-3.946c-0.317-0.313-0.511-0.754-0.51-1.254l0.013-6.5l1.306-0.114l-1.303-1.289l-0.037-9.563
+ l1.366-0.12l-1.336-1.321l0.018-6.308c0.002-1.056,0.863-1.986,1.922-2.078l25.112-2.184c0.554-0.048,1.054,0.144,1.402,0.489
+ l0.799,0.789l3.193,3.157"/>
+
+ <line fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="183.964" y1="447.223" x2="185.298" y2="448.542"/>
+
+ <path fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.333,457.602l11.873-1.039c0.553-0.049,1.004-0.535,1.006-1.09l0.001-1.389l1.306-0.115l-1.302-1.287l-0.035-8.892"/>
+ </g>
+ </g>
+ <g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M243.723,443.286c-0.026,0.287,0,5.268,0.209,6.207c0.098,0.44,4.823,0.052,4.823,0.052"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.036,443.391c0.13-0.026,4.563-0.104,4.563-0.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.141,446.466c0.131-0.002,3.231-0.051,3.388-0.079"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M216.229,443.48c-0.031,0.287,0.15,5.935,0.202,6.091"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M216.295,443.368c0.476-0.111,2.539-0.263,3.836-0.07c2.009,0.296,2.26,2.51,0.297,3.148
+ c-0.786,0.256-3.657,0.377-3.657,0.377"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M232.334,443.389c0.17,1.62,1.57,5.432,1.815,5.888c0.174,0.322,0.505,0.698,0.819,0c0.367-0.821,1.238-3.204,1.476-3.979
+ c0.216-0.708,0.538-1.576,0.931-0.356c0.41,1.278,1.633,3.948,1.816,4.313c0.417,0.828,0.663,0.183,0.949-0.798
+ c0.286-0.981,1.59-5.181,1.421-5.271"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M226.319,443.133c-0.534,0.066-3.171,1.052-2.558,4.021c0.601,2.905,3.633,2.794,5.274,1.937
+ c1.535-0.802,1.806-3.484,0.771-4.653c-0.873-0.986-2.379-1.303-3.47-1.17"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M251.452,443.479c-0.037,0.287,0.046,5.937,0.096,6.095"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M251.586,443.32c0.478-0.103,2.543-0.218,3.836-0.004c2.003,0.332,2.215,2.55,0.242,3.154
+ c-0.79,0.242-3.663,0.313-3.663,0.313"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="254.365" y1="446.697" x2="256.854" y2="449.59"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M259.699,443.286c-0.025,0.287,0,5.268,0.209,6.207c0.098,0.44,4.823,0.052,4.823,0.052"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.013,443.391c0.13-0.026,4.563-0.104,4.563-0.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.117,446.466c0.131-0.002,3.231-0.051,3.388-0.079"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M267.244,443.257c0.028,0.195,0.112,5.953,0.141,6.177"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M267.244,443.234c1.847-0.25,3.614-0.196,4.7,0.777c1.056,0.947,1.841,3.61-0.14,4.919c-1.508,0.995-4.112,0.562-4.112,0.562
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M289.229,442.874c0.171,0.138,2.771,3.492,3.111,3.857c0.341,0.364,1.173-0.624,2.195-2.1
+ c0.563-0.815,1.214-1.758,1.087-1.939"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M292.537,446.927c0.022,0.183-0.022,2.579,0.046,2.693"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M281.509,443.225c0.028,0.14,0.308,5.813,0.308,6.037"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M283.719,446.128c2.088-0.106,3.838,0.477,3.771,1.764c-0.058,1.127-0.895,1.454-1.874,1.482
+ c-1.343,0.038-3.688,0.056-3.688,0.056"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M281.632,443.196c0.476-0.111,2.781-0.252,4.068,0c1.453,0.284,1.39,2.097,0.056,2.597c-0.811,0.304-2.672,0.397-3.997,0.425
+ "/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M284.521,453.3c-0.031,0.288,0.157,6.338,0.209,6.495"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M284.609,457.577c0.066-0.234,3.605-2.104,4.874-4.239"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M286.578,456.208c1.47,1.269,3.473,3.238,3.506,3.64"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M263.75,453.702c-0.037,0.286,0.046,5.937,0.096,6.094"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.821,453.543c0.114,0,5.774-0.137,5.934-0.137"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M258.357,454.228c-0.046-0.296-0.73-1.004-2.557-0.98c-1.825,0.022-3.08,1.62-2.008,2.578c0.293,0.262,0.96,0.585,3.561,0.662
+ c2.305,0.068,2.168,1.757,1.597,2.373c-0.57,0.616-1.894,1.072-3.628,0.89c-1.239-0.13-1.826-0.802-1.781-1.075"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M281.166,454.57c0.007-0.218-0.28-1.288-2.603-1.063c-1.978,0.191-2.798,2.316-2.77,3.464
+ c0.028,1.146,0.672,2.881,3.134,2.881s2.932-1.343,2.932-1.343"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M267.784,459.786c0,0,2.252-5.44,2.369-5.775c0.224-0.644,0.86-0.518,1.167,0.146c0.716,1.544,2.676,5.629,2.676,5.629"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M268.877,457.866c0.111,0,3.732-0.195,3.732-0.195"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.098,453.489c-0.033,0.287,0.061,6.092,0.113,6.249"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M244.298,453.689c0.75,0.701,4.195,5.192,4.195,5.192c0.834,0.968,1.401,1.234,1.735,0.467s0.094-5.485-0.051-6.063"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M221.896,453.355c-0.534,0.067-3.171,1.053-2.558,4.021c0.601,2.904,3.633,2.793,5.274,1.936
+ c1.535-0.801,1.806-3.484,0.771-4.652c-0.873-0.987-2.38-1.304-3.471-1.171"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M228.643,453.703c-0.032,0.287,0.149,5.935,0.202,6.092"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M228.709,453.592c0.476-0.112,2.539-0.263,3.836-0.071c2.008,0.297,2.259,2.511,0.297,3.149
+ c-0.786,0.256-3.657,0.377-3.657,0.377"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M236.546,453.509c-0.025,0.287,0,5.268,0.209,6.208c0.099,0.439,4.823,0.052,4.823,0.052"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M236.859,453.613c0.131-0.025,4.563-0.104,4.563-0.104"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M236.965,456.689c0.13-0.003,3.23-0.051,3.387-0.08"/>
+ </g>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M293.177,452.513c-0.318,0.04-1.889,0.627-1.523,2.396c0.357,1.729,2.163,1.664,3.142,1.153
+ c0.914-0.478,1.075-2.076,0.459-2.771c-0.52-0.588-1.417-0.776-2.066-0.697"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M292.966,453.652c-0.011,0.079,0.013,1.64,0.026,1.684"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M293.002,453.608c0.132-0.028,0.703-0.06,1.06-0.001c0.554,0.092,0.612,0.704,0.067,0.871
+ c-0.219,0.067-1.012,0.087-1.012,0.087"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="293.77" y1="454.541" x2="294.457" y2="455.34"/>
+ </g>
+ </g>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M104.835,455.773c0-7.215,3.458-13.071,8.74-14.462c1.792-0.472,3.177,0.218,3.177,1.491c0,1.272-1.385,1.961-3.177,1.489
+ c-5.282-1.391-8.756-7.247-8.756-14.462s3.474-13.072,8.756-14.463c1.792-0.471,3.177,0.219,3.177,1.491s-1.385,1.961-3.177,1.489
+ c-5.282-1.39-8.756-7.247-8.756-14.462c0-3.942,1.037-7.479,2.84-10.104"/>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M339.982,54.757c-0.021,0.236,0,4.337,0.172,5.111c0.081,0.362,3.973,0.043,3.973,0.043"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M340.24,54.843c0.107-0.021,3.758-0.086,3.758-0.086"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M340.327,57.375c0.107-0.002,2.661-0.041,2.789-0.065"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.555,54.691c-0.113-0.02-0.17,2.066,0,3.438c0.168,1.372,1.314,1.879,2.48,1.86c1.164-0.019,2.18-0.658,2.273-1.879
+ c0.094-1.222-0.075-3.477-0.075-3.477"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M336.878,55.349c-0.037-0.244-0.601-0.827-2.104-0.808c-1.503,0.019-2.536,1.334-1.653,2.123c0.241,0.215,0.79,0.481,2.931,0.545
+ c1.898,0.057,1.786,1.447,1.316,1.954c-0.47,0.508-1.56,0.884-2.988,0.733c-1.02-0.107-1.504-0.66-1.467-0.886"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M346.278,54.915c-0.031,0.236,0.038,4.889,0.079,5.019"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M346.389,54.785c0.393-0.085,2.094-0.18,3.158-0.003c1.65,0.272,1.824,2.099,0.199,2.597c-0.65,0.199-3.017,0.258-3.017,0.258"
+ />
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.678" y1="57.566" x2="350.726" y2="59.948"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M252.395,227.344c3.977-1.224,10.07-2.008,16.94-2.008c11.985,0,21.701,2.386,21.701,5.329s0,26.307,0,29.25
+ c0,2.942-9.716,5.329-21.701,5.329c-4.258,0-8.229-0.302-11.579-0.821"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M314.294,230.132c0,2.649-8.744,4.797-19.53,4.797c-1.247,0-2.468-0.029-3.649-0.084"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M314.294,238.907c0,2.648-8.744,4.796-19.53,4.796c-1.248,0-2.468-0.028-3.65-0.084"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M314.294,247.682c0,2.649-8.744,4.796-19.53,4.796c-1.269,0-2.509-0.029-3.709-0.086"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M282.452,226.409c3.355-0.671,7.645-1.073,12.312-1.073c10.786,0,19.53,2.147,19.53,4.797c0,2.648,0,23.675,0,26.323
+ c0,2.649-8.744,4.797-19.53,4.797c-1.46,0-2.882-0.04-4.25-0.114"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M269.859,235.993c-0.18,0.001-0.344,0.001-0.524,0.001c-4.188,0-8.098-0.291-11.413-0.796"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M291.021,230.665c0,2.644-7.823,4.838-18.104,5.257"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M272.9,235.922c-0.992,0.041-1.992,0.064-3.025,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M291.036,240.415c0,1.811-3.666,3.409-9.288,4.373"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M281.732,244.788c-3.52,0.603-7.784,0.956-12.397,0.956c-4.194,0-8.109-0.292-11.429-0.798"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M269.899,255.492c-0.192,0.001-0.37,0.002-0.564,0.002c-4.203,0-8.128-0.294-11.451-0.802"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M291.021,250.165c0,2.372-6.298,4.383-15.025,5.073"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M275.979,255.238c-1.923,0.152-3.948,0.24-6.064,0.254"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="283.084" y1="246.118" x2="269.844" y2="259.357"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="282.178" y1="245.205" x2="269.844" y2="257.539"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="281.273" y1="244.292" x2="269.852" y2="255.713"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="280.367" y1="243.379" x2="269.823" y2="253.923"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="279.443" y1="242.484" x2="268.409" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="278.549" y1="241.562" x2="266.591" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="277.643" y1="240.648" x2="264.772" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="276.738" y1="239.735" x2="262.954" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="275.855" y1="238.799" x2="261.136" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="274.913" y1="237.923" x2="259.317" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="270.418" y1="233.358" x2="269.861" y2="233.897"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="271.277" y1="234.301" x2="269.862" y2="235.7"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="273.086" y1="236.145" x2="269.854" y2="239.35"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="272.25" y1="235.162" x2="269.861" y2="237.528"/>
+
+ <polyline fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 257.772,240.667 269.85,240.667 269.85,232.853 284.104,247.107 269.85,261.361 269.85,253.548 257.772,253.548 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="259.413" y1="240.71" x2="257.736" y2="242.371"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="261.231" y1="240.71" x2="257.75" y2="244.177"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="263.089" y1="240.687" x2="257.75" y2="246.003"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="264.936" y1="240.67" x2="257.724" y2="247.851"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="266.742" y1="240.67" x2="257.744" y2="249.643"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="268.545" y1="240.686" x2="257.756" y2="251.446"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="274.014" y1="237.021" x2="257.768" y2="253.25"/>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M214.875,227.344c-3.977-1.224-10.069-2.008-16.94-2.008c-11.985,0-21.7,2.386-21.7,5.329s0,26.307,0,29.25
+ c0,2.942,9.715,5.329,21.7,5.329c4.258,0,8.229-0.302,11.58-0.821"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M152.976,230.132c0,2.649,8.744,4.797,19.53,4.797c1.248,0,2.468-0.029,3.65-0.084"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M152.976,238.907c0,2.648,8.744,4.796,19.53,4.796c1.248,0,2.468-0.028,3.65-0.084"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M152.976,247.682c0,2.649,8.744,4.796,19.53,4.796c1.269,0,2.509-0.029,3.71-0.086"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M184.818,226.409c-3.356-0.671-7.646-1.073-12.313-1.073c-10.786,0-19.53,2.147-19.53,4.797c0,2.648,0,23.675,0,26.323
+ c0,2.649,8.744,4.797,19.53,4.797c1.46,0,2.882-0.04,4.25-0.114"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M197.41,235.993c0.18,0.001,0.344,0.001,0.524,0.001c4.188,0,8.098-0.291,11.413-0.796"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M176.25,230.665c0,2.644,7.822,4.838,18.104,5.257"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M194.37,235.922c0.992,0.041,1.991,0.064,3.024,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M176.234,240.415c0,1.811,3.665,3.409,9.288,4.373"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M185.538,244.788c3.519,0.603,7.783,0.956,12.396,0.956c4.194,0,8.11-0.292,11.429-0.798"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M197.371,255.492c0.192,0.001,0.37,0.002,0.563,0.002c4.204,0,8.128-0.294,11.452-0.802"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M176.25,250.165c0,2.372,6.298,4.383,15.024,5.073"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#A3CB8A" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M191.29,255.238c1.924,0.152,3.948,0.24,6.065,0.254"/>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="184.187" y1="246.118" x2="197.426" y2="259.357"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.092" y1="245.205" x2="197.426" y2="257.539"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="185.997" y1="244.292" x2="197.418" y2="255.713"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="186.902" y1="243.379" x2="197.446" y2="253.923"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="187.826" y1="242.484" x2="198.861" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="188.722" y1="241.562" x2="200.68" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="189.627" y1="240.648" x2="202.498" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="190.532" y1="239.735" x2="204.316" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="191.414" y1="238.799" x2="206.135" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="192.357" y1="237.923" x2="207.953" y2="253.52"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="196.853" y1="233.358" x2="197.408" y2="233.897"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="195.992" y1="234.301" x2="197.407" y2="235.7"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="194.184" y1="236.145" x2="197.416" y2="239.35"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="195.02" y1="235.162" x2="197.408" y2="237.528"/>
+
+ <polyline fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 209.497,240.667 197.421,240.667 197.421,232.853 183.166,247.107 197.421,261.361 197.421,253.548 209.497,253.548 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="207.856" y1="240.71" x2="209.533" y2="242.371"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="206.038" y1="240.71" x2="209.521" y2="244.177"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="204.181" y1="240.687" x2="209.52" y2="246.003"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="202.334" y1="240.67" x2="209.546" y2="247.851"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="200.527" y1="240.67" x2="209.526" y2="249.643"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="198.725" y1="240.686" x2="209.515" y2="251.446"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="193.257" y1="237.021" x2="209.502" y2="253.25"/>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.747,231.258c0,3.27-10.795,5.921-24.112,5.921c-13.316,0-24.112-2.651-24.112-5.921"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.747,263.757c0,3.271-10.795,5.921-24.112,5.921c-13.316,0-24.112-2.65-24.112-5.921v-32.499
+ c0-3.271,10.796-5.922,24.112-5.922c13.317,0,24.112,2.651,24.112,5.922C257.747,234.527,257.747,260.486,257.747,263.757z"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="211.539" y1="235.241" x2="211.539" y2="264.547"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="213.743" y1="236.237" x2="213.743" y2="265.666"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="216.479" y1="237.057" x2="216.479" y2="266.421"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="255.678" y1="235.241" x2="255.678" y2="264.547"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="253.474" y1="236.237" x2="253.474" y2="265.666"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="250.738" y1="237.057" x2="250.738" y2="266.421"/>
+ <polygon fill-rule="evenodd" clip-rule="evenodd" fill="#469714" points="218.625,238.864 238.599,247.708 233.705,249.021
+ 243.179,255.289 238.285,256.602 248.645,265.017 228.671,256.173 233.564,254.86 224.091,248.592 228.984,247.279 "/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="219.596" y1="240.921" x2="219.58" y2="267.065"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.425" y1="259.595" x2="233.404" y2="268.191"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="238.968" y1="261.958" x2="238.949" y2="268.037"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="243.808" y1="263.984" x2="243.79" y2="267.469"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="247.652" y1="265.678" x2="247.637" y2="267.065"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="247.668" y1="237.574" x2="247.639" y2="262.717"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="233.451" y1="238.7" x2="233.429" y2="243.861"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.314" y1="238.546" x2="228.293" y2="241.768"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="223.474" y1="237.977" x2="223.452" y2="239.805"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="219.611" y1="237.574" x2="219.593" y2="238.181"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="223.464" y1="249.693" x2="223.427" y2="267.469"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="223.486" y1="243.907" x2="223.457" y2="247.359"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="238.985" y1="249.122" x2="238.966" y2="250.749"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="239.027" y1="238.546" x2="238.98" y2="246.406"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.3" y1="257.519" x2="228.268" y2="268.037"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="228.322" y1="252.711" x2="228.295" y2="255.14"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="243.818" y1="256.195" x2="243.797" y2="259.417"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="0.375" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="243.868" y1="237.977" x2="243.809" y2="254.272"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.778,252.924c0,2.414-5.911,4.49-14.348,5.412"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M257.794,242.091c0,2.652-7.146,4.898-16.942,5.651"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M222.43,247.331c-7.658-0.992-12.907-2.967-12.907-5.24"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#469714" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M230.074,258.779c-11.607-0.428-20.552-2.887-20.552-5.855"/>
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#1478CE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M226.581,231.491c2.373,0.602,12.146,0.501,14.251-0.134c0,0,0.051-6.141,0.051-8.19s-1.977-6.004-7.175-6.004
+ s-7.175,3.954-7.175,6.004S226.581,231.491,226.581,231.491z"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.387" y1="226.757" x2="240.81" y2="226.757"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1478CE" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="226.387" y1="228.943" x2="240.81" y2="228.943"/>
+ </g>
+ <g>
+ <g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 314.688,35.599 304.091,44.349 373.702,44.349 363.235,35.207 315.365,35.207 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 303.795,45.334 304.507,48.492 373.335,48.492 373.795,45.019 "/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M316.842,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M321.278,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M325.715,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M330.152,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M334.589,37.44c0.54,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M339.025,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M343.462,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M347.898,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M352.336,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M356.772,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M361.209,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M314.354,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M319.031,39.47c0.57,0.416,1.418,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M323.71,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M328.389,39.47c0.57,0.416,1.417,0.544,1.406-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M347.102,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M351.779,39.47c0.571,0.416,1.418,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M356.458,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M361.137,39.47c0.57,0.416,1.417,0.544,1.406-0.024"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M312.482,41.472c0.57,0.417,1.416,0.545,1.406-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M317.16,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M321.839,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M326.517,41.472c0.571,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M349.908,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M354.587,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M359.266,41.472c0.57,0.417,1.416,0.545,1.406-0.024"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M364.048,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+ </g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 330.614,43.848 334.027,39.684 343.988,39.684 347.777,44.082 "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.6387" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M317.095,4.24l0.229-0.004c0.75-0.019,42.484,0.001,44.067,0.001c0.239,0,0.363,0.035,0.411,0.054
+ c0.016,0.05,0.069,0.179,0.069,0.295c0,0.931-0.935,25.151-1.055,26.78c-0.052,0.699-0.195,0.938-0.227,0.979l-41.263,0.089
+ c-0.652,0-0.74-0.323-0.74-0.483l-1.563-26.897C317.025,4.65,317.06,4.394,317.095,4.24z"/>
+ <g>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="331.369" y1="12.242" x2="329.716" y2="13.758"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="335.321" y1="8.6" x2="333.605" y2="10.17"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="335.99" y1="9.454" x2="334.07" y2="11.217"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="332.476" y1="12.715" x2="330.552" y2="14.476"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="329.15" y1="17.245" x2="328.527" y2="17.81"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="337.321" y1="9.698" x2="331.33" y2="15.214"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="340.05" y1="7.189" x2="338.557" y2="8.552"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="340.854" y1="7.907" x2="328.621" y2="19.189"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="340.987" y1="9.255" x2="329.812" y2="19.562"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="342.143" y1="9.664" x2="338.442" y2="13.07"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="334.544" y1="16.692" x2="330.847" y2="20.088"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M331.191,21.243"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="334.262" y1="18.409" x2="331.182" y2="21.243"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="344.966" y1="8.543" x2="340.171" y2="12.95"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="345.871" y1="9.166" x2="341.482" y2="13.209"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="334.41" y1="19.759" x2="329.954" y2="23.852"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="334.819" y1="20.841" x2="330.637" y2="24.692"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="345.301" y1="11.177" x2="342.535" y2="13.715"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="346.018" y1="11.978" x2="343.42" y2="14.366"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="335.449" y1="21.745" x2="332.802" y2="24.172"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="336.181" y1="22.528" x2="333.574" y2="24.926"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="346.826" y1="12.714" x2="344.114" y2="15.202"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.977" y1="12.19" x2="344.674" y2="16.153"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="337.08" y1="23.185" x2="333.713" y2="26.274"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="338.199" y1="23.611" x2="333.68" y2="27.775"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="349.563" y1="13.137" x2="344.988" y2="17.34"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.271" y1="15.789" x2="345.145" y2="18.665"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="339.614" y1="23.793" x2="334.648" y2="28.354"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="341.543" y1="23.469" x2="337.604" y2="27.097"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.623" y1="16.945" x2="344.695" y2="20.554"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="349.902" y1="17.228" x2="338.391" y2="27.845"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.465" y1="20.026" x2="338.596" y2="29.126"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="350.789" y1="17.888" x2="348.909" y2="19.606"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="348.176" y1="21.77" x2="342.67" y2="26.841"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="340.867" y1="28.529" x2="339.755" y2="29.542"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="345.425" y1="25.775" x2="343.555" y2="27.492"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="349.066" y1="22.42" x2="347.181" y2="24.147"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="345.904" y1="26.806" x2="344.186" y2="28.384"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="349.569" y1="23.433" x2="348.272" y2="24.615"/>
+ </g>
+
+ <path fill="none" stroke="#292929" stroke-width="0.65" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M342.782,9.906l0.014,0.005l0.014,0.004c0.142,0.044,0.249,0.021,0.36-0.135c0.315-0.445,0.904-1.274,0.983-1.388
+ c0.109-0.156,0.606-0.047,1.144,0.263c0.536,0.31,0.879,0.686,0.799,0.858c-0.059,0.126-0.482,1.05-0.71,1.546
+ c-0.086,0.188-0.04,0.294,0.085,0.398c0.404,0.34,0.783,0.72,1.127,1.13c0.1,0.118,0.207,0.164,0.395,0.078
+ c0.496-0.228,1.42-0.652,1.545-0.71c0.174-0.08,0.549,0.263,0.859,0.799c0.31,0.537,0.418,1.034,0.263,1.144
+ c-0.113,0.079-0.942,0.668-1.388,0.983c-0.156,0.11-0.179,0.219-0.137,0.355c0.186,0.503,0.328,1.026,0.422,1.566
+ c0.027,0.16,0.098,0.253,0.303,0.272c0.543,0.051,1.556,0.146,1.693,0.158c0.189,0.017,0.344,0.502,0.344,1.121
+ s-0.154,1.104-0.344,1.122c-0.138,0.012-1.15,0.106-1.693,0.157c-0.205,0.02-0.275,0.113-0.303,0.273
+ c-0.092,0.526-0.23,1.04-0.411,1.533l-0.005,0.015l-0.004,0.013c-0.044,0.142-0.021,0.25,0.135,0.36
+ c0.445,0.316,1.274,0.904,1.388,0.983c0.155,0.11,0.047,0.607-0.263,1.144c-0.311,0.536-0.686,0.88-0.859,0.799
+ c-0.125-0.058-1.049-0.481-1.545-0.71c-0.188-0.085-0.295-0.039-0.398,0.085c-0.34,0.404-0.719,0.783-1.13,1.127
+ c-0.118,0.101-0.164,0.208-0.078,0.395c0.228,0.496,0.651,1.421,0.71,1.546c0.08,0.173-0.263,0.549-0.799,0.858
+ c-0.537,0.31-1.034,0.419-1.144,0.263c-0.079-0.112-0.668-0.942-0.983-1.388c-0.111-0.155-0.219-0.179-0.355-0.137
+ c-0.503,0.187-1.026,0.328-1.566,0.422c-0.16,0.028-0.253,0.099-0.272,0.303c-0.051,0.544-0.146,1.557-0.158,1.693
+ c-0.017,0.19-0.502,0.345-1.121,0.345c-0.62,0-1.104-0.154-1.122-0.345c-0.012-0.137-0.106-1.149-0.158-1.693
+ c-0.019-0.204-0.112-0.274-0.272-0.303c-0.54-0.094-1.063-0.235-1.565-0.422c-0.138-0.042-0.245-0.019-0.355,0.137
+ c-0.316,0.445-0.904,1.275-0.983,1.388c-0.11,0.156-0.607,0.047-1.144-0.263s-0.88-0.686-0.799-0.858
+ c0.058-0.125,0.481-1.05,0.709-1.546c0.086-0.187,0.04-0.294-0.084-0.398c-0.404-0.34-0.783-0.719-1.127-1.13
+ c-0.101-0.117-0.208-0.163-0.395-0.078c-0.496,0.229-1.421,0.652-1.546,0.71c-0.173,0.081-0.549-0.263-0.858-0.799
+ s-0.419-1.033-0.263-1.144c0.112-0.079,0.942-0.668,1.388-0.983c0.155-0.11,0.179-0.218,0.137-0.355
+ c-0.186-0.502-0.328-1.025-0.422-1.565c-0.028-0.16-0.099-0.254-0.303-0.273c-0.544-0.051-1.557-0.146-1.693-0.157
+ c-0.19-0.018-0.345-0.503-0.345-1.122s0.154-1.104,0.345-1.121c0.137-0.013,1.149-0.107,1.693-0.158
+ c0.204-0.02,0.274-0.112,0.303-0.272c0.092-0.527,0.23-1.041,0.41-1.534l0.005-0.013l0.004-0.013
+ c0.045-0.144,0.021-0.252-0.134-0.362c-0.445-0.315-1.275-0.904-1.389-0.983c-0.155-0.109-0.046-0.606,0.264-1.144
+ c0.31-0.536,0.686-0.879,0.858-0.799c0.125,0.058,1.05,0.482,1.546,0.71c0.187,0.086,0.294,0.04,0.398-0.085
+ c0.34-0.403,0.719-0.783,1.129-1.127c0.119-0.101,0.164-0.208,0.078-0.395c-0.228-0.496-0.651-1.42-0.709-1.546
+ c-0.081-0.173,0.263-0.549,0.799-0.858s1.033-0.419,1.144-0.263c0.079,0.113,0.667,0.942,0.983,1.388
+ c0.11,0.155,0.219,0.179,0.355,0.137c0.502-0.186,1.025-0.328,1.565-0.422c0.16-0.028,0.254-0.098,0.272-0.303
+ c0.052-0.544,0.146-1.556,0.158-1.693c0.018-0.19,0.502-0.344,1.122-0.344c0.619,0,1.104,0.153,1.121,0.344
+ c0.013,0.138,0.107,1.149,0.158,1.693c0.02,0.205,0.112,0.274,0.272,0.303C341.775,9.587,342.289,9.726,342.782,9.906z
+ M334.26,18.355c0,3.002,2.434,5.436,5.437,5.436c3.002,0,5.436-2.434,5.436-5.436s-2.434-5.436-5.436-5.436
+ C336.693,12.92,334.26,15.354,334.26,18.355z"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M363.942,34.924c0.261-7.366,1.395-31.175,1.395-32.074c0-1.04-0.5-2.081-2.211-2.081s-46.841-0.021-47.651,0
+ c-0.929,0.024-1.971-0.02-1.971,2.602c0,2.143,1.885,31.435,1.885,31.435"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M129.97,54.757c-0.021,0.236,0,4.337,0.173,5.111c0.08,0.362,3.972,0.043,3.972,0.043"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M130.229,54.843c0.106-0.021,3.757-0.086,3.757-0.086"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M130.314,57.375c0.107-0.002,2.661-0.041,2.789-0.065"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M115.541,54.691c-0.112-0.02-0.169,2.066,0,3.438s1.315,1.879,2.48,1.86c1.166-0.019,2.18-0.658,2.274-1.879
+ c0.094-1.222-0.075-3.477-0.075-3.477"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M126.865,55.349c-0.037-0.244-0.601-0.827-2.104-0.808c-1.503,0.019-2.536,1.334-1.653,2.123c0.241,0.215,0.79,0.481,2.932,0.545
+ c1.898,0.057,1.785,1.447,1.315,1.954c-0.47,0.508-1.56,0.884-2.988,0.733c-1.021-0.107-1.504-0.66-1.466-0.886"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.266,54.915c-0.031,0.236,0.038,4.889,0.079,5.019"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M136.375,54.785c0.394-0.085,2.095-0.18,3.16-0.003c1.648,0.272,1.823,2.099,0.199,2.597c-0.651,0.199-3.018,0.258-3.018,0.258"
+ />
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#333333" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.664" y1="57.566" x2="140.713" y2="59.948"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 104.676,35.599 94.078,44.349 163.689,44.349 153.223,35.207 105.353,35.207 "/>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 93.782,45.334 94.494,48.492 163.322,48.492 163.782,45.019 "/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M106.829,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M111.266,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M115.702,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M120.139,37.44c0.541,0.396,1.345,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M124.576,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M129.013,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M133.449,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M137.887,37.44c0.54,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M142.323,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M146.76,37.44c0.541,0.396,1.344,0.517,1.334-0.022"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M151.196,37.44c0.541,0.396,1.344,0.517,1.335-0.022"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M104.341,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M109.02,39.47c0.57,0.416,1.416,0.544,1.406-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M113.697,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M118.376,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M137.089,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M141.768,39.47c0.57,0.416,1.417,0.544,1.406-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M146.445,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M151.124,39.47c0.57,0.416,1.417,0.544,1.407-0.024"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M102.469,41.472c0.571,0.417,1.418,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M107.147,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M111.826,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M116.504,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M139.896,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M144.574,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M149.252,41.472c0.571,0.417,1.418,0.545,1.407-0.024"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M154.035,41.472c0.57,0.417,1.417,0.545,1.407-0.024"/>
+ </g>
+
+ <polyline fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.463" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" points="
+ 120.602,43.848 124.015,39.684 133.976,39.684 137.765,44.082 "/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.6387" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M107.082,4.24l0.229-0.004c0.75-0.019,42.484,0.001,44.067,0.001c0.239,0,0.363,0.035,0.412,0.054
+ c0.015,0.05,0.068,0.179,0.068,0.295c0,0.931-0.935,25.151-1.055,26.78c-0.051,0.699-0.195,0.938-0.227,0.979l-41.263,0.089
+ c-0.652,0-0.74-0.323-0.74-0.483l-1.563-26.897C107.013,4.65,107.047,4.394,107.082,4.24z"/>
+ <g>
+ <g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="121.355" y1="12.242" x2="119.703" y2="13.758"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="125.309" y1="8.6" x2="123.593" y2="10.17"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="125.978" y1="9.454" x2="124.058" y2="11.217"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="122.463" y1="12.715" x2="120.539" y2="14.476"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="119.138" y1="17.245" x2="118.515" y2="17.81"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="127.309" y1="9.698" x2="121.317" y2="15.214"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="130.037" y1="7.189" x2="128.543" y2="8.552"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="130.842" y1="7.907" x2="118.609" y2="19.189"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="130.975" y1="9.255" x2="119.799" y2="19.562"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="132.13" y1="9.664" x2="128.43" y2="13.07"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="124.531" y1="16.692" x2="120.834" y2="20.088"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M121.18,21.243"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="124.249" y1="18.409" x2="121.169" y2="21.243"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="134.953" y1="8.543" x2="130.158" y2="12.95"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="135.858" y1="9.166" x2="131.47" y2="13.209"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="124.397" y1="19.759" x2="119.941" y2="23.852"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="124.807" y1="20.841" x2="120.625" y2="24.692"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="135.288" y1="11.177" x2="132.522" y2="13.715"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="136.005" y1="11.978" x2="133.407" y2="14.366"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="125.437" y1="21.745" x2="122.789" y2="24.172"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="126.168" y1="22.528" x2="123.562" y2="24.926"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="136.813" y1="12.714" x2="134.102" y2="15.202"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.964" y1="12.19" x2="134.661" y2="16.153"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="127.067" y1="23.185" x2="123.7" y2="26.274"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="128.188" y1="23.611" x2="123.667" y2="27.775"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="139.55" y1="13.137" x2="134.976" y2="17.34"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.258" y1="15.789" x2="135.132" y2="18.665"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="129.602" y1="23.793" x2="124.636" y2="28.354"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="131.531" y1="23.469" x2="127.59" y2="27.097"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.61" y1="16.945" x2="134.683" y2="20.554"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="139.891" y1="17.228" x2="128.378" y2="27.845"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.452" y1="20.026" x2="128.583" y2="29.126"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="140.776" y1="17.888" x2="138.896" y2="19.606"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="138.163" y1="21.77" x2="132.657" y2="26.841"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="130.854" y1="28.529" x2="129.742" y2="29.542"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="135.412" y1="25.775" x2="133.543" y2="27.492"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="139.054" y1="22.42" x2="137.168" y2="24.147"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="135.892" y1="26.806" x2="134.172" y2="28.384"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="139.557" y1="23.433" x2="138.26" y2="24.615"/>
+ </g>
+
+ <path fill="none" stroke="#292929" stroke-width="0.65" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M132.77,9.906l0.014,0.005l0.014,0.004c0.142,0.044,0.25,0.021,0.36-0.135c0.315-0.445,0.904-1.274,0.983-1.388
+ c0.109-0.156,0.606-0.047,1.144,0.263c0.536,0.31,0.879,0.686,0.799,0.858c-0.058,0.126-0.482,1.05-0.71,1.546
+ c-0.086,0.188-0.04,0.294,0.085,0.398c0.403,0.34,0.783,0.72,1.127,1.13c0.101,0.118,0.208,0.164,0.395,0.078
+ c0.496-0.228,1.42-0.652,1.546-0.71c0.173-0.08,0.549,0.263,0.858,0.799c0.31,0.537,0.419,1.034,0.263,1.144
+ c-0.113,0.079-0.942,0.668-1.388,0.983c-0.155,0.11-0.179,0.219-0.137,0.355c0.186,0.503,0.328,1.026,0.422,1.566
+ c0.028,0.16,0.099,0.253,0.303,0.272c0.544,0.051,1.556,0.146,1.693,0.158c0.19,0.017,0.344,0.502,0.344,1.121
+ s-0.153,1.104-0.344,1.122c-0.138,0.012-1.149,0.106-1.693,0.157c-0.204,0.02-0.274,0.113-0.303,0.273
+ c-0.092,0.526-0.23,1.04-0.411,1.533l-0.005,0.015l-0.004,0.013c-0.044,0.142-0.021,0.25,0.135,0.36
+ c0.445,0.316,1.274,0.904,1.388,0.983c0.156,0.11,0.047,0.607-0.263,1.144s-0.686,0.88-0.858,0.799
+ c-0.126-0.058-1.05-0.481-1.546-0.71c-0.187-0.085-0.294-0.039-0.398,0.085c-0.34,0.404-0.72,0.783-1.13,1.127
+ c-0.118,0.101-0.164,0.208-0.078,0.395c0.228,0.496,0.652,1.421,0.71,1.546c0.08,0.173-0.263,0.549-0.799,0.858
+ c-0.537,0.31-1.034,0.419-1.144,0.263c-0.079-0.112-0.668-0.942-0.983-1.388c-0.11-0.155-0.219-0.179-0.355-0.137
+ c-0.503,0.187-1.026,0.328-1.566,0.422c-0.16,0.028-0.253,0.099-0.272,0.303c-0.051,0.544-0.146,1.557-0.158,1.693
+ c-0.017,0.19-0.502,0.345-1.121,0.345s-1.104-0.154-1.122-0.345c-0.012-0.137-0.106-1.149-0.157-1.693
+ c-0.02-0.204-0.113-0.274-0.273-0.303c-0.54-0.094-1.063-0.235-1.565-0.422c-0.138-0.042-0.245-0.019-0.355,0.137
+ c-0.315,0.445-0.904,1.275-0.983,1.388c-0.11,0.156-0.607,0.047-1.144-0.263s-0.88-0.686-0.8-0.858
+ c0.059-0.125,0.482-1.05,0.711-1.546c0.085-0.187,0.039-0.294-0.085-0.398c-0.404-0.34-0.783-0.719-1.127-1.13
+ c-0.101-0.117-0.208-0.163-0.395-0.078c-0.496,0.229-1.421,0.652-1.546,0.71c-0.173,0.081-0.549-0.263-0.858-0.799
+ s-0.419-1.033-0.263-1.144c0.112-0.079,0.942-0.668,1.388-0.983c0.155-0.11,0.179-0.218,0.136-0.355
+ c-0.186-0.502-0.327-1.025-0.421-1.565c-0.028-0.16-0.099-0.254-0.303-0.273c-0.544-0.051-1.557-0.146-1.694-0.157
+ c-0.189-0.018-0.344-0.503-0.344-1.122s0.154-1.104,0.344-1.121c0.138-0.013,1.15-0.107,1.694-0.158
+ c0.204-0.02,0.274-0.112,0.303-0.272c0.092-0.527,0.23-1.041,0.411-1.534l0.004-0.013l0.004-0.013
+ c0.045-0.144,0.021-0.252-0.134-0.362c-0.445-0.315-1.275-0.904-1.388-0.983c-0.156-0.109-0.047-0.606,0.263-1.144
+ c0.31-0.536,0.686-0.879,0.858-0.799c0.125,0.058,1.05,0.482,1.546,0.71c0.187,0.086,0.294,0.04,0.398-0.085
+ c0.34-0.403,0.719-0.783,1.13-1.127c0.117-0.101,0.163-0.208,0.078-0.395c-0.229-0.496-0.652-1.42-0.71-1.546
+ c-0.081-0.173,0.263-0.549,0.799-0.858s1.034-0.419,1.144-0.263c0.079,0.113,0.668,0.942,0.983,1.388
+ c0.11,0.155,0.218,0.179,0.355,0.137c0.502-0.186,1.026-0.328,1.565-0.422c0.16-0.028,0.254-0.098,0.273-0.303
+ c0.051-0.544,0.146-1.556,0.157-1.693c0.018-0.19,0.503-0.344,1.122-0.344s1.104,0.153,1.121,0.344
+ c0.013,0.138,0.107,1.149,0.158,1.693c0.02,0.205,0.112,0.274,0.272,0.303C131.763,9.587,132.276,9.726,132.77,9.906z
+ M124.248,18.355c0,3.002,2.434,5.436,5.436,5.436s5.436-2.434,5.436-5.436s-2.434-5.436-5.436-5.436
+ S124.248,15.354,124.248,18.355z"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#292929" stroke-width="0.926" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M153.93,34.924c0.261-7.366,1.395-31.175,1.395-32.074c0-1.04-0.5-2.081-2.211-2.081s-46.841-0.021-47.651,0
+ c-0.929,0.024-1.971-0.02-1.971,2.602c0,2.143,1.885,31.435,1.885,31.435"/>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M276.011,102.793c-0.019,0.243,0.301,4.998,0.35,5.128"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M273.537,102.789c0.097-0.005,4.856-0.37,4.991-0.378"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M270.877,103.506c-0.052-0.247-0.659-0.813-2.196-0.713c-1.536,0.1-2.522,1.5-1.577,2.26c0.259,0.208,0.834,0.45,3.027,0.4
+ c1.944-0.044,1.903,1.385,1.45,1.929s-1.548,0.986-3.017,0.909c-1.049-0.055-1.573-0.595-1.547-0.827"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M259.943,108.773c0,0,1.657-4.682,1.74-4.969c0.16-0.552,0.702-0.475,0.99,0.071c0.67,1.27,2.502,4.623,2.502,4.623"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M260.779,107.108c0.094-0.005,3.135-0.329,3.135-0.329"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M254.043,103.936c0.006,0.11,0.334,5.113,0.385,5.243"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M254.305,103.878c0.15-0.052,3.573-0.275,3.684-0.281"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M254.488,106.554c0.11-0.008,2.893-0.238,3.023-0.27"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M282.562,101.217c-0.04,0.24-0.136,5.005-0.099,5.139"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M282.289,107.991c-0.223,0.243-0.035,0.581,0.44,0.244"/>
+ </g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#555555" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="241.139" y1="109.102" x2="250.443" y2="107.448"/>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M357.154,115.792c0,0.179,0.107,8.43,0.179,8.644"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M356.121,124.506c0.321,0,2.645-0.071,2.859-0.107"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M355.725,115.721c0.25,0,3.003-0.107,3.146-0.071"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M375.674,116.163c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M371.663,115.946c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M343.972,115.79c-0.188-0.031-0.281,3.438,0,5.719s2.188,3.125,4.125,3.094s3.625-1.094,3.781-3.125s-0.125-5.781-0.125-5.781"
+ />
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M332.604,116.051c0.039,0.191,0.422,7.962,0.422,8.268"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M335.631,120.027c2.858-0.146,5.255,0.652,5.164,2.414c-0.079,1.544-1.226,1.992-2.566,2.03
+ c-1.839,0.053-5.05,0.077-5.05,0.077"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M332.772,116.013c0.651-0.153,3.809-0.345,5.571,0c1.99,0.389,1.903,2.87,0.076,3.556c-1.109,0.416-3.659,0.545-5.474,0.582"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M402.589,115.689c-0.731,0.092-4.342,1.441-3.502,5.507c0.822,3.978,4.975,3.825,7.223,2.651
+ c2.103-1.098,2.474-4.772,1.057-6.372c-1.195-1.352-3.259-1.785-4.752-1.604"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M411.208,116.163c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M411.391,115.947c0.653-0.142,3.482-0.299,5.254-0.006c2.742,0.454,3.033,3.491,0.331,4.319
+ c-1.083,0.331-5.017,0.429-5.017,0.429"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="415.197" y1="120.571" x2="418.604" y2="124.532"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M390.041,116.149c0,0.18,0.107,8.32,0.178,8.535"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M390.47,116.078c0.25-0.071,5.819-0.143,5.997-0.143"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M390.541,120.434c0.18-0.004,4.711-0.142,4.926-0.181"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M363.931,115.899c-0.036,0.394-0.113,7.214,0.173,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M328.725,129.495c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M329.154,129.639c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M329.297,133.851c0.18-0.004,4.426-0.07,4.639-0.109"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M382.35,129.58c1.027,0.961,5.212,7.086,5.212,7.086s2.976-5.35,3.703-6.72c0.727-1.371,1.234-0.274,1.231,0.64
+ c-0.002,0.394,0.323,7.303,0.267,7.517"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M382.002,129.209c-0.044,0.394,0.215,8.68,0.286,8.894"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M318.789,129.761c-0.044,0.394,0.205,8.128,0.276,8.342"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M318.88,129.608c0.651-0.153,3.477-0.36,5.253-0.098c2.75,0.406,3.094,3.438,0.406,4.313c-1.076,0.351-5.008,0.517-5.008,0.517
+ "/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M427.466,130.949c0.011-0.3-0.383-1.765-3.563-1.456c-2.708,0.263-3.832,3.173-3.793,4.743c0.038,1.571,0.919,3.946,4.291,3.946
+ c3.371,0,4.016-1.839,4.016-1.839"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M396.435,138.092c0,0,3.085-7.45,3.245-7.909c0.306-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M397.932,135.463c0.153,0,5.111-0.269,5.111-0.269"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M408.447,129.468c-0.044,0.394,0.084,8.343,0.156,8.558"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M408.722,129.742c1.027,0.96,5.746,7.11,5.746,7.11c1.142,1.325,1.919,1.691,2.376,0.64c0.458-1.052,0.129-7.512-0.069-8.304"
+ />
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M338.661,129.759c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M338.844,129.543c0.653-0.142,3.482-0.299,5.254-0.006c2.742,0.454,3.033,3.491,0.331,4.319
+ c-1.083,0.331-5.017,0.429-5.017,0.429"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="342.65" y1="134.167" x2="346.057" y2="138.128"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M362.043,129.285c-0.732,0.092-4.343,1.441-3.503,5.507c0.822,3.978,4.975,3.825,7.224,2.651
+ c2.102-1.098,2.473-4.772,1.056-6.372c-1.195-1.352-3.259-1.785-4.752-1.604"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M370.661,129.759c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M370.844,129.543c0.653-0.142,3.482-0.299,5.254-0.006c2.742,0.454,3.033,3.491,0.331,4.319
+ c-1.083,0.331-5.017,0.429-5.017,0.429"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="374.65" y1="134.167" x2="378.057" y2="138.128"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M349.494,129.745c0,0.18,0.107,8.32,0.178,8.535"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M349.923,129.674c0.25-0.071,5.819-0.143,5.997-0.143"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M349.994,134.029c0.18-0.004,4.711-0.142,4.926-0.181"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M431.456,129.495c-0.035,0.394,0,7.214,0.286,8.501c0.135,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M431.886,129.639c0.179-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M432.029,133.851c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+ </g>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M369.479,415.515c0.039,0.268,0.153,8.153,0.191,8.459"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M369.479,415.484c2.528-0.343,4.949-0.27,6.437,1.064c1.445,1.297,2.521,4.944-0.191,6.735c-2.064,1.363-5.632,0.77-5.632,0.77
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M359.858,415.555c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M360.288,415.698c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M360.432,419.91c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M441.723,415.818c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M437.712,415.602c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M380.368,415.445c-0.188-0.031-0.281,3.438,0,5.719s2.188,3.125,4.125,3.094s3.625-1.094,3.781-3.125s-0.125-5.781-0.125-5.781"
+ />
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M391.803,415.527c-0.044,0.394,0.084,8.343,0.156,8.558"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M392.077,415.802c1.027,0.96,5.746,7.11,5.746,7.11c1.142,1.325,1.919,1.691,2.376,0.64c0.458-1.052,0.129-7.512-0.069-8.304"
+ />
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M348.91,415.818c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M349.093,415.603c0.653-0.142,3.482-0.299,5.254-0.006c2.742,0.454,3.033,3.491,0.331,4.319
+ c-1.083,0.331-5.017,0.429-5.017,0.429"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="352.899" y1="420.227" x2="356.306" y2="424.188"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M404.479,415.515c0.039,0.268,0.153,8.153,0.191,8.459"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M404.479,415.484c2.528-0.343,4.949-0.27,6.437,1.064c1.445,1.297,2.521,4.944-0.191,6.735c-2.064,1.363-5.632,0.77-5.632,0.77
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M414.66,424.151c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M416.156,421.522c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M425.803,415.527c-0.044,0.394,0.084,8.343,0.156,8.558"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M426.077,415.802c1.027,0.96,5.746,7.11,5.746,7.11c1.142,1.325,1.919,1.691,2.376,0.64c0.458-1.052,0.129-7.512-0.069-8.304"
+ />
+ </g>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M420.146,431.449c-0.036-0.465-1.502-1.609-4.037-1.396c-2.993,0.251-3.831,3.173-3.793,4.743
+ c0.039,1.571,0.921,4.031,4.291,3.946c3.146-0.08,3.607-1.026,3.86-1.831c0.346-1.102,0.107-2.074,0.107-2.074l-3.79,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M364.837,431.039c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M382.319,429.845c-0.731,0.092-4.343,1.441-3.502,5.507c0.821,3.978,4.975,3.825,7.223,2.651
+ c2.102-1.098,2.473-4.772,1.057-6.372c-1.196-1.352-3.259-1.785-4.753-1.604"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M401.66,438.651c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M403.156,436.022c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M372.723,430.318c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M368.712,430.102c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M391.41,430.318c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M391.593,430.103c0.653-0.142,3.482-0.299,5.254-0.006c2.742,0.454,3.033,3.491,0.331,4.319
+ c-1.083,0.331-5.017,0.429-5.017,0.429"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="395.399" y1="434.727" x2="398.806" y2="438.688"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M424.358,430.055c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M424.788,430.198c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M424.932,434.41c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M354.533,444.819c-0.024,0.275,0,5.05,0.201,5.95c0.094,0.422,4.623,0.05,4.623,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M354.834,444.92c0.125-0.025,4.374-0.101,4.374-0.101"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M354.935,447.868c0.125-0.003,3.098-0.049,3.247-0.076"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M365.719,445.509c-0.044-0.285-0.7-0.963-2.45-0.941c-1.75,0.022-2.953,1.554-1.925,2.473c0.28,0.25,0.919,0.56,3.412,0.634
+ c2.209,0.065,2.078,1.685,1.531,2.275s-1.815,1.028-3.479,0.853c-1.188-0.125-1.75-0.769-1.707-1.031"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M347.376,444.819c-0.025,0.275-0.08,5.05,0.12,5.95c0.094,0.422,4.624,0.05,4.624,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M372.719,445.509c-0.045-0.285-0.701-0.963-2.451-0.941c-1.75,0.022-2.952,1.554-1.924,2.473c0.28,0.25,0.919,0.56,3.412,0.634
+ c2.209,0.065,2.078,1.685,1.531,2.275s-1.815,1.028-3.479,0.853c-1.188-0.125-1.75-0.769-1.707-1.031"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M379.817,444.791c0.027,0.188,0.107,5.707,0.134,5.922"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M379.817,444.771c1.771-0.24,3.465-0.189,4.506,0.744c1.012,0.908,1.765,3.461-0.135,4.715
+ c-1.444,0.955-3.941,0.539-3.941,0.539"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M424.283,444.744c0,0.125,0.075,5.9,0.125,6.051"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M423.56,450.844c0.226,0,1.852-0.05,2.002-0.075"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M423.282,444.694c0.175,0,2.102-0.075,2.202-0.051"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M429.373,444.879c0.719,0.672,3.648,4.96,3.648,4.96s2.083-3.745,2.592-4.704c0.51-0.959,0.864-0.191,0.862,0.448
+ c-0.001,0.275,0.227,5.111,0.187,5.262"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M429.13,444.619c-0.03,0.275,0.15,6.075,0.2,6.226"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M417.788,445.004c-0.036,0.274,0.044,5.69,0.092,5.842"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M414.98,444.853c0.109,0,5.535-0.132,5.688-0.132"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M395.295,444.918c0.163,1.553,1.506,5.206,1.741,5.644c0.166,0.31,0.484,0.67,0.784,0c0.354-0.787,1.188-3.071,1.415-3.815
+ c0.208-0.678,0.517-1.51,0.892-0.341c0.395,1.225,1.566,3.784,1.742,4.135c0.399,0.793,0.636,0.175,0.91-0.766
+ c0.274-0.941,1.524-4.966,1.361-5.054"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M389.855,444.672c-0.512,0.064-3.039,1.009-2.451,3.854c0.575,2.785,3.482,2.678,5.056,1.856
+ c1.472-0.769,1.731-3.341,0.74-4.461c-0.837-0.945-2.281-1.249-3.327-1.122"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M406.645,444.8c-0.031,0.275,0.059,5.841,0.109,5.99"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M406.836,444.992c0.719,0.672,4.022,4.977,4.022,4.977c0.8,0.929,1.344,1.185,1.663,0.448c0.32-0.736,0.091-5.258-0.049-5.813
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M439.583,444.819c-0.024,0.275,0,5.05,0.2,5.95c0.094,0.422,4.624,0.05,4.624,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M439.884,444.92c0.125-0.025,4.374-0.101,4.374-0.101"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M439.984,447.868c0.125-0.003,3.098-0.049,3.247-0.076"/>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M337.631,448.002c0.109,0,5.535-0.131,5.688-0.131"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M448.23,448.002c0.109,0,5.534-0.131,5.688-0.131"/>
+ </g>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M74.858,228.055c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M75.288,228.198c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M75.432,232.41c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M36.337,229.039c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M47.917,229.509c0.01-0.3-0.384-1.765-3.563-1.456c-2.708,0.263-3.831,3.173-3.793,4.743c0.038,1.571,0.92,3.946,4.291,3.946
+ c3.372,0,4.016-1.839,4.016-1.839"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M51.66,236.651c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M53.156,234.022c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M64.133,228.055c-0.036,0.394-0.113,7.214,0.173,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M91.837,229.039c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M68.501,241.947c0,0.179,0.107,8.43,0.179,8.644"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M67.468,250.661c0.321,0,2.646-0.071,2.859-0.107"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M67.071,241.876c0.25,0,3.003-0.107,3.146-0.071"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M83.058,241.727c0.233,0.188,3.794,4.781,4.261,5.281s1.606-0.854,3.006-2.875c0.772-1.115,1.663-2.406,1.488-2.656"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M87.587,247.276c0.031,0.25-0.031,3.531,0.063,3.688"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M34.858,242.055c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M35.288,242.198c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M35.432,246.41c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M44.66,250.651c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M46.156,248.022c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M62.337,243.039c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M75.633,242.055c-0.036,0.394-0.113,7.214,0.173,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+ </g>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M134.479,533.515c0.039,0.268,0.153,8.153,0.191,8.459"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M134.479,533.484c2.528-0.343,4.949-0.27,6.437,1.064c1.445,1.297,2.521,4.944-0.191,6.735c-2.064,1.363-5.632,0.77-5.632,0.77
+ "/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M87.773,533.64c1.026,0.961,5.212,7.086,5.212,7.086s2.975-5.35,3.702-6.72c0.728-1.371,1.234-0.274,1.231,0.64
+ c-0.001,0.394,0.323,7.303,0.268,7.517"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M87.426,533.269c-0.044,0.394,0.214,8.68,0.286,8.894"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M68.223,533.818c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M64.212,533.602c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M52.368,533.445c-0.188-0.031-0.281,3.438,0,5.719s2.188,3.125,4.125,3.094s3.625-1.094,3.781-3.125s-0.125-5.781-0.125-5.781"
+ />
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M78.319,533.345c-0.731,0.092-4.343,1.441-3.502,5.507c0.821,3.978,4.975,3.825,7.223,2.651
+ c2.102-1.098,2.473-4.772,1.057-6.372c-1.196-1.352-3.259-1.785-4.753-1.604"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M40.66,542.151c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M42.156,539.522c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M102.16,542.151c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M103.656,539.522c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M116.223,533.818c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M112.212,533.602c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M123.858,533.555c-0.035,0.394,0,7.214,0.286,8.501c0.134,0.603,6.605,0.071,6.605,0.071"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M124.288,533.698c0.178-0.036,6.248-0.144,6.248-0.144"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M124.432,537.91c0.179-0.004,4.425-0.07,4.639-0.109"/>
+ </g>
+ </g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M99.14,547.269c-0.044,0.394,0.215,8.68,0.286,8.894"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M99.26,553.125c0.091-0.319,4.937-2.88,6.674-5.805"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M101.956,551.251c2.012,1.737,4.755,4.435,4.801,4.983"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M93.837,548.539c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M69.223,547.818c-0.052,0.393,0.063,8.13,0.131,8.346"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M65.212,547.602c0.156,0,7.906-0.188,8.125-0.188"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M75.66,556.151c0,0,3.085-7.45,3.244-7.909c0.307-0.881,1.178-0.71,1.599,0.199c0.979,2.115,3.664,7.71,3.664,7.71"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M77.156,553.522c0.153,0,5.112-0.269,5.112-0.269"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M116.837,548.539c-0.063-0.406-1-1.375-3.5-1.344s-4.219,2.219-2.75,3.531c0.401,0.358,1.313,0.801,4.875,0.906
+ c3.156,0.094,2.969,2.406,2.188,3.25s-2.594,1.469-4.969,1.219c-1.696-0.179-2.501-1.098-2.438-1.473"/>
+ </g>
+ <g>
+ <g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M78.811,562.819c-0.025,0.275,0,5.05,0.2,5.95c0.094,0.422,4.624,0.05,4.624,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M79.111,562.92c0.124-0.025,4.373-0.101,4.373-0.101"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M79.211,565.868c0.126-0.003,3.098-0.049,3.248-0.076"/>
+ </g>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M123.515,563.004c-0.036,0.274,0.044,5.691,0.092,5.842"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M120.707,562.853c0.11,0,5.535-0.132,5.688-0.132"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M67.772,562.918c0.163,1.553,1.505,5.206,1.74,5.644c0.167,0.31,0.484,0.67,0.785,0c0.353-0.787,1.188-3.071,1.415-3.815
+ c0.207-0.678,0.516-1.51,0.892-0.341c0.394,1.225,1.565,3.784,1.742,4.135c0.398,0.793,0.635,0.175,0.909-0.766
+ c0.274-0.941,1.524-4.966,1.362-5.054"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M118.345,563.509c-0.044-0.285-0.7-0.963-2.45-0.941c-1.75,0.022-2.952,1.554-1.925,2.473c0.281,0.25,0.92,0.56,3.413,0.634
+ c2.209,0.065,2.078,1.685,1.531,2.275s-1.815,1.028-3.479,0.853c-1.188-0.125-1.751-0.769-1.707-1.031"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M102.301,563.837c0.008-0.21-0.268-1.235-2.494-1.02c-1.896,0.185-2.682,2.222-2.654,3.32c0.026,1.1,0.644,2.763,3.003,2.763
+ c2.36,0,2.812-1.287,2.812-1.287"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M61.982,562.672c-0.512,0.064-3.039,1.009-2.451,3.854c0.575,2.785,3.482,2.678,5.057,1.856
+ c1.471-0.769,1.73-3.341,0.739-4.461c-0.837-0.945-2.281-1.249-3.327-1.122"/>
+ <g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M86.547,563.004c-0.036,0.274,0.044,5.691,0.091,5.842"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M86.674,562.853c0.458-0.1,2.438-0.209,3.678-0.004c1.92,0.318,2.123,2.444,0.232,3.023c-0.758,0.231-3.512,0.3-3.512,0.3"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" x1="89.339" y1="566.09" x2="91.724" y2="568.862"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M53.103,562.819c-0.025,0.275-0.079,5.05,0.121,5.95c0.094,0.422,4.624,0.05,4.624,0.05"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M107.482,562.672c-0.512,0.064-3.039,1.009-2.451,3.854c0.575,2.785,3.482,2.678,5.056,1.856
+ c1.472-0.769,1.731-3.341,0.74-4.461c-0.837-0.945-2.281-1.249-3.327-1.122"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M133.745,563.509c-0.044-0.285-0.7-0.963-2.45-0.941c-1.75,0.022-2.953,1.554-1.925,2.473c0.28,0.25,0.92,0.56,3.412,0.634
+ c2.21,0.065,2.079,1.685,1.531,2.275c-0.546,0.591-1.815,1.028-3.478,0.853c-1.188-0.125-1.751-0.769-1.707-1.031"/>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M42.131,565.502c0.109,0,5.535-0.131,5.688-0.131"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#231F20" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M139.131,565.502c0.109,0,5.535-0.131,5.688-0.131"/>
+ </g>
+ </g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M272.784,76.127c7.216,0,13.041,3.474,14.431,8.756c0.472,1.792-0.218,3.177-1.49,3.177s-1.961-1.385-1.489-3.177
+ c1.39-5.282,7.247-8.756,14.462-8.756s13.072,3.474,14.462,8.756c0.472,1.792-0.218,3.177-1.49,3.177s-1.961-1.385-1.489-3.177
+ c1.39-5.282,7.246-8.756,14.462-8.756c7.215,0,13.071,3.474,14.462,8.756c0.472,1.792-0.218,3.177-1.49,3.177
+ c-1.273,0-1.988-1.393-1.49-3.177c1.016-3.633,5.837-6.771,10.767-6.687c3.551,0.061,6.987,1.622,9.63,4.266
+ c2.644,2.644,4.206,6.079,4.266,9.63c0.084,4.931-3.053,9.751-6.687,10.767c-1.784,0.499-3.177-0.217-3.177-1.489
+ c0-1.273,1.385-1.962,3.177-1.491c4.455,1.173,7.623,5.521,8.507,11.182"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M362.505,144.234c-1.021,5.333-4.129,9.387-8.406,10.513c-1.792,0.471-3.177-0.219-3.177-1.491s1.385-1.961,3.177-1.489
+ c5.283,1.391,8.757,7.247,8.757,14.462s-3.474,13.072-8.757,14.462c-1.792,0.472-3.177-0.218-3.177-1.49s1.385-1.961,3.177-1.489
+ c5.283,1.39,8.757,7.247,8.757,14.462s-3.474,13.071-8.757,14.462c-1.792,0.472-3.177-0.218-3.177-1.49s1.385-1.961,3.177-1.49
+ c5.283,1.391,8.757,7.247,8.757,14.463c0,7.215-3.474,13.071-8.757,14.462c-1.792,0.472-3.177-0.218-3.177-1.49
+ c0-1.273,1.385-1.962,3.177-1.49c5.283,1.391,8.757,7.247,8.757,14.462c0,7.216-3.474,13.072-8.757,14.463
+ c-1.792,0.471-3.177-0.219-3.177-1.491s1.385-1.961,3.177-1.489c5.283,1.39,8.757,7.247,8.757,14.462
+ c0,2.493-0.415,4.824-1.176,6.865"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M362.871,455.773c0,0.657-0.045,1.304-0.102,1.936"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M362.785,457.709c-0.563,6.31-3.885,11.264-8.687,12.527c-1.792,0.471-3.177-0.218-3.177-1.491c0-1.272,1.385-1.961,3.177-1.489
+ c5.283,1.391,8.757,7.247,8.757,14.462s-3.474,13.072-8.757,14.462c-1.792,0.472-3.177-0.218-3.177-1.49s1.393-1.988,3.177-1.489
+ c3.634,1.016,6.771,5.836,6.687,10.766c-0.061,3.552-1.622,6.987-4.266,9.631c-2.643,2.643-6.079,4.205-9.63,4.266
+ c-4.93,0.083-9.751-3.054-10.767-6.688c-0.498-1.783,0.217-3.177,1.49-3.177c1.272,0,1.962,1.386,1.49,3.177
+ c-1.391,5.283-7.247,8.757-14.462,8.757c-7.216,0-13.072-3.474-14.462-8.757c-0.472-1.791,0.218-3.177,1.49-3.177
+ s1.961,1.386,1.489,3.177c-1.39,5.283-7.247,8.757-14.462,8.757c-1.142,0-2.249-0.087-3.311-0.254"/>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#1E83D8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="3.8637" d="
+ M359.133,392.604c2.361,2.69,3.723,6.714,3.723,11.281c0,2.069-0.286,4.027-0.818,5.801"/>
+ </g>
+</g>
+</svg>
diff --git a/integration/apidocs/src/resources/images/Create_CS.png b/integration/apidocs/src/resources/images/Create_CS.png Binary files differnew file mode 100644 index 00000000..32c291b3 --- /dev/null +++ b/integration/apidocs/src/resources/images/Create_CS.png diff --git a/integration/apidocs/src/resources/images/phpMyAdmin.png b/integration/apidocs/src/resources/images/phpMyAdmin.png Binary files differnew file mode 100644 index 00000000..80f909f9 --- /dev/null +++ b/integration/apidocs/src/resources/images/phpMyAdmin.png diff --git a/integration/apidocs/src/resources/samples/db-auth-20.json b/integration/apidocs/src/resources/samples/db-auth-20.json new file mode 100644 index 00000000..43a99e1f --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-auth-20.json @@ -0,0 +1,128 @@ +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 477 +Date: Thu, 12 Apr 2012 18:45:13 GMT + +{ + "access": { + + "token": { + "expires": "2011-12-08T22:51:02.000-06:00", + "id": "vvvvvvvv-wwww-xxxx-yyyy-zzzzzzzzzzzz" + }, + "user": { + "id": "123456", + "name": "jsmith", + "roles": [ + { + "description": "Admin Role.", + "id": "identity:admin", + "name": "identity:admin" + }, + { + "description": "Default Role.", + "id": "identity:default", + "name": "identity:default" + } + ] + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "publicURL": "https://ord.databases.api.rackspacecloud.com/v1.0/1100111", + "region": "ORD", + "tenantId": "1100111" + }, + { + "publicURL": "https://dfw.databases.api.rackspacecloud.com/v1.0/1100111", + "region": "DFW", + "tenantId": "1100111" + } + ], + "name": "cloudDatabases", + "type": "rax:database" + }, + { + "endpoints": [ + { + "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1100111", + "region": "DFW", + "tenantId": "1100111" + }, + { + "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1100111", + "region": "ORD", + "tenantId": "1100111" + } + ], + "name": "cloudLoadBalancers", + "type": "rax:load-balancer" + }, + { + "endpoints": [ + { + "tenantId": "1100111", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1100111", + "versionId": "1.0", + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/" + } + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "tenantId": "1100111", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1100111", + "versionId": "1.0", + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/" + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "tenantId": "MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee", + "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee", + "region": "ORD", + "versionId": "1", + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/" + + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "tenantId": "MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee", + "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee", + "region": "ORD" + } + ], + "name": "cloudFilesCDN", + "type": "rax:object-cdn" + }, + { + + "endpoints": [ + { + "tenantId": "1100111", + "publicURL": "https://dns.api.rackspacecloud.com/v1.0/1100111" + } + ], + "name": "cloudDNS", + "type": "rax:dns" + } + ] + } +} diff --git a/integration/apidocs/src/resources/samples/db-auth-20.xml b/integration/apidocs/src/resources/samples/db-auth-20.xml new file mode 100644 index 00000000..e0bc3b8c --- /dev/null +++ b/integration/apidocs/src/resources/samples/db-auth-20.xml @@ -0,0 +1,63 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=UTF-8 +Content-Length: 477 +Date: Thu, 12 Apr 2012 18:50:20 GMT + +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<access xmlns="http://docs.openstack.org/identity/api/v2.0" + xmlns:ns2="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + xmlns:ns3="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0" + xmlns:ns4="http://docs.openstack.org/common/api/v1.0" + xmlns:ns5="http://www.w3.org/2005/Atom"> + <token id="vvvvvvvv-wwww-xxxx-yyyy-zzzzzzzzzzzz" expires="2011-12-08T22:51:02.000-06:00"/> + <user id="123456" name="jsmith"> + <roles> + <role id="identity:admin" name="identity:admin" description="Admin Role."/> + <role id="identity:default" name="identity:default" description="Default Role."/> + </roles> + </user> + <serviceCatalog> + <service type="rax:database" name="cloudDatabases"> + <endpoint region="ORD" tenantId="1100111" publicURL="https://ord.databases.api.rackspacecloud.com/v1.0/1100111"/> + <endpoint region="DFW" tenantId="1100111" publicURL="https://dfw.databases.api.rackspacecloud.com/v1.0/1100111"/> + </service> + <service type="rax:load-balancer" name="cloudLoadBalancers"> + <endpoint region="ORD" tenantId="1100111" publicURL="https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1100111"/> + <endpoint region="DFW" tenantId="1100111" publicURL="https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1100111"/> + </service> + <service type="compute" name="cloudServersOpenStack"> + <endpoint region="DFW" tenantId="1100111" + publicURL="https://dfw.servers.api.rackspacecloud.com/v2/1100111"> + <version id="2" info="https://dfw.servers.api.rackspacecloud.com/v2/" + list="https://dfw.servers.api.rackspacecloud.com/" /> + </endpoint> + </service> + <service type="compute" name="cloudServers"> + <endpoint tenantId="1100111" + publicURL="https://servers.api.rackspacecloud.com/v1.0/1100111"> + <version id="1.0" + info="https://servers.api.rackspacecloud.com/v1.0/" + list="https://servers.api.rackspacecloud.com/"/> + </endpoint> + </service> + <service type="object-store" name="cloudFiles"> + <endpoint region="ORD" + tenantId="MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee" + publicURL="https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee" + internalURL="https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee"/> + </service> + <service type="rax:object-cdn" name="cloudFilesCDN"> + <endpoint region="ORD" + tenantId="MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee" + publicURL="https://cdn2.clouddrive.com/v1/MossoCloudFS_aaaaaaaa-bbbb-cccc-dddd-eeeeeeee"> + <version id="1" + info="https://cdn2.clouddrive.com/v1/" + list="https://cdn2.clouddrive.com/"/> + </endpoint> + </service> + <service type="rax:dns" name="cloudDNS"> + <endpoint tenantId="1100111" + publicURL="https://dns.api.rackspacecloud.com/v1.0/1100111"/> + </service> + </serviceCatalog> +</access> 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 00000000..583b584c --- /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 00000000..de97e59a --- /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 + +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<auth xmlns="http://docs.rackspacecloud.com/auth/api/v1.1"> + <token id="aaaaa-bbbbbb-cccccc-ddddd" + expires="2012-04-12T13:15:52.000-05:00" /> + <serviceCatalog> + <service name="cloudDatabases"> + <endpoint region="ORD" v1Default="false" + publicURL="https://ord.databases.api.rackspacecloud.com/v1.0/1234"/> + <endpoint region="DFW" v1Default="false" + publicURL="https://dfw.databases.api.rackspacecloud.com/v1.0/1234"/> + </service> + <service name="cloudDNS"> + <endpoint v1Default="false" publicURL="https://dns.api.rackspacecloud.com/v1.0/1234"/> + </service> + <service name="cloudServers"> + <endpoint v1Default="true" + publicURL="https://servers.api.rackspacecloud.com/v1.0/1234"/> + </service> + <service name="cloudLoadBalancers"> + <endpoint region="DFW" v1Default="false" + publicURL="https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234"/> + <endpoint region="ORD" v1Default="false" + publicURL="https://ord.loadbalancers.api.rackspacecloud.com/v1.0/1234"/> + </service> + <service name="cloudMonitoring"> + <endpoint v1Default="false" + publicURL="https://monitoring.api.rackspacecloud.com/v1.0/1234"/> + </service> + <service name="cloudServersOpenStack"> + <endpoint region="DFW" v1Default="false" + publicURL="https://dfw.servers.api.rackspacecloud.com/v2/1234"/> + </service> + <service name="cloudFiles"> + <endpoint region="DFW" v1Default="true" + publicURL="https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_aaaa-bbbbbb-ccccc-ddddd" + internalURL="https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_aaaa-bbbbbb-ccccc-ddddd" + /> + </service> + <service name="cloudFilesCDN"> + <endpoint region="DFW" v1Default="true" + publicURL="https://cdn1.clouddrive.com/v1/MossoCloudFS_aaaa-bbbbbb-ccccc-ddddd" + /> + </service> + </serviceCatalog> +</auth> 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 00000000..79a6d18e --- /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 00000000..46f2cf29 --- /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 00000000..3e121e4e --- /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 00000000..fd75a745 --- /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 + +<rootEnabled xmlns="http://docs.openstack.org/database/api/v1.0">True</rootEnabled> 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 00000000..78b17432 --- /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 00000000..9f0cc355 --- /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 + +<instance created="2011-11-03T15:57:52Z" + hostname="d735f61b985bb003a61dc72948dbf4e7174da12c.rackspaceclouddb.com" + id="00ff11ee-22dd-33cc-44bb-55aa66007799" name="myrackinstance" status="BUILD" updated="2011-11-03T15:57:55Z" + xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/00ff11ee-22dd-33cc-44bb-55aa66007799" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/00ff11ee-22dd-33cc-44bb-55aa66007799" rel="bookmark" + /> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" + rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" + rel="bookmark"/> + </links> + </flavor> +</instance> 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 00000000..1ef2bab6 --- /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 00000000..a8e584d0 --- /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 + +<?xml version="1.0" ?> +<databases xmlns="http://docs.openstack.org/database/api/v1.0"> + <database name="exampledb" character_set="utf8" collate="utf8_general_ci" /> + <database name="anotherexampledb" /> + <database name="oneMoreExampledb" /> +</databases> 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 00000000..6a83f7fc --- /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 00000000..3a2ce5f6 --- /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 00000000..cbcfc90d --- /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 00000000..7f19a4e7 --- /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 + +<?xml version="1.0" ?> +<instance flavorRef="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" name="xml_rack_instance" xmlns="http://docs.openstack.org/database/api/v1.0"> + <databases> + <database character_set="utf8" collate="utf8_general_ci" name="sampledb"/> + <database name="nextround"/> + </databases> + <users> + <user name="demouser" password="demopassword"> + <databases> + <database name="sampledb"/> + </databases> + </user> + </users> + <volume size="2"/> +</instance> + 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 00000000..f5383ce2 --- /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 00000000..8ed9e306 --- /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 + +<?xml version="1.0" ?> +<instance created="2012-01-25T21:53:18Z" hostname="e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com" id="692d8418-7a8f-47f1-8060-59846c6e024f" name="xml_rack_instance" status="BUILD" updated="2012-01-25T21:53:22Z" xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/692d8418-7a8f-47f1-8060-59846c6e024f" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/692d8418-7a8f-47f1-8060-59846c6e024f" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> +</instance> 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 00000000..022a12b2 --- /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 00000000..2f241665 --- /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 + +<?xml version="1.0" ?> +<users xmlns="http://docs.openstack.org/database/api/v1.0"> + <user name="testuser" password="password"> + <databases> + <database name="databaseC"/> + </databases> + </user> + <user name="userwith2dbs" password="password"> + <databases> + <database name="databaseA"/> + <database name="databaseB"/> + </databases> + </user> +</users> 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 00000000..6a83f7fc --- /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 00000000..9b6fa1dc --- /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 00000000..1ae90cd4 --- /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 00000000..cd3298b4 --- /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 + +<?xml version="1.0" encoding="UTF-8"?> +<auth> + <apiKeyCredentials + xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + username="jsmith" + apiKey="aaaaa-bbbbb-ccccc-12345678"/> +</auth> 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 00000000..bf15936f --- /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 00000000..ae7f8971 --- /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 + +<?xml version="1.0" encoding="UTF-8"?> +<credentials xmlns="http://docs.rackspacecloud.com/auth/api/v1.1" + username="hub_cap" + key="a86850deb2742ec3cb41518e26aa2d89"/> 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 00000000..eb958e95 --- /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 00000000..94cfc0bd --- /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 00000000..6a83f7fc --- /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 00000000..3a2ce5f6 --- /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 00000000..fdf11e64 --- /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 00000000..b960fbf9 --- /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 00000000..a1ecb28d --- /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 00000000..3dcda464 --- /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 00000000..76ba9a60 --- /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 00000000..3be87be7 --- /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 00000000..e5f31146 --- /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 00000000..9b6fa1dc --- /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 00000000..85ba6379 --- /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 00000000..66485f81 --- /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 00000000..a7868ee7 --- /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 00000000..c2f99a97 --- /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 + +<user name="root" password="7dbfe27c-789f-4880-967c-7c6e601c4166" xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..fcb4571a --- /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 00000000..6067a4d9 --- /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 + +<badRequest code="400" xmlns="http://docs.openstack.org/database/api/v1.0"> + <message> + Volume 'size' needs to be a positive integer value, -1.0 cannot be accepted. + </message> +</badRequest> 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 00000000..97299fc4 --- /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 00000000..4864b0b4 --- /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 + +<instanceFault code="500" xmlns="http://docs.openstack.org/database/api/v1.0"> + <message> + The server has either erred or is incapable of performing the requested operation. + </message> +</instanceFault> 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 00000000..866329b5 --- /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 00000000..806bc2c7 --- /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 + +<itemNotFound code="404" xmlns="http://docs.openstack.org/database/api/v1.0"> + <message> + The resource could not be found. + </message> +</itemNotFound> 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 00000000..1944f3ae --- /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 00000000..d2beb31d --- /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 00000000..685d02ca --- /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 00000000..d1d5fec0 --- /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 + +<flavor id="1" name="m1.tiny" ram="512" xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> +</flavor> 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 00000000..0d6817a5 --- /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 00000000..7981dd75 --- /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 00000000..901d65ab --- /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 00000000..516da137 --- /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 + +<flavors xmlns="http://docs.openstack.org/database/api/v1.0"> + <flavor id="1" name="m1.tiny" ram="512"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> + <flavor id="2" name="m1.small" ram="1024"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/2" rel="bookmark"/> + </links> + </flavor> + <flavor id="3" name="m1.medium" ram="2048"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/3" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/3" rel="bookmark"/> + </links> + </flavor> + <flavor id="4" name="m1.large" ram="4096"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/4" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/4" rel="bookmark"/> + </links> + </flavor> +</flavors> + 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 00000000..69f69b41 --- /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 00000000..967a5dd8 --- /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 + +<instance created="2012-04-05T16:17:29Z" + hostname="8ba52fad9ec4aab5d75eabd1136c9750f64233ba.rackspaceclouddb.com" + id="d379ba5c-9a1f-4aa9-9a17-afe237d04c65" name="myrackinstance" status="BUILD" updated="2012-04-05T16:17:29Z" + xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="http://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/d379ba5c-9a1f-4aa9-9a17-afe237d04c65" rel="self"/> + <link href="http://ord.databases.api.rackspacecloud.com/instances/d379ba5c-9a1f-4aa9-9a17-afe237d04c65" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="http://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="http://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> +</instance> + 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 00000000..9d03d2ba --- /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 00000000..7d64bdda --- /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 + +<?xml version="1.0" ?> +<update xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..1b029992 --- /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 00000000..99f5908a --- /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 00000000..892243ae --- /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 00000000..b93a9f9e --- /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 + +<?xml version="1.0" ?> +<reboot xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..2308734c --- /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 00000000..b8ad7430 --- /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 00000000..c80b418c --- /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 00000000..274ef34f --- /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 + +<?xml version="1.0" ?> +<resize flavorRef="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/3" xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..fe14f4dd --- /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 00000000..6271164b --- /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 00000000..ec1ef6d5 --- /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 00000000..fd51dc85 --- /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 + +<?xml version="1.0" ?> +<resize xmlns="http://docs.openstack.org/database/api/v1.0"> + <flavorRef>https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2</flavorRef> +</resize> + 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 00000000..48a2e91a --- /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 00000000..549a5f80 --- /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 00000000..7cec8fd8 --- /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 00000000..fe567b35 --- /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 + +<?xml version="1.0" ?> +<resize xmlns="http://docs.openstack.org/database/api/v1.0"> + <volume size="4"/> +</resize> + 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 00000000..319bbadb --- /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 00000000..e7c61103 --- /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 00000000..ee27681a --- /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 00000000..1f2165af --- /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 + +<?xml version="1.0" encoding="UTF-8"?> +<restart xmlns="http://docs.openstack.org/database/api/v1.0"/> 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 00000000..e5f31146 --- /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 00000000..9b6fa1dc --- /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 00000000..b76d4044 --- /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 00000000..08090d33 --- /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 00000000..e4e2de53 --- /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 00000000..2b839e2e --- /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 + +<?xml version="1.0" ?> +<instance created="2012-03-28T21:30:50Z" hostname="e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com" id="da6fb68c-190d-4520-af04-ecb1e2b3e850" name="json_rack_instance" status="ACTIVE" updated="2012-03-28T21:34:17Z" xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/da6fb68c-190d-4520-af04-ecb1e2b3e850" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/da6fb68c-190d-4520-af04-ecb1e2b3e850" rel="bookmark"/> + </links> + <volume size="2" used="0.124542236328"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> +</instance> + 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 00000000..c73ff9f2 --- /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 00000000..a146f788 --- /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 + +<?xml version="1.0" ?> +<update xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..23bd9c12 --- /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 00000000..23bd9c12 --- /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 00000000..4a55cfb7 --- /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 00000000..bd9a930c --- /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 00000000..bdaf6b4f --- /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 00000000..4aab91cc --- /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 + +<instances xmlns="http://docs.openstack.org/database/api/v1.0"> + <instance id="13d940c4-70bb-4ff4-8866-6ee9ab5e5cae" name="xml_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/a9f6a78d736a41d89177e0367d96ee2e/instances/13d940c4-70bb-4ff4-8866-6ee9ab5e5cae" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/13d940c4-70bb-4ff4-8866-6ee9ab5e5cae" rel="bookmark"/> + </links> + </instance> + <instance id="4137d6a4-03b7-4b66-b0ef-8c7c35c470d3" name="xml_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/a9f6a78d736a41d89177e0367d96ee2e/instances/4137d6a4-03b7-4b66-b0ef-8c7c35c470d3" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/4137d6a4-03b7-4b66-b0ef-8c7c35c470d3" rel="bookmark"/> + </links> + </instance> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances?marker=4137d6a4-03b7-4b66-b0ef-8c7c35c470d3&limit=2" rel="next" /> + </links> +</instances> + 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 00000000..ef941e56 --- /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 00000000..f0316974 --- /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 00000000..5a885c74 --- /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 00000000..c5e3ea12 --- /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 + +<instances xmlns="http://docs.openstack.org/database/api/v1.0"> + <instance id="28d1b8f3-172a-4f6d-983d-36021508444a" name="json_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/28d1b8f3-172a-4f6d-983d-36021508444a" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/28d1b8f3-172a-4f6d-983d-36021508444a" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> + </instance> + <instance id="8fb081af-f237-44f5-80cc-b46be1840ca9" name="xml_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/8fb081af-f237-44f5-80cc-b46be1840ca9" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/8fb081af-f237-44f5-80cc-b46be1840ca9" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> + </instance> +</instances> + 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 00000000..d74ea74c --- /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 00000000..d7fd8763 --- /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 00000000..d8de6465 --- /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 00000000..05dc5261 --- /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 + +<instances xmlns="http://docs.openstack.org/database/api/v1.0"> + </instance> + <instance id="28290db6-2f47-80d4-1b62-92bde6ff1a6e" name="example_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/28290db6-2f47-80d4-1b62-92bde6ff1a6e" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/28290db6-2f47-80d4-1b62-92bde6ff1a6e" rel="bookmark"/> + </links> + </instance> + <instance id="ae939ae2-3827-d005-4de6-0028a5f438d0" name="example_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/ae939ae2-3827-d005-4de6-0028a5f438d0" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/ae939ae2-3827-d005-4de6-0028a5f438d0" rel="bookmark"/> + </links> + </instance> + <instance id="006e4db2-5729-3f8e-77b0-075a1723ad37" name="example_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/006e4db2-5729-3f8e-77b0-075a1723ad37" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/006e4db2-5729-3f8e-77b0-075a1723ad37" rel="bookmark"/> + </links> + </instance> + <instance id="2806d85e-d363-2a27-f7fd-4e9911ebd8a9" name="example_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/2806d85e-d363-2a27-f7fd-4e9911ebd8a9" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/2806d85e-d363-2a27-f7fd-4e9911ebd8a9" rel="bookmark"/> + </links> + </instance> + <instance id="e0846e3b-3df4-398f-1e9a-d53650573dd1" name="example_rack_instance" status="ACTIVE"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/e0846e3b-3df4-398f-1e9a-d53650573dd1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/e0846e3b-3df4-398f-1e9a-d53650573dd1" rel="bookmark"/> + </links> + </instance> +</instances> +<links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances?marker=e0846e3b-3df4-398f-1e9a-d53650573dd1&limit=5" rel="next" /> +</links> 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 00000000..b7a3db2f --- /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 00000000..d0e2bb86 --- /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 00000000..879da3d0 --- /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 00000000..d6643d6b --- /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 + +<databases xmlns="http://docs.openstack.org/database/api/v1.0"> + <database name="anotherexampledb"/> + <database name="exampledb"/> + <database name="nextround"/> + <database name="sampledb"/> + <database name="testingdb"/> +</databases> 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 00000000..43c813d0 --- /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 00000000..0b9a2bb5 --- /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 00000000..e2a94c60 --- /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 00000000..69a60ede --- /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 + +<users xmlns="http://docs.openstack.org/database/api/v1.0"> + <user name="userwith2dbs"> + <databases> + <database name="databaseA"/> + <database name="databaseB"/> + </databases> + </user> + <user name="testuser"> + <databases/> + </user> + <user name="userwith3dbs"> + <databases> + <database name="databaseD"/> + <database name="databaseE"/> + <database name="databaseF"/> + </databases> + </user> +</users> + 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 00000000..fc35c397 --- /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 00000000..467501c2 --- /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 00000000..8080f2e8 --- /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 00000000..ae735efe --- /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 + +<account id="1234" xmlns="http://docs.openstack.org/database/api/v1.0"> + <instances> + <instance host="hostname" id="6e038c18-902f-4a41-b2a2-5f46fa62fc1b" name="json_rack_instance" status="ACTIVE"/> + <instance host="hostname" id="72eed896-134e-4212-9f25-f81b4e0795e2" name="xml_rack_instance" status="ACTIVE"/> + </instances> +</account> + 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 00000000..b30ee85b --- /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 00000000..d2b20fd8 --- /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 00000000..6466a5ef --- /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 00000000..c12da22a --- /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 + +<host name="hostname" percentUsed="51" totalRAM="2004" usedRAM="1024" xmlns="http://docs.openstack.org/database/api/v1.0"> + <instances> + <instance id="6e038c18-902f-4a41-b2a2-5f46fa62fc1b" name="json_rack_instance" server_id="64438fb6-4b98-4bd8-975e-222bc11daf74" status="ACTIVE" tenant_id="1234"/> + <instance id="72eed896-134e-4212-9f25-f81b4e0795e2" name="xml_rack_instance" server_id="6d93bc48-f736-41db-9ad7-c8594aac25fb" status="ACTIVE" tenant_id="1234"/> + </instances> +</host> + 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 00000000..b0de9507 --- /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 00000000..dd2ff53c --- /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 00000000..3843ad83 --- /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 00000000..eaa95339 --- /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 + +<instance created="2012-07-24 19:48:27.945748" deleted="False" deleted_at="None" host="hostname" id="72eed896-134e-4212-9f25-f81b4e0795e2" 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-24 19:48:30.016100" xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/72eed896-134e-4212-9f25-f81b4e0795e2" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/72eed896-134e-4212-9f25-f81b4e0795e2" rel="bookmark"/> + </links> + <volume id="VOL_dcafc495-16f3-4227-a150-79f2db965817" size="2"/> + <guest_status state_description="running"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> +</instance> + 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 00000000..551d6f08 --- /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 00000000..72f4c156 --- /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 00000000..3c5d8150 --- /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 00000000..87458a10 --- /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 + +<root_history enabled="Never" id="72eed896-134e-4212-9f25-f81b4e0795e2" user="Nobody" xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..28a7a894 --- /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 00000000..6b8ba360 --- /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 00000000..3f5e7e7d --- /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 00000000..e154a269 --- /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 + +<devices xmlns="http://docs.openstack.org/database/api/v1.0"> + <device name="fake_storage" type="test_type" used="10"> + <provision available="40" percent="10" total="50"/> + <capacity available="90" total="100"/> + </device> +</devices> + 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 00000000..56f6ce43 --- /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 00000000..2662bd28 --- /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 + +<?xml version="1.0" ?> +<update xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..0574b12a --- /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 00000000..bcea5bff --- /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 00000000..89ba6311 --- /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 00000000..cfaa3ea0 --- /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 00000000..2755708d --- /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 00000000..edd8f0e1 --- /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 + +<diagnostics fdSize="64" threads="2" version="1" vmHwm="2872" vmPeak="29160" vmRss="2872" vmSize="29096" xmlns="http://docs.openstack.org/database/api/v1.0"/> + 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 00000000..c879a96d --- /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 00000000..18243538 --- /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 00000000..613f82c8 --- /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 00000000..129d41f0 --- /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 + +<hwinfo xmlns="http://docs.openstack.org/database/api/v1.0"> + <num_cpus> + 1 + </num_cpus> + <mem_total> + 524288 + </mem_total> +</hwinfo> + + 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 00000000..0b8e59eb --- /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 00000000..0de1eb4c --- /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 00000000..9486f6bb --- /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 00000000..c607012d --- /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 + +<instances xmlns="http://docs.openstack.org/database/api/v1.0"> + <instance created="2012-07-24 19:48:27.675753" deleted="False" deleted_at="None" host="hostname" id="6e038c18-902f-4a41-b2a2-5f46fa62fc1b" 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-24 19:48:29.957559"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/6e038c18-902f-4a41-b2a2-5f46fa62fc1b" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> + </instance> + <instance created="2012-07-24 19:48:27.945748" deleted="False" deleted_at="None" host="hostname" id="72eed896-134e-4212-9f25-f81b4e0795e2" 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-24 19:48:30.016100"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/72eed896-134e-4212-9f25-f81b4e0795e2" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/72eed896-134e-4212-9f25-f81b4e0795e2" rel="bookmark"/> + </links> + <volume size="2"/> + <flavor id="1"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/1" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/1" rel="bookmark"/> + </links> + </flavor> + </instance> +</instances> + 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 00000000..6d253741 --- /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 00000000..08641a9e --- /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 00000000..d65c4e20 --- /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 00000000..99dded2c --- /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 + +<accounts xmlns="http://docs.openstack.org/database/api/v1.0"> + <account id="1234" num_instances="2"/> +</accounts> + 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 00000000..a8f7b2c2 --- /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 00000000..a7c26dd5 --- /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 00000000..db62c381 --- /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 00000000..42af1c3f --- /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 + +<hosts xmlns="http://docs.openstack.org/database/api/v1.0"> + <host instanceCount="2" name="hostname"/> +</hosts> + 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 00000000..44bd6d67 --- /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 00000000..957b65c1 --- /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 + +<instance created="2011-11-03T15:57:52Z" + hostname="d735f61b985bb003a61dc72948dbf4e7174da12c.rackspaceclouddb.com" + id="00ff11ee-22dd-33cc-44bb-55aa66007799" name="orion" status="BUILD" updated="2011-11-03T15:57:55Z" + xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/instances/00ff11ee-22dd-33cc-44bb-55aa66007799" rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/instances/00ff11ee-22dd-33cc-44bb-55aa66007799" rel="bookmark" + /> + </links> + <volume size="2"/> + <flavor id="2"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/1234/flavors/2" + rel="self"/> + <link href="https://ord.databases.api.rackspacecloud.com/flavors/2" + rel="bookmark"/> + </links> + </flavor> +</instance> 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 00000000..45e1b1b6 --- /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 00000000..eb4ce6c7 --- /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 00000000..d5618703 --- /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 00000000..d978f232 --- /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 + +<version id="v1.0" status="CURRENT" updated="2012-01-01T00:00:00Z" xmlns="http://docs.openstack.org/database/api/v1.0"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/" rel="self"/> + </links> +</version> 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 00000000..719a5566 --- /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 00000000..0b9d413a --- /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 00000000..0028b4e4 --- /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 00000000..6ec42452 --- /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 + +<versions xmlns="http://docs.openstack.org/database/api/v1.0"> + <version id="v1.0" status="CURRENT" updated="2012-01-01T00:00:00Z"> + <links> + <link href="https://ord.databases.api.rackspacecloud.com/v1.0/" rel="self"/> + </links> + </version> +</versions> diff --git a/integration/scripts/conf.json.example b/integration/scripts/conf.json.example new file mode 100644 index 00000000..ee6bc7bf --- /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 00000000..5a2f0acc --- /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 00000000..5a2f0acc --- /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 00000000..65bd382c --- /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 00000000..a842ea02 --- /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 00000000..0e29d114 --- /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 00000000..0e0aae02 --- /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 00000000..eac3c579 --- /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 00000000..eac3c579 --- /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 00000000..8033f582 --- /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 00000000..eac3c579 --- /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 00000000..65bd382c --- /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 00000000..eff96dea --- /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 00000000..10fc2de1 --- /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 00000000..242d7fa1 --- /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 00000000..7b1b3767 --- /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 00000000..c94e00ea --- /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 00000000..e286d684 --- /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 00000000..97a5e438 --- /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 00000000..960342e3 --- /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 00000000..63453a75 --- /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 00000000..98fb24ba --- /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 00000000..bec065ef --- /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 00000000..a4b666bf --- /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 00000000..80c1e65c --- /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 00000000..4824cfcf --- /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 00000000..0581fd2b --- /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 00000000..cd2992c1 --- /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 00000000..757f00b8 --- /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 00000000..a5cc2c0e --- /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 00000000..bcc55205 --- /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 00000000..2518abf2 --- /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 00000000..3b53a18b --- /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 00000000..a3e2633b --- /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 00000000..39a6ab8c --- /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 00000000..ff7efa36 --- /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 00000000..9c9709ca --- /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 00000000..a7db5d92 --- /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 00000000..9c43c6ef --- /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 <<EOL > /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 00000000..284e81ac --- /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 00000000..a2ee4633 --- /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 00000000..426072cf --- /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 00000000..8c23b5f6 --- /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 00000000..2bd40181 --- /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 00000000..0dc67d8b --- /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 00000000..77871d3f --- /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 00000000..b488f2db --- /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 00000000..f82fd32a --- /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 00000000..63bc3bf7 --- /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 00000000..33b42306 --- /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 00000000..2b221592 --- /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 00000000..63453a75 --- /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 00000000..5cd392b2 --- /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 00000000..8d989fe0 --- /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 00000000..8ac19f7c --- /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 00000000..99f68966 --- /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 00000000..8085cbaa --- /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 00000000..2134c198 --- /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 00000000..cc348c5c --- /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 00000000..48b7ad0d --- /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 00000000..bc494ae3 --- /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 00000000..0581fd2b --- /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 00000000..6cc087a7 --- /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 00000000..8787df7b --- /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 00000000..1a8647f5 --- /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 00000000..757f00b8 --- /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 00000000..065c2f98 --- /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 00000000..ec1d89d5 --- /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 <<EOL > /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 00000000..a3e1dc7c --- /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 <<EOF >>/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 00000000..5b9e33bb --- /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 00000000..48d6c8bf --- /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 00000000..6a95d2f0 --- /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 00000000..a3e2633b --- /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 00000000..afb58284 --- /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 00000000..77a12927 --- /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 00000000..dc91fdba --- /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 00000000..1538d61c --- /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 00000000..39a6ab8c --- /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 00000000..d31292ec --- /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 00000000..2a03ad50 --- /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 <<EOL > /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 00000000..a3e1dc7c --- /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 <<EOF >>/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 00000000..5d5b4265 --- /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 00000000..c2b686c4 --- /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 <<EOL > /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 00000000..a3e1dc7c --- /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 <<EOF >>/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 00000000..cd853256 --- /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 00000000..48a25d1d --- /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 <<EOL > /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 00000000..ae658957 --- /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 00000000..c2b686c4 --- /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 <<EOL > /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 00000000..a3e1dc7c --- /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 <<EOF >>/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 00000000..426072cf --- /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 00000000..57fcdf90 --- /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 00000000..86202379 --- /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 00000000..56c02cda --- /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 00000000..2d50bc6a --- /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 00000000..4093a714 --- /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 00000000..041e61fd --- /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 00000000..4093a714 --- /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 00000000..c976bfac --- /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 00000000..a3ddea3a --- /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 00000000..48bda11b --- /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 00000000..f47cbada --- /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 00000000..6f8c478c --- /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 00000000..c976bfac --- /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 00000000..9607060b --- /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 00000000..a3ddea3a --- /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 00000000..48bda11b --- /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 00000000..1f3d0d7b --- /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 00000000..8724d163 --- /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 00000000..9607060b --- /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 00000000..47bc2cf8 --- /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 00000000..2e20d6cf --- /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 00000000..1a09f552 --- /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 00000000..62875eec --- /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 00000000..76d0da45 --- /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 00000000..9b80b6c6 --- /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 00000000..fe48b022 --- /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 00000000..ce33948e --- /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 00000000..78753bc7 --- /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 00000000..6eedc1c3 --- /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 00000000..06bc2ebc --- /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 00000000..587a4064 --- /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 00000000..7333cd46 --- /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 00000000..6b9eb0ad --- /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 00000000..94c598ee --- /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 00000000..eacd4d8e --- /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 00000000..105aceb9 --- /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 <datastore_name> <default_version> + 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 <datastore_name> <version_name> <datastore_manager> <image_id> <packages> <active> + 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: + # <PROJECT>_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 00000000..b4036ba1 --- /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 00000000..accccdcd --- /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 00000000..f7ebb0b8 --- /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 00000000..cbbe153f --- /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 00000000..e52d1833 --- /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+</', re.DOTALL)
+ my_string = my_re.sub('>\g<1></', my_string)
+ return my_string
+
+ def output_request(self, url, output_headers, body, content_type, method,
+ static_auth_token=True):
+ output_list = []
+ parsed = urlparse(url)
+ method_url = shorten_url(url)
+ output_list.append("%s %s HTTP/1.1" % (method, method_url))
+ output_list.append("User-Agent: %s" % output_headers['User-Agent'])
+ output_list.append("Host: %s" % parsed.netloc)
+ # static_auth_token option for documentation purposes
+ if static_auth_token:
+ output_token = '87c6033c-9ff6-405f-943e-2deb73f278b7'
+ else:
+ output_token = output_headers['X-Auth-Token']
+ output_list.append("X-Auth-Token: %s" % output_token)
+ output_list.append("Accept: %s" % output_headers['Accept'])
+ print("OUTPUT HEADERS: %s" % output_headers)
+ output_list.append("Content-Type: %s" % output_headers['Content-Type'])
+ output_list.append("")
+ pretty_body = self.format_body(body, content_type)
+ output_list.append("%s" % pretty_body)
+ output_list.append("")
+ return '\n'.join(output_list)
+
+ def output_response(self, resp, body, content_type):
+ output_list = []
+ version = "1.1" if resp.version == 11 else "1.0"
+ lines = [
+ ["HTTP/%s %s %s" % (version, resp.status, resp.reason)],
+ ["Content-Type: %s" % resp['content-type']],
+ ["Content-Length: %s" % resp['content-length']],
+ ["Date: %s" % resp['date']]]
+ new_lines = [x[0] for x in lines]
+ joined_lines = '\n'.join(new_lines)
+ output_list.append(joined_lines)
+ if body:
+ output_list.append("")
+ pretty_body = self.format_body(body, content_type)
+ output_list.append("%s" % pretty_body)
+ output_list.append("")
+ return '\n'.join(output_list)
+
+ def format_body(self, body, content_type):
+ if content_type == 'json':
+ try:
+ if self.conf.replace_dns_hostname:
+ before = r'\"hostname\": \"[a-zA-Z0-9-_\.]*\"'
+ after = '\"hostname\": \"%s\"' % self.conf.replace_dns_hostname
+ body = re.sub(before, after, body)
+ return json.dumps(json.loads(body), sort_keys=True, indent=4)
+ except Exception:
+ return body or ''
+ else:
+ # expected type of body is xml
+ try:
+ if self.conf.replace_dns_hostname:
+ hostname = 'hostname=\"%s\"' % self.conf.replace_dns_hostname,
+ body = re.sub(r'hostname=\"[a-zA-Z0-9-_\.]*\"',
+ hostname, body)
+ return self._indent_xml(body)
+ except Exception as ex:
+ return body if body else ''
+
+
+ def write_request_file(self, name, content_type, url, method,
+ req_headers, request_body):
+ def write_request():
+ return self.output_request(url, req_headers, request_body,
+ content_type, method)
+ if print_req:
+ print("\t%s req url:%s" % (content_type, url))
+ print("\t%s req method:%s" % (content_type, method))
+ print("\t%s req headers:%s" % (content_type, req_headers))
+ print("\t%s req body:%s" % (content_type, request_body))
+ self.write_file(name, content_type, url, method, "request",
+ write_request)
+
+ def write_response_file(self, name, content_type, url, method,
+ resp, resp_content):
+ def write_response():
+ return self.output_response(resp, resp_content, content_type)
+ self.write_file(name, content_type, url, method, "response",
+ write_response)
+ if print_req:
+ print("\t%s resp:%s" % (content_type, resp))
+ print("\t%s resp content:%s" % (content_type, resp_content))
+
+ def write_file(self, name, content_type, url, method, in_or_out, func):
+ filename = "%sdb-%s-%s.%s" % (self.conf.directory,
+ name.replace('_', '-'), in_or_out,
+ content_type)
+ with open(filename, "w") as file:
+ output = func()
+ output = output.replace(self.conf.tenantID, '1234')
+ if self.conf.replace_host:
+ output = output.replace(self.conf.api_url, self.conf.replace_host)
+ pre_host_port = urlparse(self.conf.api_url).netloc
+ post_host = urlparse(self.conf.replace_host).netloc
+ output = output.replace(pre_host_port, post_host)
+ output = output.replace("fake_host", "hostname")
+ output = output.replace("FAKE_", "")
+
+ file.write(output)
+
+
+# This method is mixed into the client class.
+# It requires the following fields: snippet_writer, content_type, and
+# "name," the last of which must be set before each call.
+def write_to_snippet(self, args, kwargs, resp, body):
+ if self.name is None:
+ raise RuntimeError("'name' not set before call.")
+ url = args[0]
+ method = args[1]
+ request_headers = kwargs['headers']
+ request_body = kwargs.get('body', None)
+ response_headers = resp
+ response_body = body
+
+ # Log request
+ self.snippet_writer.write_request_file(self.name, self.content_type,
+ url, method, request_headers, request_body)
+ self.snippet_writer.write_response_file(self.name, self.content_type,
+ url, method, response_headers, response_body)
+
+ # Create a short url to assert against.
+ short_url = url
+ for prefix in (self.snippet_writer.conf.dbaas_url,
+ self.snippet_writer.conf.api_url):
+ if short_url.startswith(prefix):
+ short_url = short_url[len(prefix):]
+ self.old_info = {
+ 'url':shorten_url(short_url),
+ 'method': method,
+ 'request_headers':request_headers,
+ 'request_body':request_body,
+ 'response_headers':response_headers,
+ 'response_body':response_body
+ }
+
+
+class JsonClient(TroveHTTPClient):
+
+ content_type = 'json'
+
+ def http_log(self, *args, **kwargs):
+ return write_to_snippet(self, *args, **kwargs)
+
+
+class XmlClient(TroveXmlClient):
+
+ content_type = 'xml'
+
+ def http_log(self, *args, **kwargs):
+ return write_to_snippet(self, *args, **kwargs)
diff --git a/integration/tests/examples/examples/example_generation.py b/integration/tests/examples/examples/example_generation.py new file mode 100644 index 00000000..7ca8a02c --- /dev/null +++ b/integration/tests/examples/examples/example_generation.py @@ -0,0 +1,1042 @@ +import httplib2 +import json +import os +import re +import sys +import time +from urlparse import urlparse +import xml.dom.minidom + +from proboscis import before_class +from proboscis import test +from proboscis import TestProgram +from proboscis.asserts import * +from proboscis.asserts import Check + +from troveclient.compat import Dbaas +from troveclient.compat import TroveHTTPClient + + +from client import ConfigFile +from client import SnippetWriter +from client import JsonClient +from client import XmlClient + + +print_req = True + + +class ExampleClient(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 += '/' + print "directory = %s" % self.directory + self.api_url = config.get("api_url", None) + print "api_url = %s" % self.api_url + #auth + auth_url = config.get("auth_url", None) + print "auth_url = %s" % auth_url + username = config.get("username", None) + print "username = %s" % username + password = config.get("password", None) + print "password = %s" % password + self.tenant = config.get("tenant", None) + self.replace_host = config.get("replace_host", None) + print "tenant = %s" % self.tenant + self.replace_dns_hostname = config.get("replace_dns_hostname", None) + if auth_url: + auth_id, tenant_id = self.get_auth_token_id_tenant_id(auth_url, + username, + 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 write_request_file(self, name, content_type, url, method, + req_headers, request_body): + def write_request(): + return self.output_request(url, req_headers, request_body, + content_type, method) + if print_req: + print("\t%s req url:%s" % (content_type, url)) + print("\t%s req method:%s" % (content_type, method)) + print("\t%s req headers:%s" % (content_type, req_headers)) + print("\t%s req body:%s" % (content_type, request_body)) + self.write_file(name, content_type, url, method, write_request) + + def write_response_file(self, name, content_type, url, method, + resp, resp_content): + def write_response(): + return self.output_response(resp, resp_content, content_type) + self.write_file(name, content_type, url, method, write_response) + if print_req: + print("\t%s resp:%s" % (content_type, resp)) + print("\t%s resp content:%s" % (content_type, resp_content)) + + def write_file(self, name, content_type, url, method, func): + filename = "%sdb-%s-request.%s" % (self.directory, name, content_type) + with open(filename, "w") as file: + output = func() + output = output.replace(self.tenantID, '1234') + if self.replace_host: + output = output.replace(self.api_url, self.replace_host) + pre_host_port = urlparse(self.api_url).netloc + post_host = urlparse(self.replace_host).netloc + output = output.replace(pre_host_port, post_host) + + file.write(output) + + def version_http_call(self, name, method, json, xml, + output=True, print_resp=False): + json['url'] = "%s/%s" % (self.api_url, json['url']) + xml['url'] = "%s/%s" % (self.api_url, xml['url']) + return self.make_request(name, method, json, xml, output, print_resp) + + def http_call(self, name, method, url, json, xml, + output=True, print_resp=False): + json['url'] = "%s/%s" % (self.dbaas_url, json['url']) + xml['url'] = "%s/%s" % (self.dbaas_url, xml['url']) + return self.make_request(name, method, json, xml, output, print_resp) + + # print_req and print_resp for debugging purposes + def make_request(self, name, method, json, xml, + output=True, print_resp=False): + name = name.replace('_', '-') + print "http call for %s" % name + http = httplib2.Http(disable_ssl_certificate_validation=True) + req_headers = {'User-Agent': "python-example-client", + 'Content-Type': "application/json", + 'Accept': "application/json" + } + req_headers.update(self.headers) + + + content_type = 'json' + request_body = json.get('body', None) + url = json.get('url') + if output: + self.write_request_file(name, 'json', url, method, req_headers, + request_body) + + resp, resp_content = http.request(url, method, body=request_body, + headers=req_headers) + json_resp = resp, resp_content + if output: + filename = "%sdb-%s-response.%s" % (self.directory, name, + content_type) + self.write_response_file(name, 'json', url, method, resp, + resp_content) + + + content_type = 'xml' + req_headers['Accept'] = 'application/xml' + req_headers['Content-Type'] = 'application/xml' + request_body = xml.get('body', None) + url = xml.get('url') + if output: + filename = "%sdb-%s-request.%s" % (self.directory, name, + content_type) + output = self.write_request_file(name, 'xml', url, method, + req_headers, request_body) + resp, resp_content = http.request(url, method, body=request_body, + headers=req_headers) + xml_resp = resp, resp_content + if output: + filename = "%sdb-%s-response.%s" % (self.directory, name, + content_type) + self.write_response_file(name, 'xml', url, method, resp, + resp_content) + + + return json_resp, xml_resp + + 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+</', re.DOTALL) + my_string = my_re.sub('>\g<1></', my_string) + return my_string + + def output_request(self, url, output_headers, body, content_type, method, + static_auth_token=True): + output_list = [] + parsed = urlparse(url) + if parsed.query: + method_url = parsed.path + '?' + parsed.query + else: + method_url = parsed.path + output_list.append("%s %s HTTP/1.1" % (method, method_url)) + output_list.append("User-Agent: %s" % output_headers['User-Agent']) + output_list.append("Host: %s" % parsed.netloc) + # static_auth_token option for documentation purposes + if static_auth_token: + output_token = '87c6033c-9ff6-405f-943e-2deb73f278b7' + else: + output_token = output_headers['X-Auth-Token'] + output_list.append("X-Auth-Token: %s" % output_token) + output_list.append("Accept: %s" % output_headers['Accept']) + output_list.append("Content-Type: %s" % output_headers['Content-Type']) + output_list.append("") + pretty_body = self.format_body(body, content_type) + output_list.append("%s" % pretty_body) + output_list.append("") + return '\n'.join(output_list) + + def output_response(self, resp, body, content_type): + output_list = [] + version = "1.1" if resp.version == 11 else "1.0" + lines = [ + ["HTTP/%s %s %s" % (version, resp.status, resp.reason)], + ["Content-Type: %s" % resp['content-type']], + ["Content-Length: %s" % resp['content-length']], + ["Date: %s" % resp['date']]] + new_lines = [x[0] for x in lines] + joined_lines = '\n'.join(new_lines) + output_list.append(joined_lines) + if body: + output_list.append("") + pretty_body = self.format_body(body, content_type) + output_list.append("%s" % pretty_body) + output_list.append("") + return '\n'.join(output_list) + + def format_body(self, body, content_type): + if content_type == 'json': + try: + if self.replace_dns_hostname: + before = r'\"hostname\": \"[a-zA-Z0-9-_\.]*\"' + after = '\"hostname\": \"%s\"' % self.replace_dns_hostname + body = re.sub(before, after, body) + return json.dumps(json.loads(body), sort_keys=True, indent=4) + except Exception: + return body if body else '' + else: + # expected type of body is xml + try: + if self.replace_dns_hostname: + hostname = 'hostname=\"%s\"' % self.replace_dns_hostname, + body = re.sub(r'hostname=\"[a-zA-Z0-9-_\.]*\"', + hostname, body) + return self._indent_xml(body) + except Exception as ex: + return body if body else '' + + def get_auth_token_id_tenant_id(self, url, username, password): + body = ('{"auth":{"tenantName": "%s", "passwordCredentials": ' + '{"username": "%s", "password": "%s"}}}') + body = body % (self.tenant, username, password) + http = httplib2.Http(disable_ssl_certificate_validation=True) + req_headers = {'User-Agent': "python-example-client", + 'Content-Type': "application/json", + 'Accept': "application/json", + } + resp, body = http.request(url, 'POST', body=body, headers=req_headers) + auth = json.loads(body) + auth_id = auth['access']['token']['id'] + tenant_id = auth['access']['token']['tenant']['id'] + return auth_id, tenant_id + + +@test +def load_config_file(): + global conf + print("RUNNING ARGS : " + str(sys.argv)) + conf = None + for arg in sys.argv[1:]: + conf_file_path = os.path.expanduser(arg) + conf = ConfigFile(conf_file_path) + return + if not conf: + fail("Missing conf file.") + +def create_client(cls=TroveHTTPClient): + client = Dbaas(conf.username, conf.password, tenant=conf.tenant, + auth_url="blah/", auth_strategy='fake', + insecure=True, service_type='trove', + service_url=conf.dbaas_url, client_cls=cls) + return client + +class ClientPair(object): + """ + Combines a Json and XML version of the Dbaas client. + """ + + def __init__(self): + snippet_writer = SnippetWriter(conf) + def make_client(cls): + client = create_client(cls) + client.client.name = "auth" + client.client.snippet_writer = snippet_writer + client.authenticate() + return client + self.json = make_client(JsonClient) + self.xml = make_client(XmlClient) + self.clients = [self.json, self.xml] + + def do(self, name, url, method, status, reason, func, func_args=None): + """ + Performs the given function twice, first for the JSON client, then for + the XML one, and writes both to their respective files. + 'name' is the name of the file, while 'url,' 'method,' 'status,' + and 'reason' are expected values that are asserted against. + If func_args is present, it is a list of lists, each one of which + is passed as the *args to the two invocations of "func". + """ + func_args = func_args or [[], []] + snippet_writer = SnippetWriter(conf) + results = [] + for index, client in enumerate(self.clients): + client.client.snippet_writer = snippet_writer + client.client.name = name + args = func_args[index] + result = func(client, *args) + with Check() as check: + if isinstance(url, (list, tuple)): + check.equal(client.client.old_info['url'], url[index]) + else: + check.equal(client.client.old_info['url'], url) + check.equal(client.client.old_info['method'], method) + check.equal(client.client.old_info['response_headers'].status, + status) + check.equal(client.client.old_info['response_headers'].reason, + reason) + results.append(result) + # To prevent this from writing a snippet somewhere else... + client.client.name = "junk" + + return results + + +JSON_INDEX = 0 +XML_INDEX = 1 + +@test(depends_on=[load_config_file]) +class Versions(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def get_versions(self): + self.clients.do("versions", + "", "GET", 200, "OK", + lambda client : client.versions.index(conf.api_url)) + + + @test + def get_version(self): + self.clients.do("versions", + "/v1.0", "GET", 200, "OK", + lambda client : client.versions.index(conf.api_url + "/v1.0/")) + + +@test(depends_on=[load_config_file]) +class Flavors(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def get_flavors(self): + self.clients.do("flavors", + "/flavors", "GET", 200, "OK", + lambda client : client.flavors.list()) + + @test + def get_flavor_by_id(self): + self.clients.do("flavors_by_id", + "/flavors/1", "GET", 200, "OK", + lambda client : client.flavors.get(1)) + + +@test(depends_on=[load_config_file]) +def clean_slate(): + client = create_client() + client.client.name = "list" + instances = client.instances.list() + assert_equal(0, len(instances), "Instance count must be zero.") + + +@test(depends_on=[clean_slate]) +class CreateInstance(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def post_create_instance(self): + def create_instance(client, name): + instance = client.instances.create(name, 1, volume={'size':2}, + databases=[{ + "name": "sampledb", + "character_set": "utf8", + "collate": "utf8_general_ci" + },{ + "name": "nextround" + } + ], + users =[{ + "databases":[{ "name":"sampledb"}], + "name":"demouser", + "password": "demopassword" + } + ]) + assert_equal(instance.status, "BUILD") + return instance + self.instances = self.clients.do("create_instance", + "/instances", "POST", 200, "OK", + create_instance, + (["json_rack_instance"], ["xml_rack_instance"])) + #self.instance_j = create_instance(self.clients.json, + # "json_rack_instance") + #self.instance_x = create_instance(self.clients.xml, + # "xml_rack_instance") + + @test(depends_on=[post_create_instance]) + def wait_for_instances(self): + for instance in self.instances: + while instance.status != "ACTIVE": + assert_equal(instance.status, "BUILD") + instance.get() + time.sleep(0.1) + global json_instance + json_instance = self.instances[0] + global xml_instance + xml_instance = self.instances[1] + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class Databases(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def post_create_databases(self): + self.clients.do("create_databases", + ("/instances/%s/databases" % json_instance.id, + "/instances/%s/databases" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id : client.databases.create(id, databases=[ + { + "name": "testingdb", + "character_set": "utf8", + "collate": "utf8_general_ci" + }, + { + "name": "anotherdb" + }, + { + "name": "oneMoreDB" + } + ]), ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_create_databases]) + def get_list_databases(self): + results = self.clients.do("list_databases", + ("/instances/%s/databases" % json_instance.id, + "/instances/%s/databases" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.databases.list(id), + ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_create_databases]) + def get_list_databases_limit_two(self): + results = self.clients.do("list_databases_pagination", + ("/instances/%s/databases?limit=1" % json_instance.id, + "/instances/%s/databases?limit=2" % xml_instance.id), + "GET", 200, "OK", + lambda client, id, limit : client.databases.list(id, limit=limit), + ([json_instance.id, 1], [xml_instance.id, 2])) + assert_equal(1, len(results[JSON_INDEX])) + assert_equal(2, len(results[XML_INDEX])) + assert_equal("anotherdb", results[JSON_INDEX].next) + assert_equal("nextround", results[XML_INDEX].next) + + @test(depends_on=[post_create_databases], + runs_after=[get_list_databases, get_list_databases_limit_two]) + def delete_databases(self): + results = self.clients.do("delete_databases", + ("/instances/%s/databases/testingdb" % json_instance.id, + "/instances/%s/databases/oneMoreDB" % xml_instance.id), + "DELETE", 202, "Accepted", + lambda client, id, name : client.databases.delete(id, name), + ([json_instance.id, 'testingdb'], [xml_instance.id, 'oneMoreDB'])) + + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class Users(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def post_create_users(self): + results = self.clients.do("create_users", + ("/instances/%s/users" % json_instance.id, + "/instances/%s/users" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id : client.users.create(id, [ + { + "name": "dbuser1", + "password": "password", + "database": "databaseA" + }, + { + "name": "dbuser2", + "password": "password", + "databases": [ + { + "name": "databaseB" + }, + { + "name": "databaseC" + } + ] + }, + { + "name": "dbuser3", + "password": "password", + "database": "databaseD" + } + ]), + ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_create_users]) + def get_list_users(self): + results = self.clients.do("list_users", + ("/instances/%s/users" % json_instance.id, + "/instances/%s/users" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.users.list(id), + ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_create_users]) + def get_list_users_limit_two(self): + results = self.clients.do("list_users_pagination", + ("/instances/%s/users?limit=2" % json_instance.id, + "/instances/%s/users?limit=2" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.users.list(id, limit=2), + ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_create_users], + runs_after=[get_list_users, get_list_users_limit_two]) + def delete_users(self): + user_name = "testuser" + results = self.clients.do("delete_users", + ("/instances/%s/users/%s" % (json_instance.id, user_name), + "/instances/%s/users/%s" % (xml_instance.id, user_name)), + "DELETE", 202, "Accepted", + lambda client, id : client.users.delete(id, user=user_name), + ([json_instance.id], [xml_instance.id])) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class Root(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def post_enable_root_access(self): + results = self.clients.do("enable_root_user", + ("/instances/%s/root" % json_instance.id, + "/instances/%s/root" % xml_instance.id), + "POST", 200, "OK", + lambda client, id : client.root.create(id), + ([json_instance.id], [xml_instance.id])) + + @test(depends_on=[post_enable_root_access]) + def get_check_root_access(self): + results = self.clients.do("check_root_user", + ("/instances/%s/root" % json_instance.id, + "/instances/%s/root" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.root.is_root_enabled(id), + ([json_instance.id], [xml_instance.id])) + assert_equal(results[JSON_INDEX], True) + assert_equal(results[XML_INDEX], True) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class InstanceList(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def get_list_instance_index(self): + results = self.clients.do("instances_index", + "/instances", "GET", 200, "OK", + lambda client : client.instances.list()) + for result in results: + assert_equal(2, len(result)) + + @test + def get_instance_details(self): + results = self.clients.do("instance_status_detail", + ("/instances/%s" % json_instance.id, + "/instances/%s" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.instances.get(id), + ([json_instance.id], [xml_instance.id])) + assert_equal(results[JSON_INDEX].id, json_instance.id) + assert_equal(results[XML_INDEX].id, xml_instance.id) + + @test + def get_list_instance_index_limit_two(self): + third_instance = self.clients.json.instances.create( + "The Third Instance", 1, volume={'size':2}) + while third_instance.status != "ACTIVE": + third_instance.get() + time.sleep(0.1) + + results = self.clients.do("instances_index_pagination", + "/instances?limit=2", "GET", 200, "OK", + lambda client : client.instances.list(limit=2)) + for result in results: + assert_equal(2, len(result)) + + self.clients.json.instances.delete(third_instance.id) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class Actions(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + def _wait_for_active(self, *acceptable_states): + for instance in (json_instance, xml_instance): + instance.get() + print('instance.status=%s' % instance.status) + while instance.status != "ACTIVE": + assert_true(instance.status in acceptable_states, + "Instance status == %s; expected it to be one of these: %s" + % (instance.status, acceptable_states)) + instance.get() + time.sleep(0.1) + + @test + def instance_restart(self): + results = self.clients.do("instance_restart", + ("/instances/%s/action" % json_instance.id, + "/instances/%s/action" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id : client.instances.restart(id), + ([json_instance.id], [xml_instance.id])) + self._wait_for_active("RESTART") + + @test + def instance_resize_volume(self): + results = self.clients.do("instance_resize_volume", + ("/instances/%s/action" % json_instance.id, + "/instances/%s/action" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id : client.instances.resize_volume(id, 4), + ([json_instance.id], [xml_instance.id])) + self._wait_for_active("RESIZE") + assert_equal(json_instance.volume['size'], 4) + assert_equal(xml_instance.volume['size'], '4') + + @test + def instance_resize_flavor(self): + results = self.clients.do("instance_resize_flavor", + ("/instances/%s/action" % json_instance.id, + "/instances/%s/action" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id : client.instances.resize_flavor(id, 3), + ([json_instance.id], [xml_instance.id])) + self._wait_for_active("RESIZE") + assert_equal(json_instance.flavor['id'], '3') + assert_equal(xml_instance.flavor['id'], '3') + + +@test(depends_on=[CreateInstance], groups=['uses_instances', "MgmtHosts"]) +class MgmtHosts(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def mgmt_list_hosts(self): + results = self.clients.do("mgmt_list_hosts", + "/mgmt/hosts", "GET", 200, "OK", + lambda client : client.mgmt.hosts.index()) + with Check() as check: + for hosts in results: + check.equal(1, len(hosts)) + check.equal("fake_host", hosts[0].name) + check.equal(2, results[0][0].instanceCount) + # In XML land this is a string. :'( + check.equal("2", results[1][0].instanceCount) + + @test + def mgmt_get_host_detail(self): + results = self.clients.do("mgmt_get_host_detail", + "/mgmt/hosts/fake_host", "GET", 200, "OK", + lambda client : client.mgmt.hosts.get("fake_host")) + with Check() as check: + for host in results: + check.equal(results[0].name, "fake_host") + check.equal(results[1].name, "fake_host") + # XML entries won't come back as these types. :( + check.true(isinstance(results[0].percentUsed, int)), + check.true(isinstance(results[0].totalRAM, int)), + check.true(isinstance(results[0].usedRAM, int)), + with Check() as check: + for host in results: + check.equal(2, len(host.instances)) + for instance in host.instances: + check.equal(instance['status'], 'ACTIVE') + check.true(instance['name'] == 'json_rack_instance' or + instance['name'] == 'xml_rack_instance') + #TODO: Check with GUID regex. + check.true(isinstance(instance['id'], basestring)) + check.true(isinstance(instance['server_id'], basestring)) + check.true(isinstance(instance['tenant_id'], basestring)) + + @test + def mgmt_host_update_all(self): + results = self.clients.do("mgmt_host_update", + "/mgmt/hosts/fake_host/instances/action", + "POST", 202, "Accepted", + lambda client : client.mgmt.hosts.update_all("fake_host")) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtStorage(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def mgmt_get_storage(self): + results = self.clients.do("mgmt_get_storage", + "/mgmt/storage", "GET", 200, "OK", + lambda client : client.mgmt.storage.index()) + for index, devices in enumerate(results): + with Check() as check: + check.equal(1, len(devices)) + device = devices[0] + check.equal(int(device.capacity['available']), 90) + check.equal(int(device.capacity['total']), 100) + check.equal(device.name, "fake_storage") + check.equal(int(device.provision['available']), 40) + check.equal(int(device.provision['percent']), 10) + check.equal(int(device.provision['total']), 50) + check.equal(device.type, "test_type") + check.equal(int(device.used), 10) + if index == JSON_INDEX: + check.true(isinstance(device.capacity['available'], int)) + check.true(isinstance(device.capacity['total'], int)) + check.true(isinstance(device.provision['available'], int)) + check.true(isinstance(device.provision['percent'], int)) + check.true(isinstance(device.provision['total'], int)) + check.true(isinstance(device.used, int)) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtAccount(object): + + @before_class + def setup(self): + self.clients = ClientPair() + + @test + def mgmt_get_account_details(self): + results = self.clients.do("mgmt_get_account_details", + "/mgmt/accounts/admin", "GET", 200, "OK", + lambda client : client.mgmt.accounts.show("admin")) + with Check() as check: + for account_info in results: + check.equal(2, len(account_info.instances)) + check.equal('admin', account_info.id) + + @test + def mgmt_get_account_list(self): + results = self.clients.do("mgmt_list_accounts", + "/mgmt/accounts", "GET", 200, "OK", + lambda client : client.mgmt.accounts.index()) + for index, result in enumerate(results): + for account in result.accounts: + assert_equal('admin', account['id']) + if index == JSON_INDEX: + assert_equal(2, account['num_instances']) + else: + assert_equal("2", account['num_instances']) + + +def for_both(func): + def both(self): + for result in self.results: + func(self, result) + return both + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtInstance(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + self.results = self.clients.do("mgmt_get_instance_details", + ("/mgmt/instances/%s" % json_instance.id, + "/mgmt/instances/%s" % xml_instance.id), + "GET", 200, "OK", + lambda client, id : client.mgmt.instances.show(id), + ([json_instance.id], [xml_instance.id])) + + @test + @for_both + def created(self, result): + #TODO: use regex + assert_true(isinstance(result.created, basestring)) + + @test + def deleted(self): + assert_equal(self.results[JSON_INDEX].deleted, False) + assert_equal(self.results[XML_INDEX].deleted, "False") + + @test + @for_both + def flavor(self, result): + assert_true(result.flavor['id'] == "1" or result.flavor['id'] == "3") + assert_equal(len(result.flavor['links']), 2) + #TODO: validate the flavors format. + + @test + @for_both + def guest_status(self, result): + assert_equal(result.guest_status['state_description'], 'running') + + @test + @for_both + def host(self, result): + assert_equal(result.host, 'fake_host') + + @test + def id(self): + assert_equal(self.results[JSON_INDEX].id, json_instance.id) + assert_equal(self.results[XML_INDEX].id, xml_instance.id) + + @test + @for_both + def links(self, result): + assert_true(isinstance(result.links, list)) + for link in result.links: + assert_true(isinstance(link, dict)) + assert_true(isinstance(link['href'], basestring)) + assert_true(isinstance(link['rel'], basestring)) + + @test + def local_id(self): + #TODO: regex + assert_true(isinstance(self.results[JSON_INDEX].local_id, int)) + assert_true(isinstance(self.results[XML_INDEX].local_id, basestring)) + + @test + @for_both + def name(self, result): + #TODO: regex + assert_true(isinstance(result.name, basestring)) + + @test + @for_both + def server_id(self, result): + #TODO: regex + assert_true(isinstance(result.server_id, basestring)) + + @test + @for_both + def status(self, result): + #TODO: regex + assert_equal("ACTIVE", result.status) + + @test + @for_both + def task_description(self, result): + assert_equal(result.task_description, "No tasks for the instance.") + + @test + @for_both + def tenant_id(self, result): + assert_equal(result.tenant_id, "admin") + + @test + @for_both + def updated(self, result): + #TODO: regex + assert_true(isinstance(result.updated, basestring)) + + @test + @for_both + def volume(self, result): + #TODO: regex + assert_true(isinstance(result.volume, dict)) + assert_true('id' in result.volume) + assert_true('size' in result.volume) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtInstanceIndex(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_instance_index(self, deleted=False): + url = "/mgmt/instances?deleted=false" + results = self.clients.do("mgmt_instance_index", + "/mgmt/instances?deleted=false", "GET", 200, "OK", + lambda client : client.mgmt.instances.index(deleted=False)) + #TODO: Valdiate everything... *sigh* + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtInstanceDiagnostics(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_get_instance_diagnostics(self): + results = self.clients.do("mgmt_instance_diagnostics", + ("/mgmt/instances/%s/diagnostics" % json_instance.id, + "/mgmt/instances/%s/diagnostics" % xml_instance.id), + "GET", 200, "OK", + lambda client, id: client.diagnostics.get(id), + ([json_instance.id], [xml_instance.id])) + #TODO: validate the actual stuff that comes back (booorring!). + + +@test(depends_on=[CreateInstance]) +class MgmtInstanceRoot(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_get_root_details(self): + results = self.clients.do("mgmt_get_root_details", + ("/mgmt/instances/%s/root" % json_instance.id, + "/mgmt/instances/%s/root" % xml_instance.id), + "GET", 200, "OK", + lambda client, id: client.mgmt.instances.root_enabled_history(id), + ([json_instance.id], [xml_instance.id])) + #TODO: validate the actual stuff that comes back (booorring!). + + +@test(depends_on=[CreateInstance]) +class MgmtInstanceHWInfo(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_get_hw_info(self): + results = self.clients.do("mgmt_get_hw_info", + ("/mgmt/instances/%s/hwinfo" % json_instance.id, + "/mgmt/instances/%s/hwinfo" % xml_instance.id), + "GET", 200, "OK", + lambda client, id: client.hw_info.get(id), + ([json_instance.id], [xml_instance.id])) + + +@test(depends_on=[CreateInstance], groups=['uses_instances']) +class MgmtInstanceReboot(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_instance_reboot(self): + results = self.clients.do("instance_reboot", + ("/mgmt/instances/%s/action" % json_instance.id, + "/mgmt/instances/%s/action" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id: client.mgmt.instances.reboot(id), + ([json_instance.id], [xml_instance.id])) + + +@test(depends_on=[CreateInstance], + groups=['uses_instances'], enabled=False) +class MgmtInstanceGuestUpdate(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def mgmt_instance_guest_update(self): + results = self.clients.do("guest_update", + ("/mgmt/instances/%s/action" % json_instance.id, + "/mgmt/instances/%s/action" % xml_instance.id), + "POST", 202, "Accepted", + lambda client, id: client.mgmt.instances.update(id), + ([json_instance.id], [xml_instance.id])) + + +@test(depends_on=[CreateInstance], runs_after_groups=['uses_instances']) +class ZzzDeleteInstance(object): + + @before_class + def mgmt_get_instance_details(self): + self.clients = ClientPair() + + @test + def zzz_delete_instance(self): + results = self.clients.do("delete_instance", + ("/instances/%s" % json_instance.id, + "/instances/%s" % xml_instance.id), + "DELETE", 202, "Accepted", + lambda client, id: client.instances.delete(id), + ([json_instance.id], [xml_instance.id])) + for result in json_instance, xml_instance: + result.get() + assert_equal(result.status, "SHUTDOWN") + + +if __name__ == "__main__": + TestProgram().run_and_exit() diff --git a/integration/tests/examples/examples/local.conf b/integration/tests/examples/examples/local.conf new file mode 100644 index 00000000..682200da --- /dev/null +++ b/integration/tests/examples/examples/local.conf @@ -0,0 +1,10 @@ +{ + "directory": "/src/apidocs/src/resources/samples/", + "auth_url":"http://localhost:35357/v2.0/tokens", + "api_url":"http://localhost:8779", + "replace_host":"https://ord.databases.api.rackspacecloud.com", + "replace_dns_hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com", + "username":"examples", + "password":"examples", + "tenant":"trove" +} diff --git a/integration/tests/examples/gendoc.sh b/integration/tests/examples/gendoc.sh new file mode 100755 index 00000000..01b23271 --- /dev/null +++ b/integration/tests/examples/gendoc.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +pushd ../../apidocs +mvn clean +mvn generate-sources + +popd + diff --git a/integration/tests/examples/local.conf b/integration/tests/examples/local.conf new file mode 100644 index 00000000..adaaecd4 --- /dev/null +++ b/integration/tests/examples/local.conf @@ -0,0 +1,10 @@ +{ + "directory": "output", + "auth_url":null, + "api_url":"http://localhost:8779", + "replace_host":"https://ord.databases.api.rackspacecloud.com", + "replace_dns_hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.rackspaceclouddb.com", + "username":"examples", + "password":"examples", + "tenant":"admin" +} diff --git a/integration/tests/examples/setup.py b/integration/tests/examples/setup.py new file mode 100644 index 00000000..a7221505 --- /dev/null +++ b/integration/tests/examples/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 Example Generator", + version="0.0.9.9", + author='OpenStack', + description="Generates documentation examples.", + license='Apache', + py_modules=[], + packages=['examples'], + scripts=[] +) diff --git a/integration/tests/examples/tox.ini b/integration/tests/examples/tox.ini new file mode 100644 index 00000000..19953428 --- /dev/null +++ b/integration/tests/examples/tox.ini @@ -0,0 +1,16 @@ +# Examples: +# Run tests against Trove running locally in fake mode: +# tox -e local -- --group=blackbox +[tox] +envlist = py27 + +[testenv] +deps = + lxml==2.3 + nose + proboscis + {env:TROVE_CLIENT_PATH} + +[testenv:py27] +commands = + {envpython} {toxinidir}/examples/example_generation.py {toxinidir}/local.conf {posargs:DEFAULTS} diff --git a/integration/tests/integration/core.test.conf b/integration/tests/integration/core.test.conf new file mode 100644 index 00000000..2ba05bef --- /dev/null +++ b/integration/tests/integration/core.test.conf @@ -0,0 +1,48 @@ +{ + "report_directory":"rdli-test-report", + "start_services": false, + + + "white_box":false, + "test_mgmt":false, + "use_local_ovz":false, + "use_venv":false, + "glance_code_root":"/opt/stack/glance", + "glance_api_conf":"/vagrant/conf/glance-api.conf", + "glance_reg_conf":"/vagrant/conf/glance-reg.conf", + "glance_images_directory": "/glance_images", + "glance_image": "fakey_fakerson.tar.gz", + "instance_flavor_name":"m1.rd-tiny", + "instance_bigger_flavor_name":"m1.rd-smaller", + "nova_code_root":"/opt/stack/nova", + "nova_conf":"/home/vagrant/nova.conf", + "keystone_code_root":"/opt/stack/keystone", + "keystone_conf":"/etc/keystone/keystone.conf", + "keystone_use_combined":true, + "trove_code_root":"/opt/stack/trove", + "trove_conf":"/tmp/trove.conf", + "trove_version":"v1.0", + "trove_api_updated":"2012-08-01T00:00:00Z", + "trove_must_have_volume":false, + "trove_can_have_volume":true, + "trove_main_instance_has_volume": true, + "trove_max_accepted_volume_size": 1000, + "trove_max_instances_per_user": 55, + "trove_max_volumes_per_user": 100, + "use_nova_volume": false, + "use_reaper":false, +"root_removed_from_instance_api": true, + "root_timestamp_disabled": false, + "openvz_disabled": false, + "management_api_disabled": true, + "dbaas_image": 1, + "dns_driver":"trove.dns.rsdns.driver.RsDnsDriver", + "dns_instance_entry_factory":"trove.dns.rsdns.driver.RsDnsInstanceEntryFactory", + "databases_page_size": 20, + "instances_page_size": 20, + "users_page_size": 20, + "rabbit_runs_locally":false, + +"dns_instance_entry_factory":"trove.dns.rsdns.driver.RsDnsInstanceEntryFactory", + "sentinel": null +} diff --git a/integration/tests/integration/int_tests.py b/integration/tests/integration/int_tests.py new file mode 100644 index 00000000..e8f00fc4 --- /dev/null +++ b/integration/tests/integration/int_tests.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python +# 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. + + +"""Runs the tests. + +There are a few initialization issues to deal with. +The first is flags, which must be initialized before any imports. The test +configuration has the same problem (it was based on flags back when the tests +resided outside of the Nova code). + +The command line is picked apart so that Nose won't see commands it isn't +compatible with, such as "--flagfile" or "--group". + +This script imports all other tests to make them known to Proboscis before +passing control to proboscis.TestProgram which itself calls nose, which then +call unittest.TestProgram and exits. + +If "repl" is a command line argument, then the original stdout and stderr is +saved and sys.exit is neutralized so that unittest.TestProgram will not exit +and instead sys.stdout and stderr are restored so that interactive mode can +be used. + +""" + + +from __future__ import absolute_import +import atexit +import gettext +import logging +import os +import time +import unittest +import sys +import proboscis + +from nose import config +from nose import core + +from tests.colorizer import NovaTestRunner + + +if os.environ.get("PYDEV_DEBUG", "False") == 'True': + from pydev import pydevd + pydevd.settrace('10.0.2.2', port=7864, stdoutToServer=True, + stderrToServer=True) + + +def add_support_for_localization(): + """Adds support for localization in the logging. + + If ../nova/__init__.py exists, add ../ to Python search path, so that + it will override what happens to be installed in + /usr/(local/)lib/python... + + """ + path = os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir) + possible_topdir = os.path.normpath(path) + if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + sys.path.insert(0, possible_topdir) + + gettext.install('nova', unicode=1) + + +MAIN_RUNNER = None + + +def initialize_rdl_config(config_file): + from trove.common import cfg + from oslo_log import log + from trove.db import get_db_api + conf = cfg.CONF + cfg.parse_args(['int_tests'], default_config_files=[config_file]) + log.setup(conf, None) + try: + get_db_api().configure_db(conf) + conf_file = conf.find_file(conf.api_paste_config) + except RuntimeError as error: + import traceback + print(traceback.format_exc()) + sys.exit("ERROR: %s" % error) + + +def _clean_up(): + """Shuts down any services this program has started and shows results.""" + from tests.util import report + report.update() + if MAIN_RUNNER is not None: + MAIN_RUNNER.on_exit() + from tests.util.services import get_running_services + for service in get_running_services(): + sys.stderr.write("Stopping service ") + for c in service.cmd: + sys.stderr.write(c + " ") + sys.stderr.write("...\n\r") + service.stop() + + +def import_tests(): + + # TODO(tim.simpson): Import these again once white box test functionality + # is restored. + # from tests.dns import check_domain + # from tests.dns import concurrency + # from tests.dns import conversion + + # The DNS stuff is problematic. Not loading the other tests allow us to + # run its functional tests only. + ADD_DOMAINS = os.environ.get("ADD_DOMAINS", "False") == 'True' + if not ADD_DOMAINS: + from tests.api import delete_all + from tests.api import instances_pagination + from tests.api import instances_quotas + from tests.api import instances_states + from tests.dns import dns + from tests import initialize + from tests.smoke import instance + from tests.volumes import driver + + # Groups that exist as core int-tests are registered from the + # trove.tests.int_tests module + from trove.tests import int_tests + + # Groups defined in trove-integration, or any other externally + # defined groups can be registered here + heavy_black_box_groups = [ + "dbaas.api.instances.pagination", + "dbaas.api.instances.delete", + "dbaas.api.instances.status", + "dbaas.api.instances.down", + "dbaas.api.mgmt.hosts.update", + "fake.dbaas.api.mgmt.instances", + "fake.dbaas.api.mgmt.accounts.broken", + "fake.dbaas.api.mgmt.allaccounts" + ] + proboscis.register(groups=["heavy_blackbox"], + depends_on_groups=heavy_black_box_groups) + + +def run_main(test_importer): + + add_support_for_localization() + + # Strip non-nose arguments out before passing this to nosetests + + repl = False + nose_args = [] + conf_file = "~/test.conf" + show_elapsed = True + groups = [] + print("RUNNING TEST ARGS : " + str(sys.argv)) + extra_test_conf_lines = [] + rdl_config_file = None + nova_flag_file = None + index = 0 + while index < len(sys.argv): + arg = sys.argv[index] + if arg[:2] == "-i" or arg == '--repl': + repl = True + elif arg[:7] == "--conf=": + conf_file = os.path.expanduser(arg[7:]) + print("Setting TEST_CONF to " + conf_file) + os.environ["TEST_CONF"] = conf_file + elif arg[:8] == "--group=": + groups.append(arg[8:]) + elif arg == "--test-config": + if index >= 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 00000000..6459935c --- /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 00000000..83d313dc --- /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 00000000..52216f61 --- /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 00000000..05e1db67 --- /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 00000000..65f633d2 --- /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 <nirmal.ranganathan@rackspace.com> +.. moduleauthor:: Tim Simpson <tim.simpson@rackspace.com> +""" diff --git a/integration/tests/integration/tests/api/__init__.py b/integration/tests/integration/tests/api/__init__.py new file mode 100644 index 00000000..40d014dd --- /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 00000000..98c67aba --- /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 00000000..a21aadd7 --- /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 00000000..3a1c2de6 --- /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 00000000..965d8e77 --- /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 00000000..4dd797dc --- /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 00000000..e69de29b --- /dev/null +++ b/integration/tests/integration/tests/dns/__init__.py 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 00000000..61e5f638 --- /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 00000000..4fe460b0 --- /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 00000000..2af3b959 --- /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 00000000..3734ad54 --- /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 00000000..ddd5aa86 --- /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 00000000..e69de29b --- /dev/null +++ b/integration/tests/integration/tests/smoke/__init__.py diff --git a/integration/tests/integration/tests/smoke/instance.py b/integration/tests/integration/tests/smoke/instance.py new file mode 100644 index 00000000..3488e5f2 --- /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 00000000..671d3c17 --- /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 00000000..c3c1007c --- /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 00000000..d534ff31 --- /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 00000000..b54f92d6 --- /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/<id>/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/<id>/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 00000000..09c4cfed --- /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 00000000..221b2a41 --- /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 00000000..81051493 --- /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 00000000..b8972274 --- /dev/null +++ b/integration/xsd/common.ent @@ -0,0 +1,72 @@ + +<!ENTITY ndash "–"> +<!ENTITY mdash "—"> + +<!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'> +<!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'> +<!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'> +<!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'> + + <!-- + A collection of common faults, these are pretty much expected + in every request. + --> + <!ENTITY commonFaults + ' + <response status="400" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:badRequest"/> + <representation mediaType="application/json"/> + </response> + <response status="401" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:unauthorized"/> + <representation mediaType="application/json"/> + </response> + <response status="403" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:forbidden"/> + <representation mediaType="application/json"/> + </response> + <response status="405" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:badMethod"/> + <representation mediaType="application/json"/> + </response> + <response status="413" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:overLimit"/> + <representation mediaType="application/json"/> + </response> + <response status="422" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:unprocessableEntity"/> + <representation mediaType="application/json"/> + </response> + <response status="500" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:instanceFault"/> + <representation mediaType="application/json"/> + </response> + <response status="501" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:notImplemented"/> + <representation mediaType="application/json"/> + </response> + <response status="503" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:serviceUnavailable"/> + <representation mediaType="application/json"/> + </response> + '> + <!-- + Faults on GET + --> + <!ENTITY getFaults + ' + <response status="404" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:itemNotFound"/> + <representation mediaType="application/json"/> + </response> + '> + <!-- + Faults on POST/PUT + --> + <!ENTITY postPutFaults + ' + <response status="415" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:badMediaType"/> + <representation mediaType="application/json"/> + </response> + '> diff --git a/integration/xsd/dbaas.wadl b/integration/xsd/dbaas.wadl new file mode 100644 index 00000000..91953198 --- /dev/null +++ b/integration/xsd/dbaas.wadl @@ -0,0 +1,1177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE application [ + <!ENTITY % common SYSTEM "common.ent"> + %common; + + +]> +<application + xmlns="http://wadl.dev.java.net/2009/02" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" + xmlns:wadl="http://wadl.dev.java.net/2009/02" + xmlns:dbaas="http://docs.openstack.org/database/api/v1.0"> + + <grammars> + <include href="dbaas.xsd" /> + </grammars> + + <resources base="https://ord.databases.api.rackspacecloud.com"> + <resource id="versions" path=""> + <method href="#getVersions" /> + </resource> + <resource id="version" path="{version}"> + <method href="#getVersionInfo" /> + <resource id="accountID" path="{accountId}"> + <param name="accountId" style="template" + type="xsd:string"> + <doc>The account ID of the owner of the specified instance.</doc></param> + <resource id="instances" path="instances"> + <method href="#createInstance" /> + <method href="#getInstance" /> + + <resource id="instanceId" path="{instanceId}"> + <param name="instanceId" style="template" + type="xsd:string" > + <doc>The instance ID for the specified database instance.</doc></param> + <method href="#getInstanceById" /> + <method href="#deleteInstance" /> + <resource id="instanceAction" path="action"> + <method href="#resizeInstance" /> + <method href="#resizeVolume" /> + <method href="#restartInstance" /> + </resource> + <resource id="databases" path="databases"> + <method href="#createDatabase" /> + <method href="#getDatabases" /> + <resource id="databaseName" path="{databaseName}"> + <param name="databaseName" style="template" + type="xsd:string" > + <doc>The name for the specified database.</doc></param> + <method href="#deleteDatabase" /> + </resource> + </resource> + <resource id="users" path="users"> + <method href="#createUser" /> + <method href="#getUsers" /> + <resource id="userId" path="{name}"> + <param name="name" style="template" + type="xsd:string" > + <doc>The name for the specified user.</doc></param> + <method href="#deleteUser" /> + </resource> + </resource> + <resource id="root" path="root"> + <method href="#createRoot" /> + <method href="#isRootEnabled" /> + </resource> + </resource> + </resource> + <resource id="flavors" path="flavors"> + <method href="#getFlavors" /> + + <resource id="flavorId" path="{flavorId}"> + <param name="flavorId" style="template" type="xsd:string" > + <doc>The flavor ID for the specified flavor.</doc></param> + <method href="#getFlavorById" /> + </resource> + </resource> + </resource> + </resource> + </resources> + + <!--Token Methods... --> + <!-- Version --> + <method name="GET" id="getVersionInfo"> + <wadl:doc xml:lang="EN" title="List Version Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Returns detailed information about the specified version of the API. + </para> + <remark>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.</remark> + <remark>Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description.</remark> + <para>This operation returns detailed information about the specified version of the API.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Version Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-version-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-version-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Version Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-version-response.xml"/> + </doc> + </representation> + + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-version-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getVersions"> + <wadl:doc xml:lang="EN" title="List Versions" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Lists information about all versions of the API. + </para> + <para>This operation lists information about all versions of the API.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Versions requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-versions-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-versions-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Versions responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-versions-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-versions-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- Instance --> + <method name="POST" id="createInstance"> + <wadl:doc xml:lang="EN" title="Create Database Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Creates a new <glossterm>database instance</glossterm>.</para> + <para>This operation asynchronously provisions a new database instance. This call requires the user to specify a <glossterm>flavor</glossterm> and a <glossterm>volume</glossterm> 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.</para> + <note><title>Notes</title><itemizedlist> + <listitem> + <para>You can create only one database instance per <command>POST</command> request.</para> + </listitem> + <listitem> + <para>You can create a database instance with one or more databases, and users associated to those databases.</para> + </listitem> + <listitem> + <para>The default binding for the MySQL instance is port 3306.</para> + </listitem> + <listitem> + <para>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 + <xref linkend="Accessibility"/> for details.</para> + </listitem> + </itemizedlist></note> + <para>The following table lists the required and optional attributes for Create Instance:</para> + <table rules="all"> + <caption>Required and Optional Attributes for Create Instance</caption> + <thead> + <tr> + <td colspan="1">Applies To </td> + <td colspan="1">Name </td> + <td colspan="3">Description</td> + <td colspan="1">Required</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1" rowspan="3">Instance</td> + <td colspan="1">flavorRef</td> + <td colspan="3"><para>Reference (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".</para> + <note><para>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".</para></note> + <para>Refer to <xref linkend="GET_getFlavors__version___accountId__flavors_"/> for details.</para></td> + <td colspan="1">Yes</td> + </tr> + <tr> + <td colspan="1">(volume) size</td> + <td colspan="3">Specifies the volume size in gigabytes (GB). The value specified must be between 1 and 50.</td> + <td colspan="1">Yes</td> + </tr> + <tr> + <td colspan="1">name</td> + <td colspan="3">Name of the instance to create. The length of the name is limited to 255 characters and any characters are permitted.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1" rowspan="3">Database</td> + <td colspan="1">name</td> + <td colspan="3">Specifies <glossterm>database</glossterm> names for creating databases on instance creation. Refer to <xref linkend="POST_createDatabase__version___accountId__instances__instanceId__databases_"/> for the required xml/json format.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1">character_set</td> + <td colspan="3">Set of symbols and encodings. The default character set is <code>utf8</code>.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1">collate</td> + <td colspan="3">Set of rules for comparing characters in a character set. The default value for collate is <code>utf8_general_ci</code>.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1" rowspan="3">User</td> + <td colspan="1">name</td> + <td colspan="3">Specifies user name for the database on instance creation. Refer to <xref linkend="POST_createUser__version___accountId__instances__instanceId__users_"/> for the required xml/json format.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1">password</td> + <td colspan="3">Specifies password for those users on instance creation. Refer to <xref linkend="POST_createUser__version___accountId__instances__instanceId__users_"/> for the required xml/json format.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1">(database) name</td> + <td colspan="3">Specifies names of databases that those users can access on instance creation. Refer to <xref linkend="POST_createUser__version___accountId__instances__instanceId__users_"/> for the required xml/json format.</td> + <td colspan="1">No</td> + </tr> + </tbody> + </table> + + <para>Refer to <xref linkend="database_instance_status"/> for a list of possible database instance statuses that may be returned.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" + element="dbaas:DatabaseInstance"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Create Database Instance requests and responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-instance-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-instance-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:DatabaseInstance" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-instance-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-instance-response.json"/> + <para xmlns="http://docbook.org/ns/docbook">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 <code>self</code> link contains a <emphasis>versioned</emphasis> link to the resource. These + links should be used in cases where the link will be followed immediately. A <code>bookmark</code> + link provides a permanent link to a resource that is appropriate for long term storage.</para> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="DELETE" id="deleteInstance"> + <wadl:doc xml:lang="EN" title="Delete Database Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Deletes the specified database instance.</para> + <para>This operation deletes the specified database instance, including any associated data.</para> + <para>Refer to <xref linkend="database_instance_status"/> for a list of possible database instance statuses that may be returned.</para> + <note><para>This operation does not delete any read slaves.</para></note> + <remark>Reviewer: please provide a description of read slaves that I can add to the previous note.</remark> + <note><para>This operation is not allowed when the instance state is either <code>REBUILDING</code> or <code>BUILDING</code>.</para></note> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete Database Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-instance-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-instance-request.json"/> + </doc> + </representation> + </request> + <response status="202" /> + &commonFaults; + <response status="422" xmlns="http://wadl.dev.java.net/2009/02"> + <representation mediaType="application/xml" element="dbaas:unprocessableEntity"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete Database Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-instance-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-instance-response.json"/> + </doc> + </representation> + </response> + &getFaults; + </method> + + <method name="GET" id="getInstance"> + <wadl:doc xml:lang="EN" title="List All Database Instances" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists the status and information for all database instances.</para> + <para>This operation lists the status and information for all database instances.</para> + <para>Refer to <xref linkend="database_instance_status"/> for a list of possible database instance statuses that may be returned.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Database Instances Detail requests:</para> + + <xsdxt:code href="../apidocs/src/resources/samples/db-instances-index-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instances-index-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Database Instances responses:</para> + + <xsdxt:code href="../apidocs/src/resources/samples/db-instances-index-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instances-index-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + + <method name="GET" id="getInstanceById"> + <wadl:doc xml:lang="EN" title="List Database Instance Status and Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists status and details for a specified database instance.</para> + <para>This operation lists the status and details of the specified database instance.</para> + <para>This operation lists the volume size in gigabytes (GB) and the approximate GB used.</para> + <note><para>After instance creation, the <code>used</code> 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 <code>used</code> attribute is <emphasis>not</emphasis> returned + in the response when the status for the instance is BUILD, REBOOT, or RESIZE.</para></note> + <para>Refer to <xref linkend="database_instance_status"/> for a list of possible database instance statuses that may be returned.</para> + <para>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.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Database Instance Status and Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-status-detail-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-status-detail-request.xml"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:DatabaseInstance" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Database Instance Status and Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-status-detail-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:DatabaseInstance" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-status-detail-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- Instance Actions --> + <method name="POST" id="restartInstance"> + <wadl:doc xml:lang="EN" title="Restart Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Restart the database service on the instance.</para> + <para>The restart operation will restart only the MySQL Instance. Restarting MySQL will erase any dynamic configuration settings that you have made within MySQL.</para> + <note><para>The MySQL service will be unavailable until the instance restarts.</para></note> + <para>This operation returns a 202 Accepted response.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" element="dbaas:Restart"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Restart Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-restart-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Restart"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-restart-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Restart Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-restart-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-restart-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + &postPutFaults; + </method> + + <method name="POST" id="resizeInstance"> + <wadl:doc xml:lang="EN" title="Resize the Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Resize the memory of the instance.</para> + <para>This operation changes the memory size of the instance, assuming a valid flavorRef is provided. Restarts MySQL in the process.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" + element="dbaas:Resize"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Resize Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-instance-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Resize"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-instance-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Resize Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-instance-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-instance-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + &postPutFaults; + </method> + + <method name="POST" id="resizeVolume"> + <wadl:doc xml:lang="EN" title="Resize the Instance Volume" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Resize the <glossterm>volume</glossterm> attached to the Instance.</para> + <para>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.</para> + <note><para>You cannot increase the volume to a size larger than the API volume size limit specifies.</para></note> + <para>This operation returns a 202 Accepted response.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" + element="dbaas:Resize"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Resize Instance Volume requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-volume-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Resize"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-volume-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Resize Instance Volume responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-volume-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-resize-volume-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + &postPutFaults; + </method> + + <!-- Database --> + <method name="POST" id="createDatabase"> + <wadl:doc xml:lang="EN" title="Create Database" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Creates a new <glossterm>database</glossterm> within the specified instance.</para> + <para>This operation creates a new database within the specified instance.</para> + <para>The <code>name</code> of the database is a required attribute.</para> + <para>The following additional attributes can be specified for each database: <code>collate</code> and <code>character_set</code>.</para> + + <table rules="all"> + <caption>Required and Optional Attributes for Create Database</caption> + <thead> + <tr> + <td colspan="1">Name </td> + <td colspan="3">Description</td> + <td colspan="1">Required</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">name</td> + <td colspan="3">Specifies the database name for creating the database. Refer to the request examples for the required xml/json format.</td> + <td colspan="1">Yes</td> + </tr> + <tr> + <td colspan="1">character_set</td> + <td colspan="3">Set of symbols and encodings. The default character set is <code>utf8</code>.</td> + <td colspan="1">No</td> + </tr> + <tr> + <td colspan="1">collate</td> + <td colspan="3">Set of rules for comparing characters in a character set. The default value for collate is <code>utf8_general_ci</code>.</td> + <td colspan="1">No</td> + </tr> + </tbody> + </table> + + <para>See the MySQL documentation for information about supported character sets and collations at <link + xlink:href="http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html" + >http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html</link>.</para> + + + <note><para>The following database names are reserved and cannot be used for creating databases: lost+found, information_schema, and mysql.</para></note> + <para>Refer to the following tables for information about characters that are valid/invalid for creating database names.</para> + + <table rules="all" width="40%"> + <caption>Valid Characters That Can Be Used in a Database Name</caption> + <col width="100%"/> + <thead> + <tr> + <td>Character</td> + </tr> + </thead> + <tbody> + <tr> + <td>Letters (upper and lower cases allowed)</td> + </tr> + <tr> + <td>Numbers</td> + </tr> + <tr> + <td>'@', '?', '#', and spaces are allowed, but <emphasis>not</emphasis> at the beginning and end of the database name</td> + </tr> + <tr> + <td>'_' is allowed anywhere in the database name</td> + </tr> + </tbody> + </table> + + <table rules="all" width="40%"> + <?dbfo keep-together="always"?> + <caption>Characters That <emphasis>Cannot</emphasis> Be Used in a Database Name</caption> + <col width="100%"/> + <thead> + <tr> + <td>Character</td> + </tr> + </thead> + <tbody> + <tr> + <td>Single quotes</td> + </tr> + <tr> + <td>Double quotes</td> + </tr> + <tr> + <td>Back quotes</td> + </tr> + <tr> + <td>Semicolons</td> + </tr> + <tr> + <td>Commas</td> + </tr> + <tr> + <td>Backslashes</td> + </tr> + <tr> + <td>Forwardslashes</td> + </tr> + </tbody> + </table> + + <table rules="all"> + <caption>Length Restrictions for Database Name</caption> + <thead> + <tr> + <td>Restriction</td> + <td>Value</td> + </tr> + </thead> + <tbody> + <tr> + <td>Database-name maximum length</td> + <td>64</td> + </tr> + </tbody> + </table> + </wadl:doc> + <request> + <representation mediaType="application/xml" + element="dbaas:Database" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Create Database requests:<?rax-fo keep-with-next?></para> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-databases-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Database" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-databases-request.json"/> + </doc> + </representation> + </request> + <response status="202" > + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Create Database responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-databases-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-databases-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + + <method name="GET" id="getDatabases"> + <wadl:doc xml:lang="EN" title="List Databases for Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists databases for the specified instance.</para> + <para>This operation lists the databases for the specified instance.</para> + <note><para>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.</para></note> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Databases for Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-databases-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-databases-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:Databases" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Databases for Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-databases-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Databases" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-databases-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="DELETE" id="deleteDatabase"> + <wadl:doc xml:lang="EN" title="Delete Database" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Deletes the specified database.</para> + <para>This operation deletes the requested database within the specified database instance. Note that all data associated with the database is also deleted.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete Database requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-databases-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-databases-request.json"/> + </doc> + </representation> + </request> + <response status="202" > + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete Database responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-databases-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-databases-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- User --> + <method name="POST" id="createUser"> + <wadl:doc xml:lang="EN" title="Create User" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Creates a user for the specified database instance.</para> + <para>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.</para> + <remark>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).</remark> + <para>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.</para> + + <para>The following table lists the required attributes for Create User. Refer to the request examples for the required xml/json format:</para> + + <table rules="all" width="500"> + <caption>Required Attributes for Create User</caption> + + <thead> + <tr> + <td colspan="1">Applies To</td> + <td colspan="1">Name </td> + <td colspan="2">Description</td> + <td colspan="1">Required</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1" rowspan="3">User</td> + <td colspan="1">name</td> + <td colspan="2">Name of the user for the + database.</td> + <td colspan="1">Yes</td> + </tr> + <tr> + <td colspan="1">password</td> + <td colspan="2">User password for database + access.</td> + <td colspan="1">Yes</td> + </tr> + <tr> + <td colspan="1">(database) name</td> + <td colspan="2"><para>Name of the database that the user can access. One or more database names must be specified.</para></td> + <td colspan="1">No</td> + </tr> + </tbody> + </table> + + <note><title>Notes</title><itemizedlist> + <listitem> + <para>A user is granted all privileges on the specified databases.</para> + </listitem> + <listitem> + <para>The following user name is reserved and cannot be used for creating users: root.</para> + </listitem> + </itemizedlist></note> + + <para>Refer to the following tables for information about characters that are valid/invalid for creating database names, user names, and passwords.</para> + + <table rules="all" width="40%"> + <caption>Valid Characters That Can Be Used in a Database Name, User Name, and Password</caption> + <col width="100%"/> + <thead> + <tr> + <td>Character</td> + </tr> + </thead> + <tbody> + <tr> + <td>Letters (upper and lower cases allowed)</td> + </tr> + <tr> + <td>Numbers</td> + </tr> + <tr> + <td>'@', '?', '#', and spaces are allowed, but <emphasis>not</emphasis> at the beginning and end of the database name, user name, and password</td> + </tr> + <tr> + <td>"_" is allowed anywhere in the database name, user name, and password</td> + </tr> + </tbody> + </table> + + <table rules="all" width="40%"> + <caption>Characters That <emphasis>Cannot</emphasis> Be Used in a Database Name, User Name, and Password</caption> + <col width="100%"/> + <thead> + <tr> + <td>Character</td> + </tr> + </thead> + <tbody> + <tr> + <td>Single quotes</td> + </tr> + <tr> + <td>Double quotes</td> + </tr> + <tr> + <td>Back quotes</td> + </tr> + <tr> + <td>Semicolons</td> + </tr> + <tr> + <td>Commas</td> + </tr> + <tr> + <td>Backslashes</td> + </tr> + <tr> + <td>Forwardslashes</td> + </tr> + <tr> + <td>Spaces at the front or end of the user name or password</td> + </tr> + </tbody> + </table> + + <table rules="all"> + <caption>Length Restrictions for Database Name, User Name, and Password</caption> + <thead> + <tr> + <td>Restriction</td> + <td>Value</td> + </tr> + </thead> + <tbody> + <tr> + <td>Database name maximum length</td> + <td>64</td> + </tr> + <tr> + <td>User name maximum length</td> + <td>16</td> + </tr> + <tr> + <td>Password maximum length</td> + <td>unlimited (no restrictions)</td> + </tr> + </tbody> + </table> + </wadl:doc> + <request> + <representation mediaType="application/xml" + element="dbaas:Users" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Create User requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-users-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-users-request.json"/> + </doc> + </representation> + </request> + <response status="202" > + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Create User responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-users-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-create-users-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + + <method name="GET" id="getUsers"> + <wadl:doc xml:lang="EN" title="List Users in Database Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists the users in the specified database instance.</para> + <para>This operation lists the users in the specified database instance, along with the associated databases for that user. </para> + <note><para>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.</para></note> + + <para>The following notes apply to MySQL users:</para> + <itemizedlist spacing="compact"> + <listitem> + <para>User names can be up to 16 characters long.</para> + </listitem> + <listitem> + <para>When you create accounts with INSERT, you must use FLUSH PRIVILEGES to tell the server to reload the grant tables.</para> + </listitem> + <listitem> + <para>For additional information, refer to: <link + xlink:href="http://dev.mysql.com/doc/refman/5.1/en/user-account-management.html" + >http://dev.mysql.com/doc/refman/5.1/en/user-account-management.html</link></para> + </listitem> + </itemizedlist> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Users in Database Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-users-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-users-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:Users" > + <doc> + <?hard-pagebreak?> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Users in Database Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-users-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Users" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-list-users-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="DELETE" id="deleteUser"> + <wadl:doc xml:lang="EN" title="Delete User" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Deletes the user identified by {name} for the specified database instance.</para> + <para>This operation deletes the specified user for the specified database instance.</para> + <warning><para>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.</para></warning> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete User requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-users-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-users-request.json"/> + </doc> + </representation> + </request> + <response status="202" > + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Delete User responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-users-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-delete-users-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- Root --> + <method name="POST" id="createRoot"> + <wadl:doc xml:lang="EN" title="Enable Root User" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Enables the root user for the specified database instance and returns the root password.</para> + <para>This operation enables login from any host for the root user and provides the user with a generated root password.</para> + <note><para>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.</para></note> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Enable Root User requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-enable-root-user-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-enable-root-user-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:User"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Enable Root User responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-enable-root-user-response.xml"/> + </doc> + </representation> + + <representation mediaType="application/json" + element="dbaas:User"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-enable-root-user-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="isRootEnabled"> + <wadl:doc xml:lang="EN" title="List Root-Enabled Status" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Returns true if root user is enabled for the specified database instance or false otherwise.</para> + <para>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.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Check Root User Access requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-check-root-user-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-check-root-user-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:RootEnabled" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Check Root User Access responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-check-root-user-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:RootEnabled" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-check-root-user-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- Flavor --> + <method name="GET" id="getFlavors"> + <wadl:doc xml:lang="EN" title="List Flavors" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists information for all available <glossterm baseform="flavor">flavors</glossterm>.</para> + <para>This operation lists information for all available flavors.</para> + <para>This resource is identical to the flavors found in the OpenStack Nova API, but without the disk property.</para> + <remark>Reviewer: please check that the xml example below is now correct. Previously it was reported to be incorrect.</remark> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Flavors requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:Flavors" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Flavors responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Flavors" > + <doc> + <?hard-pagebreak?> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + + + <method name="GET" id="getFlavorById"> + <wadl:doc xml:lang="EN" title="List Flavor By ID" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Lists all flavor information about the specified flavor ID.</para> + <para>This operation lists all information for the specified flavor ID with details of the RAM.</para> + <para>This resource is identical to the flavors found in the OpenStack Nova API, but without the disk property.</para> + <note><para>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.</para></note> + <remark>Writer: need to confirm that this behavior is not changed in subsequent releases, and if it is prevented, remove the Note above.</remark> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Flavor By ID requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-by-id-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-by-id-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" + element="dbaas:Flavor" > + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Flavor By ID responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-by-id-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" + element="dbaas:Flavor" > + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-flavors-by-id-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + +</application> diff --git a/integration/xsd/dbaas.xsd b/integration/xsd/dbaas.xsd new file mode 100644 index 00000000..3e1876a5 --- /dev/null +++ b/integration/xsd/dbaas.xsd @@ -0,0 +1,613 @@ +<?xml version="1.0" encoding="UTF-8"?> +<schema elementFormDefault="qualified" + attributeFormDefault="unqualified" + targetNamespace="http://docs.openstack.org/database/api/v1.0" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns="http://www.w3.org/2001/XMLSchema" + xmlns:dbaas="http://docs.openstack.org/database/api/v1.0" + xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0"> + + <annotation> + <xsd:appinfo + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <xsdxt:title>DBaaS</xsdxt:title> + </xsd:appinfo> + <xsd:documentation + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <p> + This is the main index XML Schema document + for DBaaS API Schema Types Version 1.0. + </p> + </xsd:documentation> + </annotation> + + <element name="user" type="dbaas:User"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A MySQL User.</p> + </xsd:documentation> + </annotation> + </element> + <element name="users" type="dbaas:Users"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A MySQL User.</p> + </xsd:documentation> + </annotation> + </element> + <element name="instance" type="dbaas:instance"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A Database Instance.</p> + </xsd:documentation> + </annotation> + </element> + <element name="instances" type="dbaas:instances"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Database Instances.</p> + </xsd:documentation> + </annotation> + </element> + <element name="flavor" type="dbaas:Flavor"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Settings such as memory used to create a database instance.</p> + </xsd:documentation> + </annotation> + </element> + <element name="flavors" type="dbaas:Flavors"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of flavors.</p> + </xsd:documentation> + </annotation> + </element> + <element name="database" type="dbaas:Database"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A Database.</p> + </xsd:documentation> + </annotation> + </element> + <element name="databases" type="dbaas:Databases"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>List of Databases.</p> + </xsd:documentation> + </annotation> + </element> + <element name="restart"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Restart the Database</p> + </xsd:documentation> + </annotation> + </element> + <element name="resize" type="dbaas:Resize"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Resize an Instance</p> + </xsd:documentation> + </annotation> + </element> + + <!--Complex Types--> + <complexType name="Users"> + <sequence> + <element name="users" type="dbaas:User" minOccurs="1" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of database user names.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="User"> + <sequence> + <element name="databases" type="dbaas:Database" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of databases.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name for the user.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="password" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The password for the user.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="status" type="dbaas:UserStatusType" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The status of the user.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="instances"> + <sequence> + <element name="instance" type="dbaas:instance" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of database instances.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="instance"> + <sequence> + <xsd:element name="databases" type="dbaas:Database" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of optional databases.</p> + </xsd:documentation> + </annotation> + </xsd:element> + <xsd:element name="users" type="dbaas:User" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of optional users.</p> + </xsd:documentation> + </annotation> + </xsd:element> + <xsd:element name="links" type="dbaas:Links" minOccurs="0" maxOccurs="1"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The links for the type of instance.</p> + </xsd:documentation> + </annotation> + </xsd:element> + <xsd:element name="volume" type="dbaas:Volume" minOccurs="0" maxOccurs="1"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The volume attached to the instance.</p> + </xsd:documentation> + </annotation> + </xsd:element> + </sequence> + <attribute name="id" type="xsd:ID" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A unique database instance id.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="created" type="xsd:dateTime" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Date/Time the instance was created.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="updated" type="xsd:dateTime" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Date/Time the instance was last updated.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="flavorRef" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The flavor reference of the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="status" type="dbaas:StatusType" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The status of the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="hostname" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The hostname attached to the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="rootEnabled" type="xsd:boolean" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Whether or not root is enabled for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Link"> + <attribute name="rel" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The type of link.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="href" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The URL.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="type" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The type of link.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Flavors"> + <sequence> + <element name="Flavors" type="dbaas:Flavor" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of flavors.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="Flavor"> + <sequence> + <element name="links" type="dbaas:Link" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of links.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + <attribute name="id" type="xsd:ID" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A unique flavor id.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="ram" type="xsd:integer" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The RAM in megabytes.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <xsd:complexType name="Links"> + <xsd:sequence> + <xsd:element name="link" type="dbaas:Link" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of links.</p> + </xsd:documentation> + </annotation> + </xsd:element> + </xsd:sequence> + </xsd:complexType> + + <complexType name="Databases"> + <sequence> + <element name="database" type="dbaas:Database" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of databases.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="Database"> + <attribute name="character_set" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The Database character set.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="collate" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The Collation type of the database.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="RootEnabled"> + <attribute name="rootEnabled" type="xsd:boolean" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Whether or not root is enabled for the given instance.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Volume"> + <attribute name="size" type="xsd:integer" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Size of the volume in GBs.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="used" type="xsd:float" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Used space on the attached volume in GBs.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="FlavorRef"> + <attribute name="flavorRef" type="xsd:ID" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A unique flavor id.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Resize"> + <sequence> + <xsd:element name="volume" type="dbaas:Volume" minOccurs="0" maxOccurs="1"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The volume attached to the instance.</p> + </xsd:documentation> + </annotation> + </xsd:element> + <xsd:element name="flavorRef" type="dbaas:FlavorRef" minOccurs="0" maxOccurs="1"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>New flavorRef to size the instance.</p> + </xsd:documentation> + </annotation> + </xsd:element> + </sequence> + </complexType> + + <!--Simple Types--> + <simpleType name="StatusType"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The instance status.</p> + </xsd:documentation> + </annotation> + <restriction base="xsd:string"> + <enumeration value="ACTIVE"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Healthy status.</p> + </xsd:documentation> + </annotation> + </enumeration> + <enumeration value="SHUTDOWN"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Down status.</p> + </xsd:documentation> + </annotation> + </enumeration> + <enumeration value="BUILD"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>unavailable status.</p> + </xsd:documentation> + </annotation> + </enumeration> + </restriction> + </simpleType> + + <simpleType name="UserStatusType"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The mysql user status.</p> + </xsd:documentation> + </annotation> + <restriction base="xsd:string"> + <enumeration value="ENABLED"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Enabled status.</p> + </xsd:documentation> + </annotation> + </enumeration> + </restriction> + </simpleType> + + <!-- Fault Elements --> + <element name="badRequest" type="dbaas:BadRequestFault"/> + <element name="unauthorized" type="dbaas:UnauthorizedFault"/> + <element name="forbidden" type="dbaas:ForbiddenFault"/> + <element name="badMethod" type="dbaas:BadMethodFault"/> + <element name="overLimit" type="dbaas:OverLimitFault"/> + <element name="unprocessableEntity" type="dbaas:UnprocessableEntityFault"/> + <element name="instanceFault" type="dbaas:InstanceFault" /> + <element name="notImplemented" type="dbaas:NotImplementedFault" /> + <element name="serviceUnavailable" type="dbaas:ServiceUnavailableFault"/> + <element name="itemNotFound" type="dbaas:ItemNotFoundFault"/> + <element name="badMediaType" type="dbaas:BadMediaTypeFault"/> + + <!-- Fault Types --> + <complexType name="BaseFault"> + <sequence> + <element name="message" type="xsd:string"> + <annotation> + <xsd:documentation + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <p> + A human readable message that is appropriate for display + to the end user. + </p> + </xsd:documentation> + </annotation> + </element> + <element name="details" type="xsd:string" minOccurs="0"> + <annotation> + <xsd:documentation + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <p> + 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. + </p> + </xsd:documentation> + </annotation> + </element> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> + </sequence> + <attribute name="code" type="xsd:int" use="required"> + <annotation> + <xsd:documentation + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <p> + The HTTP status code associated with the current fault. + </p> + </xsd:documentation> + </annotation> + </attribute> + <anyAttribute namespace="##other" processContents="lax"/> + </complexType> + + <complexType name="BadRequestFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="UnauthorizedFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="ForbiddenFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="BadMethodFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="OverLimitFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + <attribute name="retryAt" type="xsd:dateTime" use="optional"> + <annotation> + <xsd:documentation + xml:lang="EN" + xmlns="http://www.w3.org/1999/xhtml"> + <p> + An optional dateTime denoting when an operation should + be retried. + </p> + </xsd:documentation> + </annotation> + </attribute> + </extension> + </complexContent> + </complexType> + + <complexType name="UnprocessableEntityFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="InstanceFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="NotImplementedFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="ServiceUnavailableFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="ItemNotFoundFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + + <complexType name="BadMediaTypeFault"> + <complexContent> + <extension base="dbaas:BaseFault"> + </extension> + </complexContent> + </complexType> + +</schema> diff --git a/integration/xsd/management.wadl b/integration/xsd/management.wadl new file mode 100644 index 00000000..22712192 --- /dev/null +++ b/integration/xsd/management.wadl @@ -0,0 +1,625 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE application [ + <!ENTITY % common SYSTEM "common.ent"> + %common; +]> + +<application + xmlns="http://wadl.dev.java.net/2009/02" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" + xmlns:wadl="http://wadl.dev.java.net/2009/02" + xmlns:dbaas="http://docs.openstack.org/database/api/v1.0"> + + <grammars> + <include href="dbaas.xsd"/> + <include href="management.xsd"/> + </grammars> + <resources base="https://localhost:8775"> + <resource id="mgmt" path="mgmt"> + <resource id="hosts" path="hosts"> + <method href="#gethosts"/> + <resource id="hostid" path="{hostId}"> + <param name="hostId" style="template" type="xsd:string"> + <doc>The host ID for the specified host.</doc></param> + <method href="#gethostbyid"/> + <resource id="instances-hostId" path="instances"> + <resource id="instanceAction-hostId" path="action"> + <method href="#updateHostInstances" /> + </resource> + </resource> + </resource> + </resource> + <resource id="instances" path="instances"> + <method href="#getIndex"/> + <resource id="instanceId" path="{instanceId}"> + <param name="instanceId" style="template" type="xsd:string"> + <doc>The instance ID for the specified database instance.</doc></param> + <method href="#showinstances"/> + <resource id="rootdetails" path="root"> + <method href="#getrootdetails"/> + </resource> + <resource id="diagnostics" path="diagnostics"> + <method href="#getdiagnosticdetails"/> + </resource> + <resource id="instanceAction" path="action"> + <method href="#rebootInstance" /> + </resource> + <resource id="hwInfo" path="hwinfo"> + <method href="#getHwInfo" /> + </resource> + </resource> + </resource> + <resource id="storage" path="storage"> + <method href="#indexstorage"/> + </resource> + <resource id="accounts" path="accounts"> + <method href="#getaccounts"/> + <resource id="accountid" path="{accountId}"> + <param name="accountId" style="template" type="xsd:string"> + <doc>The account ID for the specified account.</doc> + </param> + <method href="#getaccountbyid"/> + </resource> + </resource> + </resource> + </resources> + + <!--Methods--> + <method name="GET" id="getaccounts"> + <wadl:doc xml:lang="EN" title="List All Active Accounts" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Lists all of the accounts owning at least one instance that is not deleted. + </para> + <para>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.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Active Accounts requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-accounts-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-accounts-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:Hosts"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Active Accounts responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-accounts-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Hosts"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-accounts-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="gethosts"> + <wadl:doc xml:lang="EN" title="List All Compute Hosts" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Lists all of the compute hosts. + </para> + <remark>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.</remark> + <remark>Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description.</remark> + <para>This operation returns a list of all the hosts from the database that are running the binary 'nova-compute'.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Compute Hosts requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-hosts-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-hosts-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:Hosts"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Compute Hosts responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-hosts-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Hosts"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-list-hosts-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="gethostbyid"> + <wadl:doc xml:lang="EN" title="List All Instances for a Host" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Lists all of the instances for the specified host. + </para> + <remark>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.</remark> + <remark>Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description.</remark> + <para>This operation returns the following information:</para> + <itemizedlist spacing="compact"> + <listitem> + <para>host name</para> + </listitem> + <listitem> + <para>percent of RAM used</para> + </listitem> + <listitem> + <para>total RAM on host</para> + </listitem> + <listitem> + <para>used RAM on host</para> + </listitem> + <listitem> + <para>a list of the compute instances running on the given host</para> + </listitem> + </itemizedlist> + <para>If the host name does not exist, a 404 not found error is returned.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Instances for a Host requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-host-detail-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-host-detail-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:Host"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Instances for a Host responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-host-detail-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Host"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-host-detail-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getIndex"> + <wadl:doc xml:lang="EN" title="List Management Index of Instances" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Lists all of the database instances, optionally filtered by deleted status. + </para> + </wadl:doc> + <request> + <param name="deleted" style="query" type="xsd:boolean"> + <wadl:doc xmlns="http://docbook.org/ns/docbook"> + <para>This query parameter specifies whether or not to list deleted instances. + If <code>true</code>, deleted instances are listed. If <code>false</code>, deleted instances are not listed. + </para> + </wadl:doc> + </param> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Instances requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-index-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-index-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:ManagementIndexInstances"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List All Instances response:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-index-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-index-response.json"/> + <para xmlns="http://docbook.org/ns/docbook">By default, all instances (both deleted and not deleted) are displayed. + Use the <code>deleted</code> query parameter to list only the deleted or not deleted instances.</para> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="showinstances"> + <wadl:doc xml:lang="EN" title="List Database Instance Status and Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Returns detailed information for the specified Database Instance. + </para> + <remark>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.</remark> + <remark>Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description.</remark> + <para>This operation returns detailed information about the status and details for the specified database instance.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Database Instance Status and Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-instance-details-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-instance-details-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="ManagementInstances"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Database Instance Status and Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-instance-details-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="ManagementInstances"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-instance-details-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <!-- Instance Actions --> + <method name="POST" id="rebootInstance"> + <wadl:doc xml:lang="EN" title="Reboot the Instance" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Perform a reboot of the instance.</para> + <para>This operation will reboot the underlying OS along with the mysql instance.</para> + <para>This operation returns a 202 Accepted response.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" element="dbaas:Reboot"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Reboot Instance requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-reboot-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Reboot"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-reboot-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Reboot Instance responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-reboot-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-instance-reboot-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + &postPutFaults; + </method> + + <method name="POST" id="updateHostInstances"> + <wadl:doc xml:lang="EN" title="Update All Instances on Host" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc">Update all instances on a specified host.</para> + <para>This operation will call on all guest agents in a specified host to install new versions of itself.</para> + <para>This operation returns a 202 Accepted response.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml" element="dbaas:Update"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Update all Instances on Host requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-host-update-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Update"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-host-update-request.json"/> + </doc> + </representation> + </request> + <response status="202"> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Update all Instances on Host responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-host-update-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-host-update-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + &postPutFaults; + </method> + + <method name="GET" id="indexstorage"> + <wadl:doc xml:lang="EN" title="List Storage Device Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Returns detailed information for the storage device. + </para> + <remark>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.</remark> + <remark>Reviewer: These tables probably need 4 columns: name; parameter type: e.g. template, query, etc.; data type: string, etc.; required?; description.</remark> + <para>This operation returns detailed information about the details for the storage device.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Storage Device Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-storage-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-storage-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:Devices"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Storage Device Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-storage-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Devices"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-storage-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getaccountbyid"> + <wadl:doc xml:lang="EN" title="List Account Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Returns detailed information for the specified account. + </para> + <remark>Reviewer: This section needs examples.</remark> + <para>This operation returns: </para> + <itemizedlist spacing="compact"> + <listitem> + <para>account name</para> + </listitem> + <listitem> + <para>id</para> + </listitem> + <listitem> + <para>list of hosts that have the id</para> + </listitem> + <listitem> + <para>instance name</para> + </listitem> + <listitem> + <para>status of each instance on the hosts</para> + </listitem> + </itemizedlist> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Account Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-account-details-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-account-details-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:Account"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the List Account Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-account-details-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:Account"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-account-details-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getrootdetails"> + <wadl:doc xml:lang="EN" title="Get Root Details" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Gets the root flag details for the specified instance. + </para> + <remark>Reviewer: Need the description for the summary table above and the detailed description for the next paragraph.</remark> + <para>This operation shows the root flag details for the specified instance.</para> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Root Details requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-root-details-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-root-details-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:RootDetails"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Root Details responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-root-details-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json" element="dbaas:RootDetails"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-get-root-details-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getdiagnosticdetails"> + <wadl:doc xml:lang="EN" title="Get Diagnostics Info" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Get the instance agent and other instance diagnostics info. + </para> + <para>This operation provides the following info for the guest agent managing the specified instance.</para> + <table rules="all"> + <caption>Return Attributes for Get Diagnostics Info</caption> + <thead> + <tr> + <td colspan="1">Name</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">version</td> + <td colspan="3">The version of the guest installed on the instance.</td> + </tr> + <tr> + <td colspan="1">fdSize</td> + <td colspan="3">Number of file descriptor slots currently allocated.</td> + </tr> + <tr> + <td colspan="1">vmSize</td> + <td colspan="3">Virtual memory size.</td> + </tr> + <tr> + <td colspan="1">vmPeak</td> + <td colspan="3">Peak virtual memory size.</td> + </tr> + <tr> + <td colspan="1">vmRss</td> + <td colspan="3">Resident set size.</td> + </tr> + <tr> + <td colspan="1">vmHwm</td> + <td colspan="3">Peak resident set size ("high water mark").</td> + </tr> + <tr> + <td colspan="1">threads</td> + <td colspan="3">Number of threads in process containing this thread.</td> + </tr> + </tbody> + </table> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Diagnostics Info requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-diagnostics-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:DiagnosticsDetails"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Diagnostics Info requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-diagnostics-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> + + <method name="GET" id="getHwInfo"> + <wadl:doc xml:lang="EN" title="Get Hardware Info" xmlns="http://docbook.org/ns/docbook"> + <para role="shortdesc"> + Get the instance hardware info from the guest agent. + </para> + <para>This operation provides the following info from the guest agent managing the specified instance.</para> + <table rules="all"> + <caption>Return Attributes for Get Hardware Info</caption> + <thead> + <tr> + <td colspan="1">Name</td> + <td colspan="3">Description</td> + </tr> + </thead> + <tbody> + <tr> + <td colspan="1">mem_total</td> + <td colspan="3">Total memory an instance sees.</td> + </tr> + <tr> + <td colspan="1">num_cpus</td> + <td colspan="3">Number of CPUs an instance sees.</td> + </tr> + </tbody> + </table> + </wadl:doc> + <request> + <representation mediaType="application/xml"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Hardware Info requests:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-hwinfo-request.json"/> + </doc> + </representation> + </request> + <response status="200"> + <representation mediaType="application/xml" element="dbaas:DiagnosticsDetails"> + <doc> + <para xmlns="http://docbook.org/ns/docbook">The following examples show the Get Hardware Info responses:</para> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.xml"/> + </doc> + </representation> + <representation mediaType="application/json"> + <doc> + <xsdxt:code href="../apidocs/src/resources/samples/db-mgmt-instance-hwinfo-response.json"/> + </doc> + </representation> + </response> + &commonFaults; + &getFaults; + </method> +</application> diff --git a/integration/xsd/management.xsd b/integration/xsd/management.xsd new file mode 100644 index 00000000..4b69123f --- /dev/null +++ b/integration/xsd/management.xsd @@ -0,0 +1,398 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--(C) 2010 Rackspace Hosting, All Rights Reserved--> +<schema elementFormDefault="qualified" + attributeFormDefault="unqualified" + targetNamespace="http://docs.openstack.org/database/api/v1.0" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns="http://www.w3.org/2001/XMLSchema" + xmlns:dbaas="http://docs.openstack.org/database/api/v1.0" + xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0"> + <xsd:import schemaLocation="./dbaas.xsd" + id="dbaas" + namespace="http://docs.openstack.org/database/api/v1.0" /> + + <annotation> + <xsd:appinfo xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <xsdxt:title>Dbaas Management</xsdxt:title> + </xsd:appinfo> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>This schema defines entity contains entities related to the dbaas Management API.</p> + </xsd:documentation> + </annotation> + + <element name="host" type="dbaas:Host"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Information about a Host.</p> + </xsd:documentation> + </annotation> + </element> + <element name="hosts" type="dbaas:Hosts"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>List of Hosts.</p> + </xsd:documentation> + </annotation> + </element> + + <element name="devices" type="dbaas:Devices"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Information about storage devices.</p> + </xsd:documentation> + </annotation> + </element> + + <element name="account" type="dbaas:Account"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Information about Account.</p> + </xsd:documentation> + </annotation> + </element> + <element name="config" type="dbaas:Config"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Details of a configuration item</p> + </xsd:documentation> + </annotation> + </element> + <element name="configs" type="dbaas:Configs"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>List of Configs.</p> + </xsd:documentation> + </annotation> + </element> + + <element name="rootdetails" type="dbaas:RootDetails"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Details of a configuration item</p> + </xsd:documentation> + </annotation> + </element> + + <element name="diagnosticsDetails" type="dbaas:DiagnosticsDetails"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Diagnostic details of a guest.</p> + </xsd:documentation> + </annotation> + </element> + + <element name="reboot"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Reboot an Instance</p> + </xsd:documentation> + </annotation> + </element> + + <!--Complex Types--> + <complexType name="Hosts"> + <sequence> + <element name="host" type="dbaas:Host" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of Host names.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="ManagementInstance"> + <extension base="dbaas:instance"> + <attribute name="root_enabled_at" type="xsd:dateTime"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Timestamp of the first time MySQL root was enabled for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="root_enabled_by" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>ID of the first user to enable MySQL root for the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + </extension> + </complexType> + + <complexType name="ManagementInstances"> + <sequence> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of database instances, with additional management information.</p> + </xsd:documentation> + </annotation> + <element name="instance" type="dbaas:ManagementInstance" minOccurs="0" maxOccurs="unbounded"> + </element> + </sequence> + </complexType> + + <complexType name="ManagementInstanceIP"> + <attribute name="address" type="xsd:string"/> + <attribute name="virtual_interface_id" type="xsd:id"/> + </complexType> + + <complexType name="ManagementInstanceIPs"> + <sequence> + <element name="ip" type="dbaas:ManagementInstanceIP" minOccurs="0" maxOccurs="unbounded" /> + </sequence> + </complexType> + + <complexType name="ManagementIndexInstance"> + <sequence> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A single database instance, with some additional management information.</p> + </xsd:documentation> + <atrribute name="account_id" type="xsd:string"/> + <attribute name="created_at" type="xsd:string"/> + <attribute name="deleted" type="xsd:boolean"/> + <attribute name="deleted_at" type="xsd:string"/> + <attribute name="flavorid" type="xsd:integer"/> + <attribute name="host" type="xsd:string"/> + <attribute name="id" type="xsd:string"/> + <attribute name="state" type="xsd:string"/> + <element name="ips" type="dbaas:ManagementInstanceIPs"/> + </annotation> + </sequence> + </complexType> + + <complexType name="ManagementIndexInstances"> + <sequence> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of database index instances, with some additional management information.</p> + </xsd:documentation> + <element name="instance" type="dbaas:ManagementIndexInstance" minOccurs="0" maxOccurs="unbounded"> + </element> + </annotation> + </sequence> + </complexType> + + <complexType name="Host"> + <sequence> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of database instances on the Host.</p> + </xsd:documentation> + </annotation> + <element name="instance" type="dbaas:ManagementInstance" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name of the Host.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="percentUsed" type="xsd:float" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The percent of the used RAM on the Host.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="totalRAM" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The total amount of RAM on the Host.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="usedRAM" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The used amount of RAM on the Host.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Devices"> + <sequence> + <element name="device" type="dbaas:Device" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A storage device on the Host.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="Device"> + <attribute name="id" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The id of the storage device.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="name" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name of the storage device.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="type" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The type of the storage device.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="availablesize" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The available size of the storage device.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="totalsize" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The total size of the storage device.</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <complexType name="Account"> + <attribute name="name" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The name of the account.</p> + </xsd:documentation> + </annotation> + </attribute> + <sequence> + <element name="hosts" type="dbaas:Hosts" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>List of hosts that the Account has instances running on.</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="Configs"> + <sequence> + <element name="config" type="dbaas:Config" minOccurs="0" maxOccurs="unbounded"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>A configuration entry</p> + </xsd:documentation> + </annotation> + </element> + </sequence> + </complexType> + + <complexType name="Config"> + <attribute name="key" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The configuration entry key</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="value" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The value of the configuration entry</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="description" type="xsd:string" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The description of the configuration entry</p> + </xsd:documentation> + </annotation> + </attribute> + </complexType> + + <xsd:complexType name="RootDetails"> + <attribute name="id" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The instance id of that is checked by the call.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="root_enabled_at" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The date timestamp that a user enabled root on the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="root_enabled_by" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The user that enabled root on the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + </xsd:complexType> + + <xsd:complexType name="DiagnosticsDetails"> + <attribute name="version" type="xsd:string" use="required"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>The version of the guest installed on the instance.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="fdSize" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Number of file descriptor slots currently allocated.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="vmSize" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Virtual memory size.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="vmPeak" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Peak virtual memory size.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="vmRss" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Resident set size.</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="vmHwm" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Peak resident set size ("high water mark").</p> + </xsd:documentation> + </annotation> + </attribute> + <attribute name="threads" type="xsd:integer" use="optional"> + <annotation> + <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> + <p>Number of threads in process containing this thread.</p> + </xsd:documentation> + </annotation> + </attribute> + </xsd:complexType> + +</schema> |